Why does app crash at NSData getBytes? - ios

NSData is extended to determine the file type:
extension NSData {
var dataType: String? {
// Ensure data length is at least 1 byte
guard self.length > 0 else { return nil }
// Get first byte
var c = [UInt8](count: 1, repeatedValue: 0)
self.getBytes(&c, length: 1)
// Identify data type
switch (c[0]) {
case 0xFF:
return "jpg"
case 0x89:
return "png"
case 0x47:
return "gif"
case 0x49, 0x4D:
return "tiff"
default:
return nil //unknown
}
}
}
The method above is called on a NSData object from image data that is fetched from a server.
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
do {
// Fetch image synchronously from server
let query = PFQuery(className: <...>)
let result = try query.getFirstObject()
guard
let imageObject = result.objectForKey(<...>) as? PFFile,
let imageData = try? imageObject.getData(),
let image = imageData.dataType == "gif" ? UIImage.animatedImageWithAnimatedGIFData(imageData) : UIImage(data: imageData)
else {
return
}
<...>
} catch (let error as NSError) {
<...>
}
}
However the app very rarely crashes at line self.getBytes:
What is the reason for this?
The buffer of getBytes is &c, an UnsafeMutablePointer - do I have to take any special memory considerations because of that?
Update
The crashes still occur with the following variation of the code:
// Get first byte
var c: UInt8 = 0;
self.getBytes(&c, length: 1)
Update
The crashes still occur with the following variation of the code:
// Get first byte
var c = [UInt8](count: 1, repeatedValue: 0)
c.withUnsafeMutableBufferPointer {
buffer in
getBytes(buffer.baseAddress, length: 1)
}
guard c.indices.contains(0) else { return nil }
I got the following crash and included the whole thread, maybe someone can spot a hint:
Thread 18 Crashed:
0 libsystem_platform.dylib 0x21a8e198 _platform_memmove$VARIANT$CortexA9 + 92
1 Foundation 0x22512923 __34-[_NSDispatchData getBytes:range:]_block_invoke + 176
2 libdispatch.dylib 0x218d238d _dispatch_data_apply + 82
3 libdispatch.dylib 0x218d4a51 dispatch_data_apply + 26
4 Foundation 0x22512865 -[_NSDispatchData getBytes:range:] + 86
5 Foundation 0x2267730b -[_NSDispatchData getBytes:length:] + 24
6 MyAppName 0x00079ba0 partial apply forwarder for (extension in MyAppName):__ObjC.NSData.(dataType.getter : Swift.String?).(closure #1) (NSData+Extension.swift:54)
7 MyAppName 0x00079c14 partial apply forwarder for reabstraction thunk helper from #callee_owned (#inout Swift.UnsafeMutableBufferPointer<Swift.UInt8>) -> (#unowned (), #error #owned Swift.ErrorType) to #callee_owned (#inout Swift.UnsafeMutableBufferPointer<Swift.UInt8>) -> (#out (), #error #owned Swift.ErrorType) (NSData+Extension.swift:0)
8 MyAppName 0x00079cb8 generic specialization <Swift.UInt8, ()> of Swift.Array.withUnsafeMutableBufferPointer <A> ((inout Swift.UnsafeMutableBufferPointer<A>) throws -> A1) throws -> A1 (NSData+Extension.swift:0)
9 MyAppName 0x00079a70 (extension in MyAppName):__ObjC.NSData.dataType.getter : Swift.String? (NSData+Extension.swift:55)
10 MyAppName 0x00079948 #objc (extension in MyAppName):__ObjC.NSData.dataType.getter : Swift.String? (NSData+Extension.swift:0)
11 MyAppName 0x000d2264 MyAppName.DataManager.(fetchImagesFromServer (MyAppName.ImageSet) -> ()).(closure #1) (DataManager.swift:1214)
12 libdispatch.dylib 0x218cd823 _dispatch_call_block_and_release + 8
13 libdispatch.dylib 0x218dc5e9 _dispatch_root_queue_drain + 1558
14 libdispatch.dylib 0x218dbfcd _dispatch_worker_thread3 + 94
15 libsystem_pthread.dylib 0x21a91b29 _pthread_wqthread + 1022
16 libsystem_pthread.dylib 0x21a91718 start_wqthread + 6
Update
The crashes still occur with the following variation of the code:
// Get first byte
var c = UnsafeMutablePointer<UInt8>.alloc(1)
defer { c.dealloc(1) }
self.getBytes(c, length: 1)
switch (c[0]) { ...

With the help of an Apple engineer (via a TSI ticket) the issue was finally identified.
All code permutations above for reading the first byte are valid and working.
The issue was that the NSData object was created when a file was fetched from a server using the Parse iOS SDK which stores the data in a temporary file with file protection key NSFileProtectionCompleteUntilFirstUserAuthentication.
The file protection key allows reading data of the NSData object only after the user unlocks the device once after reboot. Although the data is not readable before unlocking, the NSData object can be created and even the NSData.length property is accessible. However, attempting to read the data would throw an exception.
I changed the code and added a check if the protected data is available before attempting to read it with UIApplication.sharedApplication().protectedDataAvailable.
You may wonder why a file was fetched by the app before the device was even unlocked. The app was started by a remote user notification. That explains why the crash happened so rarely.
Learned 2 things:
Always check your file protection key
Apple technical support gave a super in-depth explanation and is worth the money

A Swift array is more like a C++ std::vector than a C array: it has other contents besides the array elements. You can't get a pointer to the first element using &c. You need to ask the array for a pointer to its elements, like this:
var c = [UInt8](count: 1, repeatedValue: 0)
c.withUnsafeMutableBufferPointer { buffer in
getBytes(buffer.baseAddress, length: 1)
}

For your particular case, it seems overkill to use an array, though. Why not just do:
var c: UInt8 = 0;
self.getBytes(&c, length: 1)

Related

Firebase crashlytics reports many insight available crashes in my app

Below is my code
func getImageFrom(_ asset: PHAsset, completion: #escaping (UIImage) -> Void) {
var thumbnail = UIImage()
let imageManager = PHCachingImageManager()
let options = PHImageRequestOptions()
options.version = .current
let size = CGSize(width: 150.0, height: 150.0)
imageManager.requestImage(for: asset, targetSize: size, contentMode: .aspectFill,
options: nil) { (image, info) in
guard let img = image else { return }
thumbnail = img
}
completion(thumbnail)
}
I'm using this function to get an Image from PHAsset.
some time crash is reported at
thumbnail = img or at completion(thumbnail).
using this function in below code block
func getImageDataAndFindDupImages() {
var i = 0
var similarImages = [OSTuple<NSString, NSString>]()
similarImages = OSImageHashing.sharedInstance().similarImages(with: .high) { () -> OSTuple<NSString, NSData>? in
#if DEBUG
print(i)
#endif
var tuple = OSTuple<NSString, NSData>()
if self.tempArrAssets.count > 0 {
DispatchQueue.main.async {
self.lblNumberOfPhoto.text = R.string.localizable.scanning_photos_number("\(i+1)", "\(self.arrPhotoAssets.count)")
}
if !self.tempArrAssets.isEmpty {
let tempArrAsset = self.tempArrAssets.removeFirst()
self.getImageFrom(tempArrAsset) { (imageAsset) in
self.arrImageFromAsset.append(imageAsset)
if let imgData = imageAsset.pngData() {
tuple = (OSTuple<NSString, NSData>(first: NSString(string: "\(i)"), andSecond: imgData as NSData))
}
}
i += 1
return tuple
} else {
return nil
}
} else {
return nil
}
}
print("To make dup tuples:", Date().timeIntervalSince(self.date))
}
I'm using getImageFrom in getImageDataAndFindDupImages
This is a log for the crash
Crashed: com.apple.mobileslideshow.accessCallbacks
0 libobjc.A.dylib 0x18a874020 objc_retain + 16
1 Duplicate Cleaner 0x1001f0e80 closure #1 in DuplicatePhotoFinderVC.getImageDataAndFindDupImages() + 519 (DuplicatePhotoFinderVC.swift:519)
2 Duplicate Cleaner 0x1001f16a4 thunk for #escaping #callee_guaranteed () -> (#owned OSTuple<NSString, NSData>?) + 4338357924 (<compiler-generated>:4338357924)
3 CocoaImageHashing 0x1004f3514 -[OSSimilaritySearch similarImagesWithProvider:withHashDistanceThreshold:forImageStreamHandler:forResultHandler:] + 46 (OSSimilaritySearch.m:46)
4 CocoaImageHashing 0x1004f3924 -[OSSimilaritySearch similarImagesWithProvider:withHashDistanceThreshold:forImageStreamHandler:] + 95 (OSSimilaritySearch.m:95)
5 CocoaImageHashing 0x1004f28d0 -[OSImageHashing similarImagesWithProvider:withHashDistanceThreshold:forImageStreamHandler:] + 222 (OSImageHashing.m:222)
6 CocoaImageHashing 0x1004f27b0 -[OSImageHashing similarImagesWithHashingQuality:withHashDistanceThreshold:forImageStreamHandler:] + 201 (OSImageHashing.m:201)
7 CocoaImageHashing 0x1004f2734 -[OSImageHashing similarImagesWithHashingQuality:forImageStreamHandler:] + 190 (OSImageHashing.m:190)
8 Duplicate Cleaner 0x1001ee63c DuplicatePhotoFinderVC.getImageDataAndFindDupImages() + 316 (DuplicatePhotoFinderVC.swift:316)
9 Duplicate Cleaner 0x1001ee434 DuplicatePhotoFinderVC.fetchPhotosandCheckforDuplicate() + 146 (DuplicatePhotoFinderVC.swift:146)
10 Duplicate Cleaner 0x1001eea1c closure #1 in DuplicatePhotoFinderVC.checkForPermissionAndScanPhotos() + 162 (DuplicatePhotoFinderVC.swift:162)
11 Duplicate Cleaner 0x10021bdec thunk for #escaping #callee_guaranteed (#unowned PHAuthorizationStatus) -> () + 4338531820 (<compiler-generated>:4338531820)
12 Photos 0x195ff9098 __39+[PHPhotoLibrary requestAuthorization:]_block_invoke + 64
13 AssetsLibraryServices 0x19f048edc __79-[PLPrivacy _isPhotosAccessAllowedWithScope:forceHandler:accessAllowedHandler:]_block_invoke.14 + 520
14 AssetsLibraryServices 0x19f01486c __pl_dispatch_async_block_invoke + 36
15 libdispatch.dylib 0x18a7fe610 _dispatch_call_block_and_release + 24
16 libdispatch.dylib 0x18a7ff184 _dispatch_client_callout + 16
17 libdispatch.dylib 0x18a7ab404 _dispatch_lane_serial_drain$VARIANT$mp + 608
18 libdispatch.dylib 0x18a7abdf8 _dispatch_lane_invoke$VARIANT$mp + 420
19 libdispatch.dylib 0x18a7b5314 _dispatch_workloop_worker_thread + 588
20 libsystem_pthread.dylib 0x18a84eb88 _pthread_wqthread + 276
21 libsystem_pthread.dylib 0x18a851760 start_wqthread + 8
I'm not able to understand why I'm getting these crashes. If you have any knowledge please guide me.
You are passing nil to options parameter in the imageManager.requestImage(for:targetSize: contentMode:options:). If you pass nil as options, the method calls the completion block asyncrohonously. So there is a chance by the time your completion block gets called the method getImageFrom(: completion:) has exited and the local variable thumbnail has been destroyed(I am not talking about the UIImage object being destroyed but the variable which holds address of the UIImage itself). Then trying to set the thumbnail varibale which is no more on the stack, inside the block might be the reason there is a crash.
To solve your problem Create an instance of PHImageRequestOptions set its isSynchronous property to true like so.
let options = PHImageRequestOptions()
options.isSynchronous = true
Now pass this options as a parameter to the imageManager.requestImage(for:targetSize: contentMode:options:)
As a result your imageManager.requestImage(for:targetSize: contentMode:options:) will call the block synchronously meaning before the getImageFrom(: completion:) method exits and so the variable thumbnail is still alive on the stack to be used.
Hoping this should solve your problem.
PS: Take care you call the method getImageFrom(: completion:) in a background thread or your main thread will be frozen

iOS app rejected by Apple due to IPV6 issue but crash report indicates assertion failed

My iOS app does not use any networking and works fine on my iPhone, but Apple rejected it supposedly due to IPv6 incompatibility issues.
2. 1 PERFORMANCE: APP COMPLETENESS
Performance - 2.1
Your app crashes on iPhone running iOS 10.0.3 connected to an IPv6 network when we:
Specifically, we found your app crashes when we tap “Analyze.”
We have attached detailed crash logs to help troubleshoot this issue.
However when I symbolicated the crash report they sent, it shows a particular thread crashed. The backtrace is below.
Thread 5 name: Dispatch queue: com.apple.HealthKit.HKHealthStore.client.0x1700f6d00
Thread 5 Crashed:
0 libswiftCore.dylib 0x0000000100273ae8 _assertionFailed(StaticString, String, StaticString, UInt, flags : UInt32) -> Never (__hidden#14874_:167)
1 libswiftCore.dylib 0x0000000100273ae8 _assertionFailed(StaticString, String, StaticString, UInt, flags : UInt32) -> Never (__hidden#14874_:167)
2 PickerTest 0x00000001000c1d1c type metadata accessor for String? (PyschicBrain.swift:0)
3 PickerTest 0x00000001000bfe78 PsychicBrain.(isHeartRateInfoAvailableForDates() -> Bool).(closure #1) (PyschicBrain.swift:0)
4 libdispatch.dylib 0x000000018f98d200 _dispatch_call_block_and_release + 24
5 libdispatch.dylib 0x000000018f98d1c0 _dispatch_client_callout + 16
6 libdispatch.dylib 0x000000018f99b444 _dispatch_queue_serial_drain + 928
7 libdispatch.dylib 0x000000018f9909a8 _dispatch_queue_invoke + 652
8 libdispatch.dylib 0x000000018f99b940 _dispatch_queue_override_invoke + 360
9 libdispatch.dylib 0x000000018f99d38c _dispatch_root_queue_drain + 572
10 libdispatch.dylib 0x000000018f99d0ec _dispatch_worker_thread3 + 124
11 libsystem_pthread.dylib 0x000000018fb952c8 _pthread_wqthread + 1288
12 libsystem_pthread.dylib 0x000000018fb94db4 start_wqthread + 4
Stack frame #3 (above) says the closure in the function isHeartRateInfoAvailableForDates() failed, which means the HKSampleQuery failed.
My question: I'm assuming this is because no heart-rate data was found for that interval. On my phone, in this scenario, samples.count() returns 0 and hence the function returns FALSE. When Apple folks test it, obviously they get a different error. I've asked Apple for console logs but I would like to know a more elegant way of error handling.
func isHeartRateInfoAvailableForDates() -> Bool {
predicate = HKQuery.predicateForSamples(withStart: pvtStartDate, end: pvtEndDate, options: HKQueryOptions())
guard let sampleType = HKSampleType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else {
fatalError("HKSampleType.quantityTypeForIdentifier failed")
}
let dispatchGrp = DispatchGroup()
// Entering query block
//
dispatchGrp.enter()
let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: Int(HKObjectQueryNoLimit), sortDescriptors: nil) {
query, results, error in
guard let samples = results as? [HKQuantitySample] else {
fatalError("An error occured fetching the user's heart rate: \(error?.localizedDescription)");
}
self.pvtSampleCount = samples.count
// leave group once query results are available
dispatchGrp.leave()
}
pvtHealthStore.execute(query)
// Now wait for the query to complete
//
dispatchGrp.wait(timeout: DispatchTime.distantFuture)
return pvtSampleCount > 0
}

Realm sometimes doesn't return latest results

Most of the time it I get all the results, but sometimes I don't, and that's obviously a problem. I have a function like:
func addProduct(thisProduct:Product) {
let realm = try! Realm()
try! realm.write {
realm.add(thisProduct)
}
countProductsAddedToday()
// sometimes doesn't include the just added product to the count
}
The problem is my function countProductsAddedtoday() is not always returning the correct number of products when called immediately after a write transaction like in this case. If it gets called later (by tapping a button in my app), it returns the right count.
I'm hoping this is a common problem and that I'm doing something I'm not supposed to be doing.
The function countProductsAddedToday() is like this:
func countProductsAddedToday() -> Int {
let predicate = NSPredicate(format: "timeAdded >= %d", unixTimestampForToday)
let realm = try! Realm()
let results = realm.objects(Product).filter(predicate)
return results.count
}
Additional information: I can't be sure but I think the problem happens more often when adding products quickly, but then stops happening. Is there some delayed writing or caching of results or something like that?
The error I'm getting when adding products on a physical device:
0 Realm 0x000000010042d7e0 _ZN5realm4util18terminate_internalERNSt3__118basic_stringstreamIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE + 44
1 Realm 0x000000010042da0c _ZN5realm4util9terminateEPKcS2_l + 340
2 Realm 0x00000001002a70ac _ZN5realm5_impl16RealmCoordinator19run_async_notifiersEv + 2124
3 Realm 0x00000001002a6668 _ZN5realm5_impl16RealmCoordinator9on_changeEv + 40
4 Realm 0x000000010026adf4 _ZN5realm5_impl20ExternalCommitHelper6listenEv + 672
5 Realm 0x000000010026b89c _ZZN5realm5_impl20ExternalCommitHelperC1ERNS0_16RealmCoordinatorEENK3$_0clEv + 24
6 Realm 0x000000010026b878 _ZNSt3__112__async_funcIZN5realm5_impl20ExternalCommitHelperC1ERNS2_16RealmCoordinatorEE3$_0JEE9__executeIJEEEvNS_15__tuple_indicesIJXspT_EEEE + 64
7 Realm 0x000000010026b82c _ZNSt3__112__async_funcIZN5realm5_impl20ExternalCommitHelperC1ERNS2_16RealmCoordinatorEE3$_0JEEclEv + 24
8 Realm 0x000000010026b73c _ZNSt3__119__async_assoc_stateIvNS_12__async_funcIZN5realm5_impl20ExternalCommitHelperC1ERNS3_16RealmCoordinatorEE3$_0JEEEE9__executeEv + 32
9 Realm 0x000000010026bfc0 _ZNSt3__114__thread_proxyINS_5tupleIJMNS_19__async_assoc_stateIvNS_12__async_funcIZN5realm5_impl20ExternalCommitHelperC1ERNS5_16RealmCoordinatorEE3$_0JEEEEEFvvEPSB_EEEEEPvSG_ + 388
10 libsystem_pthread.dylib 0x0000000180cebb28 <redacted> + 156
11 libsystem_pthread.dylib 0x0000000180ceba8c <redacted> + 0
12 libsystem_pthread.dylib 0x0000000180ce9028 thread_start + 4
Change your function to:
func countProductsAddedToday() -> Int {
let predicate = NSPredicate(format: "timeAdded >= %d", unixTimestampForToday)
let realm = try! Realm()
realm.refresh()
let results = realm.objects(Product).filter(predicate)
return results.count
}

Crash on iOS 8 when dispatch_group_wait() return because of timeout

I have two operations fetching a value. I only care about the sum both values. And I don't care about the value at all if it takes too long.
So I thought it would be an easy task for GCD using groups. Unfortunately, the below code only works fine on iOS 9. Each time I have no matching calls of dispatch_group_enter()/dispatch_group_leave() I get a crash.
The documentation states clearly that I have to match both call. But when I use a timeout on dispatch_group_wait(), it is impossible to have the same amount of leave calls as enter calls; that is the whole point of specifying a timeout.
So is this a known bug in iOS 8? Am I doing something wrong? Is there another solution to my initial problem that works on iOS 8 as well?
EDIT: Actually we can boil it down to this:
var sync_group: dispatch_group_t = dispatch_group_create();
dispatch_group_enter(sync_group);
let maxWait = dispatch_time(DISPATCH_TIME_NOW, Int64(60 * NSEC_PER_SEC))
let result = dispatch_group_wait(sync_group, maxWait)
sync_group = dispatch_group_create();
Works as expected on iOS 9 but does crash on iOS 8 on the last line because the old dispatch_group_t instance can not be release. Any easy workarounds?
EDIT 2: Turns out it is broken on iOS 9.0 too. It only works as as it should in iOS 9.1+.
Original Code:
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
/* We want this method to block until the network code succeeded. */
let sync_group: dispatch_group_t = dispatch_group_create();
/* Start async operation 1. */
dispatch_group_enter(sync_group);
self.someAsyncOperation1({(value: Int, finalValue: Bool) in
if (finalValue) {
valOp1 = value
dispatch_group_leave(sync_group);
}
})
/* Start async operation 2. */
dispatch_group_enter(sync_group);
self.someAsyncOperation2({(value: Int, finalValue: Bool) in
if (finalValue) {
valOp2 = value
dispatch_group_leave(sync_group)
}
})
/* Block current thread until all leaves were called. If it takes more then 60 sec we don't care and let go. */
let maxWait = dispatch_time(DISPATCH_TIME_NOW, Int64(60 * NSEC_PER_SEC))
let result = dispatch_group_wait(sync_group, maxWait)
if (result > 0) {
/* This will result in a crash when we leave the scope: SIGTRAP in dispatch_semaphore_dispose */
return
}
dispatch_async(dispatch_get_main_queue(), {
let newValue = valOp1 + valOp2
self.lastKnownNotificationCombinedCounter = newValue
success(newValue)
})
})
The actual crash loops like that:
Exception Type: SIGTRAP
Exception Codes: #0 at 0x3958a2a4
Thread 2 Crashed:
0 libdispatch.dylib 0x3958a2a4 _dispatch_semaphore_dispose$VARIANT$mp + 48
1 libdispatch.dylib 0x3958b491 _dispatch_dispose$VARIANT$mp + 30
2 libdispatch.dylib 0x3957ea8f -[OS_dispatch_object _xref_dispose] + 44
3 myApp 0x00176a24 block_destroy_helper67 + 354
4 myApp 0x00176ab8 0x2e000 + 1346232
5 myApp 0x00178334 0x2e000 + 1352500
6 libsystem_blocks.dylib 0x395d3adb _Block_release + 216
7 Foundation 0x2c4143b9 -[NSBlockOperation dealloc] + 58
8 libobjc.A.dylib 0x39036d57 objc_object::sidetable_release(bool) + 164
9 libobjc.A.dylib 0x390371a9 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 402
10 libdispatch.dylib 0x39589423 _dispatch_root_queue_drain + 1176
11 libdispatch.dylib 0x3958a1fb _dispatch_worker_thread3 + 104
12 libsystem_pthread.dylib 0x396fae25 _pthread_wqthread + 666
13 libsystem_pthread.dylib 0x396fab78 start_wqthread + 6
I came up with this workaround:
private let MAX_TRIES = 20;
func dispatch_group_wait_ios8Safe(group: dispatch_group_t, _ timeout: dispatch_time_t) -> Int {
if #available(iOS 9, *) {
/* Just forward the call. */
return dispatch_group_wait(group, timeout)
} else {
/* Forward the call to original function and store result. */
let firstResult = dispatch_group_wait(group, timeout)
var result = firstResult, tries = 0
while(result > 0 && tries < MAX_TRIES) {
dispatch_group_leave(group)
result = dispatch_group_wait(group, DISPATCH_TIME_NOW)
tries += 1
}
/* Return original result. */
return firstResult
}
}
So until someone comes up with a better solution I stick with this.

Swift: Downcast causes application to crash, why?

I have following snippets of code where I have crash on some devices:
Crashed: com.apple.root.default-qos
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x00000000cbd
Code:
var obj:AnyObject = command.arguments[0] as AnyObject!
var theData:AnyObject = obj["getContactImagesByEmails"] as AnyObject!
if let contactImagesByEmails:AnyObject = obj["emails"]{
if contactImagesByEmails is Array<String>{
/*line 176*/ let array:Array<String> =
contactImagesByEmails as Array<String> // CRASH happens here
results = WmSqliteImagesModel.getInstance.getImagesByEmailAsWmContactImage(array) as Dictionary<String,AnyObject>
}
}
Full stack trace
Thread : Crashed: com.apple.root.default-qos
0 libswiftCore.dylib 0x0000000100559794 swift_unknownRetain + 32
1 MyApp 0x000000010017c8a0 MyApp.Plugin.(getContactImagesByEmails (MyApp.Plugin) -> (ObjectiveC.CDVInvokedUrlCommand) -> ()).(closure #1) (Plugin.swift:176)
2 MyApp 0x000000010017c8a0 MyApp.Plugin.(getContactImagesByEmails (MyApp.Plugin) -> (ObjectiveC.CDVInvokedUrlCommand) -> ()).(closure #1) (Plugin.swift:176)
3 MyApp 0x00000001001790b0 partial apply forwarder for reabstraction thunk helper from #callee_owned () -> (#unowned ()) to #callee_owned (#in ()) -> (#out ()) with unmangled suffix "125" (Plugin.swift:62)
4 MyApp 0x0000000100179120 partial apply forwarder for reabstraction thunk helper from #callee_owned (#in ()) -> (#out ()) to #callee_owned () -> (#unowned ()) with unmangled suffix "128" (Plugin.swift:62)
5 libdispatch.dylib 0x00000001937e13ac _dispatch_call_block_and_release + 24
6 libdispatch.dylib 0x00000001937e136c _dispatch_client_callout + 16
7 libdispatch.dylib 0x00000001937ed40c _dispatch_root_queue_drain + 1152
8 libdispatch.dylib 0x00000001937ee75c _dispatch_worker_thread3 + 108
9 libsystem_pthread.dylib 0x00000001939bd2e4 _pthread_wqthread + 816
The Plugin.swift:176 points to:
let array:Array<String> = contactImagesByEmails as Array<String>
Do I miss something? I think this code should be safe.
if contactImagesByEmails is Array<String> returns true, why contactImagesByEmails as Array<String> fails?
Please help,
[EDIT]
command has type of CDVInvokedUrlCommand
#interface CDVInvokedUrlCommand : NSObject {
NSString* _callbackId;
NSString* _className;
NSString* _methodName;
NSArray* _arguments;
}
I don't know if this can actually fixes the problem, but there's a bit of redundancy in the 2 ifs and the following let. It can simply be written as:
if let contactImagesByEmails = obj["emails"] as? Array<String> {
results = WmSqliteImagesModel.getInstance.getImagesByEmailAsWmContactImage(contactImagesByEmails) as Dictionary<String,AnyObject>
}
Moreover, it would be safer to combine optional binding and optional downcast when invoking getImagesByEmailAsWmContactImage
if let results = WmSqliteImagesModel.getInstance.getImagesByEmailAsWmContactImage(contactImagesByEmails) as? Dictionary<String,AnyObject> {
...
}
Last, are you sure getInstance is a property and not a method? Shouldn't it be:
if let results = WmSqliteImagesModel.getInstance().getImagesByEmailAsWmContactImage(contactImagesByEmails) as? Dictionary<String,AnyObject> {
...
}
Perhaps try an alternate approach to getting the casted value:
if let contactImagesByEmails = obj["emails"] as? [String] {
results = WmSqliteImagesModel.getInstance.getImagesByEmailAsWmContactImage(contactImagesByEmails) as Dictionary<String,AnyObject>
}
I also wonder if your crash is not from naming your variable "array" in your original code...
Simplify this step by step with fewer references to AnyObject (and certainly not AnyObject!). The compiler is probably letting through something that is illegal, because when you start pulling out AnyObject you're saying "I know exactly what I'm doing; don't check this."
You'd expect this code to be something like:
let obj = command.arguments[0]
if let contactImagesByEmails = obj["emails"] as? [String] {
results = WmSqliteImagesModel.getInstance.getImagesByEmailAsWmContactImage(array) as? [String: AnyObject]
}
In the end results would be a [String:AnyObject]?
The key point here is that you should get rid of as many references to AnyObject as you can, and you should use if-let-as? to determine if the types are what you expect.
The fact that command.arguments is filled with things of various types that you have to type-check is pretty dangerous and a sign of a serious design problem. If there really are several types in there, you should use an enum rather than type-checking against random things like [String:AnyObject]. That's what enums are explicitly for. (If this is bridging from ObjC; enums may not be possible here, but even in ObjC, the right answer is seldom to have an NSArray filled with heterogeneous types. You create a class there to hold them rather than an enum.)

Resources