TL; DR. To use the “Swift” code inside the “Objective-C” you will have to sacrifice some “features” of the “Swift” and write a wrapper over the code that will not use techniques that are incompatible with the “Objective-C” ( “structures” , “generics” , “Enum associated values” , “protocol extensions” , etc.), and will be based on the NSObject heirs .
pod install
, open the “xcworkspace” file .import
entire framework, as we used to do in Swift, nor try to import individual framework API public files, as we used to do in Objective-C. In any file in which we need access to the framework's functionality, we import a file called <>-Swift.h
- this is an automatically generated header file that is a guide to “Objective-C” files to the public “API” contained in imported “Swift” files. It looks like this: #import "YourProjectName-Swift.h"
NSObject
and sees only the public “API”. And inside classes, public properties , initializers, and methods must be annotated with @objc
.@objc
. But in this case, probably, we have the opportunity and the necessary code to write in “Objective-C”. Therefore, it makes more sense to focus on the case when we want to import someone else's “Swift” code into our project. In this case, most likely, we have no possibility to add any inheritance or anything to the necessary classes. What to do in this case? It remains to write wrappers ! public class SwiftClass { public func swiftMethod() { // Implementation goes here. } }
NSObject
, and in it declare a private member of the external class type. To be able to call the methods of the outer class, we define methods in our class that internally call the corresponding methods of the outer class through a private member of the class (it sounds confusing, but I think everything’s clear by code): import Foundation import SwiftFramework public class SwiftClassObjCWrapper: NSObject { private let swiftClass = SwiftClass() public func swiftMethod() { swiftClass.swiftMethod() } }
NSObject
class and NSObject
annotations appears after importing Foundation .)@objc
comes to the rescue: @objc(SwiftClass) public class SwiftClassObjCWrapper: NSObject { private let swiftClass = SwiftClass() @objc public func swiftMethod() { swiftClass.swiftMethod() } }
SwiftClass *swiftClass = [SwiftClass new]; [swiftClass swiftMethod];
@objc
and used inside Objective-C. Swift and Objective-C are different languages ​​with different capabilities and different logic, and quite often when writing a Swift code, we use its capabilities that Objective-C does not have or that are implemented fundamentally differently. @objc public func anotherSwiftMethod(parameter: Int = 1) { // Implementation goes here. }
[swiftClassObject anotherSwiftMethodWithParameter:1];
1
is the value passed by us, the argument has no default value.)do(thing:)
“Objective-C” will transform into doWithThing:
which may not coincide with our intention. In this case, the @objc
annotation @objc
comes to the rescue: @objc(doThing:) public func do(thing: Type) { // Implementation goes here. }
throws
, then “Objective-C” will add one more parameter to its signature — an error that the method may throw. For example: @objc(doThing:error:) public func do(thing: Type) throws { // Implementation goes here. }
NSError *error = nil; [swiftClassObject doThing:thingValue error:&error]; if (error != nil) { // Handle error. }
NSObject
, then, as mentioned above, there are no problems. But most often it turns out that it is not. In this case, the wrapper helps us out again. For example, the original "Swift" code: class SwiftClass { func swiftMethod() { // } } class AnotherSwiftClass { func anotherSwiftMethod() -> SwiftClass { return SwiftClass() } }
@objc(SwiftClass) public class SwiftClassObjCWrapper: NSObject { private let swiftClassObject: SwiftClass init(swiftClassObject: SwiftClass) { self.swiftClassObject = swiftClassObject super.init() } @objc public func swiftMethod() { swiftClassObject.swiftMethod() } } @objc(AnotherSwiftClass) public class AnotherSwiftClassWrapper: NSObject { private let anotherSwiftClassObject = AnotherSwiftClass() @objc func anotherSwiftMethod() -> SwiftClassObjCWrapper { return SwiftClassObjCWrapper(swiftClassObject: anotherSwiftClassObject.anotherSwiftMethod()) } }
AnotherSwiftClass *anotherSwiftClassObject = [AnotherSwiftClass new]; SwiftClass *swiftClassObject = [anotherSwiftClassObject anotherSwiftMethod]; [swiftClassObject swiftMethod];
public class SwiftClass { } public protocol SwiftProtocol { func swiftProtocolMethod() -> SwiftClass } public func swiftMethod(swiftProtocolObject: SwiftProtocol) { // Implementation goes here. }
SwiftClass
: @objc(SwiftClass) public class SwiftClassObjCWrapper: NSObject { let swiftClassObject = SwiftClass() }
SwiftProtocol
, but using wrapped versions of the classes: @objc(SwiftProtocol) public protocol SwiftProtocolObjCWrapper { func swiftProtocolMethod() -> SwiftClassObjCWrapper }
class SwiftProtocolWrapper: SwiftProtocol { private let swiftProtocolObject: SwiftProtocolObjCWrapper init(swiftProtocolObject: SwiftProtocolObjCWrapper) { self.swiftProtocolObject = swiftProtocolObject } func swiftProtocolMethod() -> SwiftClass { return swiftProtocolObject.swiftProtocolMethod().swiftClassObject } }
@objc public func swiftMethodWith(swiftProtocolObject: SwiftProtocolObjCWrapper) { methodOwnerObject.swiftMethodWith(swiftProtocolObject: SwiftProtocolWrapper(swiftProtocolObject: swiftProtocolObject)) }
@interface ObjectiveCClass: NSObject<SwiftProtocol> @end @implementation ObjectiveCClass - (SwiftClass *)swiftProtocolMethod { return [SwiftClass new]; } @end
(ObjectiveCClass *)objectiveCClassObject = [ObjectiveCClass new]; [methodOwnerObject swiftMethodWithSwiftProtocolObject:objectiveCClassObject];
enum
as @objc
.enum
type, but want to use it inside „Objective-C“? We can, as usual, wrap a method that uses instances of this enum type, and give it our own enum
. For example: enum SwiftEnum { case firstCase case secondCase } class SwiftClass { func swiftMethod() -> SwiftEnum { // Implementation goes here. } } @objc(SwiftEnum) enum SwiftEnumObjCWrapper: Int { case firstCase case secondCase } @objc(SwiftClass) public class SwiftClassObjCWrapper: NSObject { let swiftClassObject = SwiftClass() @objc public func swiftMethod() -> SwiftEnumObjCWrapper { switch swiftClassObject.swiftMethod() { case .firstCase: return .firstCase case .secondCase: return .secondCase } } }
Source: https://habr.com/ru/post/345946/
All Articles