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

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).

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];

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

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.

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

Exposing Swift internal func to Objective-C file(in framework) without making it public

I have a Swift framework in which I have an Objective-C file. From that file I want to access a method which is declared as internal. I know that I can change it to public to make it available. But I don't want to expose it to the client app. Is there any way to achieve this?
In .swift:
#objc internal class func callBack(str: String) {
print("Swift method was called | Passed value: " + str)
}
In .m:
- (void)callSwiftFunc {
// This is not available, only if I set public in Swift
[SwiftClass callBackWithStr:#"blabla"];
}
You can explicitly set the objc selector and then add a category which declares the method:
replace #objc with #objc(callBackWithStr:), then simply add a forward declaration:
#interface SwiftClass ()
+ (void)callBackWithStr:(NSString *)string;
#end

iOS - Can't access Swift Singleton from Objective-C

I can't access my Swift Singleton Class from my Objective-C ViewController.
Xcode does recognize my Swift Class, it builds, so I don't think it is a bridging header issue.
Here is my Swift Singleton Class :
#objc class MySingleton : NSObject {
static let shared = MySingleton()
private override init() {}
}
And then in my .m file I import this header :
#import "myProject-Swift.h"
And use the Singleton this way :
MySingleton *testSingleton = [MySingleton shared];
or this way :
[MySingleton shared];
It does recognize the MySingleton class type, but I can't access any functions or properties of this class.
What am I missing? All the similar posts didn't help.
EDIT: I made a test function
func testPrint() {
print("Singleton worked")
}
And called it this way in the objective-c file :
[[MySingleton shared] testPrint];
No known instance for selector 'testPrint'
In your Swift Singleton class, add #objc for the method testPrint() like:
#objc func testPrint() {
print("Singleton worked")
}
Now, you access the method from Objective-C class:
[[MySingleton shared] testPrint];
use #objc attribute before the testPrint() function. refer documentation
To be accessible and usable in Objective-C, a Swift class must be a descendant of an Objective-C class or it must be marked #objc.
You need to add #objc to any property or function you want to expose to Objective-C, including the static shared property:
class MySingleton : NSObject {
#objc static let shared = MySingleton()
private override init() { }
#objc func testPrint() { print("Singleton worked") }
}
Then, the call from Objective-C file:
MySingleton *singleton = [MySingleton shared];
[singleton testPrint];
Note that marking the whole class as #objc isn't a replacement for the above. It is actually not needed anymore, and it's enough to inherit from NSObject.

Resources