Can Objective-C code call Swift class extensions?
Asked Answered
S

6

123

I searched some posts, I think I cannot write an extension under Swift, and call it from Objective-C code, right?

@objc like attributes only support methods, class, protocols ?

Snelling answered 24/11, 2014 at 4:4 Comment(2)
Why don't you just give it a try?Stanger
ide complained error, but I want to get a definite anwser.Snelling
S
119

You can write a Swift extension and use it in Objective-C code. Tested with Xcode 6.1.1.

All you need to do is:

  • create your extension in Swift (@objc annotation needed since Swift 4.0.3)

  • #import "ProjectTarget-Swift.h" in your Objective-C class (where "ProjectTarget" represents the XCode target the Swift extension is associated with)

  • call the methods from the Swift extension

Sn answered 4/2, 2015 at 12:9 Comment(5)
I've done exactly this but it's still a compile time error. Hmmm. EDIT: I imported my bridging header, instead of "ProjectTarget-Swift.h". Der.Driven
Both are bridging headers. Difference is that one is for using Swift code in ObjC and the other one is for using ObjC code in Swift. The Swift header is invisible. The other one, should be managed by you.Sn
William Hu, see mclaughlinj's answer!Grade
As of Swift 4.0.3, the @objc annotation is needed if you want to use the extension in Objective C class files.Unseat
NOTE: If the name of your project target has a dash in it, then the dash needs to be removed from the import statement. For example if your project target's name is "project-target", then the import statement should read "projecttarget-Swift.h".Anticyclone
L
84

I found out that in Swift 4.0 I had to add @objc in front of my extension keyword in order for the Swift extension methods to become visible by an instance of the Objc class I was extending.

In short:

File configuration setup:

CustomClass.h
CustomClass.m
CustomClassExtension.swift

In CustomClassExtension:

@objc extension CustomClass
{
    func method1() 
    {
        ...
    }
}

In my AppDelegate.m:

self.customClass = [[CustomClass alloc] init];
[self.customClass method1];
Leifeste answered 23/10, 2017 at 20:9 Comment(2)
If a method uses generics, it can't be used by Objective-C and you'll need to add the @nonobjc annotation.Moorhead
Just a sidenote, @objcMembers does not work in this case. Don't know why.. :-(Wherewithal
D
51

This solution works for Swift 2.2 and Swift 3. Note that only extensions for classes (not for structs or enums) will be accessible from Objective-C.

import UIKit

extension UIColor {

    //Custom colours
    class func otherEventColor() -> UIColor {
        return UIColor(red:0.525, green:0.49, blue:0.929, alpha:1)
    }
}

Then #import "ProductModuleName-Swift.h" in your ObjC file.

Swift 4

extension UIColor {

    // As of Swift 4.0.3, the @objc annotation is needed if you want to use the extension in Objective-C files
    @objc
    class func otherEventColor() -> UIColor {
        return UIColor(red:0.525, green:0.49, blue:0.929, alpha:1)
    }
}
Distrait answered 28/8, 2015 at 16:45 Comment(7)
It's not "YourProjectsNameHere..." but rather "YourTargetsNameHere...": to share targets, you have to lock it to name: dr2050.postach.io/post/…Clomb
@DanRosenstark You are correct. I am changing it to "ProductModuleName-Swift.h" as Apple suggests: developer.apple.com/library/ios/documentation/Swift/Conceptual/…Distrait
Works fine for swift 3.Micromho
Is there a reason why you say public is needed? Works fine without public modifier for me. Are we assuming that the extension is not in the same project but being imported from another project?Godber
Do I need to use bridging header?Chimborazo
No, you use the bridging header to import ObjC code in Swift. Here it's the other way around.Distrait
@LeeKang I just tried #import "ProductModuleName-Swift.h" without making the extension public and it worked for me as well.Comanchean
M
44

As covered in the other answers, importing the generated Swift header works in most cases.

An exception to this is when the category is defined on a bridged type (i.e. the extension is defined on String and not NSString). These categories will not automatically be bridged to their Objective-C counterparts. To get around this, you'll either need to use the Objective-C type (and cast the return value in your Swift code with as String) or define an extension for both the Swift and Objective-C types.

Mentally answered 29/2, 2016 at 16:57 Comment(2)
Adding to the answer above, you should make your swift extension public and set its type as NSString e.g. public extension NSString. Should you get an unresolved identifier or similar error at compile time, you can cast again back to String e.g. let sVal = self as String and then call the necessary code on sValCherrylchersonese
@Cherrylchersonese actually you don't need public, it will work without scope specifier. And it (export to Obj-C, I mean) doesn't work for Strings, because of they are structs, not classes and there are available only from Swift. So, yes – you can write extension for NSString and use cast, or write extensions, as mclaughlinj said, for both NSString class and String structRoca
C
3

Import "#import "ProductModuleName-Swift.h" header in objective-c file and add @objc infront of your extentsion in swift file. It will working fine in swift 4.2 and swift 5

Crosshead answered 29/5, 2019 at 11:28 Comment(0)
R
2

If think you have configured everything correctly (marked your Swift entities with @objcMembers or @objc, imported the bridging header "ProductModuleName-Swift.h" and so on) – but still getting the No visible @interface for 'FooClass' declares the selector 'fooSelector' error:

Check if you see your interfaces in the bridging header by ctrl + cmd clicking on it. If you don't, check out my answer to this question: Swift to Objective-C header does not contain Swift classes

Rolf answered 15/9, 2020 at 13:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.