Circular reference, Obj-C -> Swift, Swift -> Obj-C - ios

I'm trying to create an Objc class, with a property of an enum type, where the enum type is created in swift. After this, I want to use the Objc class in Swift.
(Swift)Enum:
import Foundation
#objc enum AffectsUnit:Int {
case time
case person
case group
}
(Objc)class
#import "JSONModel.h"
#import "MyApp-Swift.h"
#interface AddPlayerToTime : JSONModel
#property (nonatomic) AffectsUnit affectsUnit;
#end
The Objc class has an empty method file.
So far, so good. No compiler warnings.
Since i also want to use my objc class in Swift. I need to import the class in my Bridging-Header.h.
#import "AddPlayerToTime.h"
Now my swift code can see the AddPlayerToTime class, but the project will no longer compile. The error I get is:
Unknown type name 'AffectsUnit'
I'm 99% sure it breaks because of circular reference. Since my swift code is importing AddPlayerToTime class and AddPlayerToTime is importing my swift code. But I do not know how to fix this. All post on this circular reference matter, seem to suggest using #class declaration. But since I'm trying to refer to an enum, not a class, this is not a solution for me.
Am I trying to accomplish something, that simple can't be done?
Edit1:
Please note: I want as much code as possible, to remain on the Swift side.

You should be able to move the enum declation over to the Obj-C side and still have it visible to Swift, like this:
typedef NS_ENUM(NSInteger, AffectsUnit)
{
AffectsUnitTime,
AffectsUnitPerson,
AffectsUnitGroup
};

Related

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.

No type or protocol named 'AVCapturePhotoCaptureDelegate'

I'm trying to use my Swift class into Objective-C code and I came across this answer to do so. However, after import MyModule-Swift.h, I'm getting compile error in MyModule-Swift.h:
No type or protocol named 'AVCapturePhotoCaptureDelegate'
Even though it contains #import AVFoundation; at the top. I'm clueless how to solve this. All I'm trying to do is to use one of the model class (written in Swift) in Objective-C.
I think you are using .mm file.
Use .m instead.

Using Swift `enum` as a property type in Objective-C

I have a Swift enum defined like this:
#objc enum SomeEnum: Int {
case one, two
}
I can use SomeEnum in Objetive-C files normally, but when I want to declare a property in header file like this:
#property (nonatomic, assign) SomeEnum someEnum;
Compiler gives error message Unknown type name 'SomeEnum'. What's interesting I can create a property in private interface of the class. Also I tried cleaning the build folder, didn't help. File <module>-Swift.h is already imported in .pch file. Do you know what is the source of the problem?
You can do the following trick:
ObjcClass.h
#import <Foundation/Foundation.h>
#interface ObjcClass : NSObject
#property (nonatomic, assign) SomeEnum someEnum;
#end
ObjcClass.m
#import "<module>-Swift.h" // The order is important here
#import "ObjcClass.h"
#implementation ObjcClass
#end
Downsides:
You have to include <module>-Swift.h before classes using Swift enums.
The other issue is if you decide to export the Objective-C class to Swift via Bridging header then you'll have a problem to resolve that enum.
If you can avoid mixing Swift and Objective-C in that way I suggest you do. Instead you can use Swift classes shared with Objective-C and then use forward declarations (i.e #class X) in headers.
The other option would be to move enum to Objective-C side as others suggested in comments.
Side note: <module>-Swift.h is better be included in implementation files, don't include it in headers ever to avoid any trouble and circular imports.

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.

iOS: forward declaration of non objects type class

I have property of type CLLocationCoordinate2D, how can I use forward declaration to declare that?
#class CLLocationCoordinate2D;
do not works
What you are looking for here is not forward declaration at all. You want to make use of the struct CLLocationCoordinate2D. First off that is not a class - for forward declaring structs look here.
BUT in your case you do not even have to use forward declaration since you only need to / have to include the correct header in your project. Namely CoreLocation:
#import CoreLocation;
or a little bit old fashioned
#import <CoreLocation/CoreLocation.h>
Additionally you probably have to add the CoreLocation framework as well if you have not done that already.

Resources