Call swift code from objc code in objc project

I read the article here... https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c

and this is what I did.


  1. Created a header file for swift file.
  2. added @class SwiftFile.h
  3. #import MyAppTargetName-Swift.h in the objc file from where I want to reference my swift code.

Now in my swift file when I write...


@interface ClassName
{
MySwiftClass *swiftClass;
}

I get error message Unknown Type Name "MySwiftClass".


How can I resolve this and get going?

Am I doing something wrong?

Does the bridging header i created along with the swift file need to me edited?


Neerav

Accepted Reply

I’m honestly not sure how you managed to get into the state you’ve got into. It’s hard to debug such problems without having access to your project. As an alternative, I’ve included working instructions below. Please try repeating this at your end. That’ll help in one of two ways:

  • If they don’t work, there’s something wonky in your environment.

  • If they do work, you have a working example that you can compare against your main project.

Here’s what I did:

  1. In Xcode 11.3, I created a new app from the macOS > App template. I named it MyApp, set the language to Objective-C, and the UI to Storyboard.

  2. In the Project navigation on the left, I selected

    main.m
    and then chose File > New > File.
  3. I selected the macOS > Swift template and named the file

    MySwiftClass.swift
    .
  4. After saving the file, Xcode asked me whether I wanted to create a bridging header. I clicked Create Bridging Header. This isn’t necessary for this task, but you’ll likely need it later and it’s best to do it at this point.

  5. I change

    MySwiftClass.swift
    to look like this:
    import Foundation
    
    
    @objc
    class MySwiftClass: NSObject {
    
    
    @objc
    func run() {
        print("MySwiftClass.run \(Date())")
    }
    }

    .

  6. In the Project navigation on the left, I selected

    main.m
    again.
  7. I added this line after the existing

    #import
    :
    #import "MyApp-Swift.h"

    .

  8. Inside the

    @autoreleasepool
    I added this code:
    MySwiftClass * obj = [[MySwiftClass alloc] init];
    [obj run];

    .

  9. I ran the app. It printed:

    MySwiftClass.run 2020-02-20 10:37:03 +0000

    .

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

OK then, what version of Xcode are you using? And on what version of macOS are you running it?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Adding some more details based on my current experience: My task is to convert an OBJC codebase from premium to freemium on IOS16, and I want to use the new features of StoreKit2 (originalPurchaseDate and originalAppVersion) in my objective-C code. I'm using the Storekit2 example code (https://developer.apple.com/documentation/storekit/in-app_purchase/implementing_a_store_in_your_app_using_the_storekit_api), especially the file "store.swift", and my problem was to be able to call the functions in that file from my OBJC modules. So here is what helped me:

  1. Create an object in store.swift, and flag it with @objc (see below). As the object is in the swift file, it can call all the swift code. The @objc makes the object visible to the OBJC code as well.
  2. Each property and each function that should be visible to OBJC must be proceeded with "@objc"
import Foundation
import StoreKit

@objc(MySwiftObject)
class MySwiftObject : NSObject {

	@objc
	var someProperty: AnyObject = "Some Initializer Val" as NSString

	override init() {
	}

	@objc
	func someFunction(someArg: Any) -> NSString {
		return "You sent me \(someArg)" as NSString
	}

	@objc
	func myAppTransaction() async {
		do {
			// Get the appTransaction.
			if #available(iOS 16.0, *) {
				let shared = try await AppTransaction.shared
				if case .verified(let appTransaction) = shared {
					// Get the major version number of the version the customer originally purchased.
					let versionComponents = appTransaction.originalAppVersion.split(separator: ".")
					let originalMajorVersion = versionComponents[0]

					if originalMajorVersion < "2" {
						// This customer purchased the app before the business model changed.
						// Deliver content that they're entitled to based on their app purchase.
					}

					else {
						// This customer purchased the app after the business model changed.

					}

				}

			} else {
				// Fallback on earlier versions

			}

		}

		catch {

			// Handle errors.

		}



	}

}
  1. To make anything visible in OBJC code, we need an include file, which is auto-generated by the compiler - this was the point that I didn't understand for a long time. The name of the include file is "-Swift.h", for e.g. my app name is "dogwalk", so the include file is named "dogwalk-Swift.h", this is what you can use in the #include statement, no help from autocomplete, you have to type the full statement. See below for the OBJC example code:
#import "dogwalk-Swift.h"

	MySwiftObject * myOb = [[MySwiftObject alloc] init];

	myOb.someProperty = @"Hello World";
	NSLog(@"MyOb.someProperty: %@", myOb.someProperty);

	NSString * retString = [myOb someFunctionWithSomeArg:@"my string example"];
	NSLog(@"RetString: %@", retString);

	[myOb myAppTransactionWithCompletionHandler:^{

		NSLog(@"appTransaction completed");

	}];