Swift functions are not included into autogenerated -Swift.h umbrella file - objective-c

I'm trying to access Swift class methods from Objective-C, following this guide
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
This is my Swift class:
import Foundation
#objc public class MySwiftClass: NSObject {
// modifiers public, open do not help
func Hello()->String {
return "Swift says hello!";
}
}
And here is an excerpt from the autogenerated "umbrella" file MyProductModuleName-Swift.h
SWIFT_CLASS("_TtC25_MyProductModuleName12MySwiftClass")
#interface MySwiftClass : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
It seems like XCode completely ignores methods of MySwiftClass, and as a result I can't access them from Objective-C. Xcode version is 9.2.
Is it a bug or I missed anything?

You need to add #objc before each function you want to access from Objective-C:
#objc public class MySwiftClass: NSObject {
// modifiers public, open do not help
#objc func Hello() -> String {
return "Swift says hello!";
}
}

Related

Unable to call some methods from an Objective-C class in a Swift extension of that class

I have an objective-c class something like:
#interface MyClass: NSObject { }
- (MyObject *)coolMethod;
#end
#implementation MyClass
- (MyObject *)coolMethod {
return [self doCoolStuff];
}
#end
...and a Swift extension something like:
extension MyClass {
#objc func addedMethodInSwift() {
let coolObj = coolMethod() // <<<< compiler error - method not found
}
}
The code will not compile, because the compiler cannot find the implementation of coolMethod(), however certain other methods from the Objective-C class can be called from the Swift extension.
I have checked and the Objective-C header file is included in the project's bridging header, so it should be visible to Swift. The method in question is definitely visible in the header file.
Why can't coolMethod() be called from the Swift extension?
Check that both your objective-C class and the return type of the function (in your case MyObject.h) are included in the Bridging header. Methods with return types not included in the bridging header are not available from Swift.

Can not find Swift Protocol declaration in Obj-C class

I have created on Class in Swift and that class and its protocol I am using in Obj-C enabled project but I am getting below error while compiling my project.
cannot find protocol declaration for 'SpeechRecognizerDelegate'; did
you mean 'SFSpeechRecognizerDelegate'?
Can anyone guide me on this how can I use Swift class protocol in my Obj-C class.
Here is my Swift code:
protocol SpeechRecognizerDelegate : class {
func speechRecognitionFinished(_ transcription:String)
func speechRecognitionError(_ error:Error)
}
class SpeechRecognizer: NSObject, SFSpeechRecognizerDelegate {
open weak var delegate: SpeechRecognizerDelegate?
}
Protocol use in Obj-C:
#import "ARBot-Swift.h"
#interface ChatScreenViewController : JSQMessagesViewController <SpeechRecognizerDelegate>
Let me know if required more info.
Thanks in Advance.
in Swift:
#objc public protocol YOURSwiftDelegate {
func viewReceiptPhoto()
func amountPicked(selected: Int)
}
class YourClass: NSObject {
weak var delegat: YOURSwiftDelegate?
}
in Objective-C headerFile.h
#protocol YOURSwiftDelegate;
#interface YOURController : UIViewController < YOURSwiftDelegate >
in Objective-C Implementation.m
SwiftObject * swiftObject = [SwiftObject alloc] init];
swiftObject.delegate = self
Define your Swift protocol like this inside your Swift file.
#objc protocol SpeechRecognizerDelegate: class{
func speechRecognitionFinished(_ transcription:String)
func speechRecognitionError(_ error:Error)
}
Create a Swift Module inside your project setting then use it. You can find here complete blog for the mix language coding.
Then use Protocol inside Objective C class,
We required to add protocol inside Objective C file -
#import "ARBot-Swift.h"
#interface ChatScreenViewController : JSQMessagesViewController <SpeechRecognizerDelegate>
Then you need to conform to the protocol methods -
- (void)viewDidLoad {
[super viewDidLoad];
SpeechRecognizer * speechRecognizer = [[SpeechRecognizer alloc] init];
speechRecognizer.delegate = self;
}
#pragma mark - Delegate Methods
-(void)speechRecognitionFinished:(NSString *) transcription{
//Do something here
}
-(void)speechRecognitionError:(NSError *) error{
//Do something here
}
I had a similar issue after following (import header + Objc annotation on protocol). I got the warning when using Swift code from Objective C headers. Solved by only importing into the implementation .m files.
Add #objc attribute to your protocol:
#objc protocol SpeechRecognizerDelegate : class {
//...
}
Include Swift Classes in Objective-C Headers Using Forward Declarations
//MySwiftClass.swift
#objc protocol MySwiftProtocol {}
#objcMembers class MySwiftClass {}
// MyObjcClass.h
#class MySwiftClass;
#protocol MySwiftProtocol;
#interface MyObjcClass : NSObject
- (MySwiftClass *)returnSwiftClassInstance;
- (id <MySwiftProtocol>)returnInstanceAdoptingSwiftProtocol;
// ...
#end

objc_getClass: load swift class inheriting NSObject

I'm trying to dynamically load a Swift class inheriting from NSObject using the objc runtime. (I'm trying to load the class from ObjC, not from Swift)
My Swift class:
#objc public class TestClass : NSObject {
#objc public func testMethod() -> String {
return "String"
}
}
According to Apple's documentation,
The #objc attribute makes your Swift API available in Objective-C and the Objective-C runtime
But the result of objc_getClass("TestClass") is (null).
Am I doing something wrong? Or is it not possible at all to load swift classes inheriting an ObjC class using the objc runtime?
You need to specify an Objective-C name for your class, not just include #objc:
#objc(TestClass) public class TestClass : NSObject {
#objc public func testMethod() -> String {
return "String"
}
}
NSClassFromString("TestClass") // TestClass.Type
objc_getClass("TestClass") // TestClass
Otherwise your class will not be registered with the Objective-C runtime, and calls like objc_getClass or NSClassFromString will return nil.
What if you try objc_getClass("YourAppName.TestClass")? Most likely the module name is prepended. You can verify the exact name which is used behind the scenes by using NSStringFromClass([TestClass class])

How to extend an Objective-C class with Swift in a framework?

I have an (OS X) Objective-C framework to which I want to add some Swift extensions and I'm using Xcode 7ß6 to work with this. There is class in the framework (let's call it "Sample") implemented in the files "Sample.h" and "Sample.m" .. "Sample.h" contains:
#import <Foundation/Foundation.h>
#interface Sample : NSObject
#property int x;
#end
.. and "Sample.m" contains:
#import "Sample.h"
#implementation Sample
- (instancetype) init {
if ((self = [super init]) == nil) return nil;
self.x = 99;
return self;
}
#end
I have added "Sample.swift" to the framework containing:
import Foundation
extension Sample {
func PrettyPrint () {
print("\(x)")
}
}
This is clearly a trivial version of what I want to do in a larger context, here I want to use the Swift file to extend "Sample" by adding a "PrettyPrint" function.
.. the framework builds without error, but the framework function "PrettyPrint" is not visible to a calling app. App code calling into the framework like:
import Foundation
import TestKit
let sample = Sample()
sample.PrettyPrint()
fails on "sample.PrettyPrint()" with: Value of type 'Sample' has no member 'PrettyPrint'
Why does this fail? and Can it be made to work?
Extra info: If I remove the file "Sample.swift" from the framework and place it the app which is calling into the framework, the "Sample" class is successfully extended and "sample.PrettyPrint()" works as expected (printing "99").
Have you tried making the extension and function public?
public extension Sample {
public func PrettyPrint () {
print("\(x)")
}
}
In a similar case, Tie's answer really helped me but it didn't work for me, I had to add #objc for the method:
public extension SearchBarWidget {
#objc public func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.endEditing(true)
}
}
The SearchBarWidget class was #objc public signatured.

How to declare a constant in swift that can be used in objective c

if I declare the swift constant as a global constant like:
let a = "123"
but the a cannot be found in objective c.
How to solve this?
From Apple Doc:
You’ll have access to anything within a class or protocol that’s marked with the #objc attribute as long as it’s compatible with Objective-C. This excludes Swift-only features such as those listed here:
Generics
Tuples
Enumerations defined in Swift
Structures defined in Swift
Top-level functions defined in Swift
Global variables defined in Swift
Typealiases defined in Swift
Swift-style variadics
Nested types
Curried functions
Therefore its not possible to access global variables(Constants) or global functions defined in Swift.
Possible Solutions:
From the Apple Document Swift programming language, You can Declare Type Properties as
class var constant: Int = {
return 10
}()
But currently in Swift(beta-3) Type properties are not supported.
You can declare a Class function to get a constant value:
In Swift:
class func myConst() -> String {
return "Your constant"
}
Accessing from Objective-C:
NSString *constantValue = [ClassName myConst];
NSLog(#"%#", constantValue);
Swift code:
public class MyClass: NSObject {
public static let myConst = "aConst"
}
and then in Objective-C:
[MyClass myConst]
Isn't this working as well? As in this works for me.
Also this is somewhat shorter as creating a object first (alloc, init). Making a new function for every constant is... not pretty :/
Update for Swift 4
Because of the changes in Swift 4's Objective-C inference, you need to add the #objc annotation to the declared constant as well. The previous declaration then becomes:
#objcMembers
public class MyClass: NSObject {
public static let myConst = "aConst"
}
The calling Objective-C code remains the same.
Using #objcMembers makes all constants available (as if you'd write #objc before each constant), but I've had times where the compiler somehow wouldn't generate the corresponding ObjC code.
In those cases I'd suggest adding the #objc decorator before the constant as well.
I.e.: #objc public static let myConst = "aConst"
You should not have any problem by using let in Objective-C, next example was made with Xcode 7.2 :
MyClass.swift
import Foundation
import UIKit
#objc class MyClass : NSObject { // <== #objc AND NSObject ARE BOTH NECESSARY!!!
let my_color = UIColor( red:128/255,green:32/255,blue:64/255,alpha:1 ) // <== CONSTANT!!!
}
MyObjectiveC.m
#import "PROJECTNAME-Swift.h" // <== NECESSARY TO RECOGNIZE SWIFT CLASSES!!!
#interface MyObjectiveC ()
#end
#implementation MyObjectiveC
#synthesize tableview; // <== ANY UI OBJECT, JUST AS EXAMPLE!!!
- (void) viewDidLoad () {
MyClass * mc = [ [ MyClass alloc ] init ]; // <== INSTANTIATE SWIFT CLASS!!!
tableview.backgroundColor = mc.my_color; // <== USE THE CONSTANT!!!
}
#end
PROJECTNAME is the name of your Xcode project, as shown in Project Navigator.
In your swift class,
let constant: Float = -1
class YourClass: NSObject {
class func getMyConstant() -> Float {return constant}
...
}
Clean, build to let xcode prepare this method useable for obj-c.
Then at your obj-c class
if ([YourClass getMyConstant] != 0) {
...
}
First of all you need to know about the important of auto-generated Swift header file.
It is the one that will made the magic to transcribe the Swift code to be understandable from Objective-C.
This file is auto-generated by Xcode (do not look for it in your project).
The important of this file is to use the correct name, it can match with your target name, but, may not, it is the product module name. (Search for it in your project settings as "Product module")
You need to import this file on the Objective-C class that you want to use a Swift class and also the Swift class name of your Swift file.
#import <ProductModuleName-Swift.h>
#class MySwiftClassName;
My Swift class should have the prefix #objc and inherit from NSObject:
#objc class MySwiftClassName: NSObject {
let mySwiftVar = "123"
}
Then you can call your Swift variable from the Objective-C file:
MySwiftClassName *mySwiftClassO = [[MySwiftClassName alloc] init];
NSString *myVar = mySwiftClassO.mySwiftVar;
Make sure to clean and rebuild your project after each change to force regenerate this auto-generated file.
If your Swift header file was auto-generated correctly you can navigate to it by clicking over the import file name and check if all the code you need was properly transcribed.
In the following post you can find more detailed information about this. https://solidgeargroup.com/bridging-swift-objective-c
Classes func don't work. The only solution I have found out is this one:
class YourController: NSObject {
#objc static let shared = YourController()
private override init() { }
#objc class func sharedInstance() -> YourController {
return YourController.shared
}
#objc let terms = "Your-String-here"
And then on Obj-c file:
[[YourController sharedInstance].terms]