I have a framework target in which most of the classes are written in Objective C. Recently we have started introducing Swift files in the code. We make private Objective C files available to swift code using modules(more on this can be found here).
This approach worked well until recently when I tried subclassing one of my Objective C class using Swift, I got an error in the Generated MyFramework-Swift.h file which said "Module TestSwift not found" where TestSwift is the name of the module I provided in the modulemap file. However, if I try subclassing the classes which are listed in the umbrella header of my framework(public classes), it works.
import TestSwift
#objc public class NewSwiftClass: ExistingObjectiveCClass {
//throws error in the generated MyFramework-Swift.h file while compiling
}
If I keep my swift class internal, it works
import TestSwift
#objc class NewSwiftClass: ExistingObjectiveCClass {
//works fine
}
but I would like to use this Swift class in my Objective C files hence cannot keep it internal.
TL;DR: I'm unable to subclass an existing Objective C class using Swift inside a framework target.
I believe this is impossible in Swift because it's impossible in Objective-C.
If you have a class A in your framework that is not part of your umbrella header, and you want B to subclass it and be in your umbrella header, you can't do it.
You have to declare the inheritance in your interface declaration #interface B: A, which goes in B's header and thus in the umbrella header. But the compiler is going to complain: "What is A?" You could import A's header there, but unlike Swift's import, Objective-C's #import literally drops the contents of A's header into the B header. Which means A is now in the umbrella header too i.e. public.
Mixing Swift with Objective-C isn't magic. The compiler still needs to be able to make a valid Objective-C header that accurately describes the Swift interface. So unless you can think of a way to make Objective-C do this, you can't do it in Swift.
The only alternative I can think of is to change your "is a" relationship into a "has a" relationship i.e.
#objc public class NewSwiftClass {
let parent: ExistingObjectiveCClass
}
obviously you lose most of the benefits of actual inheritance but you'll still have the parent around as a substitute for super. You could also declare a public protocol that both classes conform to to ensure that you get consistency between their methods.
Related
I frequently work on projects that utilize both Swift and Objective-C code. I heavily depend on Swift extensions, and it isn't unusual to see multiple *Extension.swift files with hundreds of lines of code within these projects.
The problem arrises when I just need to use one or two Swift classes in Objective-C. If import ProjectName-Swift.h exposes all Swift files to Objective-C, how is the compiler linking these classes? More importantly, are my Swift extensions being linked to all Objective-C files that import the bridging header?
If I just want a single Swift Dog class within Objective-C, does this come with the penalty of including/exposing all Swift classes/struct/functions/extensions to my DogWalker.m class? Will it end up compiling down to a larger object?
I have a swift project into which i have imported a objective-C project and the bridging header was automatically created. I am trying to build an application to book tickets. I am storing all the data collected across the views in a swift class object.
Now there are many tutorials to use swift files in objective c project and objective c files in swift project but none talk about the communication both ways.
I wanna reuse the object created in swift class in objective C class .
I have been using NSUserDefaults to do 2 way communication .
There is no need to use NSUserDefaults as "bridge". ObjC can directly access swift classes, if they subclass NSObject.
Open Xcode target build settings, in search field, type swift. Find Objective-C Generated Interface Header Name. Commonly, it will have something Project-Swift.h. In ObjC code import this header, and your swift classes and functions will be available in ObjC.
More information can be found in official documentation:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
Unfortunately, I have a class defined in Objective C (Class.h, Class.m) and another class, with the same name in Swift(Class.swift).
I have a bridging header, where I import the class
#import "Class.h"
Now, I want to create a method that returns the class from Objective C.
Is there a way to specify that I want to use the Objective C class instead of the swift one, or should I start renaming my classes?
Rename classes. I am not 100% how Swift classes that are not tagged #objc are treated, but nothing good can come of having two classes with the same name, even if they're written in different languages. If the Swift version is visible to the Objective-C side, you have a definite problem, and you cannot be sure when instantiate a Class which one you will get. It could theoretically change on every run of the program, though in practice the class loader is probably too deterministic for that to occur.
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).
I have an Objective C Class called Donald, I also have a C++ class called Donald in a static library that I would like to use in the same project. They both have a header file called Donald.h. Is there a way to do this?
You can include both header files by specifying a bit more of the path e.g.
#import "staticlibraryheaders/Donald.h"
#import "Donald.h"
However, you might find that the code won't compile since you are declaring two types both called Donald. If the compiler sees:
Donald* duck;
How does it know to type duck as a pointer to an instance of the C++ class or the Objective-C class? You might be able to fix that if the C++ class is in a C++ namespace. However, that hits the limit of my C++ knowledge.