I've got a simple class that uses an NSURLSession.
class test {
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration());
func f() {
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0), { () -> Void in
var task = self.session.dataTaskWithRequest(request, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
if error != nil {
// cry
return;
}
var error: NSError? = nil;
var dict = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as! Dictionary<String, String>;
// use dict
});
task.resume();
});
}
When I try to deserialize the data as JSON, the application crashes.
I've determined that the data seems to be of the right length but the content looks like garbage in the debugger, so it seems to me that the data object passed in is broken. Furthermore, I suspect some stack smashing as I can step through this completion handler and see that it's the attempt to deserialize the data that's crashing, but when the actual crash occurs, the stack in the debugger mentions nothing about the completion handler or any of my code.
I've seen several samples of using NSURLSession that look pretty much exactly like mine that just work. I tried using the shared session instead of making a new one, but that did not help either.
What is causing this crash?
Seems that the JSON was not actually all strings- I brainfarted and one of them was actually a number.
The real problem in the question is that Swift is completely worthless when handling the problem of force casts failing. Not only do you not get any kind of useful error at runtime that you could handle or recover from, but the debugging information presented when it occurs is completely misleading and points to totally the wrong place. You simply get a trap in a function without symbols with the wrong callstack.
Related
What is the difference between:
guard let json_data = Data(contentsOf: path) else {return nil}
and
let json_data = try? Data(contentsOf: path)
I dont want to use optional while loading the data into the variable. I want other ways to try it.
Thanks in advance.
The options are:
Your first example, unwrapping it with guard, is missing a try?:
func foo() -> Bar? {
guard let jsonData = try? Data(contentsOf: path) else { return nil }
// if you get here, `jsonData` is not `Optional`
…
}
This will safely unwrap your optional and let you do whatever you want if the unwrapping failed. (In your example, you are returning nil.)
Your second example, yields an optional, which you presumably need to unwrap with an if statement.
func foo() -> Bar? {
let jsonData = try? Data(contentsOf: path)
// jsonData is `Optional` in this example
if let jsonData {
…
} else {
return nil
}
}
We would generally favor the first option over this, where the “early exit” of the guard makes it a little easier to read the code, but there are cases where you might use this pattern.
An option that hasn’t been considered is to actually throw the error (using try instead of try?):
func foo() throws -> Bar {
let jsonData = try Data(contentsOf: path)
// `jsonData` is not `Optional`
…
}
Now, this only passes the buck of handling the error to the caller (i.e., a do-catch block). But it does have a few virtues over the prior two examples, namely that (a) the useful information of the error object is not just discarded, thereby making it easier to diagnose problems during the development process; and (b) you don’t have to return an optional.
Yet another option (to be used only if you know that this will always succeed, e.g., you are reading a well-known file from your bundle that you know must always succeed) is try!, a “force-try”:
func foo() -> Bar {
let jsonData = try! Data(contentsOf: path)
// `jsonData` is not `Optional`
…
}
Now, this will crash if the Data(contentsOf:) can ever fail, so only use this in scenarios where you know that this is impossible.
Personally, I would generally favor option 3 (where I capture what went wrong) in cases where the Data(contentsOf:) might ever plausibly fail at runtime, and I might consider option 4 (where it crashes with a meaningful error message) when I know it is impossible for it to ever fail at runtime. That having been said, more than once I found myself using option 4 and I later regretted not using option 3, simply because there was some weird edge-case that I neglected to consider.
In short, nowadays I tend to defensively catch errors, log the full error in the console and show a nice localized message in the UI (i.e., option 3). I almost never use try?, because if something can fail, it’s rarely a good idea to discard the useful diagnostic information.
It will be better to be optional once you are loading the data to avoid application crash in and problems in case there was no data there. This is consider as a safe feature.
Until now I had no problems, which is the weird part. Swift never complained about this code. A user got an error signing up and me debugging put a print statement and maybe because of the introspection of the code now swift caught the error and is saying:
Generic parameter 'T' could not be inferred
I thought this is how you handle continuation, what's wrong in my code?
The funny thing is that it works sometimes, others it throws the error above :/
The error is in line: try await withCheckedThrowingContinuation
Here's the full code:
// Signup
final func signUp(username: String, email: String, password: String) async throws -> (message: String, didItSucceed: Bool) {
print("➡️ About to sign up user")
try await withCheckedThrowingContinuation { continuation in
let userAttributes = [AuthUserAttribute(.email, value: email)]
let options = AuthSignUpRequest.Options(userAttributes: userAttributes)
_ = Amplify.Auth.signUp(
username: username,
password: password,
options: options
) { result in
switch result {
case .success(let signUpResult):
print("✅ Signup confirmed. Next-> needs email verification from \(email)")
switch signUpResult.nextStep {
case .done:
print("✅ Finished sign up!")
case .confirmUser(_, _):
DispatchQueue.main.async {
// Confirm the Auth State to confirm code passing in the username to confirm
self.authState = .login
}
continuation.resume(returning: ("Check your Email 📧", true))
}
case .failure(let error):
var errorMessageToTheUser = "Something went wrong 🤔"
print(error)
continuation.resume(returning: (errorMessageToTheUser, false))
}
}
}
}
EDIT:
If I remove the print() on top of the withCheckedThrowingContinuation the exception goes away. But the error when user signs up still remains and it crashes at line:
try await withCheckedThrowingContinuation { continuation in
and takes (the error) to the #main struct saying: Thread 1: EXC_BAD_ACCESS (code=1, address=0x46b56a8a6b20)
What's wrong?
First of all, you should be using withUnsafeThrowingContinuation. The Checked version is just for testing that you are doing everything correctly; it has overhead that you don't want to encounter unless you have to. (However, as we shall see, you are not doing everything correctly, so this was actually a good choice for the moment!)
Second, if you look at the docs, you will see that this is a generic function, whose type T is resolved through the resolution of the CheckedContinuation (your continuation). So instead of merely saying
continuation in
you can, if necessary, supply the type information:
(continuation: CheckedContinuation<WhatType, Error>) -> Void in
(I do not know what type you should put for WhatType because you have not given enough information.)
Third, you are crashing because you are using the continuation incorrectly. You must call continuation.resume exactly once on every possible path of exit within your implementation, and you are not doing that (look at the .done path for example). I think you will find that when you get this worked out, the other problems will just go away.
Fourth, the reason why the print statement changed everything is that before you put that there, there was an implicit return before your try await. Thanks to that, the compiler was able to infer the types in question. But you took that away when you added the print. If you put return try await your compilation problems might all go away (though you will still crash because you are not using the continuation properly).
I was previously using RxSwift and I decided I did not want to use it anymore and was able to convert everything over to Bond which I am much more familiar with. Since the new changes though to Bond v5, I cannot seem to figure out how to observe values in UserDefaults. The following code ends up giving me a fatal error.
userDefaults.reactive
.keyPath(LocationManager.HomeLocationKey, ofType: String.self, context: .immediateOnMain)
.map(self.initLocation(from:))
.bind(to: self.homeLocation)
userDefaults is a reference to UserDefaults.standard and LocationManager.HomeLocationKey is a string. I am providing the initLocation function below as I know it will be asked for. Below that function I will post the error that I am receiving after the app starts up.
func initLocation(from string: String?) -> Location?
{
guard let dataString = string
else { log.warning("Location data did not exist, returning nil"); return nil }
let json = JSON.parse(dataString)
return Location(from: json)
}
Error:
fatal error: Could not convert nil to String. Maybe `dynamic(keyPath:ofExpectedType:)` method might be of help?): file /Users/sam/Documents/iOS Apps/Drizzle/Pods/Bond/Sources/Shared/NSObject+KVO.swift, line 58
It might not be obvious, but if the observed value can be nil, the ofType argument must be an Optional type. In your case, that would be:
userDefaults.reactive
.keyPath(LocationManager.HomeLocationKey, ofType: Optional<String>.self, context: .immediateOnMain)
...
Just starting to learn Swift coming from Obj-C - this is something simple I'm not understanding:
class func queryForAllUsersWithCallback(completion: (users :[User]?, error :NSError?) ->()) {
var query = PFQuery(className:User.parseClassName())
query.findObjectsInBackgroundWithBlock ({
(objects:[AnyObject]?, error: NSError?) in
completion(users: objects, error: error);
})
}
Give me a compiler error:
Cannot invoke 'findObjectsInBackgroundWithBlock' with an argument list of type '(([AnyObject]?, NSError?) -> _)'
If I comment out the line:
completion(users: objects, error: error);
the error goes away, so the warning is misleading.
completion takes as its first argument an array of User, whereas objects is an array of AnyObject. There’s no guarantee what is in objects is of the correct type (could be a motley collection of various types for all the compiler knows) so it won’t compile.
If you do a conditional cast it should compile, i.e.:
completion(users: objects as? [User], error: error)
Note, this will check at runtime that every element in objects really is of the correct type. If any of them aren’t, the whole array will be nil when passed to the completion handler. This will compile, since the argument is optional, but might be quite surprising/fail silently or even worse, crash because somewhere inside completion might be the assumption it isn’t nil, so it could get force-unwrapped.
So you might instead want to put some error handling in:
if let users = objects as? [User] {
completion(users: users, error: error)
}
else {
// log or fatalError or something
}
(apologies if the syntax of some of the above isn’t quite right, I haven’t tested the code since your snippet isn’t reproducible/stand-alone)
You just need to cast the objects to User as:
completion(users: objects as? [User], error: error)
I'm using the
loadItemForTypeIdentifier:options:completionHandler: method on an NSItemProvider object to extract a url from Safari via a Share extension in iOS 8.
In Objective-C, this code compiles and works:
[itemProvider loadItemForTypeIdentifier:(#"public.url" options:nil completionHandler:^(NSURL *url, NSError *error) {
//My code
}];
In Swift however, I'm getting "NSSecureCoding!' is not a subtype of 'NSURL" compile error when I try to do something similar:
itemProvider.loadItemForTypeIdentifier("public.url", options: nil, completionHandler: { (urlItem:NSURL, error:NSError!) in
//My code
})
If I add the bang to NSURL argument type as in NSURL! I get "Cannot convert the expression's type 'Void' to type 'Void'" compile error. And if I leave the default argument typed as NSSecureCoding!, it compiles, but the block/closure doesn't run.
What am I doing wrong?
You don't need to specify the types for urlItem or error as they can be inferred from the declaration of loadItemForTypeIdentifier:options:completionHandler. Just do the following:
itemProvider.loadItemForTypeIdentifier("public.url", options: nil, completionHandler: {
(urlItem, error) in
//My code
})
Even better, you can move the closure outside of the method call:
itemProvider.loadItemForTypeIdentifier("public.url", options: nil) {
(urlItem, error) in
//My code
}
This API makes use of reflection internally: it looks at the type of the block’s first parameter to decide what to return. This is dependent on Objective-C’s looser enforcement of the block signature compared to Swift — and therefore does not work in Swift. (Yes, still.) See this discussion on the developer forums.
I recommend filing a bug report with Apple and writing small Objective-C wrapper methods to read each type of data you need.
It’s possible I’ve overlooked something. If someone has found a neat way to make this work in Swift I’m keen to hear it.
The new API to use is canLoadObject and loadObject, use it as this:
if (itemProvider.canLoadObject(ofClass: NSURL.self)) {
print("==== attachment is URL")
itemProvider.loadObject(ofClass: NSURL.self, completionHandler:
{
(data, error) in
print("==== url object = \(data)")
})
}
The same can be used for UIImage
https://developer.apple.com/documentation/uikit/drag_and_drop/data_delivery_with_drag_and_drop
Just in case you or other ones still need a solution, it is simple. Just add a side variable with a cast to NSURL. Here it is:
itemProvider.loadItemForTypeIdentifier("public.url", options: nil) {
(urlItem, error) in
let url : NSURL = urlItem : NSURL
// Whatever you like to do with your new url
}