Accessing Objective-C header properties from Swift subclasses - ios

I came late to the party and just switched to Swift. I will, however, continue using my libraries written in Objective-C but I'm having trouble accessing parent classes' properties from Swift. A typical structure looks like:
A.swift -> B.h -> C.h
Where A is part of a project that uses an Objective-C library. I've added the B and C header files in my bridging header and everything works fine. The only issue I'm having is accessing the public properties of my header files. For instance,
// C.h
#interface C
#property(nonatomic, readonly) Foo *foo
#end
Would normally be accessed with dot syntax from any subclass written in Objective-C. In Swift, instances of A do not appear to have members of type Foo. The only solution I have found so far is to manually add getters to all my properties but I would rather not modify my libraries' source code. Is there another approach or am I missing something?

Related

Building an Objective-C framework: Unable to find header file at path when accessing module from Swift?

Intro
I'm looking to create an Objective-C framework inside XCode to be used by our Swift Application, but am running into an issue where the header files can not be found at their path when building and then importing the module (Swift needs a module as far as I know) defined by the framework target, but said path is fine when simply compiling the framework.
Example
For example: In my Objective-C Framework I have two classes. Class A and class B. A is a superclass of B, so naturally I included A's header file within B's header in order to declare their relationship, like so:
//Inside file: ClassA.h_________________
#interface ClassA : NSObject
//Some implementation
#end
//Inside file: ClassB.h ________________
#include "src/models/ClassA.h"
#interface ClassB: ClassA
//Some implementation
#end
Now everything's looking great and runs as expected when running the target which solely defines my framework. Upon attempting import the module from within our Swift application, the aforementioned #include "src/models/ClassA.h" statement fails to be resolved.
On a side note: My Swift application uses another similar, albeit smaller framework without a folder structure.
Current Status
Steps I've taken to ensure the header "should" be found:
My Swift application target depends on the Objective-C framework.
My Framework is set to define a module inside it's build settings.
The header files of both ClassA and ClassB are marked as public under XCode's Build Phases tab.
Inside my frameworks umbrella header, I've imported the headers of both ClassA and ClassB like so:
//Inside header file called MyFramework.h
#import <MyFramework/ClassA.h>
#import <MyFramework/ClassB.h>
Now I realize that Swift accessing the module has access to ClassA and ClassB as their are lying directly inside the Headers folder produced by the framework/module, which is why the import statements in the umbrella header are no longer the file path these files normally have within my project.
But as soon as my Swift application goes to parse the header of ClassB and reads the include statement to include ClassA.h an error is thrown that the header file can not be found.
Question
How can I make this work, or at least make use of ClassA and ClassB from within my Swift Application accessing this framework module?
By the way, I need the folder paths because I have multiple header files with the name "ClassA" and "ClassB" (lets call them that for this example).
Bit more info in case helpful: It's a truly large java framework with some thousands of files translated into Objective-C by making use of "j2objc". As such some of the files share the same name.

Category for obj-c class derived from swift

I am running project with lots of legacy code with objc and swift.
I've been using objc MPOldKeychainManager, which is now deprecated and swift's NewKeychainManager is to be used.
The problem is following: MPOldKeychainManager had some categories written and I don't want to rewrite them as swift extensions.
What I've done is:
naming the class derived from the NewKeychainManager (visible in "myTarget-Swift.h") to "MPOldKeychainManager"
removing the objc declaration of MPOldKeychainManager
...hoping that the categories will still work.
objc(MPOldKeychainManager)
class NewKeychainManager: KeychainManager {
}
Unfortunately, old extensions can't see the MPOldKeychainManager (derived from swift), even though I've updated the imported header to myTarget-Swift.h
#import "myTarget-Swift.h" //previously - objc "MPOldKeychainManager.h"
#interface MPOldKeychainManager (Authentication)
Question: is it possible to use categories for objc classes derived from swift?
I have already tried totally new naming
I have already tried loads of clean-builds
In case you haven't seen it, here is a useful resource for migrating from Objective-C to Swift: https://developer.apple.com/documentation/swift/migrating_your_objective_c_code_to_swift . Among other things, it states that one cannot subclass a Swift class in Objective-C. What you are trying to do is specify a different Objective-C name, MPOldKeychainManager, for the NewKeychainManager Swift class.
This will, actually, work if you add an ampersand before objc, like so:
#objc(MPOldKeychainManager)
class NewKeychainManager: KeychainManager {
}
You can then use all your existing categories in Objective-C. You will, however, have a problem using them in Swift, because to be usable in Swift they need to be available in the bridging header, and you won't be able to use the class' Objective-C name (MPOldKeychainManager) in the bridging header.
You can, however, write an Objective-C wrapper class that will have a method corresponding to each category method and also taking a NewKeychainManager pointer. A wrapper method can then delegate to the category method, which is available to Objective-C code, so you won't have to re-implement your category methods in Swift.
Let's say an Objective-C category has method authenticateUser::
#interface MPOldKeychainManager (Authentication)
-(void)authenticateUser:(int32_t)uid;
#end
The method could be wrapped as follows:
#interface OldKCMWrapper : NSObject
+(void)authenticateUser:(int32_t)uid withManager:(NewKeychainManager*)inst;
#end
This interface declaration must be available, directly or indirectly, via the bridging header. Then, somewhere in your Objective-C code, the wrapper could be implemented thus:
#implementation OldKCMWrapper
+(void)authenticateUser:(int32_t)uid withManager:(MPOldKeychainManager*)inst {
[inst authenticateUser:uid];
}
#end
The wrapper can then be used in Swift code, e.g.:
let kcm = NewKeychainManager()
OldKCMWrapper.authenticateUser(321, with: kcm)
In fact, the wrapper could be used in a Swift extension of NewKeychainManager. You would still have a Swift extension with equivalents of all the Objective-C category methods, but you would not have to re-implement their code in Swift: methods in the extension would simply delegate to the wrapper.
Hopefully this is helpful. There are other ways of implementing this idea, possibly more elegant.

Can I have a class that's partially implemented in Swift, and partially implemented in Objective C?

I want to extend my class which I wrote in Swift. I want to write the extension in Objective C because I need to put some Objective C code which I can't port into Swift. I know that I can create a .h and .m and then include the .h at the bridging header. But in the .h, I need to include the original .swift class file right?
How can I solve this? Can I include using myclass-swift.h? Thanks.
The following documentation may be helpful: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-ID122. You've probably already looked at that, but just in case. The following question may also help: Extending a Swift class with Objective C category.
As you know, to use Objective-C code in Swift there is the bridging header. To go the other way around, there is the auto-generated *-Swift.h header that should be imported in .m (and .mm) implementation files. The documentation says it should not be imported into .h files. In fact, the compiler won't let you import it into a .h file that is included, directly or indirectly, in the bridging header. However, with some care you can import it into other .h files.
Now, suppose your Swift class is called SwiftClass and you want to add to it a method called ocFunction() implemented in Objective-C. One approach, essentially presented in the aforementioned answer, is to implement a category in an Objective-C source file (.m):
#implementation SwiftClass (OCExtension)
-(void)ocFunction {...}
#end
Then modify your SwiftClass to include the following:
class SwiftClass : NSObject
{
...
#nonobjc func ocFunction()
{
self.perform(Selector(("ocFunction")))
}
...
}
The referenced answer suggests doing this in an extension, but since you have full control of the SwiftClass source, you can just do it in the class directly. BTW, the Objective-C category function could be named something other than the SwiftClass's function, thus eliminating the need for #nonobjc.
Another approach might be to define an Objective-C wrapper interface like this:
#interface SwiftClassOC : NSObject
+(void)ocFunction:(SwiftClass*)sc;
#end
and make it available to Swift via the bridging header. The implementation would go into a .m file. Then your ocFunction() in SwiftClass would look like
func ocFunction()
{
SwiftClassOC.ocFunction(self)
}
Please note that SwiftClassOC is stateless, so it's essentially a set of helper functions that take a SwiftClass pointer. SwiftClass is the one maintaining the state.
You can extend an Objective-C class with Swift, but you cannot extend a Swift class with Objective-C.
I want to write the extension in Objective C because I need to put some Objective C code which I can't port into Swift
You can do that, but not as part of the Swift class. What I usually do is make a "helper" class in Objective-C and import it into Swift.

Swift & Objective-C project - generated -Swift.h error - Cannot find interface declaration for UIViewController

I've got a project using the Swift generated Bridging Header and have the project set up correctly (no spaces in Names, Uses Modules, pre-declaring Swift classes in my .mm file, Deleting derived data, doing a clean rebuild etc...). The Bridging Header is being generated fine, but the automatically generated -Swift.h has errors in it. What's even worse, those errors are on the generated (in project creation - Swift) versions of AppDelegate and ViewController which would normally compile fine. The errors in the -Swift.h are:
#interface AppDelegate : UIResponder <UIApplicationDelegate>
>> Cannot find interface declaration for 'UIResponder', superclass
of 'AppDelegate'
>> Cannot find protocol declaration for 'UIApplicationDelegate'
#interface ViewController : UIViewController
>> Cannot find interface declaration for 'UIViewController', superclass
of 'ViewController'
I've searched Stack Overflow and the net and can't find any answer that addresses this particular issue. Has anyone else had this? Or, is there a way I can mark my AppDelegate and ViewController classes so Xcode doesn't try and create Objective-C stubs for those Swift classes as I don't actually need them?
Just add
#import <UIKit/UIKit.h>
before
#import <YourProject-Swift.h>
in .mm file. for me it helps.
I had same problem and discovered that..
-Swift.h only work best with Objective-C, not Objective-C++ (.mm)
So I use the Objective-C class to connect with the Swift class. Also You can always load your Objective-C++ class in your Objective-C class.
Try to import UIKit/UIKit.h into bridging header file.
Swift doesn’t play very well with Objective-C++. Since I need to interface with C++, I tend to write an Objective-C++ class MyClass_CPP containing just the interface with C++, then an Objective-C subclass MyClass that can play nicely with Swift.

Why are there two #interfaces for view controllers?

I'm learning some Objective-C, specifically iOS-related stuff. Whenever I generate a new UIViewController, or UITableViewController, etc, both the generated .h and .m files contain an #interface. The application will continue to compile if I delete the #interface from the .m file, also. What is the purpose of this #interface in the .m file?
The #interface in the .m file is a class extension (a category with no name). It's now in the templates to let you put any declaration of internal methods or ivars you don't want to expose to other parts of the code.
With the advent of the modern Objective-C runtime and the progress of clang, a well written class should:
1) have only public methods in the header
2) have ivars and internal methods declared in the internal class extension
Technically, it is a "class extension" (see ("Extensions" section of) the Objective-C intro docs). In practice, it has become common to use this as a "private" interface (it is only "visible" to the file in which it exists). Objective-C does not stop anyone else from calling these methods, but they won't "see" them (think of the .h file as your public interface and this anonymous category as your private interface).
It is handy for declaring #propertys which you don't want to expose publicly.

Resources