How to import Swift package into Objective-C using Xcode

Hello,

Hope someone can help as I've been pulling my hair out over this.

Say I have a pure Swift language SPM package library called "TestPackage" that exposes some of it's API to Objective-C:

@objc
class TestClass: NSObject {}

How do I then consume this in a mixed-language Xcode app target from Objective-C .h files like:

#import <Foundation/Foundation.h>

@import TestPackage;

NS_ASSUME_NONNULL_BEGIN

@interface SomeObject : NSObject

- (void)setObject:(TestClass *)testObj;

@end

NS_ASSUME_NONNULL_END

I get "Module 'TestPackage' not found" error and "Expected an type" error on - (void)setObject:(TestClass *)testObj;

An example Xcode projects can be found here:

Which uses TestPackage SPM package:

For my specific use-case, I also need to reference the Swift type in the Objective-C header and then use the type when calling the Objective-C setObject method.

Does this help?

Unfortunately not, though that OP does appear to have the same issue as me and I've seen threads where it's apparently fixed in Xcode 11.4 but I don't see any change of behaviour and I'm using Xcode 12.3. My test project attached is super simple and it just doesn't work.

@Aciid mentions a fix on another post in an earlier Swift version but I think that only applies when SPM is performing the build operations. Seems Xcode likes to do it's own thing and isn't generating the headers.

I can't believe I'm the only developer in the world who needs to use SPM libraries in mixed Swift/Objective-C codebases where the symbols need to used in Objective-C and Swift application code?

If it helps, I tried to make a very simple Swift package and use it in an Obj-C project. I found that the classes had to be marked as public.

I could instantiate the class in Obj-C and call its functions, but alas I could not work out how to access the class's properties.

import Foundation

@objcMembers public class SwiftClass : NSObject {
    @objc let text = "Foo"
    
    public func sayHello() {
        print(text)
    }
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    
    SwiftClass *swiftClass = [[SwiftClass alloc] init];
    NSLog(@"swiftClass.txt: %@", swiftClass.text); 
     // Error: Property 'text' not found on object of type 'SwiftClass *'
    [swiftClass sayHello];
}

Properties default to internal, not just methods. Have you tried making text public?

1 Like

That works. Thanks. I could have sworn that I had tried that....

Just to be clear as shown in my example projects, I'm unable to import the Swift module in the Objective-C .h files. I just get module not found. My TestClass is public, though the initialiser isn't though this shouldn't prevent the module from being created.

Apologies, I read your post too quickly and made some assumptions.

1 - The Obj-c .h file error: You don't @import swift modules into Obj-C headers, you forward declare classes instead: e.g. @class TestClass;

2 - The 'expected a type' error in ViewController.swift appears to go away if the @objc annotation is on the same line as the Class declaration. e.g. as in my post above

import Foundation

@objcMembers public class TestClass : NSObject {
    @objc let text = "Foo"
    
    public func sayHello() {
        print(text)
    }
}

After that there's an error compiling because you are calling setObject on TestClass, which is actually a method on your SomeObject class, not TestClass.

Hmm, having said that... When I use @class in SomeObject.h the setObject: method appears not to be exposed to Swift... Other methods and properties do though... e.g. isEnabled and a simple test method which I added.

There seems to be a way to work around it by using id instead of TestClass in the method.
e.g.

  • ( void )setObject2:( id )testObj;

Or this page may help:

Yep that's exactly the issue I've had, the symbols are converted to Any when using forward class declarations but I can't import the module either to resolve the symbol when removing the @class but I do have circular dependencies. Tricky stuff, will take a look at that link, thanks!

The better solution is simply don't use Swift Package Manager as a replacement for Xcode project files. SPM wasn't designed to work with modules declared in Xcode project files and Apple provides neither support/workarounds, nor do they seem interested in writing tests to make sure Swift Packages can fully replace all the use-cases of Xcode project files, especially in mixed-source apps...

Terms of Service

Privacy Policy

Cookie Policy