My Swift enumeration is as below:
#objc enum NetworkError: Int, RawRepresentable {
case NoData
case Generic
func description() -> String {
switch self {
case .NoData: return "No data available"
case .Generic: return "Something goes wrong, please try again later"
}
}
}
My question is, how to call the description method from my Objective-C class. In Swift the call is as simple as:
NetworkError.Generic.description()
NetworkError.NoData.description()
Thanks
I don't think you can do this. If you look at the generated header the enum looks something like this to Objective-C:
typedef SWIFT_ENUM(NSInteger, NetworkError) {
NetworkErrorNoData = 0,
NetworkErrorGeneric = 1,
};
It's just a basic C style enum, it's not an object with methods, more info https://developer.apple.com/swift/blog/?id=22
Related
I've got Swift enum like this:
#objc public enum Status: Int {
case unknown;
case ok;
case failed;
}
It's properly bridged to Objective-C, and I can use it as, say StatusUnknown in Objective-C.
Now I have a function with callback:
+ (void)fetch:(void (^_Nonnull)(BOOL success))completion
And all I want is to replace BOOL with my Status enum. How to do that?
Clearly not like this:
+ (void)fetch:(void (^_Nonnull)(Status success))completion // Error: Unknown type name
I could use NSInteger like this:
+ (void)fetch:(void (^_Nonnull)(NSInteger success))completion
but then it's not really limiting values to Status enum.
So what is the best way to convey enum here?
Note:
I simplified question, in reality enum is not called status and has many more values.
Signature of the function has to match previous signature, but with different argument
To be compatible with objective-c enum must be inherited from Int, like
#objc public enum Status: Int {
case unknown
case ok
case failed
}
make sure generated bridge header file "YOURPROJECT-Swift.h" contains
typedef SWIFT_ENUM(NSInteger, Status, closed) {
StatusUnknown = 0,
StatusOk = 1,
StatusFailed = 2,
};
then in your .m file
#import "YOURPROJECT-Swift.h"
...
+ (void)fetch:(void (^_Nonnull)(Status success))completion
{
// do anything needed
}
Clean/Build - all compiled well. Tested with Xcode 11.2.
I have this enum in swift:
#objc enum HomeViewDataType: Int {
case statistics
case allTime
}
and this protocol;
#objc(TCHomeViewDataUpdaterDelegate)
protocol HomeViewDataUpdaterDelegate {
....
func homeViewDataType() -> HomeViewDataType
}
If I ask Xcode to add the protocol stubs automatically it will add the enum keyword inside the return type parenthesis:
- (enum HomeViewDataType)homeViewDataType
{
<code>
}
I never seen this before: (enum HomeViewDataType)
Any idea why?
It works with or without the enum keyword btw.
I am creating a swift framework. In that one class is like this as shown below.
import Foundation
#objc public class classA: NSObject {
public override init (){
super.init();
}
/**
Singleton intance is returned.
*/
public class var sharedInstance: classA {
struct Static {
static let instance = popeye();
}
return Static.instance
}
}
Now when i add this framework into a Objective c project and try to access "sharedInstance" i get this error.
Property 'sharedInstance' not found on object of type ClassA.
Fix it Replace 'sharedInstance' with 'sharedInstance'
But even if i try use Fix it, this issue isnt solved.
NOTE: This issue doesn't happen when i integrate this framework with a swift project!!!
I AM STUCK.. :(
I tried to reproduce your problem. At first the syntax highlighter in Xcode flagged the same error in Objective-C that you mentioned, but the code actually was built and ran fine.
However, there is a cleaner way of doing this. In your code you are using a computed type property, which is evaluated every time you access it! You work around this by introducing the struct Static, where you essentially do what could be done in classA itself, like this:
/**
Singleton intance is returned.
*/
public static var sharedInstance: classA = popeye()
Here we used a stored type property, which is a recommended way to implement singletons, see here:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html
And here is some documentation on different kinds of properties:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
Finally i was able to fix this with a minor change !! :)
Swift framework code
#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"
}
}
Objective C parent project code
SingletonTest *aTest = [SingletonTest sharedInstance];
NSLog(#"Singleton says: %#", [aTest testTheSingleton]);
I have a block declare in Objective C file like this :
- (void) getUserCurrentProfile:(void (^)(UserInfo *userInfo,NSError * error)) callBack {
if ([FBSDKAccessToken currentAccessToken]) {
//code here
}];
}
in Swift file I call it :
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
FBManager.getUserCurrentProfile({(userInfo:UserInfo?, error:NSError?) -> Void in
appDelegate.showHomeView()
})
But I totally get this error again :
Can anyone give me an idea?
P/S: I read this issue : Swift : Use closure compatible with Objective-C block. And do the same thing. But it doesn't work
getUserCurrentProfile is an instance method, and you are calling it as a class method. You should call it on an instance of FBManager (sharedInstance maybe?):
FBManager.sharedInstance.getUserCurrentProfile { userInfo, error in)
appDelegate.showHomeView()
}
The error says that it cannot convert the closure to FBManager, and is correct, as you are calling it as a class function and the compiler expects and instance to operate on. The above call could have also been written in the curried function call:
FBManager.getUserCurrentProfile(FBManager.sharedInstance) { userInfo, error in)
appDelegate.showHomeView()
}
Here's a block type that I am defining in objective-c
typedef void (^arrayBlock)(NSArray *blockArray);
I have an objective-c class with a method that uses this as a return block
-(void)loadTimesWithSuccessBlock:(arrayBlock)successBlock;
When I try to use this method in Swift, this is what autocomplete gives me.
let timeClockLibrarian = HMFTimeClockLibrarian()
timeClockLibrarian.loadTimesWithSuccessBlock { ([AnyObject]!) -> Void in
//Where is blockArray?
}
I'm assuming that [AnyObject]! is supposed to be the NSArray. But I don't see how I'm supposed to get access to that variable?
If I were to use this method in Objective-C I get a result like this:
[timeClockLibrarian loadTimesWithSuccessBlock:^(NSArray *blockArray) {
//I can use the blockArray here :)
}];
[AnyObject]! is indeed only the type of the variable; autocomplete didn't name it. You just need to do something like (blockArray: [AnyObject]!).
let timeClockLibrarian = HMFTimeClockLibrarian()
timeClockLibrarian.loadTimesWithSuccessBlock { (blockArray: [AnyObject]!) -> Void in
// your code here
}
Write like this:
let timeClockLibrarian = HMFTimeClockLibrarian()
timeClockLibrarian.loadTimesWithSuccessBlock { blockArray in
doSomething(blockArray)
}
If you want to refer to weak self use this:
let timeClockLibrarian = HMFTimeClockLibrarian()
timeClockLibrarian.loadTimesWithSuccessBlock { [weak self] blockArray in
self?.doSomething(blockArray)
}
You may also want to get rid of implicit unwrapping. If so, specify nullability in Obj-C code:
typedef void (^arrayBlock)(nullable NSArray *blockArray);