Swift CocoaPod Library in Objective-C Project Migration from Swift 3 to 4/5 - objective-c

I'm using a Swift CocoaPod in my Objective-C project. Originally it was a Swift 3 project and I simply had to add the #import "<Project Name>-Swift.h" into my .m or .h file. I've since updated to the latest version of the CocoaPod which I believe is now Swift 5. Therefore this is no longer working as none of the properties can be found.
I've looked at a number of different resources online including Apple's documentation and the procedure is unclear here what to do. It seems I have to go in and edit the .swift file and add #objc in front of each property or method I wish to access? This seems to go against what CocoaPods is about as the next time I update all my changes will be blown away.
I tried looking at the generated header but when I highlight the #import "<Project Name>-Swift.h" line I just get taken to the NSObject declaration. Obviously it is just a build time item I'm guessing.
According to the Apple documentation (https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c) the Swift declarations must be marked with the public or open modifier and when I checked the swift file I found that they indeed are.
The pod I'm using is HGCircularSlider. An example of what I'm trying to access is in the RangeCircularSlider class is here:
open var startPointValue: CGFloat = 0.0 {
didSet {
guard oldValue != startPointValue else { return }
if startPointValue < minimumValue {
startPointValue = minimumValue
}
if distance > 0 {
endPointValue = startPointValue + distance
}
setNeedsDisplay()
}
}
As we can see here the declaration is public. I'm assuming I need to set some sort of build setting?
In addition I have set use_frameworks! in my Podfile. I've also checked that the build target within the pod project and Objective-C Generated Interface Header Name is HGCircularSlider-Swift.h. This is exactly what I'm using in my Objc .m file.
Welcome any thoughts or comments where else I should look?
Update 1
One other thing of note. In my .h and .m I have a #property of type RangeCircularSlider which it can see fine. No error there. It's accessing the properties and methods within the object that is causing the errors.
Property 'startPointValue' not found on object of type 'RangeCircularSlider *'
So it sees the swift file but as originally stated, no properties can be found.
Update 2
So I've discovered that if I add #objc to the front of the individual properties I'm trying to access in the Swift file the generated header file then see's them and I can therefore access them. So for the example function above it would then be
#objc open var startPointValue: CGFloat = 0.0 {
But these seems to be a bit of a hack as for when I want to install an updated version of this CocoaPod, then those changes I made to the pod's file would be blown away.
Any suggestions how to do this without having to edit the swift files in the CocoaPod?

For anyone else that runs across this. The online resources I found were helpful but didn't detail this specific case.
First off. Leave the CocoaPod code as it is. This will allow you to update it without having to worry about modifying it each time.
Next create a Swift file within your Objective C Project. xCode will ask if you want a header generated for it, say yes. This file will be called -Swift.h.
In this Swift file subclass the Swift file from the CocoaPod you are interested in accessing.
For example :
import Foundation
import HGCircularSlider
class CircularSliderObjc: RangeCircularSlider {
}
Next add in the properties you wish to access with getter and setters.
#objc override open var startPointValue: CGFloat {
get {
return super.startPointValue;
}
set {
super.startPointValue = newValue;
}
}
Then finally change the import in your Objective-C file to your project's generated header file that I mentioned above (#import "-Swift.h"
). If you have a property pointing to the class in the CocoaPod change it to your new Swift Class. For example :
#property (weak, nonatomic) IBOutlet CircularSliderObjc *rangeSlider;
In this example I have it setup as an Outlet for InterfaceBuilder.
After that, you're done. It seems like a lot of work but it's quite easy and simple. Not quite as fast as just importing the header but since Swift 4 and 5 you can no longer just access open vars in Swift as properties in objc. They need the #objc declaration to make that work. Added security to the language I'm guessing.
Hope this helps someone.

Related

Changing the implementation of Objective C method in Swift subclass

I am using an external library in my project. It is integrated via CocoaPods. I have access to the source code. The library is working well, but I need some modifications at some places in order for it to serve my purposes. It is written in Objective C. My project is in Swift.
I need to change the implementation of one method in the library. The problem is it is in the .m file and uses a lot of stuff declared only in the .m file. For example:
-(NSString*)methodToChange
{
NSArray<NSNumber*>* data = [self someInternalMethod:1];
uint value = data[0].unsignedIntValue;
return [self anotherInternalMethod:value];
}
I tried subclassing it like this:
class MySubclass : MySuperclassWithMethodToChange {
override var methodToChange: String {
//trying to use someInternalMethod and anotherInternalMethod
//unsuccessfully because they are not visible
}
}
The internal methods are using and modifying properties from the .m file that are also not accessible. Is there any way to solve this?
I would suggest forking the original library repository and making the necessary changes in your fork. You can then have your Podfile point to that. If your changes could be useful to others, make them in a way that doesn't break existing functionality and contribute them back to the library by opening a pull request.
If the original library gets updated later, you will have to merge those changes from the so-called "upstream" repository into yours. This does not happen automatically, so you'll have full control (and responsibility) over that process. See https://help.github.com/en/articles/syncing-a-fork for how this would look like.

How can I run an Objective-C library on Swift? [duplicate]

In Swift, how does one call Objective-C code?
Apple mentioned that they could co-exist in one application, but does this mean that one could technically re-use old classes made in Objective-C whilst building new classes in Swift?
Using Objective-C Classes in Swift
If you have an existing class that you'd like to use, perform Step 2 and then skip to Step 5. (For some cases, I had to add an explicit #import <Foundation/Foundation.h to an older Objective-C File.)
Step 1: Add Objective-C Implementation -- .m
Add a .m file to your class, and name it CustomObject.m.
Step 2: Add Bridging Header
When adding your .m file, you'll likely be hit with a prompt that looks like this:
Click Yes!
If you did not see the prompt, or accidentally deleted your bridging header, add a new .h file to your project and name it <#YourProjectName#>-Bridging-Header.h.
In some situations, particularly when working with Objective-C frameworks, you don't add an Objective-C class explicitly and Xcode can't find the linker. In this case, create your .h file named as mentioned above, then make sure you link its path in your target's project settings like so:
Note:
It's best practice to link your project using the $(SRCROOT) macro so that if you move your project, or work on it with others using a remote repository, it will still work. $(SRCROOT) can be thought of as the directory that contains your .xcodeproj file. It might look like this:
$(SRCROOT)/Folder/Folder/<#YourProjectName#>-Bridging-Header.h
Step 3: Add Objective-C Header -- .h
Add another .h file and name it CustomObject.h.
Step 4: Build your Objective-C Class
In CustomObject.h
#import <Foundation/Foundation.h>
#interface CustomObject : NSObject
#property (strong, nonatomic) id someProperty;
- (void) someMethod;
#end
In CustomObject.m
#import "CustomObject.h"
#implementation CustomObject
- (void) someMethod {
NSLog(#"SomeMethod Ran");
}
#end
Step 5: Add Class to Bridging-Header
In YourProject-Bridging-Header.h:
#import "CustomObject.h"
Step 6: Use your Object
In SomeSwiftFile.swift:
var instanceOfCustomObject = CustomObject()
instanceOfCustomObject.someProperty = "Hello World"
print(instanceOfCustomObject.someProperty)
instanceOfCustomObject.someMethod()
There is no need to import explicitly; that's what the bridging header is for.
Using Swift Classes in Objective-C
Step 1: Create New Swift Class
Add a .swift file to your project, and name it MySwiftObject.swift.
In MySwiftObject.swift:
import Foundation
#objc(MySwiftObject)
class MySwiftObject : NSObject {
#objc
var someProperty: AnyObject = "Some Initializer Val" as NSString
init() {}
#objc
func someFunction(someArg: Any) -> NSString {
return "You sent me \(someArg)"
}
}
Step 2: Import Swift Files to ObjC Class
In SomeRandomClass.m:
#import "<#YourProjectName#>-Swift.h"
The file:<#YourProjectName#>-Swift.h should already be created automatically in your project, even if you can not see it.
Step 3: Use your class
MySwiftObject * myOb = [MySwiftObject new];
NSLog(#"MyOb.someProperty: %#", myOb.someProperty);
myOb.someProperty = #"Hello World";
NSLog(#"MyOb.someProperty: %#", myOb.someProperty);
NSString * retString = [myOb someFunctionWithSomeArg:#"Arg"];
NSLog(#"RetString: %#", retString);
Notes:
If Code Completion isn't behaving as you expect, try running a quick build with ⌘⇧R to help Xcode find some of the Objective-C code from a Swift context and vice versa.
If you add a .swift file to an older project and get the error dyld: Library not loaded: #rpath/libswift_stdlib_core.dylib, try completely restarting Xcode.
While it was originally possible to use pure Swift classes (Not descendents of NSObject) which are visible to Objective-C by using the #objc prefix, this is no longer possible. Now, to be visible in Objective-C, the Swift object must either be a class conforming to NSObjectProtocol (easiest way to do this is to inherit from NSObject), or to be an enum marked #objc with a raw value of some integer type like Int. You may view the edit history for an example of Swift 1.x code using #objc without these restrictions.
See Apple's guide to Using Swift with Cocoa and Objective-C. This guide covers how to use Objective-C and C code from Swift and vice versa and has recommendations for how to convert a project or mix and match Objective-C/C and Swift parts in an existing project.
The compiler automatically generates Swift syntax for calling C functions and Objective-C methods. As seen in the documentation, this Objective-C:
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
turns into this Swift code:
let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
Xcode also does this translation on the fly — you can use Open Quickly while editing a Swift file and type an Objective-C class name, and it'll take you to a Swift-ified version of the class header. (You can also get this by cmd-clicking on an API symbol in a Swift file.) And all the API reference documentation in the iOS 8 and OS X v10.10 (Yosemite) developer libraries is visible in both Objective-C and Swift forms (e.g. UIView).
Here are step-by-step instructions for using Objective-C code (in this case, a framework provided by a third-party) in a Swift project:
Add any Objective-C file to your Swift project by choosing File -> New -> New File -> Objective-C File. Upon saving, Xcode will ask if you want to add a bridging header. Choose 'Yes'.
(source: derrrick.com)
In simple steps:
A prompt appears, and then click on OK... If it does not appear, then we create it manually like in the following... Create one header file from iOS source and give the name ProjectName-Bridging-Header (example: Test-Bridging-Header), and then go to build setting in the Swift compiler code -> Objective-C bridge add Objective-C bridge name ..(Test/Test-Bridging-Header.h). Yeah, that's complete.
Optionally, delete the Objective-C file you added (named "anything" in the GIF image above). You don't need it any more.
Open the bridging header file -- the filename is of the form [YourProject]-Bridging-Header.h. It includes an Xcode-provided comment. Add a line of code for the Objective-C file you want to include, such as a third-party framework. For example, to add Mixpanel to your project, you will need to add the following line of code to the bridging header file:
#import "Mixpanel.h"
Now in any Swift file you can use existing Objective-C code, in the Swift syntax (in the case of this example, and you can call Mixpanel SDK methods, etc.). You need to familiarize yourself with how Xcode translates Objective-C to Swift. Apple's guide is a quick read. Or see this answer for an incomplete summary.
Example for Mixpanel:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Mixpanel.sharedInstanceWithToken("your-token")
return true
}
That's it!
Note: If you remove the bridging header file from your project, be sure to go into Build Settings and remove the value for "Objective-C Bridging Header" under "Swift Compiler - Code Generation".
You can read the nice post Swift & Cocoapods. Basically, we need to create a bridging header file and put all Objective-C headers there. And then we need to reference it from our build settings. After that, we can use the Objective-C code.
let manager = AFHTTPRequestOperationManager()
manager.GET(
"http://example.com/resources.json",
parameters: nil,
success: { (operation: AFHTTPRequestOperation!,
responseObject: AnyObject!) in
println("JSON: " + responseObject.description)
},
failure: { (operation: AFHTTPRequestOperation!,
error: NSError!) in
println("Error: " + error.localizedDescription)
})
Also have a look at Apple's document Using Swift with Cocoa and Objective-C as well.
I wrote a simple Xcode 6 project that shows how to mix C++, Objective-C and Swift code:
https://github.com/romitagl/shared/tree/master/C-ObjC-Swift/Performance_Console
In particular, the example calls an Objective-C and a C++ function from the Swift.
The key is to create a shared header, Project-Bridging-Header.h, and put the Objective-C headers there.
Please download the project as a complete example.
One more thing I would like to add here:
I am very thankful for #Logan's answer. It helps a lot to create a bridge file and setups.
But after doing all these steps I'm still not getting an Objective-C class in Swift.
I used the cocoapods library and integrated it into my project. Which is pod "pop".
So if you are using Objective-C pods in Swift then there may be a chance that you can not able to get or import the classes into Swift.
The simple thing you have to do is:
Go to <YOUR-PROJECT>-Bridging-Header file and
Replace the statement #import <ObjC_Framework> to #import ObjC_Framework
For example: (Pop library)
Replace
#import <pop/POP.h>
with
#import pop;
Use clang import when #import is not working.
Quote from the documentation:
Any Objective-C framework (or C library) that’s accessible as a module
can be imported directly into Swift. This includes all of the
Objective-C system frameworks—such as Foundation, UIKit, and
SpriteKit—as well as common C libraries supplied with the system. For
example, to import Foundation, simply add this import statement to the
top of the Swift file you’re working in:
import Foundation
This import makes all of the Foundation APIs—including NSDate, NSURL,
NSMutableData, and all of their methods, properties, and
categories—directly available in Swift.
Just a note for whoever is trying to add an Objective-C library to Swift: You should add -ObjC in Build Settings -> Linking -> Other Linker Flags.
After you created a Bridging header, go to Build Setting => Search for "Objective-C Bridging Header".
Just below you will find the ""Objective-C Generated Interface Header Name" file.
Import that file in your view controller.
Example: In my case: "Dauble-Swift.h"
Mixing Objective-C and Swift
Hight level diagram:
Same Target
//Objective-C exposes API for Swift through <target_name>-Bridging-Header.h
.swift uses .h.m = Swift consumer, Objective-C producer = <target_name>-Bridging-Header.h
//Swift exposes API for Objective-C through <target_name>-Swift.h
.h.m uses .swift = Objective-C consumer, Swift producer = <target_name>-Swift.h
Swift consumer, Objective-C producer
Add a new header .h and Implementation .m files - Cocoa class file(Objective-C)
For example <MyFileName>.h and <MyFileName>.m
Configure bridging header
When you see Would you like to configure an Objective-C bridging header click - Yes
<target_name>-Bridging-Header.h will be generated automatically
Build Settings -> Objective-C Bridging Header(SWIFT_OBJC_BRIDGING_HEADER)
Add Class to Bridging-Header
In <target_name>-Bridging-Header.h add a line #import "<MyFileName>.h"
After that you can use from Swift Objective-C's MyFileName.h,MyFileName.m
P.S. If you should add an existing Objective-C file into Swift project add Bridging-Header.h beforehand and import it
Objective-C consumer, Swift producer
Add a <MyFileName>.swift and extends NSObject
Import Swift Files to ObjC Class
Add #import "<target_name>-Swift.h" into your Objective-C file
Expose public Swift code by #objc [About]
After that you are able to use from Objective-C Swift's <MyFileName>.swift
[<product_name>-Swift.h file not found]
Different Targets
[.modulemap] and [umbrella.h] are used for exposing Objective-C for Swift. And Swift.h for exposing Swift code for Objective-C
[Vocabulary]
Click on the New file menu, and chose file select language Objective. At that time it automatically generates a "Objective-C Bridging Header" file that is used to define some class name.
"Objective-C Bridging Header" under "Swift Compiler - Code Generation".
Create a .h file from NewFile -> Source -> header file
Then save the name of file Your_Target_Name-Bridging-Header.h People here gets common mistake by taking their project name but it should be the Project's Target's name if in case both are different, generally they are same.
Then in build settings search for Objective-C Bridging Header flag and put the address of your newly created bridging file, you can do it right click on the file -> show in finder -> drag the file in the text area then the address will be populated.
Using #import Your_Objective-C_file.h
In the swift file you can access the ObjC file but in swift language only.
In the Swift 4.2.1 project in Xcode 10.1 you can easily add Objective-C file. Follow the steps below to bridge Objective-C file to Swift project.
Step_01: Create new Xcode project using Swift language:
File > New > Project > objc.
Step_02: In Swift project add new Objective-C file:
File > New > File... > macOS > Objective-C File.
Step_03: If you add a new Objective-C file into Swift project at very first time, Xcode asks you:
Would you like to configure an Objective-C bridging header?
Step_04: Select the option:
Create Bridging Header.
Step_05: A corresponding file will be generated with a name:
Objc-Bridging-Header.h.
Step_06: Now, you need setup Bridge file path in bridge header. In Project Navigator click on project with name objc and then choose:
Build Settings > Objective-C Bridging Header > Objc-Bridging-Header.h.
Step_07: Drag-and-drop your Objc-Bridging-Header.h into that box to generate a file path.
Step_08: Open your Objc-Bridging-Header.h file and import the Objective-C file which you want to use in your Swift file.
#import "SpecialObjcFile.m"
Here's a content of SpecialObjcFile.m:
#import <Foundation/Foundation.h>
#interface Person: NSObject {
#public
bool busy;
}
#property
bool busy;
#end
Step_09: Now in your Swift file, you can use an Objective-C class:
override func viewDidLoad() {
super.viewDidLoad()
let myObjcContent = Person()
print(myObjcContent.busy)
}
Logans answer works fine except in latest Swift 5 it gives some compiler error. Here is the fix for people who are working on Swift 5.
Swift 5
import Foundation
class MySwiftObject : NSObject {
var someProperty: AnyObject = "Some Initializer Val" as AnyObject
override init() {}
func someFunction(someArg:AnyObject) -> String {
let returnVal = "You sent me \(someArg)"
return returnVal
}
}
Making any Swift class subclass of NSObject makes sense also I prefer for using any Swift class to be seen in Objective-C classes like:
#objc(MySwiftClass)
#objcMembers class MySwiftClass {...}
Apple has provided official guide in this doc:
how-to-call-objective-c-code-from-swift
Here is the relevant part:
To import a set of Objective-C files into Swift code within the same app target, you rely on an Objective-C bridging header file to expose those files to Swift. Xcode offers to create this header when you add a Swift file to an existing Objective-C app, or an Objective-C file to an existing Swift app.
If you accept, Xcode creates the bridging header file along with the file you were creating, and names it by using your product module name followed by "-Bridging-Header.h". Alternatively, you can create a bridging header yourself by choosing File > New > File > [operating system] > Source > Header File
Edit the bridging header to expose your Objective-C code to your Swift code:
In your Objective-C bridging header, import every Objective-C header you want to expose to Swift.
In Build Settings, in Swift Compiler - Code Generation, make sure the Objective-C Bridging Header build setting has a path to the bridging header file. The path should be relative to your project, similar to the way your Info.plist path is specified in Build Settings. In most cases, you won't need to modify this setting.
Any public Objective-C headers listed in the bridging header are visible to Swift.
Two way Approach to use objective-c objective-c
1
Create bridge-header.h file in Xcode Project
import .h file in bridge-Header file
Set path of bridge-Header in Build settings.
Clean the Project
2
Create objective-c files in project(it automatically set path in Build Settings for you )
import .h file in bridge-Header file
Now good to go
Thanks

Swift autogenerated header missing new swift classes

I am working in this project where I have a mix of old pre-ARC et ARC Objective-c files co-existing with swift since the start of the year. For some reason, that I can not find, to day all new Swift files I have added set to public are not being added to the Swift auto-generated header.
I have check target ownerships, build phase, that it is public as #objc and inherits NSObject. still I can not call the class from Objc and I can from other swift class. it is as if it was set to be private to the swift module or something even after setting it access public..
for example :
#objc public class SomeSwiftClass: NSObject
{
public func sayit()
{
print("Hello!")
}
}
this does not show in any of the OBjc classes where project-swift.h is imported. I am able to call previously created classes, but not the one I added today.
I would guess that some setting is wrong if all my classes are no longer available, but no just the new class !!?!??!?!?!?!?!?!?!
Edit:
- restarting Xcode does not fix it.
- rebooting system does not fix it.
- deleting derived data does not fix it.
strange thing building while having 1 line of code in Objc accessing the swift class does not cause an error. So Its looking like some type of problem with code completion.

#import "Project-Swift.h" File does not refresh

I have a project with Swift and Objective-C code. In my current project I have the problem that the #import "Project-Swift.h" file does not refresh if I add new Swift modules.
#import "Project-Swift.h" // does not refresh if I add .swift files
So in the Objective-C universe, the swift code is not available. the files exists, but does only contains default #defines etc. No project related stuff.
Clear Cache, rebuild, delete Derived Data does not help.
Solved it. Forgot #objc(<class>). It is mandatory.
Details here (Migrating)
Migrating Objective-C Code to Swift
And here (Swift Type Compatibility)
Interacting with Objective-C APIs
In my case it worked by building for the other target I had on my project:
Check you have prefixed your class with #objc
Choose the other scheme (target)
Build || Run
The bridging file updates
Change the scheme back to the target I was working on
Just use #objc before Swift Class that you want to use in Object-C code then Build the project.
For example:
#objc class Person : NSObject
{
// Swfit code goes here.
}
Now #import "Project-Swift.h" will be available in you project to use.

How do I call Objective-C code from Swift?

In Swift, how does one call Objective-C code?
Apple mentioned that they could co-exist in one application, but does this mean that one could technically re-use old classes made in Objective-C whilst building new classes in Swift?
Using Objective-C Classes in Swift
If you have an existing class that you'd like to use, perform Step 2 and then skip to Step 5. (For some cases, I had to add an explicit #import <Foundation/Foundation.h to an older Objective-C File.)
Step 1: Add Objective-C Implementation -- .m
Add a .m file to your class, and name it CustomObject.m.
Step 2: Add Bridging Header
When adding your .m file, you'll likely be hit with a prompt that looks like this:
Click Yes!
If you did not see the prompt, or accidentally deleted your bridging header, add a new .h file to your project and name it <#YourProjectName#>-Bridging-Header.h.
In some situations, particularly when working with Objective-C frameworks, you don't add an Objective-C class explicitly and Xcode can't find the linker. In this case, create your .h file named as mentioned above, then make sure you link its path in your target's project settings like so:
Note:
It's best practice to link your project using the $(SRCROOT) macro so that if you move your project, or work on it with others using a remote repository, it will still work. $(SRCROOT) can be thought of as the directory that contains your .xcodeproj file. It might look like this:
$(SRCROOT)/Folder/Folder/<#YourProjectName#>-Bridging-Header.h
Step 3: Add Objective-C Header -- .h
Add another .h file and name it CustomObject.h.
Step 4: Build your Objective-C Class
In CustomObject.h
#import <Foundation/Foundation.h>
#interface CustomObject : NSObject
#property (strong, nonatomic) id someProperty;
- (void) someMethod;
#end
In CustomObject.m
#import "CustomObject.h"
#implementation CustomObject
- (void) someMethod {
NSLog(#"SomeMethod Ran");
}
#end
Step 5: Add Class to Bridging-Header
In YourProject-Bridging-Header.h:
#import "CustomObject.h"
Step 6: Use your Object
In SomeSwiftFile.swift:
var instanceOfCustomObject = CustomObject()
instanceOfCustomObject.someProperty = "Hello World"
print(instanceOfCustomObject.someProperty)
instanceOfCustomObject.someMethod()
There is no need to import explicitly; that's what the bridging header is for.
Using Swift Classes in Objective-C
Step 1: Create New Swift Class
Add a .swift file to your project, and name it MySwiftObject.swift.
In MySwiftObject.swift:
import Foundation
#objc(MySwiftObject)
class MySwiftObject : NSObject {
#objc
var someProperty: AnyObject = "Some Initializer Val" as NSString
init() {}
#objc
func someFunction(someArg: Any) -> NSString {
return "You sent me \(someArg)"
}
}
Step 2: Import Swift Files to ObjC Class
In SomeRandomClass.m:
#import "<#YourProjectName#>-Swift.h"
The file:<#YourProjectName#>-Swift.h should already be created automatically in your project, even if you can not see it.
Step 3: Use your class
MySwiftObject * myOb = [MySwiftObject new];
NSLog(#"MyOb.someProperty: %#", myOb.someProperty);
myOb.someProperty = #"Hello World";
NSLog(#"MyOb.someProperty: %#", myOb.someProperty);
NSString * retString = [myOb someFunctionWithSomeArg:#"Arg"];
NSLog(#"RetString: %#", retString);
Notes:
If Code Completion isn't behaving as you expect, try running a quick build with ⌘⇧R to help Xcode find some of the Objective-C code from a Swift context and vice versa.
If you add a .swift file to an older project and get the error dyld: Library not loaded: #rpath/libswift_stdlib_core.dylib, try completely restarting Xcode.
While it was originally possible to use pure Swift classes (Not descendents of NSObject) which are visible to Objective-C by using the #objc prefix, this is no longer possible. Now, to be visible in Objective-C, the Swift object must either be a class conforming to NSObjectProtocol (easiest way to do this is to inherit from NSObject), or to be an enum marked #objc with a raw value of some integer type like Int. You may view the edit history for an example of Swift 1.x code using #objc without these restrictions.
See Apple's guide to Using Swift with Cocoa and Objective-C. This guide covers how to use Objective-C and C code from Swift and vice versa and has recommendations for how to convert a project or mix and match Objective-C/C and Swift parts in an existing project.
The compiler automatically generates Swift syntax for calling C functions and Objective-C methods. As seen in the documentation, this Objective-C:
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
turns into this Swift code:
let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
Xcode also does this translation on the fly — you can use Open Quickly while editing a Swift file and type an Objective-C class name, and it'll take you to a Swift-ified version of the class header. (You can also get this by cmd-clicking on an API symbol in a Swift file.) And all the API reference documentation in the iOS 8 and OS X v10.10 (Yosemite) developer libraries is visible in both Objective-C and Swift forms (e.g. UIView).
Here are step-by-step instructions for using Objective-C code (in this case, a framework provided by a third-party) in a Swift project:
Add any Objective-C file to your Swift project by choosing File -> New -> New File -> Objective-C File. Upon saving, Xcode will ask if you want to add a bridging header. Choose 'Yes'.
(source: derrrick.com)
In simple steps:
A prompt appears, and then click on OK... If it does not appear, then we create it manually like in the following... Create one header file from iOS source and give the name ProjectName-Bridging-Header (example: Test-Bridging-Header), and then go to build setting in the Swift compiler code -> Objective-C bridge add Objective-C bridge name ..(Test/Test-Bridging-Header.h). Yeah, that's complete.
Optionally, delete the Objective-C file you added (named "anything" in the GIF image above). You don't need it any more.
Open the bridging header file -- the filename is of the form [YourProject]-Bridging-Header.h. It includes an Xcode-provided comment. Add a line of code for the Objective-C file you want to include, such as a third-party framework. For example, to add Mixpanel to your project, you will need to add the following line of code to the bridging header file:
#import "Mixpanel.h"
Now in any Swift file you can use existing Objective-C code, in the Swift syntax (in the case of this example, and you can call Mixpanel SDK methods, etc.). You need to familiarize yourself with how Xcode translates Objective-C to Swift. Apple's guide is a quick read. Or see this answer for an incomplete summary.
Example for Mixpanel:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Mixpanel.sharedInstanceWithToken("your-token")
return true
}
That's it!
Note: If you remove the bridging header file from your project, be sure to go into Build Settings and remove the value for "Objective-C Bridging Header" under "Swift Compiler - Code Generation".
You can read the nice post Swift & Cocoapods. Basically, we need to create a bridging header file and put all Objective-C headers there. And then we need to reference it from our build settings. After that, we can use the Objective-C code.
let manager = AFHTTPRequestOperationManager()
manager.GET(
"http://example.com/resources.json",
parameters: nil,
success: { (operation: AFHTTPRequestOperation!,
responseObject: AnyObject!) in
println("JSON: " + responseObject.description)
},
failure: { (operation: AFHTTPRequestOperation!,
error: NSError!) in
println("Error: " + error.localizedDescription)
})
Also have a look at Apple's document Using Swift with Cocoa and Objective-C as well.
I wrote a simple Xcode 6 project that shows how to mix C++, Objective-C and Swift code:
https://github.com/romitagl/shared/tree/master/C-ObjC-Swift/Performance_Console
In particular, the example calls an Objective-C and a C++ function from the Swift.
The key is to create a shared header, Project-Bridging-Header.h, and put the Objective-C headers there.
Please download the project as a complete example.
One more thing I would like to add here:
I am very thankful for #Logan's answer. It helps a lot to create a bridge file and setups.
But after doing all these steps I'm still not getting an Objective-C class in Swift.
I used the cocoapods library and integrated it into my project. Which is pod "pop".
So if you are using Objective-C pods in Swift then there may be a chance that you can not able to get or import the classes into Swift.
The simple thing you have to do is:
Go to <YOUR-PROJECT>-Bridging-Header file and
Replace the statement #import <ObjC_Framework> to #import ObjC_Framework
For example: (Pop library)
Replace
#import <pop/POP.h>
with
#import pop;
Use clang import when #import is not working.
Quote from the documentation:
Any Objective-C framework (or C library) that’s accessible as a module
can be imported directly into Swift. This includes all of the
Objective-C system frameworks—such as Foundation, UIKit, and
SpriteKit—as well as common C libraries supplied with the system. For
example, to import Foundation, simply add this import statement to the
top of the Swift file you’re working in:
import Foundation
This import makes all of the Foundation APIs—including NSDate, NSURL,
NSMutableData, and all of their methods, properties, and
categories—directly available in Swift.
Just a note for whoever is trying to add an Objective-C library to Swift: You should add -ObjC in Build Settings -> Linking -> Other Linker Flags.
After you created a Bridging header, go to Build Setting => Search for "Objective-C Bridging Header".
Just below you will find the ""Objective-C Generated Interface Header Name" file.
Import that file in your view controller.
Example: In my case: "Dauble-Swift.h"
Mixing Objective-C and Swift
Hight level diagram:
Same Target
//Objective-C exposes API for Swift through <target_name>-Bridging-Header.h
.swift uses .h.m = Swift consumer, Objective-C producer = <target_name>-Bridging-Header.h
//Swift exposes API for Objective-C through <target_name>-Swift.h
.h.m uses .swift = Objective-C consumer, Swift producer = <target_name>-Swift.h
Swift consumer, Objective-C producer
Add a new header .h and Implementation .m files - Cocoa class file(Objective-C)
For example <MyFileName>.h and <MyFileName>.m
Configure bridging header
When you see Would you like to configure an Objective-C bridging header click - Yes
<target_name>-Bridging-Header.h will be generated automatically
Build Settings -> Objective-C Bridging Header(SWIFT_OBJC_BRIDGING_HEADER)
Add Class to Bridging-Header
In <target_name>-Bridging-Header.h add a line #import "<MyFileName>.h"
After that you can use from Swift Objective-C's MyFileName.h,MyFileName.m
P.S. If you should add an existing Objective-C file into Swift project add Bridging-Header.h beforehand and import it
Objective-C consumer, Swift producer
Add a <MyFileName>.swift and extends NSObject
Import Swift Files to ObjC Class
Add #import "<target_name>-Swift.h" into your Objective-C file
Expose public Swift code by #objc [About]
After that you are able to use from Objective-C Swift's <MyFileName>.swift
[<product_name>-Swift.h file not found]
Different Targets
[.modulemap] and [umbrella.h] are used for exposing Objective-C for Swift. And Swift.h for exposing Swift code for Objective-C
[Vocabulary]
Click on the New file menu, and chose file select language Objective. At that time it automatically generates a "Objective-C Bridging Header" file that is used to define some class name.
"Objective-C Bridging Header" under "Swift Compiler - Code Generation".
Create a .h file from NewFile -> Source -> header file
Then save the name of file Your_Target_Name-Bridging-Header.h People here gets common mistake by taking their project name but it should be the Project's Target's name if in case both are different, generally they are same.
Then in build settings search for Objective-C Bridging Header flag and put the address of your newly created bridging file, you can do it right click on the file -> show in finder -> drag the file in the text area then the address will be populated.
Using #import Your_Objective-C_file.h
In the swift file you can access the ObjC file but in swift language only.
In the Swift 4.2.1 project in Xcode 10.1 you can easily add Objective-C file. Follow the steps below to bridge Objective-C file to Swift project.
Step_01: Create new Xcode project using Swift language:
File > New > Project > objc.
Step_02: In Swift project add new Objective-C file:
File > New > File... > macOS > Objective-C File.
Step_03: If you add a new Objective-C file into Swift project at very first time, Xcode asks you:
Would you like to configure an Objective-C bridging header?
Step_04: Select the option:
Create Bridging Header.
Step_05: A corresponding file will be generated with a name:
Objc-Bridging-Header.h.
Step_06: Now, you need setup Bridge file path in bridge header. In Project Navigator click on project with name objc and then choose:
Build Settings > Objective-C Bridging Header > Objc-Bridging-Header.h.
Step_07: Drag-and-drop your Objc-Bridging-Header.h into that box to generate a file path.
Step_08: Open your Objc-Bridging-Header.h file and import the Objective-C file which you want to use in your Swift file.
#import "SpecialObjcFile.m"
Here's a content of SpecialObjcFile.m:
#import <Foundation/Foundation.h>
#interface Person: NSObject {
#public
bool busy;
}
#property
bool busy;
#end
Step_09: Now in your Swift file, you can use an Objective-C class:
override func viewDidLoad() {
super.viewDidLoad()
let myObjcContent = Person()
print(myObjcContent.busy)
}
Logans answer works fine except in latest Swift 5 it gives some compiler error. Here is the fix for people who are working on Swift 5.
Swift 5
import Foundation
class MySwiftObject : NSObject {
var someProperty: AnyObject = "Some Initializer Val" as AnyObject
override init() {}
func someFunction(someArg:AnyObject) -> String {
let returnVal = "You sent me \(someArg)"
return returnVal
}
}
Making any Swift class subclass of NSObject makes sense also I prefer for using any Swift class to be seen in Objective-C classes like:
#objc(MySwiftClass)
#objcMembers class MySwiftClass {...}
Apple has provided official guide in this doc:
how-to-call-objective-c-code-from-swift
Here is the relevant part:
To import a set of Objective-C files into Swift code within the same app target, you rely on an Objective-C bridging header file to expose those files to Swift. Xcode offers to create this header when you add a Swift file to an existing Objective-C app, or an Objective-C file to an existing Swift app.
If you accept, Xcode creates the bridging header file along with the file you were creating, and names it by using your product module name followed by "-Bridging-Header.h". Alternatively, you can create a bridging header yourself by choosing File > New > File > [operating system] > Source > Header File
Edit the bridging header to expose your Objective-C code to your Swift code:
In your Objective-C bridging header, import every Objective-C header you want to expose to Swift.
In Build Settings, in Swift Compiler - Code Generation, make sure the Objective-C Bridging Header build setting has a path to the bridging header file. The path should be relative to your project, similar to the way your Info.plist path is specified in Build Settings. In most cases, you won't need to modify this setting.
Any public Objective-C headers listed in the bridging header are visible to Swift.
Two way Approach to use objective-c objective-c
1
Create bridge-header.h file in Xcode Project
import .h file in bridge-Header file
Set path of bridge-Header in Build settings.
Clean the Project
2
Create objective-c files in project(it automatically set path in Build Settings for you )
import .h file in bridge-Header file
Now good to go
Thanks