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.
Related
I'm about creating a base engine, but i have a problem,
I have 2 files,
/// ListPresenter.swift
///
class ListPresenter: PaginationPresenter<User> {
override func next() {
}
}
and
/// BasePaginationPresenter.swift
///
protocol PaginationPresenterProtocol {
func next()
}
typealias PaginationPresenter<T: Object> = BasePaginationPresenter<T> & PaginationPresenterProtocol
class BasePaginationPresenter<T: Object>: BasePresenter<T> {
public var page: Int
public var items = [T]()
}
basically, the ListPresenter can inherit to BasePaginationPresenter, but i don't wanna it happen, is there a way to prevent it?
i did some ways like make BasePaginationPresenter private/fileprivate, but there was an error :
Type alias must be declared private or fileprivate because its
underlying type uses a fileprivate type
kindly advice, and thank you!
Goal: to master the ObjC <--> Swift API syntax
Problem: Working with multi-parameter function API.
I haven't been doing ObjC in years and now I need to interface ObjC with Swift packages.
Here's the Swift-Package code:
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!"
override public init() {}
#objc public func sayHello() {
print(text)
}
#objc public func doSomething(msg: String) {
print("Inside doSomething: \(msg)")
}
#objc public func doSomething(msg: String, answer: String) {
print("Inside doSomething with msg: \(msg) and answer: \(answer)")
}
#objc public func sayHelloTo(whom: String) -> String {
"Hello \(whom)!"
}
}
I'm having trouble with the multi-parameter syntax.
Objective-C likes to append 'with' or 'to', etc. to parameter names. I've learned this via trial & error.
But I'm having trouble with the multi-parameter syntax:
Question: What's the correct syntax for multi-parameter Objective-C method calls?
Simple Solution, adding the appropriate label:
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)
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 {
}
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