118

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 ?

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
sprhawk
  • 1,477
  • 2
  • 10
  • 9

6 Answers6

116

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

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
marius bardan
  • 4,962
  • 4
  • 29
  • 32
  • 8
    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. – Jason Renaldo Mar 30 '15 at 13:09
  • 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. – marius bardan Mar 30 '15 at 13:22
  • William Hu, see mclaughlinj's answer! – appleitung Apr 22 '16 at 12:44
  • 40
    As of Swift 4.0.3, the @objc annotation is needed if you want to use the extension in Objective C class files. – Rizwan Ahmed Dec 24 '17 at 11:02
  • 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". – Tyler Wood Oct 06 '20 at 19:44
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];
Kylo-Rey
  • 1,266
  • 10
  • 12
  • 3
    If a method uses generics, it can't be used by Objective-C and you'll need to add the `@nonobjc` annotation. – ThomasW Mar 01 '18 at 10:08
  • Just a sidenote, @objcMembers does not work in this case. Don't know why.. :-( – Raunak Aug 22 '19 at 11:18
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)
    }
}
leviathan
  • 11,080
  • 5
  • 42
  • 40
Foti Dim
  • 1,303
  • 13
  • 19
  • It's not "YourProjectsNameHere..." but rather "YourTargetsNameHere...": to share targets, you have to lock it to name: http://dr2050.postach.io/post/importing-swift-to-objective-c-multiple-modules – Dan Rosenstark Dec 06 '15 at 00:25
  • 1
    @DanRosenstark You are correct. I am changing it to "ProductModuleName-Swift.h" as Apple suggests: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-ID126 – Foti Dim Dec 30 '15 at 14:19
  • 1
    Works fine for swift 3. – Viktor Kucera Nov 23 '16 at 10:57
  • 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? – Lee Kang Dec 13 '16 at 23:15
  • Do I need to use bridging header? – jegadeesh Jul 06 '17 at 05:05
  • No, you use the bridging header to import ObjC code in Swift. Here it's the other way around. – Foti Dim Jul 08 '17 at 06:45
  • @LeeKang I just tried #import "ProductModuleName-Swift.h" without making the extension public and it worked for me as well. – David Marcus Jul 11 '17 at 17:56
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.

mclaughj
  • 12,645
  • 4
  • 31
  • 37
  • 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 `sVal` – RunLoop Mar 25 '17 at 13:16
  • @RunLoop 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 struct – Alex Nazarov Aug 02 '17 at 14:54
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

Siva
  • 700
  • 6
  • 25
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

Tysac
  • 257
  • 2
  • 11