yar1vn yar1vn - 6 months ago 54
iOS Question

How to access an internal Swift class in Objective-C within the same framework?

Working on a mixed framework. imported inside the Obj-C file but the internal classes are not visible, only the public ones.

The documentation clearly states the internal clasees should be available between Swift and Obj-C:


Importing Swift into Objective-C
To import a set of Swift files in the same framework target as your Objective-C code, you don’t
need to import anything into the umbrella header for the framework.
Instead, import the Xcode-generated header file for your Swift code
into any Objective-C .m file you want to use your Swift code from.
Because the generated header for a framework target is part of the
framework’s public interface, only declarations marked with the public
modifier appear in the generated header for a framework target. 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).


Code Sample (Create a new project with a framework)

// SwiftObject.swift

public class SwiftObject: NSObject {
public class func doSomething() {}
}

internal class YetAnotherSwiftObject: NSObject {
internal class func doSomething() {}
}





// SomeObject.m file

@implementation SomeObject

- (void)someMethod {
[SwiftObject doSomething];
}

- (void)someOtherMethod {
[YetAnotherSwiftObject doSomething]; // Use of undeclared identifier
}

@end

Answer

As indicated in the docs, declarations marked with internal modifier don't appear in the generated header, so the compiler does not know about them and thus complaints. Of course, you could send messages using performSelector approach, but that's not convenient and bug-prone. We just need to help the compiler know that those declarations are there.

First, we need to use @objc attribute variant that allows you to specify name for your symbol in Objective-C:

// SwiftObject.swift

@objc(SWIFTYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject {
    internal class func doSomething() {}
}

And then you just need to create @interface declaration with the methods you want to use in your code - so the compiler will be happy, and also apply SWIFT_CLASS macro with the symbol name you've specified earlier - so the linker would pick the actual implementation:

// SomeObject.m file

SWIFT_CLASS("SWIFTYetAnotherSwiftObject")
@interface YetAnotherSwiftObject : NSObject

+ (void)doSomething;

@end


@implementation SomeObject

- (void)someOtherMethod {
    [YetAnotherSwiftObject doSomething]; // Should work now !!!
}

@end
  • I've used the interface declaration in .m file just for clarity, the better option would be to combine such declarations in .h file, and include it.
  • By declaring methods in that interface we're making a promise to compiler, and it won't complain if you'll put there a method that does not exist (or with wrong signature, etc.) Obviously, you'll crash in runtime in that case - so be cautious.
Comments