Cannot find methods with parameters of Swift class in Objective-C - ios

I have a problem with the Swift and Objective-C interoperability. In my Objective-C class the methods with parameters of my Swift class are not recognized. I have integrated the Swift class with the Bridging-Header correctly. Furthermore the Swift class inherits from NSObject and the variables and methods are declared with #objc. I have also tried #objcmembers... I can't find a solution to my problem on the internet. I am really desperate. My Swift class uses classes from other projects, which are also declared with #objc. Below is an example of my problem:
Project A: SwiftClassA
public class SwiftClassA: NSObject {
#objc public init() {
// do something
}
#objc public func aFunction() {
// do something
}
}
Project B: SwiftClassB
import ProjectA
public class SwiftClassB: NSObject {
#objc public init(testA: Double, testB: Double) {
// do something
}
#objc public func bFunction() {
let classA = SwiftClassA()
// do something
}
}
Project C: SwiftClassC
import ProjectA
import ProjectB
public class SwiftClassC: NSObject {
#objc public var classA: SwiftClassA
#objc public init(classA: SwiftClassA) {
self.classA = classA
}
#objc public func cFunction(testA: Double, testB: Double) {
let classB = SwiftClassB(testA: testA, testB: testB)
// do something
}
#objc public func cFunction() {
// do something
return
}
}
Project C: ObjcClass
#import <ProjectA-Swift.h>
#import <ProjectC-Swift.h>
#property (nonatomic, strong) SwiftClassC *swiftClassC;
#implementation ObjcClass
- (ObjcClass *_Nonnull)init {
SwiftClassA *swiftClassA = [[SwiftClassA alloc] init];
_swiftClassC = [[SwiftClassC alloc] initWithClassA: swiftClassA];
return self;
}
- (void)objcFunction {
[_swiftClassC cFunctionWithTestA: 1.0 testB: 1.0]; // Property ‚FunctionWithTestA' not found on object of type ’SwiftClassC *’
[_swiftClassC cFunction];
}
#end

You need to mark the classes as #objc too, it isn't enough to mark their methods as #objc.
#objc public class SwiftClassA: NSObject {
#objc public init() {
// do something
}
#objc public func aFunction() {
// do something
}
}
#objc public class SwiftClassB: NSObject {
...
}
#objc public class SwiftClassC: NSObject {
...
}

Without actually seeing the project, it's tough to diagnose, however I would recommend not putting #objc in front of everything within your Swift classes. Instead, put #objcMembers in front of your Swift class declaration.
Your declarations would look like this:
#objcMembers
public class SwiftClassA: NSObject {
public override init() {
// do something
}
public func aFunction() {
// do something
}
}
#objcMembers
public class SwiftClassB: NSObject {
public init(testA: Double, testB: Double) {
// do something
}
public func bFunction() {
let classA = SwiftClassA()
// do something
}
}
#objcMembers
public class SwiftClassC: NSObject {
public var classA: SwiftClassA
public init(classA: SwiftClassA) {
self.classA = classA
}
public func cFunction(testA: Double,
testB: Double) {
let classB = SwiftClassB(testA: testA,
testB: testB)
// do something
}
public func cFunction() {
// do something
return
}
}
The other thing to be aware of is if your project doesn't compile, you won't get the swift header file that Objective-C reads from:
#import <ProjectA-Swift.h>
#import <ProjectC-Swift.h>
In the Objective-C code I've converted, I occasionally run into issues with the import declaration where it wants #import "ProjectA-Swift.h" instead of the one you used, so you could try playing around with that, as well.
Lastly, might want to open up your Objective-C header and click the little 4 square thingie at the upper left corner and click "Swift 5 generated interface", which, when clicked, will give you something that looks very similar to a protocol declaration...there won't be methods and vars, but it'll show you what the compiler is expecting from a method signature standpoint.
If you encounter a situation where Objective-C wants a specific signature for a method, you can use an alias in front of the method that doesn't match. An alias would look something like this:
#objc(myMethod:parameter1:parameter2)

Related

Objective-C accessing Swift Package functions

Goal: To use Swift Package within Objective-C.
All I'm trying to do is to have a simple, rudimentary understanding of the correct syntax of access Swift vars & func() from ObjC.
This is the second part of questions about using Objective-C with a Swift package.
Here's my Swift Package file that I'm trying to access...
I'm concentrating on the Swift class access.
But I would also like to know how to access the struct.
import Foundation
public struct RicStruct {
public private(set) var text = "Hello, World!"
public init() {
}
public func sayHello() -> String {
"Hello Ric!"
}
}
public class RicClass: NSObject {
#objc public var msg = "Mother has a feeling, I might be too appealing."
#objc public let text = "Hello Everybody!"
public override init() {}
public func sayHello() {
print(text)
}
public func doSomething() {
print("Inside doSomething()")
}
}
This is the Objective-C parent attempting to access the Swift-Package vars & func:
Question: What's the proper syntax of accessing:
the Swift class & struct functions,
the Swift var/let?
You declared
public func sayHello() {
print(text)
}
That makes the method sayHello public across module, but it does not expose it to Objective-C. Instead:
#objc public func sayHello() {
print(text)
}
I didn't add the '#objc' qualifier to the Swift function. I had mistakenly thought that it's not necessary due to being a member of a NSObject class per class declaration.
Also, didn't need to add the .h ObjC <--> Swift bridge file.

Call a method with dynamic class name in swift

How can we call class functions with a dynamic class name?
Assume the following example where I have two class with methods with same signature
class Foo{
class func doSomething()
}
class Foobar {
class func doSomething()
}
class ActualWork{
//call following method with a variable type so that it accepts dynamic class name
func callDynamicClassMethod(x: dynamicClass)
x.doSomething()
}
How can this be implemented so that x accepts values at run time
Edit: Sorry, I missed to mention that I was looking for any other ways other than protocol oriented approach. This is more of an exploratory question to explore if there is a more direct approach/pods/libraries to achieve this.
I liked this question, because it made me to think a lit'bit outside of the box.
I'll answer it, by dividing it into a few parts.
First
call class functions
Class function is basically a Type methods, which can be achieved using the static word inside the class context.
Taking that into account, you can get a simple solution, using protocol and passing the class reference (conforming to that protocol) like this:
protocol Aaa{
static func doSomething();
}
class Foo : Aaa{
static func doSomething() {
print("Foo doing something");
}
}
class FooBar : Aaa{
static func doSomething() {
print("FooBar doing something");
}
}
class ActualWork{
//Using class (static) method
func callDynamicClassMethod <T: Aaa> (x: T.Type) {
x.doSomething();
}
}
//This is how you can use it
func usage(){
let aw = ActualWork();
aw.callDynamicClassMethod(x: Foo.self);
aw.callDynamicClassMethod(x: Foo.self);
}
Second
In case you don't really need the method on the class context, you may consider using instance methods. In that case the solution would be even simpler, like this:
protocol Bbb{
func doSomething();
}
class Bar : Bbb{
func doSomething() {
print("Bar instance doing something");
}
}
class BarBar : Bbb{
func doSomething() {
print("BarBar instance doing something");
}
}
class ActualWork{
//Using instance (non-static) method
func callDynamicInstanceMethod <T: Bbb> (x: T){
x.doSomething();
}
}
//This is how you can use it
func usage(){
let aw = ActualWork();
aw.callDynamicInstanceMethod(x: Bar());
aw.callDynamicInstanceMethod(x: BarBar());
}
Third
If you need to use the class func syntax, as OP originally did:
class func doSomething()
You CANNOT simply use a protocol. Because protocol is not a class...
So compiler won't allow it.
But it's still possible, you can achieve that by using
Selector with NSObject.perform method
like this:
class ActualWork : NSObject{
func callDynamicClassMethod<T: NSObject>(x: T.Type, methodName: String){
x.perform(Selector(methodName));
}
}
class Ccc : NSObject{
#objc class func doSomething(){
print("Ccc class Doing something ");
}
}
class Ddd : NSObject{
#objc class func doSomething(){
print("Ccc class Doing something ");
}
#objc class func doOther(){
print("Ccc class Doing something ");
}
}
//This is how you can use it
func usage() {
let aw = ActualWork();
aw.callDynamicClassMethod(x: Ccc.self, methodName: "doSomething");
aw.callDynamicClassMethod(x: Ddd.self, methodName: "doSomething");
aw.callDynamicClassMethod(x: Ddd.self, methodName: "doOther");
}
Generics and Protocol oriented programming will do the job:
protocol Doable {
static func doSomething()
}
class Foo: Doable {
static func doSomething() {
debugPrint("Foo")
}
}
class Foobar: Doable {
static func doSomething() {
debugPrint("Foobar")
}
}
class ActualWork {
func callDynamicClassMethod<T: Doable>(x: T.Type) {
x.doSomething()
}
}
let work = ActualWork()
work.callDynamicClassMethod(x: Foo.self)
work.callDynamicClassMethod(x: Foobar.self)
you can achieve this with help of Protocol
protocol common {
static func doSomething()
}
class Foo : common{
static func doSomething() {
print("Foo")
}
}
class Foobar : common {
static func doSomething() {
print("Foobar")
}
}
class ActualWork{
//call following method with a variable type so that it accepts dynamic class name
func callDynamicClassMethod(x: common.Type) {
x.doSomething()
}
}
let fooObj : common = Foo()
let Foobarobj : common = Foobar()
let workObk = ActualWork()
workObk.callDynamicClassMethod(x:Foo.self)
workObk.callDynamicClassMethod(x:Foobar.self)
I think, there are three solutions. I shared an sample below.
Use "protocol" that has "doSomething()" function requirements.
Create a function which gets function definition as a parameter.
Use reflection. you can use EVReflection that is good Api for reflection.
sample code:
protocol FooProtocol {
static func doSomething()
}
class Foo: FooProtocol {
class func doSomething() {
print("Foo:doSomething")
}
}
class Foobar: FooProtocol {
class func doSomething() {
print("Foobar:doSomething")
}
}
class ActualWork {
func callDynamicClassMethod<T: FooProtocol>(x: T.Type) {
x.doSomething()
}
func callDynamicClassMethod(x: #autoclosure () -> Void) {
x()
}
func callDynamicClassMethod(x: () -> Void) {
x()
}
}
ActualWork().callDynamicClassMethod(x: Foo.self)
ActualWork().callDynamicClassMethod(x: Foobar.self)
print("\n")
ActualWork().callDynamicClassMethod(x: Foo.doSomething())
ActualWork().callDynamicClassMethod(x: Foobar.doSomething())
print("\n")
ActualWork().callDynamicClassMethod(x: Foo.doSomething)
ActualWork().callDynamicClassMethod(x: Foobar.doSomething)
Looks like you are searching for duck typing, and this is harder to achieve in a statically typed language (with some exceptions, listed in the linked Wikipedia page).
This is because dynamically calling a method requires knowledge about the layout of the target object, thus either inheritance of the class declaring the method, or conformance to a protocol that requires that method.
Starting with Swift 4.2, and the introduction of dynamic member lookup, there is another approach to solve your problem, however it also involves some ceremony:
// This needs to be used as base of all classes that you want to pass
// as arguments
#dynamicMemberLookup
class BaseDynamicClass {
subscript(dynamicMember member: String) -> () -> Void {
return { /* empty closure do nothing */ }
}
}
// subclasses can choose to respond to member queries any way they like
class Foo: BaseDynamicClass {
override subscript(dynamicMember member: String) -> () -> Void {
if member == "doSomething" { return doSomething }
return super[dynamicMember: member]
}
func doSomething() {
print("Dynamic from Foo")
}
}
class Bar: BaseDynamicClass {
override subscript(dynamicMember member: String) -> () -> Void {
if member == "doSomething" { return doSomething }
return super[dynamicMember: member]
}
func doSomething() {
print("Dynamic from Bar")
}
}
func test(receiver: BaseDynamicClass) {
receiver.doSomething()
}
test(receiver: Bar()) // Dynamic from Bar
To conclude, in the current Swift version there is no way to have both the argument and the method dynamic, some common ground needs to be set.

Method cannot be marked #objc because its result type cannot be represented in Objective-C

am exposing swift API's in Objective-C and the Objective-C runtime.
When i add "#objc" before the function throws an error "Method cannot be marked #objc because its result type cannot be represented in Objective-C"
My code is here
#objc public static func logIn(_ userId: String) -> User? { }
User is optional struct. how to solve this.
The key bit of information is this:
User is optional struct
If User is a struct, then it can't be represented in Objective-C, just the same as a Swift class that doesn't inherit from NSObject.
In order for the method logIn(_:) to be able to be marked #objc, then every type referenced in the method declaration has to be representable in Objective-C. You're getting the error message because User isn't.
To fix it, you're either going to have to change the declaration of User from this:
struct User {
// ...
}
...to this:
class User: NSObject {
// ...
}
...or redesign logIn(_:) so that it doesn't return a User.
You can find more information about this here. In particular, this answer offers the following potential solution:
The best thing i found was to wrap in a Box class
public class Box<T>: NSObject {
let unbox: T
init(_ value: T) {
self.unbox = value
}
}
Change the definition of your class as below
class User: NSObject {
}
In this way this class will be available in Objective-C
Your class or protocol, must be inherited (extended) by NSObject or anyother class in its hierarchy, containing your code (function) with #objc notation.
Example:
class TestClass {
public static func logIn(_ userId: String) -> User? { }
}
To use/declare #objc with this function, class must extend NSObject (or any other class in its hierarchy)
class TestClass {
#objc public static func logIn(_ userId: String) -> User? { }
}
Update:
struct may not work with optional value in Objective-C, as a work around, you should change User from struct to class
Try following code and see:
public class User : NSObject {
// Your code
}
public final class Manager : NSObject {
#objc public static func logIn(_ userId: String) -> User? {
return nil
}
}
Here is snapshot with editor
Only an NSObject-derived class type can be seen by Objective-C. Use:
class User : NSObject {
}

Override function error in swift

I got a struct :
struct ErrorResultType: ErrorType {
var description: String
var code: Int
}
and a protocol:
protocol XProtocol {
func dealError(error: ErrorResultType)
}
Now I want to make an extention of UIViewController:
extension UIViewController: XProtocol {
func dealError(error: ErrorResultType) {
// do something
}
}
So I can subclass from this and override the function like:
class ABCViewController: UIViewController {
--->override func dealError(error: ErrorResultType) {
super.dealError(error)
// do something custom
}
}
But it goes wrong with: Declarations from extensions cannot be overridden yet
It doesn't make any sense to me. When I replace all ErrorResultType with AnyObject, the error won't appear any more.
Anything I missed?
For now the method in the extension must be marked with #objc to allow overriding it in subclasses.
extension UIViewController: XProtocol {
#objc
func dealError(error: ErrorResultType) {
// do something
}
}
But that requires all types in the method signature to be Objective-C compatible which your ErrorResultType is not.
Making your ErrorResultType a class instead of a struct should work though.
If i am not making mistake this is connected with Swift official extension mechanism for adding methods to classes.
Conclusion :
At the moment, it's not possible to override entities declared in
extension by subclassing, like so:
class Base { }
extension Base {
var foo: String { return "foo" }
}
class Sub: Base {
override var foo: String { return "FOO" } // This is an error
}
Please check this resource for more information : https://github.com/ksm/SwiftInFlux/blob/master/README.md#overriding-declarations-from-extensions

Swift - How can I override an extension method in a concrete subclass

I have an extension on UIView implementing a protocol
protocol SomeProtocol {
var property : Int
}
extension UIView : SomeProtocol {
var property : Int {
get {
return 0
}
set {
// do nothing
}
}
}
in a concrete subclass I want to override this extension method:
class Subclass : UIView, SomeProtocol {
var _property : Int = 1
var property : Int {
get { return _property}
set(val) {_property = val}
}
}
I set breakpoints and see that the extension method is called and not the concrete subclass method:
var subclassObject = Subclass()
someObject.doSomethingWithConcreteSubclassObject(subclassObject)
// other code;
fun doSomethingWithConcreteSuclassObject(object : UIView) {
var value = object.property // always goes to extension class get/set
}
As others have noted, Swift does not (yet) allow you to override a method declared in a class extension. However, I'm not sure whether you'll ever get the behavior you want even if/when Swift someday allows you to override these methods.
Consider how Swift deals with protocols and protocol extensions. Given a protocol to print some metasyntactic variable names:
protocol Metasyntactic {
func foo() -> String
func bar() -> String
}
An extension to provide default implementations:
extension Metasyntactic {
func foo() -> String {
return "foo"
}
func bar() -> String {
return "bar"
}
}
And a class that conforms to the protocol:
class FooBar : Metasyntactic {
func foo() -> String {
return "FOO"
}
func bar() -> String {
return "BAR"
}
}
Swift will use dynamic dispatch to call the appropriate implementations of foo() and bar() based on each variable's runtime type rather than on the type inferred by the compiler:
let a = FooBar()
a.foo() // Prints "FOO"
a.bar() // Prints "BAR"
let b: Metasyntactic = FooBar()
b.foo() // Prints "FOO"
b.bar() // Prints "BAR"
If, however, we extend the protocol further to add a new method:
extension Metasyntactic {
func baz() -> String {
return "baz"
}
}
And if we override our new method in a class that conforms to the protocol:
class FooBarBaz : Metasyntactic {
func foo() -> String {
return "FOO"
}
func bar() -> String {
return "BAR"
}
func baz() -> String {
return "BAZ"
}
}
Swift will now use static dispatch to call the appropriate implementation of baz() based on the type inferred by the compiler:
let a = FooBarBaz()
a.baz() // Prints "BAZ"
let b: Metasyntactic = FooBarBaz()
b.baz() // Prints "baz"
Alexandros Salazar has a fantastic blog post explaining this behavior in depth, but suffice it to say that Swift only uses dynamic dispatch for methods declared in the original protocol, not for methods declared in protocol extensions. I imagine the same would be true of class extensions, as well.
I know this question has been asked a while ago. But this will be handy for someone who looking for an easier way. There is a way of overriding an extension methods. I know its bit hacky but it does the job beautifully.
If you declare your protocol with #objc
#objc protocol MethodOverridable {
func overrideMe()
}
In Extension
extension MainClass: MethodOverridable {
func overrideMe() {
print("Something useful")
}
}
Subclass - You can able to override it in your subclass. It works like a magic. Well, not really when adding #objc it exposes your protocol to Objective-C and its Runtime. That allows your subclass to override.
class SubClass: MainClass {
override func overrideMe() {
print("Something more useful")
}
}
Swift 5
class Class
{
#objc dynamic func make() { print("make from class") }
}
class SubClass: Class {}
extension SubClass {
override func make() {
print("override")
}
}
It looks like you can override property for 2nd super class property. For example, you can access UIView property by making extension to the UILabel wanting to override frame property of UIView. This sample works for me in Xcode 6.3.2
extension UILabel {
override public var frame: CGRect {
didSet {
println("\(frame)")
}
}
}
You can't do this through normal means.
It's in Apple's docs that you can't override a method in an extension in a subclass.
Also, extensions can add new functionality to a type, but they cannot override existing functionality.
https://docs.swift.org/swift-book/LanguageGuide/Extensions.html
I think you forgot to override the superclass property in your subclass:
class Subclass : UIView {
var _property : Int = 1
override var property : Int {
get { return _property}
set(val) {_property = val}
}
}

Resources