Objective-c Completion handler to Delphi - ios

In Objective-c header I have this :
- (void)offerForConstraints:(RTCMediaConstraints *)constraints
completionHandler:(nullable void (^)(RTCSessionDescription *_Nullable sdp,
NSError *_Nullable error))completionHandler;
That I translate like this:
type
TWebRTCPeerConnectionOfferForConstraintsCompletionHandler =
procedure(sdp: RTCSessionDescription; error: NSError) of object;
procedure offerForConstraints(
constraints: RTCMediaConstraints;
completionHandler: TWebRTCPeerConnectionOfferForConstraintsCompletionHandler); cdecl;
But every time I call offerForConstraints I Have :
Access violation at address 0000000183398910, accessing address
000000004D555468 At address: $0000000183398910 (objc_msgSend + 16)
sometimes I also have this error:
-[__NSCFDictionary offerForConstraints:completionHandler:]: unrecognized selector sent to instance 0x1c0271b40
or this one:
-[_CFXNotificationObserverRegistration offerForConstraints:completionHandler:]: unrecognized selector sent to
instance 0x1c4273a40
Any idea what going wrong?

Related

FCNDEFMessage queryNDEFStatusWithCompletionHandler:]: unrecognized selector sent to instance

I'm trying to obtain some information about a NFC tag, the session is successfully started and after reading a tag, we jump into the following function:
- (void) readerSession:(NFCNDEFReaderSession *)session didDetectNDEFs:(NSArray<NSObject<NFCNDEFTag> * > *)tags {
NSLog(#"NFCNDEFReaderSession didDetectNDEFs");
for (NSObject<NFCNDEFTag> *tag in tags) {
[tag queryNDEFStatusWithCompletionHandler:^(NFCNDEFStatus status, NSUInteger capacity, NSError * _Nullable error) {
if (!error) {
NSLog(#"success");
}
}];
}
}
We even get an iteration in the loop, but in the moment I try to obtain some information about my tag, the app crashes with the following error message:
019-11-06 16:01:12.955810+0100 MyApp[336:10691] -[NFCNDEFMessage queryNDEFStatusWithCompletionHandler:]: unrecognized selector sent to instance 0x281cfce30
2019-11-06 16:01:12.956200+0100 MyApp[336:10691] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NFCNDEFMessage queryNDEFStatusWithCompletionHandler:]: unrecognized selector sent to instance 0x281cfce30'
*** First throw call stack:
(0x18e45180c 0x18e179fa4 0x18e35536c 0x18e455c88 0x18e457a6c 0x100636cc0 0x100b097fc 0x100b0abd8 0x100b11b48 0x100b12718 0x100b1dadc 0x18e16ef88 0x18e171ad4)
libc++abi.dylib: terminating with uncaught exception of type NSException
Note: NFCNDEFTag is a protocol, so maybe it's connected with the way I'm declaring it: NSObject<NFCNDEFTag>?
Let's see the doc of NFCNDEFReaderSessionDelegate.
You have:
- (void)readerSession:(NFCNDEFReaderSession *)session
didDetectNDEFs:(NSArray<NFCNDEFMessage *> *)messages;
and
- (void)readerSession:(NFCNDEFReaderSession *)session
didDetectTags:(NSArray<__kindof id<NFCNDEFTag>> *)tags;
You wrote:
- (void)readerSession:(NFCNDEFReaderSession *)session
didDetectNDEFs:(NSArray<NSObject<NFCNDEFTag> * > *)tags {
Which is a mix of the two.
But in Objective-C, the selector will be:
readerSession:didDetectNDEFs & readerSession:didDetectTags: for the official one, and yours is readerSession:didDetectNDEFs, which is the same as the first one. So you lured the compiler and the runtime for yours.
But it doesn't care of the type of the parameters, so it's not the correct object, so it doesn't implement the method queryNDEFStatusWithCompletionHandler:, so it crashes with that error message.
How to fix it?
Implement the real delegate methods, with the good parameters types, use the correct one, you can't modify them like that as you want.

How to write blocks (closures) in swift3 right way?

I am trying to find how I can write this blocks in swift3, any help would be appreciated.
I am using obj-c PocketSDK in swift3 project.
https://github.com/Pocket/Pocket-ObjC-SDK
I can use this SDK fine with proper Bridge-Header settings, but still not sure how I can write blocks part.
obj-c
[[PocketAPI sharedAPI] callAPIMethod:#"get"
withHTTPMethod:PocketAPIHTTPMethodGET
arguments:arguments
handler:^(PocketAPI *api, NSString *apiMethod, NSDictionary *response, NSError *error) {
}];
This is the type def in PocketSDK.
-(void)callAPIMethod:(NSString *)apiMethod withHTTPMethod:(PocketAPIHTTPMethod)HTTPMethod arguments:(NSDictionary *)arguments delegate:(id<PocketAPIDelegate>)delegate;
typedef void(^PocketAPIResponseHandler)(PocketAPI *api, NSString*apiMethod, NSDictionary *response, NSError *error);
swift? (This shows error.)
let arguments: [String: Any] = [
"state": "unread",
"count": 20
]
PocketAPI.shared().callMethod("get", with: PocketAPIHTTPMethodGET, arguments: arguments) {
(api: PocketAPI,
apiMethod: String,
esponse: [AnyHashable:Any],
error: Error) in
}
#Updated
I could avoid the compile error without type def, but still get the unrecognized selector if I put arguments. If I set nil in arguments, I don't get it though. Anything wrong with the dictionary?
var arguments = [String : Any]()
arguments["count"] = 20
arguments["state"] = "unread"
PocketAPI.shared().callMethod("get", with: PocketAPIHTTPMethodGET, arguments: arguments) {
(api,
apiMethod,
response,
error) in
}
Error.
-[_SwiftTypePreservingNSNumber length]: unrecognized selector sent to instance 0x174624540 2017-08-15 14:05:51.345611+0900
Voicepaper2[1062:286998] * Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[_SwiftTypePreservingNSNumber
length]: unrecognized selector sent to instance 0x174624540'
* First throw call stack:
Solved! This was the final answer, thanks!
let arguments: [String: Any] = [
"state": "unread",
"count": "20"
]
PocketAPI.shared().callMethod("get", with: PocketAPIHTTPMethodGET, arguments: arguments) {
(api,
apiMethod,
response,
error) in
}

Cannot understand NSError/NSObject pointer passing behavior

I am now confused by pointer to pointer even though I've read Why does NSError need double indirection? (pointer to a pointer) and NSError * vs NSError ** and much more.
I've done some thinking and still got some questions.
Here I wrote this:
NSError *error = [NSError errorWithDomain:#"before" code:0 userInfo:nil];
NSLog(#"outside error address: %p", &error];
[self doSomethingWithObj:nil error:&error];
In order to test the above NSError method, I wrote this:
- (id)doSomethingWithObj:(NSObject *)obj error:(NSError *__autoreleasing *)error
{
NSLog(#"inside error address: %p", error);
id object = obj;
if (object != nil)
{
return object;
}
else
{
NSError *tmp = [NSError errorWithDomain:#"after" code:0 userInfo:nil];
*error = tmp;
return nil;
}
}
But I found that the two logging addresses are different. Why is that?
2016-08-19 19:00:16.582 Test[4548:339654] outside error address: 0x7fff5b3e6a58
2016-08-19 19:00:16.583 Test[4548:339654] inside error address: 0x7fff5b3e6a50
Shouldn't they be the same since that was just a simple value copy? If they should be different, how can pointer to pointer end up pointing to the same NSError instance?
The variable in the caller has type NSError*. The address has type NSError* *. The function expect NSError* __autoreleasing *. Therefore the compiler creates a hidden variable of type NSError* __autoreleasing, copies the NSError* into the hidden variable before the call, and copies it back after the call to get the semantics of __autoreleasing right.
So, after initialisation on the first line, error is a pointer to an NSError object.
In the first log, you are logging the address at which that pointer is held. That's the effect of the & address-of operator. Anyway, that is the address gets passed into the doSomething method.
You're passing in: pointer -> pointer -> nserror-object.
But notice the double indirection in the signature of doSomething. The autoreleasing annotation makes it hard to spot, but its NSError **.
So the compiler takes your parameter and 'unwraps' it twice.
It starts as pointer -> pointer -> nserror-object. Then after the first indirection it becomes pointer -> nserror-object. Then after the second indirection it becomes nserror-object.
Ergo, you are logging two different things. The first is the address of the pointer to the nserror-object. The second is the address of the nserror-object itself.
EDIT:
#MANIAK_dobrii points out that the object pointed to by error is itself different in the before and after case.
That's true. If an error occurs in doSomething then it creates an entirely new NSError instance in the else clause. It then loads that back into the error pointer. That's why you would see two different addresses, afterwards the error pointer is pointing to another object entirely.

Convert NSData to NSArray error

I use iCloudDocumentSync library to store/retrieve files in iCloud (iCloud Container). I have NSArray of objects and I convert this array to NSData:
NSKeyedArchiver.archivedDataWithRootObject(foundationArray)
and store file to iCloud. All works fine.
When i retrieved this file, and try convert NSData to NSArray I have error:
iCloud.sharedCloud().retrieveCloudDocumentWithName(backupName, completion: { (document, data, error) -> Void in
if error == nil {
print(document.fileURL)
// let addressBook = NSKeyedUnarchiver.unarchiveObjectWithFile(document.fileURL.URLString)
let addressBook = NSKeyedUnarchiver.unarchiveObjectWithData(data)
print(addressBook)
progressHUD.hide(true)
}
})
Eror:
*** Terminating app due to uncaught exception 'AutocodingException', reason: 'Expected 'url' to be a NSURL, but was actually a __NSCFString'
*** First throw call stack:
(0x185cc822c 0x19799c0e4 0x185cc816c 0x1003354f0 0x1003355c8 0x186b40d98 0x186b47fd0 0x186ad67d8 0x186b40d98 0x186b401b4 0x186b43154 0x186b42fec 0x10033540c 0x1003355c8 0x186b40d98 0x186b47fd0 0x186ad67d8 0x186b40d98 0x186b401b4 0x186b3f3dc 0x1000bdfc8 0x1000bcdf4 0x10076a0d0 0x101878fd4 0x101878f94 0x10187dc28 0x185c7f544 0x185c7d5ec 0x185ba8f74 0x18f6036fc 0x18a7aad94 0x1000cccd0 0x198046a08)
libc++abi.dylib: terminating with uncaught exception of type NSException
You have to convert your url into NSURL, you are passing to argument type that accept NSURL.
let UrlToUse = NSURL(string: url)

Explicit old fashioned error handling in Swift 2

Apple introduced fancy new error handling in Swift 2
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014216-CH7-ID10
I'm working with project that uses AFNetoworking v2.x where AFHTTPRequestSerializer has 2 selectors:
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters DEPRECATED_ATTRIBUTE;
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError * __autoreleasing *)error;
First is deprecated but second is converted automatically to first signature by Swift 2 compiler. Old fashioned method now doesn't allowed:
var error: NSError?
let request = self!.operationManager.requestSerializer.requestWithMethod(method, URLString: url?.absoluteString, parameters: params, error: error)
gives me a compile time error:
Cannot convert value of type 'NSError?' to expected argument type '()'
But brand new notation reduces selector with error processing to deprecated variant without it.
do {
let request = try
self!.operationManager.requestSerializer.requestWithMethod(method, URLString: url?.absoluteString, parameters: params)
} catch let error as NSError {
// ...
}
What is best practice in Swift 2 to solve this problem? Is there any way to specify certain selector in this situation?
UPD: More precise link on Swift 2 feature that become a reason of my problem. https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10
Seems this works properly in Xcode 7.1 (I recall having this issue in 7.0) just change your method to use an error pointer i.e &error
var error: NSError?
let request = self!.operationManager.requestSerializer.requestWithMethod(method, URLString: url?.absoluteString, parameters: params, error: &error)
Swift 2 prefers to provide Try Catch block to necessary methods only. Thus if your method have cases where you might need error handling then only provide this block.
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let NSError = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}

Resources