Swift Generic Class implementing an Objective-C Protocol - objective-c

My intention is to create a generic class in Swift which conforms to an Objective-C protocol:
The class is:
class BaseViewFactoryImpl<T> : NSObject, BaseView {
func getNativeInstance() -> AnyObject {
return String("fsd")
}
}
The protocol BaseView is:
#protocol BaseView < NSObject >
- (id)getNativeInstance;
#end
The compiler tells me:
Type 'BaseViewFactoryImpl<T>' does not conform to protocol 'BaseView'
If I delete <T> then there is no error.
What is wrong here? How can I get the correct generic class implementation?

//BaseViewFactory.swift
class BaseViewFactoryImpl<T> : NSObject, BaseView {
func getNativeInstance() -> AnyObject {
return String("fsd")
}
//BaseViewProtocol.h
#protocol BaseView <NSObject>
- (id)getNativeInstance;
#end
//BridgingHeader.h
#import "BaseClassProtocol.h"
Your code should work. Have you created the bridging header to import the obj-C protocol file?

Starting with Swift 3, you need to use Any, not AnyObject, to represent generic id values coming from Objective-C.
This builds with no errors:
class BaseViewFactoryImpl<T> : NSObject, BaseView {
func getNativeInstance() -> Any {
return "fsd"
}
}

If you create a new generic view model, when you try to create any subclass of the generic view model, you need to declare the subclass as a generic class as well. It's kind of annoy.
For a better way, you can use typealias to declare the instance's type instead of using generic:
protocol BaseView {
typealias T
func getNativeInstance() -> T!
}
class StringViewModel : BaseView {
typealias T = String
func getNativeInstance() -> String! {
return T()
}
}

Related

How I will implement delegate to pass value from a child swift class to a parent objective C class?

I am trying to pass value from a swift class to an objective C class, but delegate which have been created in swift class, couldn't be recognised from objective class.
swift class:
protocol ChildViewControllerDelegate
{
func childViewControllerResponse(parameter)
}
class ChildViewController:UIViewController
{
var delegate: ChildViewControllerDelegate?
}
objective C class:
#import "Project-Swift.h"
#interface MainViewController()<ChildViewControllerDelegate>
{
// Define Delegate Method
func childViewControllerResponse(parameter)
{
}
}
Objective C class can't detect delegate. How I will solve this problem or what did I wrong?
Use #objc to make ChildViewControllerDelegate visible to Objective-C, i.e.
#objc protocol ChildViewControllerDelegate {
func childViewControllerResponse(parameter: String)
}
Change the parameter type as per your requirement.

Overriding Obj-C-methods in Swift using Swift enums

I have the following Objective-C class which uses an Objective-C enum:
MyClass.h:
typedef NS_ENUM(NSInteger, MyEnum) {
MyEnumCase1
};
#interface MyClass : NSObject
-(void)method:(MyEnum)param;
#end
MyClass.m:
#implementation MyClass
-(void)method:(MyEnum)param {}
#end
I can subclass MyClass in Swift and override the method like this:
SubClass.swift
class SubClass: MyClass {
override func method(_ param: MyEnum) {}
}
But if I define the enum in Swift instead of in Objective-C, overriding the method fails:
MyClass.h:
// Forward declare the enum in Objective-C
typedef NS_ENUM(NSInteger, MyEnum);
#interface MyClass : NSObject
-(void)method:(MyEnum)param;
#end
SubClass.swift:
#objc enum MyEnum: NSInteger {
case case1
}
class SubClass: MyClass {
override func method(_ param: MyEnum) {} // Error
}
In this case, overriding the method fails with the error
Method does not override any method from its superclass
The enum itself works in Swift, I can add the following method to SubClass, and it compiles:
func useEnum() {
let x = MyEnum.case1
}
Why does the overriding fail?
When I opened the Generated Interface of MyClass.h, Xcode showed something like this:
import Foundation
open class MyClass : NSObject {
}
Nothing more but comments.
Seems Swift cannot import incomplete enums, so methods which use such types are not imported neither.
So, your #objc enum MyEnum just declares a new enum type, and override func method(_ param: MyEnum) is an attempt to override a method which does not exist in its superclass, from the Swift side.
The enum itself works in Swift
Of course. The enum works even if you removed the line of typedef (with all lines using it) from MyClass.h .
The enum works even if you specify some different type than NSInteger:
#objc enum MyEnum: UInt8 {
case case1
}
Seems you cannot write an actual definition of an enum in Swift, which is declared as an incomplete enum in Objective-C.

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

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!";
}
}

No visible #interface for 'MySwiftClass' declares the selector 'addX:andY'

We are trying to reference Swift methods inside an Objective-C implementation.
Swift class:
import Foundation
#objc class MySwiftClass: NSObject {
override init() {
super.init()
}
func sayHello() -> Void {
print("hello");
}
func addX(x:Int, andY y:Int) -> Int {
return x+y
}
}
Objective-C implementation (Objective-c.m):
#import "ProductModuleName-Swift.h"
MySwiftClass* getData = [[MySwiftClass alloc]init];
[getData sayHello] //works
[getData addX:5 addY:5] //No visible #interface for 'MySwiftClass' declares selector 'addX:addY'
The last line of code gives the following error:
No visible #interface for 'MySwiftClass' declares selector 'addX:addY'
If you command-click on "ProductModuleName-Swift.h" in the Xcode
source file editor then you can see how the Swift methods are mapped to Objective-C.
In your case that would be
#interface MySwiftClass : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
- (void)sayHello;
- (NSInteger)addXWithX:(NSInteger)x andY:(NSInteger)y;
#end
which is called as
MySwiftClass* getData = [[MySwiftClass alloc]init];
[getData sayHello];
NSInteger result = [getData addXWithX:5 andY:5];
A better Swift 3 method name might be
func add(x: Int, y:Int) -> Int
because x is already the argument (external) name of the first
parameter. You can also add an #objc() attribute to the Swift definition
to control the Objective-C name. For example, with
#objc(addX:andY:)
func add(x: Int, y: Int) -> Int {
return x+y
}
it would be called from Objective-C as
NSInteger result = [getData addX:5 andY:5];
As #ekscrypto pointed out, in Swift 4 and later you need to annotate individual functions with #objc. Prior to that, a single, class-level #objc was enough.
Of course in Objective-C class you must add import of NAME_PROJECT-swift.h.
If your project name is Sample then you must add:
#import Sample-swift.h
And then:
Swift 4 or Less
#objc class MySwiftClass: NSObject {
func sayHello(){
//function
}
func addX(){
//function
}
}
Swift 4 or Greater
#objc class MySwiftClass: NSObject {
#objc func sayHello(){
//function
}
#objc func addX(){
//function
}
}
In my case I had forgotten to add:
#import "MyProject-Swift.h"
Into the obj c file.

How to use NSSet<Class> in Swift (exported as Set<NSObject>)?

I have to fulfill a protocol requirement that is defined in Objective-C like this:
#protocol AProtocol <NSObject>
+ (NSSet<Class> * _Nullable)someClasses;
#end
I want to implement this protocol in a subclass written in Swift. I want to return a Set of classes of another Object. The class I want to return is defined like this:
class B: NSObject {}
The class that conforms to the protocol is defined like this:
class A: NSObject, AProtocol {
static func someClasses() -> Set<NSObject>? {
return [B.self]
}
}
Why is NSSet<Class> bridged to Set<NSObject> instead of Set?
This solution is crashing, how can I solve the problem?
NSSet<Class> is bridged to Set<NSObject> because AnyClass does not conform to Hashable which is a precondition for the ValueType of Set.
It can be solved with the following extension for NSObjectProtocol:
extension NSObjectProtocol where Self: NSObject {
static var objcClass: NSObject {
return (self as AnyObject) as! NSObject
}
}
This returns the class of the object casted as NSObject. It is necessary to cast it first to AnyObject because the type system of Swift is so strong that it would not compile or give a warning when directly casting a type to an instance type. In Objective-C this is fine because Class is also just an object. Since NSObject is implemented in Objective-C and the extension is just for NSObjectProtocol, this is save to use (even with the force cast).
Implementing the extension on NSObjectProtocol and not on NSObject itself brings the positive effect that it is not exported to Objective-C.