Call a Swift Singleton from Objective-C - objective-c

I'm having some trouble accessing a Swift Singleton from Objective-C.
#objc class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
}
swiftSharedInstance can not be reached.

Nicky Goethlis's answer is correct but I just want to add another way of Singleton creation termed as One line Singleton" in Swift which I came across recently and it does not use Struct:
Singleton.swift
#objc class Singleton: NSObject {
static let _singletonInstance = Singleton()
private override init() {
//This prevents others from using the default '()' initializer for this class.
}
// the sharedInstance class method can be reached from ObjC. (From OP's answer.)
class func sharedInstance() -> Singleton {
return Singleton._singletonInstance
}
// Some testing
func testTheSingleton() -> String {
return "Hello World"
}
}
SomeObjCFile.m
Singleton *singleton = [Singleton sharedInstance];
NSString *testing = [singleton testTheSingleton];
NSLog(#"Testing---> %#",testing);

Swift 5 and above
final class Singleton: NSObject {
#objc static let shared = Singleton()
#objc var string: String = "Hello World"
private override init() {}
}
use in Objective-C
#import <ProjectName-Swift.h> // change ProjectName to actual project name
NSLog("Singleton String = %#", [Singleton shared].string);

For now I have the following solution. Maybe I am overlooking something that would enable me to access "swiftSharedInstance" directly?
#objc class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
// the sharedInstance class method can be reached from ObjC
class func sharedInstance() -> SingletonTest {
return SingletonTest.swiftSharedInstance
}
// Some testing
func testTheSingleton() -> String {
return "Hello World"
}
}
Then in ObjC I can get the sharedInstance class method (after importing the xcode generated swift header bindings)
SingletonTest *aTest = [SingletonTest sharedInstance];
NSLog(#"Singleton says: %#", [aTest testTheSingleton]);

To make members of the SingletonTest class accessible (swiftSharedInstance is a member of this class), use #objcMembers modifier on the class, or add #objc modifier directly on the swiftSharedInstance:
#objc #objcMembers class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
}
Or:
#objc class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
#objc class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
}

After creating the Bridging header, be sure to have the Objective-C Generated Interface Header Name set in your Build Settings from your app target. If the value is empty, add the following value:
$(SWIFT_MODULE_NAME)-Swift.h
You need add #objc property wrapper to your singleton:
#objc final class Singleton: NSObject {
#objc static let sharedInstance = Singleton()
#objc func foo() { }
}
Then, in the Objective-C class, import the following:
// Replace the "App" with your Target name.
#import "App-Swift.h"
Finally, after compiling the project, you will be able to use your singleton from Swift inside your Objective-C class:
[[Singleton sharedInstance]foo];

You pretty much have it. To use Swift classes in Obj-C you both need to #import "SingletonTest-Swift.h the generated header or forward declaration with #class MySwiftClass.
Additionally the class needs to inherit from an Obj-C class like you have don here with NSObject or be marked with #objc to expose it. You don't need to do both though, #objc is there to be a more granular option when choosing things to expose.
Apple has some good documentation on all of this and there are two different WWDC sessions you can watch on the topic of Obj-C interoperability as well.

Don't forget to set sharedInstance as public
public final class TestSwiftMain: NSObject {
#objc public static let sharedInstance = TestSwiftMain()
private override init() {}
#objc public func test() {
print("testing swift framework")
}
}
Using it in Objc
[[testSwiftMain sharedInstance] test];

Update 12 Oct 2022
ObcMember So you won't have to write objC behind every function
Swift Class
#objcMembers class SwiftHelpingExtentions: NSObject {
static let instanceShared = SwiftHelpingExtentions()
func testingMethod() {
print("testing")
}
}
}
On objective C View Controller
import : #import "App-Swift.h"
Call The method:
[SwiftHelpingExtentions.instanceShared testingMethod];

Related

Can't call custom swift init from objective-c class

I have a custom class with a custom init written in swift. I need to call that init from an Objective-C class.
Swift
#objc public class MyClass: NSObject {
public init(configuration config: Data)
{
super.init()
// Do Stuff
}
}
Objective-C
[[MyClass alloc] initWithConfiguration:CONFIG];
But when I call the init from Objective-C the compiler complains that
No visible #interface for 'MyClass' declares the selector
'initWithConfiguration:'
What am I missing here?
You have to add #objc attribute to initializers too. Like this:
#objc public init(configuration config: Data)
{
super.init()
// Do Stuff
}
And after this, don't forget to re-build (CMD+B), otherwise Xcode will stupidly emit an error.

resolveInstanceMethod calling from Swift code

How can I call + (BOOL)resolveInstanceMethod:(SEL)aSEL while changing dynamic variable variable name?
E.g.
I've got an ObjC class:
#interface SomeClass: NSObject
///
#end
#implementation SomeClass: NSObject
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
//// code
}
#end
Swift
#objcMembers
public final class SomeSwiftClass: SomeClass {
public dynamic var value: NSNumber?
public dynamic func mmFunc() {
print("mmFunc")
}
}
code:
let anInstance = SomeSwiftClass()
anInstance.value = NSNumber(10) /// how can I call resolveInstanceMethod? Doesn't work
anInstance.mmFunc() //doesn't work too.
If I implement the same code in ObjC it works.
Any ideas?
All the following works w/o changes in your classes (tested with Xcode 11.2 / iOS 13.2).
func test() {
let anInstance = SomeSwiftClass()
let hasMethod = SomeSwiftClass.resolveInstanceMethod(#selector(SomeSwiftClass.mmFunc))
print("Result: \(hasMethod)")
anInstance.value = NSNumber(10)
anInstance.mmFunc()
}
if not at your side, see if objc-swift bridging is correctly configured and all needed includes are added.
Update: compiler was confused by ambiguous name of value, changing it to other more specific name works as below
#interface SomeClass: NSObject
#property (nonatomic) BOOL isValid;
#end
#objc
public class SomeSwiftClass: SomeClass {
#objc public var specificValue: NSNumber = NSNumber(5)
#objc public func mmFunc() {
print("mmFunc")
}
}
#objc
final class Runner: NSObject {
#objc public static func test() {
let anInstance = SomeSwiftClass()
let hasProperty = SomeSwiftClass.resolveInstanceMethod(#selector(getter: SomeSwiftClass.isValid))
print("Has property: \(hasProperty)")
let swiftProperty = SomeSwiftClass.resolveInstanceMethod(#selector(getter: SomeSwiftClass.specificValue))
print("Swift property: \(swiftProperty)")
let hasMethod = SomeSwiftClass.resolveInstanceMethod(#selector(SomeSwiftClass.mmFunc))
print("Has method: \(hasMethod)")
anInstance.specificValue = NSNumber(10)
anInstance.mmFunc()
}
}

Exposing a Swift variable in Objective-C

I am using a Swift class which uses a variable sharedInstance. When I am trying to use this variable in my Objective-C file, I am getting the error:
No known class method for selector 'sharedInstance'
My Swift class:
class SpeakToMeForUnity : NSObject, SFSpeechRecognizerDelegate {
fileprivate var speechRecognizer : SFSpeechRecognizer?
fileprivate var recognitionRequest : SFSpeechAudioBufferRecognitionRequest?
fileprivate var recognitionTask : SFSpeechRecognitionTask?
fileprivate var audioEngine : AVAudioEngine?
static let sharedInstance = SpeakToMeForUnity()
override fileprivate init() {
super.init()
self.speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))!
self.audioEngine = AVAudioEngine()
self.speechRecognizer?.delegate = self
}
// Some other functions
}
Below is my Objective-C file:
#import <Speech/Speech.h>
#import "SpeakToMeForUnity-Swift.h"
extern "C"
{
void _prepareRecording()
{
SpeakToMeForUnity *instance = [SpeakToMeForUnity sharedInstance];
[instance prepareRecording];
}
void _recordButtonTapped()
{
SpeakToMeForUnity *instance = [SpeakToMeForUnity sharedInstance];
[instance recordButtonTapped];
}
}
I am using Swift 4 and Xcode 10.3.
Normally to make Swift available in Objective-C you just need to import your swift module #import "ProductModuleName-Swift.h", as you're doing.
If you need the swift code in the Objective-C header use #class MySwiftClass; or #protocol MySwiftClass;.
You can force some class or property to be accessible in Objective-C by adding the attribute #objc.
Remember that private or fileprivate access level attributes will not be accessible unless you add the previous attribute.
To expose things to ObjC, you need to mark them #objc. In this case you should add this before class and before the static let.
In previous versions of Swift, inheritance from NSObject automatically implied #objc, but that was removed, and it now needs to be explicit.

Invoke Swift closure in Objective-C

I want to invoke a Swift closure in Objective-C.
There is an error like this even though I declared the function:
No visible #interface for “User” declares the selector “isReady”
Swift:
#objcMember
class User:NSObject {
func isReady(isTrue: Bool) -> Bool {
return true
}
}
Objective-C:
User *user = [[User alloc] init];
[_user isReady]. <- error
Add to the function #objc modifier:
#objcMember
class User:NSObject {
#objc public func isReady(isTrue: Bool) -> Bool {
return true
}
}
And add public modifier to the function to allow access from other modules (swift code builds as module and ObjC code should export it and access via open interfaces).

objective-c equivalent to java anonymouse interface implementation

I hope the title is precise enough.
I was wondering, how I can pass a interface implementation to an object in objc language.
In java it would look like:
public interface MyInterface {
void onData();
}
The implementing class
public class ImplementMyInterface {
// ...
// other methods
///
void registerInterface(){
MyInterface myInterface = new MyInterface(){
#Override
public void onData(){
// process data here within the class
}
};
}
}
And in objc?
id myinterface.
How to implement it in the class?
Is there only the possibility to let the class inherit the interface?
Like
interface MyInterface : NSObject
and the implementing class
MyImplementingClass : MyInterface
Or is there another possibility?
Thank you in advance
Objective-C has anonymous functions (blocks), but it doesn't have anonymous classes. So, the only way to implement a protocol (which is the objective-c term for an interface) is to make some class conform to that protocol (using your terminology, make that class "inherit" from that protocol) and add a protocol implementation inside that class' implementation.
I was able to solve my problem.
I was only able to import the MyInterface header file in my ImplementMyInterface.m file, but rather in the ImplementMyInterface.h file.
So everything I could do was inside the ImplementMyInterface.m file.
// ImplementMyInterface.m
#import "MyInterface.h"
// inner class
#interface MyInternalInterface : NSObject<MyInterface>
#property (retain) ImplementMyInterface * implementation;
#end
// the actual class
#implementation ImplementMyInterface
MyInternalInterface * _internalInterface;
+(instancetype) build {
// construct myself
ImplementMyInterface * implementatMyInterface = [[ImplementMyInterface alloc] init];
// init inner class object
_internalInterface = [[MyInternalInterface alloc] init];
// register myself
[_internalInterface setImplementation:implementatMyInterface];
return implementatMyInterface;
}
- (NSString *) theActualData {
return #"The actual data";
}
// end of implementation class
#end
// implementation of inner class
#implementation MyInternalInterface
#synthesize implementation;
- (NSString *) onData {
if(implementation != nil)
return [implementation theActualData];
return #"";
}
// end of ImplementMyInterface.m