Objective-C completion block in Swift errors - ios

I'm trying to run a method from an Objective-C class with a completion block in a Swift class but I'm having some troubles.
Obj-C code:
typedef void(^completionBlock)(NSDictionary *);
+ (void)getVersionsFromAPI:(completionBlock)sendData
{
NSDictionary *dict = [[NSDictionary alloc] init];
// Do stuff
sendData(dict);
}
Swift code:
API.getVersionsFromAPI { (ver : NSDictionary) -> Void in
self.version = ver.mutableCopy() as NSMutableDictionary;
}
I'm getting an error that says '[NSObject : AnyObject]!' is not a subtype of 'NSDictionary' on the first line.

I presume that the version property is an optional NSMutableDictionary:
var version: NSMutableDictionary?
If that's correct, than you should fix your code as follows:
API.getVersionsFromAPI { (ver: [NSObject: AnyObject]?) in
if let ver = ver {
self.version = NSMutableDictionary(dictionary: ver)
}
}
I've successfully compiled this code in Xcode 6.1.1

Related

I don't understand why an Objective-C delegate function works and a Swift delegate function crash. Can you explain to me?

I have a framework built in Objetive-C. That framework is to connect and interact with a Bluetooth device.
In the demo code, the Objetive-C delegate function looks like. The demo code was provided by the creator of the framework.
-(void)babyScaleManagerScanDevices:(NSArray<ELBabyScaleDeviceModel *> *)babyScaleDevices{
NSLog(#"babyScaleManagerScanDevices = %#",babyScaleDevices);
ELBabyScaleDeviceModel *model = babyScaleDevices.firstObject;
}
I've included the framework in my swift project and imported the headers. I'm trying to obtain the same result by doing:
func babyScaleManagerScanDevices(_ babyScaleDevices: [ELBabyScaleDeviceModel]?) {
guard let device = babyScaleDevices?.first else {
print("Error unwrapping first device")
return
}
print("Device: \(String(describing: device))")
}
I get the following exception:
Thread 1: Precondition failed: NSArray element failed to match the Swift Array Element type
Expected ELBabyScaleDeviceModel but found ELPeripheralModel
Precondition failed: NSArray element failed to match the Swift Array Element type
Expected ELBabyScaleDeviceModel but found ELPeripheralModel: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1100.2.274.2/swift/stdlib/public/core/ArrayBuffer.swift, line 354
Inspecting babyScaleDevices array show:
babyScaleDevices [ELBabyScaleDeviceModel]? 1 value some
[0] ELPeripheralModel * 0x281cae100 0x0000000281cae100
This result is the same in the demo code in Objetive-C and my Swift project.
The class ELBabyScaleDeviceModel.h looks like:
#import "ELPeripheralModel.h"
NS_ASSUME_NONNULL_BEGIN
#interface ELBabyScaleDeviceModel : ELPeripheralModel
#end
NS_ASSUME_NONNULL_END
Can you explain me what is happening?
You have to specify Array to NSArray
Add this line to your code
let devices = babyScaleDevices as NSArray
You can try this
func babyScaleManagerScanDevices(_ babyScaleDevices: [ELBabyScaleDeviceModel]?) {
let devices = babyScaleDevices as NSArray
guard let device = devices.firstObject else {
print("Error unwrapping first device")
return
}
print("Device: \(String(describing: device))")
}
And after then check this -> Array vs NSArray
Try to change
func babyScaleManagerScanDevices(_ babyScaleDevices: [ELBabyScaleDeviceModel]?)
to
func babyScaleManagerScanDevices(_ babyScaleDevices: [Any]?)
and cast specific elements to ELBabyScaleDeviceModel, for example, in for.
It seems like the creator of this framework put ELPeripheralModel in the array instead of ELBabyScaleDeviceModel
I think its just the way that the code is bridged to Swift.
Could you try to specify the type as [ELPeripheralModel] and then cast it?
func babyScaleManagerScanDevices(_ babyScaleDevices: [ELPeripheralModel]?) {
guard let devices = devices = babyScaleDevices as? [ELBabyScaleDeviceModel],
let device = devices?.first else {
print("Error unwrapping first device")
return
}
print("Device: \(String(describing: device))")
}

Corrupt pointer when using Realm in iOS app after upgrading from Xcode 9.4 to Xcode 11 and Swift 3.0 to Swift 4.2

I have a hybrid app with Swift and Objective-C code. When running the code in Xcode 9.4 there was no issues. After upgrading to Xcode 11 (plus requested upgrade to Swift 4.2) the app crashes with EXC_BAD_ACCESS error while attempting to reference a deallocated instance in the Realm 0.93.2 library.
Here are the sequence of events:
Start up:
#UIApplicationMain
class VGPAppDelegate: UIResponder, UIApplicationDelegate, BITCrashManagerDelegate, BITHockeyManagerDelegate {
var window: UIWindow?
internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//trigger the ui appearance
VGPUIAppearance.triggerAppearance()
//setup network monitoring
TSReachabilityManager.default()
//Update Realm Scheme
VGPRealmMigrator.updateRealmSchema()
setUpHockeyApp()
setupAppearance()
if didCrashInLastSessionOnStartup() {
// Delay launch for sending the crash report
} else {
setupApplication()
}
return true
}
Following call to VGPRealmMigrator.updateRealmSchema()
for (Class cls in s_localNameToClass.allValues) {
RLMObjectSchema *schema = [RLMObjectSchema
schemaForObjectClass:cls];
[schemaArray addObject:schema];
// override sharedSchema classs methods for performance
RLMReplaceSharedSchemaMethod(cls, schema);
// set standalone class on shared shema for standalone object creation
schema.standaloneClass = RLMStandaloneAccessorClassForObjectClass(schema.objectClass, schema);
}
free(classes);
Note: schema has a valid pointer here in Realm > RLMSchema.mm > Intitialise
Now in Realm > RLMAccessor.mm
static Class RLMCreateAccessorClass(Class objectClass,
RLMObjectSchema *schema,
NSString *accessorClassPrefix,
IMP (*getterGetter)(RLMProperty *, RLMAccessorCode, NSString *),
IMP (*setterGetter)(RLMProperty *, RLMAccessorCode)) {
// throw if no schema, prefix, or object class
if (!objectClass || !schema || !accessorClassPrefix) {
#throw RLMException(#"Missing arguments");
}
if (!RLMIsKindOfClass(objectClass, RLMObjectBase.class)) {
#throw RLMException(#"objectClass must derive from RLMObject or Object");
}
// create and register proxy class which derives from object class
NSString *accessorClassName = [accessorClassPrefix stringByAppendingString:schema.className];
Class accClass = objc_getClass(accessorClassName.UTF8String);
if (!accClass) {
accClass = objc_allocateClassPair(objectClass, accessorClassName.UTF8String, 0);
objc_registerClassPair(accClass);
}
App crashes in statement containing reference schema.className. Here schema has null pointer. Zombie shows 'Message sent to deallocated instance'.
I was using an ancient version (0.93.2) of Realm. Fixed the problem by:
pod install --repo-update
pod install (that gave me the latest version which is 3.17.3)

Calling Wrapper Function From Javascript is Crashing App - React-Native

Hello I am trying to call a function in javascript that I am exporting via Objective C. When I call my function in javascript my app is crashing.
RCT_EXPORT_METHOD(getModelAsync:()
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSError *error;
NSString *contents = [[UIDevice currentDevice] model];
if (contents) {
resolve(contents);
} else {
reject(#"Test", #"Something is broken",error);
}
}
Its failing on this on the if statement with this Error: Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
if (!RCTIsIdentifierHead(**input)) {
return NO;
All help is welcome, thanks!
I came across this issue today and managed to solved it. It looks like the function argument types aren't matching up. This error seems to be triggered, when the function types aren't compatible. Something like the following code snippet, would trigger this error, because a dictionary is not compatible with type string and thus the function argument cannot be correctly cast.
Module.m
RCT_EXTERN_METHOD(myFunction: (NSDictionary)options)
Module.swift
#objc
func myFunction(_ options: String) -> Void {
...
}
To fix it, make sure you are doing something like this:
FixedModule.m
RCT_EXTERN_METHOD(myFunction: (NSDictionary)options)
FixedModule.swift
#objc
func myFunction(_ options: NSDictionary) -> Void {
...
}
I hope this helps!

completion handler's error in swift 3 and Xcode 8

I have working project in Xcode 7.3 with swift 2.2 version. Now I have updated Xcode 8 and migrated to swift 3. Now my project contains errors specially for blocks like success block of afnetworking.
Which gives error as
Cannot convert value of type '() -> ()' to expected argument type '((URLSessionDataTask, Any?) -> Void)?'
I don't understand how to solve this to work as per swift 3.
And there is also same like error in Facebook login.
Which gives error as
Cannot convert value of type '(FBSDKLoginManagerLoginResult!, NSError!) -> Void' to expected argument type 'FBSDKLoginManagerRequestTokenHandler!'
and
Cannot convert value of type '(_, _, NSError!) -> Void' to expected argument type 'FBSDKGraphRequestHandler!'
This all errors are related to handler blocks in swift 3. I don't understand the errors and so that can't able to solve. Any help will be appreciated. Thanks in advance.
For facebook - the problem is in new Swift rules about converting objective-c function parameters into Swift.
Previously, if parameters in objective-c code did not have nullability attributes(like nonnull or nullable), Swift converts it with ! making them non optional(forced unwrapping). Now it convert it with ? making them optional. That why you are getting an error. Before you were putting as a callback for login:
(FBSDKLoginManagerLoginResult!, NSError!) -> Void
Now you need to put:
(FBSDKLoginManagerLoginResult?, Error?) -> Void
Also, as you see, now you will not see NSError class. Instead of that Swift will put Error.This is also new rule. Now all "NS" prefixed in class names is removed in Swift(NSObject -> Object; NSError -> Error).
Example of working code for facebook login in Swift 3.0:
let manager = FBSDKLoginManager()
manager.logIn(withReadPermissions: ["public_profile"], from: self.controller) {
(loginResult: FBSDKLoginManagerLoginResult?, error: Error?) in
}
Example of working code for facebook request in Swift 3.0:
let request = FBSDKGraphRequest()
request.start {
(connection: FBSDKGraphRequestConnection?, result: Any?, error: Error?) in
}
As you see, now it is using Any type instead of objective-c id. In Swift 2.2 it was using AnyObject. It is also new Swift converting rule.
You do not need to specify callback parameters type. I did that in code for highlighting their real types. So you can just write code without them:
let manager = FBSDKLoginManager()
manager.logIn(withReadPermissions: ["public_profile"], from: self.controller) { (loginResult, error) in }
let request = FBSDKGraphRequest()
request.start { (connection, result, error) in }
But you need to remember that they are optional now.
In conclusion some converting rules that may affect you callback code:
Closure parameters are optional if in objective-c are not specified nullability attributes
All "NS" prefixes is removed for objective-c classes in Swift
If objective-c function had id parameter, in Swift 3.0 it will have type Any instead of AnyObject
Though I didn't know the error before that what Xcode want to inform me about the error, but I have removed type specification with object and it worked.
As
manager.post(methodname, parameters: param, progress: nil, success:{ (dataTask, responseObj) in
if let dict : NSDictionary = responseObj as? NSDictionary {
print("Response of \(methodname) : \(dict)")
if dict.object(forKey: "response") as? String == "success" {
CompletionHandler(true, dict)
} else {
CompletionHandler(false, dict)
}
}
})
Here with respect to question error is given at dataTask and responseObj which are with type specified. After removing type it worked fine.
Same as with facebook login
#IBAction func fbLoginClicked(_ sender: AnyObject) {
let app = UIApplication.shared.delegate as! AppDelegate
app.fbLoginManager = FBSDKLoginManager()
app.fbLoginManager.logOut()
app.fbLoginManager.loginBehavior = FBSDKLoginBehavior.native
app.fbLoginManager.logIn(withReadPermissions: ["email"], from: self, handler: { (result, error) -> Void in
if error != nil {
print(error?.localizedDescription)
} else {
if (result! as FBSDKLoginManagerLoginResult).isCancelled == true {
} else {
self.fetchFacebookUserDetail()
}
}
})
}
Here also I have removed type specification of result and error and problem solved. And followed this in whole app and it worked. I can run the project without error and also it is working. Thanks.

Conforming Swift Callback With An Objective-C NSDictionary

Our app uses a Push Plugin: https://github.com/phonegap/phonegap-plugin-push
One thing the Push Plugin does is swizzle the AppDelegate.m file. Since our AppDelegate file is a .swift, I need to import the core functionality instead.
However, I am running into a problem converting a Swift function to go inside of the NSDictionary Object it expects.
Here is the code I am converting:
void (^safeHandler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result){
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(result);
});
};
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithCapacity:2];
[params setObject:safeHandler forKey:#"handler"];
PushPlugin *pushHandler = [self getCommandInstance:#"PushNotification"];
pushHandler.handlerObj = params;
So the first thing I did was convert the (^safeHandler) to Swift as such:
let safeHandler: (UIBackgroundFetchResult) -> () =
{
result in dispatch_async(dispatch_get_main_queue())
{
completionHandler(result)
}
}
Next I need create the NSMutableDictionary object that gets set as the handlerObj on pushHandler.
Putting safeHandler directly into the dictionary throws the error
(UIBackgroundFetchResult) -> () does not confirm to AnyObject
protocol.
So I tried changing the dictionary type to be [NSObject : closure]() by doing the following:
typealias closure = (UIBackgroundFetchResult) -> Void
var params:Dictionary<String, closure> = Dictionary<String, closure>()
params["handler"] = safeHandler
But when I try to assign the object to the pushHandler
pushHandler.handlerObj = params
I get the error again
Cannot assign value of type 'Dictionary' (aka
'Dictionary ()>') to type
'[NSObject : AnyObject]!'
So back to square one. It seems in Objective-C passing in functions is OK but not so with Swift?
Do you know how I can workaround this issue and get the function into the [NSObject: AnyObject] the Objective-C code is expecting?

Resources