How to develop multi-language framework with objective-c and swift - objective-c

Well, I want to know how to move some parts of framework from objective-c to swift without big pain
Ok, as far as I know:
1) Any project not a framework.
You make Swift-Bridging-Header.h or CatsAndGuns-Bridging-Header.h and put in it many import items.
You assure yourself in Build Settings -> (Search Swift) -> Objective-C Bridging Header. And put here your name.
Also check Packaging -> Defines Modules -> Yes
2) Framework
Well, you want to develop framework with mix objective-c and swift together.
And here you have pain: you can't use bridging headers in framework targets.
So, you come to google or documentation and start to read. Documentation
Ok, now you feel free and gorgeous about your knowledge, but.. you still don't know how to do it.
Create umbrella header and name it: YourProjectName-Swift.h. CatsAndGuns-Swift.h, ok.
Make it visible to framework level, so, navigate to right bar and put target membership -> your_framework_target (CatsAndGuns.framework in my case, of course) -> Public
And everything should be ok? remember to put every import as: #import <CatsAndGuns/Rocket.h>
Ok, let's check Rocket.h file in our framework. it is a Project-visible file, so, can it be accessed via in umbrella header (oh, module-swift.h) or not?
Now I have:
No bridging header in project ( as i understand from answer )
Manually created umbrella header with project-visible Rocket.h: #import <CatsAndGuns/Rocket.h>
errors in swift class that it can't see objective-c declarations.
Product module name without spaces.

Related

Objective C : having name collision while adding a framework in my app via cocoapod

Objective C: I have added a framework in my iOS app via Cocoapod. This framework has few interfaces, file names(.h .m) and xib files with same name as I have used in my app(i.e. name collision). I am able to build my app successfully(i.e. no name collision error) but I am facing runtime issues like framework's xib file (nib) is linking to my app's .h and .m file.
I think app's class name is having priority over frameworks class.
How should I deal with this problem?
There is no easy way around this. When CocoaPods are used to build a static library, all of the classes and symbols are concatenated into the main binary during the linking process.
This is why Apple recommends that all SDKs and projects use a class prefix of at least 3 characters (like ABCMyClass) and Apple reserves all two letter prefixes for themselves. Many people do not follow these rules, and it can cause this exact issue.
The only way for you to move forward is:
Rename you class, add a prefix (or different prefix)
Change the Cocoapods dependency via pull request.

What is the use of bridging header? Should we avoid using bridging header?

What is the use of bridging header?
Is it just for using Objective-C and Swift code in the same project?
Should we avoid using bridging header?
Say, if there are two third party library which are very similar; one of them is in Objective-C and other is in Swift. Should we use the Swift library or use Objective-C library. Are there any downside of using bridging headers?
Apple has written a great book that covers this in depth. It can be found here:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
I will quote it to answer your questions:
"What is the use of bridging header?
Is it just for using Objective-C and Swift code in the same project?"
To import a set of Objective-C files in the same app target as your Swift code, you rely on an Objective-C bridging header to expose those files to Swift. Xcode offers to create this header file when you add a Swift file to an existing Objective-C app, or an Objective-C file to an existing Swift app.
The answer to this question is yes. It is just there to make Swift and Objective-C work together in the same project.
"Should we avoid using bridging header? Say, if there are two third party library which are very similar; one of them is in Objective-C and other is in Swift. Should we use the Swift library or use Objective-C library. Are there any downside of using bridging headers?"
There are always tradeoffs. The first answer to this is no you should not avoid using a bridging header; however, as far as third party libraries you have to look at many factors. Which one has more functionality? Is it being maintained and/or added to frequently?
Using an Objective-C library will also add things to be aware of and work around. From the book:
Troubleshooting Tips and Reminders
Treat your Swift and Objective-C files as the same collection of code, and watch out for naming collisions.
If you’re working with frameworks, make sure the Defines Module (DEFINES_MODULE) build setting under Packaging is set to “Yes".
If you’re working with the Objective-C bridging header, make sure the Objective-C Bridging Header (SWIFT_OBJC_BRIDGING_HEADER) build setting under Swift Compiler - Code Generation is set to a path to the bridging header file relative to your project (for example, “MyApp/MyApp-Bridging-Header.h").
Xcode uses your product module name (PRODUCT_MODULE_NAME)—not your target name (TARGET_NAME)—when naming the Objective-C bridging header and the generated header for your Swift code. For information on product module naming, see Naming Your Product Module.
To be accessible and usable in Objective-C, a Swift class must be a descendant of an Objective-C class or it must be marked #objc.
When you bring Swift code into Objective-C, remember that Objective-C won’t be able to translate certain features that are specific to Swift. For a list, see Using Swift from Objective-C.
If you use your own Objective-C types in your Swift code, make sure to import the Objective-C headers for those types before importing the Swift generated header into the Objective-C .m file you want to use your Swift code from.
Swift declarations marked with the private modifier do not appear in the generated header. Private declarations are not exposed to Objective-C unless they are explicitly marked with #IBAction, #IBOutlet, or #objc as well.
For app targets, declarations marked with the internal modifier appear in the generated header if the app target has an Objective-C bridging header.
For framework targets, only declarations with the public modifier appear in the generated header. You can still use Swift methods and properties that are marked with the internal modifier from within the Objective-C part of your framework, as long they are declared within a class that inherits from an Objective-C class. For more information on access-level modifiers, see Access Control in The Swift Programming Language (Swift 2.2).
Que : What is the use of bridging header?
Its correct to say, Bridging header allows user to use Objective-C classes/files in their swift code in same project.
A Swift bridging header allows you to communicate with your old Objective-C classes from your Swift classes. You will need one if you plan to keep portions of your codebase in Objective-C. It should be noted that even if you decide to convert all of your code to Swift, some classes or libraries you may use such as SVProgressHUD haven’t been rewritten in Swift and you will need to use a bridging header to use them.
Que : Should we avoid using bridging header?
Considering your question there are 2 possible cases.
case 1 : Lets say your project is developed in Objective-C and now you are developing new features using swift in it, in this case you have to have BridgingHeader as you need access of your Objective-C classes in swift code.
case 2 : If your project is developed in swift then there is no need to have Bridging header, as well if its in only Objective-C and you are not planning to move it in swift then also you don't need it.
Read more about Using swift with cocoa and Objective-C in apple documentation.
Following apple document image indicates usage of Bridging header
No, there are no downsides to using Obj-c code in your Swift project. Bridging header only exposes your Obj-c files to Swift. The two languages can coexist in the same project with no problems, as you can expose your Swift code to the Obj-c just as easily too - xCode will generate a header for all of your public Swift declarations. Although everything is possible, if you start a new project you should stick to one language so the project is easier to understand. For example if you decide on Swift you should only use Obj-c for libraries that are not available in Swift.
The bridging header allows the use of Swift and Objective-C in the same project. There are no downsides to having a bridging header in your project as the two languages can work well together within the same app.
Removing a bridging header from a project after it has been added may cause errors, as it is referenced in other places in the project when it is created.
If you only intend to use one of the two languages, a bridging header is unnecessary. On the other hand, if you are using both Swift and Objective-C, a bridging header is required and will not cause any issues.
Here is a link to find more information on the subject:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
I hope that answered your question. Good luck with your project!
What is the use of bridging header?
You have already got that answer. You're right.
Should we avoid using bridging header?
No. Its good when a third party library developed in Obj-C and may not available in Swift yet. You should use bridging header to have a best library for your app.
It depends which on you choose. In case of networking? If your project is in Obj-C based you can use AFNetworking or with the case of Swift you can use AlamoFire, you can still use AFNetworking in Swift but its not suggestable.
Bridging headers are a great way to get Objective - C code into your Swift project. If you have two libraries, one that is in Swift and one that is in Objective - C, choose the one that will offer more functionality in your app. If they offer the same functionality, I'd just go with the Swift library -> My reasoning: if the Objective-C library isn't widely used and there aren't many tutorials on how to convert the Objective - C code into Swift, it can be very time consuming to figure it out on your own. If you use a Swift library, the code is already formatted in the correct language, saving you time and potentially money (depending on if this is a hobby for you or not). As far as any downsides to using a bridging header, their really isn't! With so many libraries written in Objective-C, you almost need a bridging header in your app. Take, for example, Reachability (Here is a video on implementation in Swift). This is a library that Apple created to handle network interruptions in your app. This is a great tool for developers and requires a bridging header. Here is a great YouTube video on how to use a bridging header, but if you add a header file into your Swift file, Xcode typically asks to crete one for you. Hope this helps!

Public and Private umbrella header in Objective C

I have this code base which is Objective C and Swift mix. Some places Swift uses Objective and and vice versa. I need to create a framework now based of off this codebase but do not want to include all the objective c files in my umbrella header. Here's my problem:
Inside my framework I still need to be able to use swift from objc and vice versa; but do not want to expose all those objc files that are being used internally by swift classes. Bridging header is not allowed in frameworks so all the headers needed by swift need to go in umbrella header.
I'm wondering if it's possible to have all the objc headers needed by internal swift code go in a file which would be my private umbrella header and all the files that I need to expose would go in public umbrella header.
Any suggestions?
I am successfully using explicitly declared modules as a solution of sorts for this issue for the Objective-C -> Swift case. I have not separated the module declaration into a separate private module map but declared both the framework module and an explicit module inside the same modulemap because of the concern raised in one of the comments to the question (I wasn't sure if or how it is possible to use the header generated by the private module map inside the same framework).
Here's an excerpt of the modulemap I have defined for my MPFoundation.framework, which includes an explicit module MPManuscriptCompiler_Protected that imports the header "MPManuscriptCompiler+Protected.h" which is not included in the umbrella header for the framework:
framework module MPFoundation {
umbrella header "MPFoundation.h"
export *
module * { export * }
explicit module MPManuscriptCompiler_Protected {
header "MPManuscriptCompiler+Protected.h"
export *
}
}
I then use this explicit module MPManuscriptCompiler_Protected in my Swift subclass which is present in the same framework like so:
import MPFoundation.MPManuscriptCompiler_Protected
My solution is really technically just a workaround though: for this to work, "MPManuscriptCompiler+Protected.h" can be marked a private or project level header in the framework, so it will not be visible in the umbrella header and will not be available for header based imports with its filename. So, this works around having to include this header in the umbrella header.
However, the module created this way is publicly exposed in the framework and available for eyes that should not see it. I did not investigate this further as practically this solves the issue well enough (am yet to hit issues where I would have by accident imported that protected header where it wasn't supposed to be imported).

module.map for accessing (Swift and Objective-C) classes in main target from test target

I am in the process of adding Swift classes to an existing Objective-C project. As part of this, I have added a MyProjectTests.swift to the target MyProjectTests. It imports Swift classes from target MyProject with import MyProject and that works just fine.
I now want to use #import Swift; in MyProjectTests.mas well. However, the compiler issues the error Module 'MyProject' not found.
I have these questions:
Make both import and #import succeed in test target
Why can it be the case that the Swift compiler sees module MyProject but the Objective-C compiler does not? What build settings in MyProjectTest do I have to change to make #import MyProject succeed as well.
Export Objective-C classes from main target
Ultimately MyProjectTest.swift and MyProjectTest.m also need access to Objective-C classes from target MyProject. So far I have multi-targetted such files, but I want to switch to using modules also here.
My current understanding is that this is a matter of providing a module.map file which would list header files for the classes I wish to "export".
What are the exact steps I have to go through? Where should I place the header file and which build settings do I need to change in the two targets MyProject and MyProjectTests?
I currently have a (so far empty) module.map inside my project and build settings for target MyProject include Defines Module: Yes, Product Module Name: MyProject.
UPDATE I am by now wondering whether it might be impossible to expose (Objective-C) files from an iOS application (instead of framework) project as a module. But then it already seems to work for Swift files (somehow).
I've by now concluded that this is not possible with current Xcode (6.1.1) tooling. (What a waste of time!)
The old scheme of bi-targeting source files to both MyProject and MyProjetTest also presents several challenges for a mixed Objective-C/Swift project with a non-trivial amount of code:
Its Objective-C part defines a legacy NS_ENUM(Integer, Repeat) which name-clashes with Swift.Repeat<T>. Referring to it as MyProject.Repeat (not MyProjectTests.Repeat) causes problems when compiling for target MyProjectTests, which changing this target's Project Name (also) to MyProject (not: MyProjectTests) does not seem to solve.
Compilation of constructs where Swift class A employs Objective-C class B, which in turn employs Swift class C does not seem to be possible in a straightforward way. Since the compiler has not yet produced MyProject-Swift.h with the definition of C, it cannot compile B. But since it cannot compile B it cannot compile A and therefore cannot produce MyProject-Swift.h. Catch 22, or so it seems.
Bi-targeted Swift code imports Swift classes from auto-generated MyProject-Swift.h. For the target MyProjectTests this name does not apply, yet that's what it is in the source files. I did not want to go down the road of changing MyProjectTests' Project Name (see above). Importing the right auto-generated file via the targets *.pch may be possible, but then it may be not ...

Xcode/clang: Why do some, not all, of my headers give "warning: no rule to process file xxx for architecture arm7"

I am building an iOS 5 app with ARC using clang on Xcode 4.2/Lion. Good practice for me is to try to get rid of as many warnings as possible but I'm lost on this one. The app has quite a few classes, but a limited number of their header files (8 or 9) give linker warnings like this:
warning: no rule to process file '$(PROJECT_DIR)/TKMyClass.h' of type sourcecode.objj.h for architecture arm7`
I don't see any significant commonalities across these headers: a couple are 3rd party OSS code; the rest are all my own. One is a plain-C header file (with no corresponding .c) containing nothing but constants, #defines and enums; a couple are UIView and UIViewController subclasses created with Xcode's templates; the rest are ordinary Obj-C classes, some of which inherit from NSObject and some of which don't. All (of my classes) were created from scratch within the project at various times. Both older and newer classes give no warnings.
My project uses a mix of C++, Objective-C++ and Objective C classes. The warning-generating classes here are mostly Objective C (their implementations are in .m files) but the two 3rd party classes are implemented in Objective C++ (.mm).
The project otherwise builds fine and runs in the simulator and on iDevices.
To my shame I'm not particularly familiar with the project settings pages in Xcode. I expect the solution is contained in there somewhere but I'm not sure where to start without breaking things.
That means that you have accidentally added header files to be compiled. You need to go into your projects Build Phases and remove all header files from the Compile Sources section.
Check your Architecture (Project and Target): Standard (armv7) - $(ARCHS_STANDARD_32_BIT) and go to Build Pharse (Compile Source) and cheek there should not be any .h file added there.