How to call Swift from an Objective C project App Delegate? - ios

This code is from a Swift project App delegate. It is used to help configure Stripe with a publishable key.
//Appdelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
//The code helps configure Stripe with a publishable key.
STPPaymentConfiguration.shared().publishableKey = Constants.publishableKey
...
}
Two errors are displayed when building the app after adding the Swift line to the Objective C App Delegate
//AppDelegate.h
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
STPPaymentConfiguration.shared().publishableKey = Constants.publishableKey
Property 'shared' not found on object of type 'STPPaymentConfiguration'
Use of undeclared identifier 'Constants'
This was a similar error in compiling before #objc was added to the demo Swift function, MockApiClient. Should it be added elsewhere? I've tried adding #objc to the enum as mentioned in the answer here to no avail yet.
//Constants.swift
//This is the file the original Swift app delegate accesses
import Foundation
enum Constants {
static let publishableKey = "pk_live_..."
static let baseURLString = "http://54.33.123.227:1234"
static let defaultCurrency = "usd"
static let defaultDescription = "Receipt" //change to describe actual app & charge
}
Steps taken:
Opened the Objective C project and created a bridging header
Created a demo class in Swift while still in the Obj C project to make sure it can be used, in this case to print from an Objective C file when the view is loaded. Specifically derived from an NSObject. Adding the override to the initializer and using the #objc prefix.
// MockApiClient.swift
import Foundation
class MockApiClient: NSObject
{
override init()
{
print("Initializer called in Mock API client")
}
#objc func executeRequest()
{
print("The execute request has been called in the Mock API Client")
}
}
//ViewController.h
//Prints the Swift request written in the MockApiClient the the view loads
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MockApiClient *client = [MockApiClient new];
[client executeRequest];
}
Copied the #import "ViewController.h" import to the automatically generated project-Bridging-Header.h file to expose the Objective C in it to swift
Added the necessary Swift files to the Objective C project so that the Constants.publishablekey data from Constants.swift can be found
How can this Swift App delegate code be added to the App delegate of an Objective C project?
Edit: error when adding #objc to the enum declaration in Constants.swift

Edit: error when adding #objc to the enum declaration in Constants.swift
Swift enums used as namespace cannot be exposed to Objective-C.
You may need to use class to make it work both for Swift and Objective-C:
#objcMembers
class Constants: NSObject {
static let publishableKey = "pk_live_..."
static let baseURLString = "http://54.33.123.227:1234"
static let defaultCurrency = "usd"
static let defaultDescription = "Receipt" //change to describe actual app & charge
private override init() {}
}

The ability of Objective-C to see things defined in Swift depends on the automatically generated header file. This is not the bridging header. It is a header file buried in your derived data called YourProject-Swift.h. Your Objective-C .m file needs to #import "YourProject-Swift.h" (using the correct name).
Then, your Swift things need to get into that file. For that to happen, they need to be of a type that Objective-C can see at all (i.e. classes) and they need to be explicitly exposed to Objective-C with appropriate #objc attributes.

Related

How to access a method in a class written in swift using the app delegate

I am a newbie for objc. I have written a class exposing it to the JS side using swift. The content in that file looks like as follows,
import Foundation
#objc(AppLinkModule)
class AppLinkModule: NSObject{
#objc
static var appLink: String?
#objc
func setLink(link: String){
AppLinkModule.appLink = link;
}
#objc
func getLink(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
resolve(AppLinkModule.appLink);
AppLinkModule.appLink = nil;
}
}
The AppLinkModule.m file is as follows,
#import "React/RCTBridgeModule.h"
#interface RCT_EXTERN_MODULE(AppLinkModule, NSObject)
RCT_EXTERN_METHOD(getLink)
#end
What I actually want is to call the setLink method from the AppDelegate.m file or else directly set to the static variable appLink. Some instructions to achieve this will be greatly appreciated.
This looks fine.
If you want to call getLink() from your App Delegate, you'll need to import the Auto-generated Swift Bridging Header File that Xcode creates when you compile your code. This will allow the Objective-c AppDelegate.m file to find your exposed swift code.
#import <ProjectName-Swift.h> will allow your Swift to be exposed to the AppDelegate, and you already have marked the class and functions #objc so they will be visible.
Then you just need to either create a singleton or shared instance of your AppLinkModule (React Native Modules are all run statically) or you need to instantiate your module class.
Once you've done so, you can call this function in the AppDelegate like so:
[[[AppLinkModule ] shared ] getLink];

Is it possible to call objective C application delegate method from Swift class of the same project?

I'm merging my Swift project with already existing Objective-C code. I need to call some important methods of Swift class from objective C app delegate. I tried all methods given in net, but it was no use. Can any one help me out?
Yes, it's possible but with some limitations.
You can use only classes which inherited from NSObject, with public attribute and marked with #objc. At Objective-C code you should import "ProductModuleName-Swift.h" file which generated by compiler.
Here is an example of Swift class:
import Foundation
#objc public class ExampleClass: NSObject {
#objc public var someInstanceProperty = "Property"
#objc public func someFunction() {
print("Some function")
}
}
Notice that this class inherited from NSObject and have #objc and public attributes. After command+B you can take a look at generated bridge header through Assistance editor:
Then you should import the bridge header at your Objective-C class.
#import "ProductModuleName-Swift.h"
And then you can use your Swift class at Objective-C code like any other Objective-C class:
__auto_type const someClass = [ExampleClass new];
[someClass someFunction];
NSLog(#"%#", someClass.someInstanceProperty);
Here is an additional information from Apple:
Importing Swift into Objective-C

How to use Notification.Name extension from Swift to Objective-C?

I created an extension for Notification.Name as below:
public extension Notification.Name {
public static let blahblahblah = Notification.Name(rawValue: "blahblahblah")
}
Now I want to use this extension in Objective-C, but it's not accessible even if its public.
How can I access and use this Swift extension in both Objective-C and Swift?
Previously I was using constant values in Objective-C, but now I'm upgrading my code and want to use this extension.
My extension in swift file
extension Notification.Name {
static let purchaseDidFinish = Notification.Name("purchaseDidFinish")
}
#objc extension NSNotification {
public static let purchaseDidFinish = Notification.Name.purchaseDidFinish
}
// OBJECTIVE-C
#import YourProjectName-Swift.h
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(purchaseDidFinish) name:NSNotification.purchaseDidFinish object:nil];
// SWIFT
NotificationCenter.default.addObserver(self, selector: #selector(purchaseDidFinish), name: .purchaseDidFinish, object: nil)
#objc func purchaseDidFinish(notification: Notification) {
print("purchaseDidFinish")
}
#leanne's answer was super helpful
Notification.Name doesn't exist in Objective-C. And the Objective-C type NotificationName is really just an NSString. To use Swift stuff in Objective-C, the class must be available in both, and can't be a Swift struct (like Notification or String, say).
To do what you want, then, you need to have two extensions:
one for the Swift Notification.Name, as you have; and,
one for an Objective-C object (NSString, say, or perhaps NSNotification if you prefer).
1) Add an Objective-C-compatible object extension to your Swift file:
public extension NSNotification {
public static let blahblahblah: NSString = "blahblahblah"
}
Note: in Swift 4, properties must be computed for Objective-C compatibility. That would look like:
#objc public extension NSNotification {
public static var blahblahblah: NSString {
return "blahblahblah"
}
}
Note the var in the computed property: computed properties can't be immutable, so can't use let.
2) In the Objective-C file, import Xcode's generated Swift header file (below any other imports):
#import "YourProjectName-Swift.h"
Note: replace YourProjectName with the actual name of your project. So, if your project is named "CoolGameApp", the Swift header would be "CoolGameApp-Swift.h". If your project name has spaces, like "Cool Game App", replace them with dashes: "Cool-Game-App-Swift.h"
3) Rebuild the project.
Now, you should be able to access the extension in Objective-C:
[[NSNotificationCenter defaultCenter] postNotificationName:NSNotification.blahblahblah object:self];
In addition to the answers here, I had to add #objc to my NSNotification extension before my Obj-C code could see it (Swift 4).

Using GTM v5 TAGCustomFunction in Swift

I'm integrating GTM v5 (GTM + Firebase) in a Swift project, and I want to be able to call some methods when tags are triggered. However, it doesn't seem to work with Swift, although similar implementations in Objective C and Android projects did work.
Here's the class conforming to the TAGCustomFunction protocol :
import Foundation
import GoogleTagManager
final class Tags: NSObject, TAGCustomFunction {
func execute(withParameters parameters: [AnyHashable : Any]!) -> NSObject! {
print("YEAH ! IT WORKS !")
return nil
}
}
Everything is working well, even though I see these kind of logs:
GoogleTagManager info: Processing logged event: applicationStart with parameters: (null)
But the log I'm printing is not showing…
I'm sure about the configuration of the container since this one is correctly loaded, and I use the exact same container for my Objective C project, in which it works perfectly.
I think TAGCustomFunction needs to have class/method #objc annotations, and the class-level #objc annotation needs to specify the class name, i.e.
import Foundation
import GoogleTagManager
#objc(Tags)
final class Tags: NSObject, TAGCustomFunction {
#objc func execute(withParameters parameters: [AnyHashable : Any]!) -> NSObject! {
print("YEAH ! IT WORKS !")
return nil
}
}
After reading this SO post, I suddenly remembered that a Swift project could embed some objective C classes.
The solution to my problem was ridiculously easy to set up once I realized that, eventhough I've never had to do it before.
I created a new Cocoa Touch Class like the following :
Here is the .m :
#import "MyCustomTagClass.h"
#import "MySwiftClass-Swift.h"
#implementation MyCustomTagClass
- (NSObject*)executeWithParameters:(NSDictionary*)parameters {
[MySwiftClass myMethod];
}
#end
And here is the .h :
#import <Foundation/Foundation.h>
#import <GoogleTagManager/TAGCustomFunction.h>
#interface MyCustomTagClass : NSObject <TAGCustomFunction>
- (NSObject*)executeWithParameters:(NSDictionary*)parameters;
#end
Note that I import a header for my Swift class, which is automatically generated by Xcode. Just add -Swift.h after the name of your class to import it, just as I did in the .m example above.
Last, but not least, update your Swift class with #objc annotations at class and method declaration lines :
import Foundation
#objc class MySwiftClass: NSObject {
//...
#objc static func myMethod() {
// do something...
}
}
I hope this helped !

How to access swift class in objective-c file

I have a swift project and i import a singleton, objective-c coded class in the project.
I tried to import the productname_swift.h file but no luck.
How can i access swift class in that singleton class?
Project made in Swift : To use Swift class in Objective C
To use Swift class in Objective C , follow given steps :
Create one Objective C class named User.
A popup display with "Would You like to configure an Objective-C bridging Header". Choose Create Bridging Header.
User.h
#import <Foundation/Foundation.h>
#interface User : NSObject
+(id) sharedUser ;
#end
User.m
#import "User.h"
#import "SwiftInObjectiveC-swift.h"
#implementation User
//Singleton of User
+(id)sharedUser{
static User *sharedUser = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedUser = [[self alloc] init];
//Class Method of ViewController.swift
[ViewController mySwiftClassFunction];
//Instance Method of ViewController.swift
ViewController *vc = [[ViewController alloc] init];
[vc mySwiftFunction];
});
return sharedUser;
}
-(void) myObjcectivecMethod {
ViewController *vc = [[ViewController alloc] init];
[vc mySwiftFunction];
}
Add #objc in your .swift class in front of Class name.
import UIKit
#objc class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mySwiftFunction() {
print("Swift Function")
}
class func mySwiftClassFunction (){
print("Swift Class Function")
}
}
Go to Build Settings.
Set Product Module Name : ProjectName
Set Defines Module : YES
Set Embedded Content Contains Swift : YES
Set Install Objective-C Compatibility Header : YES
Set Objective-C Bridging Header : SwiftInObjectiveC/SwiftInObjectiveC-Bridging-Header.h
Import Auto generated header "ProjectName-swift.h" in your *.m file.
Clean and Run your Project.
It will work!!!
Follow Apple link for Mix and Match for detailed information.
It is very simple use Swift classes in Objective-C. Just follow steps given below
Open your Objective-C project.
Add a new swift file to the project.
One default dialogue will open to create a bridging header (If it
does not open by default, add a bridging header file).
Go to build settings type Swift Compiler and you will see Your
ProjectName-Swift.h in Swift Compiler- General like below
Import you desired swift classes build and run.
Adding multiple Swift files to an Objective-C project.
Let’s say you want to add Class1.swift, Class2.swift, and Class3.swift file to the SwiftToObjC project
Add Class1.swift file to the project.
XCode will ask "Would You like to configure an Objective-C bridging Header"? You choose "Create Bridging Header". So the SwiftToObjC-Bridging-Header.h file as well as the SwiftToObjC-Swift.h file are generated.
In the SwiftToObjC-Bridging-Header.h file you add this line
#class Class1;
When you add Class2.swift and Class3.swift to the project, you also add these two classes to the SwiftToObjC-Bridging-Header.h file as follows.
#class Class1;
#class Class2;
#class Class3;


5. In the target SwiftToObjC, under Build Settings, Swift-Compiler General you should see these two settings:
Objective-C Bridging Header -> SwiftToObjC-Bridging-Header.h
Objective-C Generated Interface Header Name -> SwiftToObjC-Swift.h


6. In the target SwiftToObjC, under Build Settings, Define Module is set to YES.
If you want to access Class1 in say MyViewController, then in MyViewController.m, you should import.
#import "SwiftToObjC-Bridging-Header.h"
In your Class1.swift file, declare your class as “#objc public” as follows. The same #objc public is used for Class2 and Class3.

#objc public class Class1: UITableViewController
Note that: UITableViewController is only an example for demonstration purpose.
That’s all.

Resources