I haven't read too much into Swift but one thing I noticed is that there are no exceptions.
So how do they do error handling in Swift? Has anyone found anything related to error-handling?
Swift 2 & 3
Things have changed a bit in Swift 2, as there is a new error-handling mechanism, that is somewhat more similar to exceptions but different in detail.
1. Indicating error possibility
If function/method wants to indicate that it may throw an error, it should contain throws keyword like this
func summonDefaultDragon() throws -> Dragon
Note: there is no specification for type of error the function actually can throw. This declaration simply states that the function can throw an instance of any type implementing ErrorType or is not throwing at all.
2. Invoking function that may throw errors
In order to invoke function you need to use try keyword, like this
try summonDefaultDragon()
this line should normally be present do-catch block like this
do {
let dragon = try summonDefaultDragon()
} catch DragonError.dragonIsMissing {
// Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
// Other specific-case error-handlng
} catch {
// Catch all error-handling
}
Note: catch clause use all the powerful features of Swift pattern matching so you are very flexible here.
You may decided to propagate the error, if your are calling a throwing function from a function that is itself marked with throws keyword:
func fulfill(quest: Quest) throws {
let dragon = try summonDefaultDragon()
quest.ride(dragon)
}
Alternatively, you can call throwing function using try?:
let dragonOrNil = try? summonDefaultDragon()
This way you either get the return value or nil, if any error occurred. Using this way you do not get the error object.
Which means that you can also combine try? with useful statements like:
if let dragon = try? summonDefaultDragon()
or
guard let dragon = try? summonDefaultDragon() else { ... }
Finally, you can decide that you know that error will not actually occur (e.g. because you have already checked are prerequisites) and use try! keyword:
let dragon = try! summonDefaultDragon()
If the function actually throws an error, then you'll get a runtime error in your application and the application will terminate.
3. Throwing an error
In order to throw an error you use throw keyword like this
throw DragonError.dragonIsMissing
You can throw anything that conforms to ErrorType protocol. For starters NSError conforms to this protocol but you probably would like to go with enum-based ErrorType which enables you to group multiple related errors, potentially with additional pieces of data, like this
enum DragonError: ErrorType {
case dragonIsMissing
case notEnoughMana(requiredMana: Int)
...
}
Main differences between new Swift 2 & 3 error mechanism and Java/C#/C++ style exceptions are follows:
Syntax is a bit different: do-catch + try + defer vs traditional try-catch-finally syntax.
Exception handling usually incurs much higher execution time in exception path than in success path. This is not the case with Swift 2.0 errors, where success path and error path cost roughly the same.
All error throwing code must be declared, while exceptions might have been thrown from anywhere. All errors are "checked exceptions" in Java nomenclature. However, in contrast to Java, you do not specify potentially thrown errors.
Swift exceptions are not compatible with ObjC exceptions. Your do-catch block will not catch any NSException, and vice versa, for that you must use ObjC.
Swift exceptions are compatible with Cocoa NSError method conventions of returning either false (for Bool returning functions) or nil (for AnyObject returning functions) and passing NSErrorPointer with error details.
As an extra syntatic-sugar to ease error handling, there are two more concepts
deferred actions (using defer keyword) which let you achieve the same effect as finally blocks in Java/C#/etc
guard statement (using guard keyword) which let you write little less if/else code than in normal error checking/signaling code.
Swift 1
Runtime errors:
As Leandros suggests for handling runtime errors (like network connectivity problems, parsing data, opening file, etc) you should use NSError like you did in ObjC, because the Foundation, AppKit, UIKit, etc report their errors in this way. So it's more framework thing than language thing.
Another frequent pattern that is being used are separator success/failure blocks like in AFNetworking:
var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
success: { (NSURLSessionDataTask) -> Void in
println("Success")
},
failure:{ (NSURLSessionDataTask, NSError) -> Void in
println("Failure")
})
Still the failure block frequently received NSError instance, describing the error.
Programmer errors:
For programmer errors (like out of bounds access of array element, invalid arguments passed to a function call, etc) you used exceptions in ObjC. Swift language does not seem to have any language support for exceptions (like throw, catch, etc keyword). However, as documentation suggests it is running on the same runtime as ObjC, and therefore you are still able to throw NSExceptions like this:
NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()
You just cannot catch them in pure Swift, although you may opt for catching exceptions in ObjC code.
The questions is whether you should throw exceptions for programmer errors, or rather use assertions as Apple suggests in the language guide.
Update June 9th 2015 - Very important
Swift 2.0 comes with try, throw, and catch keywords and the most exciting is:
Swift automatically translates Objective-C methods that produce errors into methods that throw an error according to Swift's native error handling functionality.
Note: Methods that consume errors, such as delegate methods or methods
that take a completion handler with an NSError object argument, do not
become methods that throw when imported by Swift.
Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C (Swift 2 Prerelease).” iBooks.
Example: (from the book)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:#"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
NSLog(#"Error: %#", error.domain);
}
The equivalent in swift will be:
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
print ("Error: \(error.domain)")
}
Throwing an Error:
*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]
Will be automatically propagated to the caller:
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
From Apple books, The Swift Programming Language it's seems errors should be handle using enum.
Here is an example from the book.
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/br/jEUH0.l
Update
From Apple news books, "Using Swift with Cocoa and Objective-C". Runtime exceptions not occur using swift languages, so that's why you don't have try-catch. Instead you use Optional Chaining.
Here is a stretch from the book:
For example, in the code listing below, the first and second lines are
not executed because the length property and the characterAtIndex:
method do not exist on an NSDate object. The myLength constant is
inferred to be an optional Int, and is set to nil. You can also use an
if–let statement to conditionally unwrap the result of a method that
the object may not respond to, as shown on line three
let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
println("Found \(fifthCharacter) at index 5")
}
Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C.” iBooks. https://itun.es/br/1u3-0.l
And the books also encourage you to use cocoa error pattern from Objective-C (NSError Object)
Error reporting in Swift follows the same pattern it does in
Objective-C, with the added benefit of offering optional return
values. In the simplest case, you return a Bool value from the
function to indicate whether or not it succeeded. When you need to
report the reason for the error, you can add to the function an
NSError out parameter of type NSErrorPointer. This type is roughly
equivalent to Objective-C’s NSError **, with additional memory safety
and optional typing. You can use the prefix & operator to pass in a
reference to an optional NSError type as an NSErrorPointer object, as
shown in the code listing below.
var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
encoding: NSUTF8StringEncoding,
error: &writeError)
if !written {
if let error = writeError {
println("write failure: \(error.localizedDescription)")
}
}
Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C.” iBooks. https://itun.es/br/1u3-0.l
There are no Exceptions in Swift, similar to Objective-C's approach.
In development, you can use assert to catch any errors which might appear, and need to be fixed before going to production.
The classic NSError approach isn't altered, you send an NSErrorPointer, which gets populated.
Brief example:
var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
println("An error occurred \(error)")
} else {
println("Contents: \(contents)")
}
The recommended 'Swift Way' is:
func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}
var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
println("write failure 1: \(writeError!.localizedDescription)")
// assert(false) // Terminate program
}
However I prefer try/catch as I find it easier to follow because it moves the error handling to a separate block at the end, this arrangement is sometimes called "Golden Path". Lucky you can do this with closures:
TryBool {
write("~/Error2")(error: $0) // The code to try
}.catch {
println("write failure 2: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
Also it is easy to add a retry facility:
TryBool {
write("~/Error3")(error: $0) // The code to try
}.retry {
println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
return write("~/Error3r") // The code to retry
}.catch {
println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
// assert(false) // Terminate program
}
The listing for TryBool is:
class TryBool {
typealias Tryee = NSErrorPointer -> Bool
typealias Catchee = NSError? -> ()
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return self.retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) {
var error: NSError?
for numRetries in 0...retries { // First try is retry 0
error = nil
let result = tryee(&error)
if result {
return
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
catchee(error)
}
}
You can write a similar class for testing an Optional returned value instead of Bool value:
class TryOptional<T> {
typealias Tryee = NSErrorPointer -> T?
typealias Catchee = NSError? -> T
typealias Retryee = (NSError?, UInt) -> Tryee
private var tryee: Tryee
private var retries: UInt = 0
private var retryee: Retryee?
init(tryee: Tryee) {
self.tryee = tryee
}
func retry(retries: UInt, retryee: Retryee) -> Self {
self.retries = retries
self.retryee = retryee
return self
}
func retry(retryee: Retryee) -> Self {
return retry(1, retryee)
}
func retry(retries: UInt) -> Self {
// For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
self.retries = retries
retryee = nil
return self
}
func retry() -> Self {
return retry(1)
}
func catch(catchee: Catchee) -> T {
var error: NSError?
for numRetries in 0...retries {
error = nil
let result = tryee(&error)
if let r = result {
return r
} else if numRetries != retries {
if let r = retryee {
tryee = r(error, numRetries)
}
}
}
return catchee(error)
}
}
The TryOptional version enforces a non-Optional return type that makes subsequent programming easier, e.g. 'Swift Way:
struct FailableInitializer {
init?(_ id: Int, error: NSErrorPointer) {
// Always fails in example
if error != nil {
error.memory = NSError(domain: "", code: id, userInfo: [:])
}
return nil
}
private init() {
// Empty in example
}
static let fallback = FailableInitializer()
}
func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
return FailableInitializer(id, error: error)
}
var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
println("failableInitializer failure code: \(failError!.code)")
failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap
Using TryOptional:
let failure2 = TryOptional {
failableInitializer(2)(error: $0)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
let failure3 = TryOptional {
failableInitializer(3)(error: $0)
}.retry {
println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
return failableInitializer(31)
}.catch {
println("failableInitializer failure code: \($0!.code)")
return FailableInitializer.fallback
}
Note auto-unwrapping.
Edit: Although this answer works, it is little more than Objective-C transliterated into Swift. It has been made obsolete by changes in Swift 2.0. Guilherme Torres Castro's answer above is a very good introduction to the preferred way of handling errors in Swift. VOS
It took a bit of figuring it out but I think I've sussed it. It seems ugly though. Nothing more than a thin skin over the Objective-C version.
Calling a function with an NSError parameter...
var fooError : NSError ? = nil
let someObject = foo(aParam, error:&fooError)
// Check something was returned and look for an error if it wasn't.
if !someObject {
if let error = fooError {
// Handle error
NSLog("This happened: \(error.localizedDescription)")
}
} else {
// Handle success
}`
Writing the function that takes an error parameter...
func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {
// Do stuff...
if somethingBadHasHappened {
if error {
error.memory = NSError(domain: domain, code: code, userInfo: [:])
}
return nil
}
// Do more stuff...
}
Basic wrapper around objective C that gives you the try catch feature.
https://github.com/williamFalcon/SwiftTryCatch
Use like:
SwiftTryCatch.try({ () -> Void in
//try something
}, catch: { (error) -> Void in
//handle error
}, finally: { () -> Void in
//close resources
})
As Guilherme Torres Castro said, in Swift 2.0, try, catch, do can be used in the programming.
For example, In CoreData fetch data method, instead of put &error as a parameter into the managedContext.executeFetchRequest(fetchRequest, error: &error), now we only need to use use managedContext.executeFetchRequest(fetchRequest) and then handle the error with try, catch (Apple Document Link)
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults{
people = results
}
} catch {
print("Could not fetch")
}
If you have already download the xcode7 Beta. Try to search throwing errors in Documentations and API Reference and choose the first showing result, it gives a basic idea what can be done for this new syntax. However, fully documentation is not post for many APIs yet.
More fancy Error Handling techniques can be found in
What's New in Swift (2015 Session 106 28m30s)
This is an update answer for swift 2.0. I am looking forward feature rich Error handling model like in java. Finally, they announced the good news. here
Error handling model: The new error handling model in Swift 2.0 will
instantly feel natural, with familiar try, throw, and catch keywords.
Best of all, it was designed to work perfectly with the Apple SDKs and
NSError. In fact, NSError conforms to a Swift’s ErrorType. You’ll
definitely want to watch the WWDC session on What’s New in Swift to
hear more about it.
e.g :
func loadData() throws { }
func test() {
do {
try loadData()
} catch {
print(error)
}}
Starting with Swift 2, as others have already mentioned, error handling is best accomplished through the use of do/try/catch and ErrorType enums. This works quite well for synchronous methods, but a little cleverness is required for asynchronous error handling.
This article has a great approach to this problem:
https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/
To summarize:
// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData
// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
{
completionHandler()
}
then, the call to the above method would be as follows:
self.loadData("someString",
completionHandler:
{ result: LoadDataResult in
do
{
let data = try result()
// success - go ahead and work with the data
}
catch
{
// failure - look at the error code and handle accordingly
}
})
This seems a bit cleaner than having a separate errorHandler callback passed to the asynchronous function, which was how this would be handled prior to Swift 2.
Error handling is a new feature of Swift 2.0. It uses the try, throw and catch keywords.
See the Apple Swift 2.0 announcement on the official Apple Swift blog
Nice and simple lib to handle exception:
TryCatchFinally-Swift
Like a few others it wraps around the objective C exception features.
Use it like this:
try {
println(" try")
}.catch { e in
println(" catch")
}.finally {
println(" finally")
}
enum CheckValidAge : Error{
case overrage
case underage
}
func checkValidAgeForGovernmentJob(age:Int)throws -> Bool{
if age < 18{
throw CheckValidAge.underage
}else if age > 25{
throw CheckValidAge.overrage
}else{
return true
}
}
do {
try checkValidAgeForGovernmentJob(age: 26)
print("You are valid for government job ")
}catch CheckValidAge.underage{
print("You are underage for government job ")
}catch CheckValidAge.overrage{
print("You are overrage for government job ")
}
Change age in try checkValidAgeForGovernmentJob(age: 26)
Out Put
You are overrage for government job
What I have seen is that because of the nature of the device you don't want to be throwing a bunch of cryptic error handling messages at the user. That is why most functions return optional values then you just code to ignore the optional. If a function comes back nil meaning it failed you can pop a message or whatever.
Related
I've continued this question here, because the focus has changed, but is related.
Vapor is communicating between a server and and iOS client. Just setup code, for learning purposes.
Now, I want to send a dictionary of values via the established connection using JSON. I'm getting caught up in the unexplained logic of demo code, but here's where I am, in my vapor routes:
app.webSocket("respond") { req, ws in
ws.onText { ws, text in
print(text)
let msgDict = "{'name' = 'bobby'}"
let encoder = JSONEncoder()
let data = try encoder.encode(msgDict)
ws.send(data)
}
}
This won't compile: Invalid conversion from throwing function of type '(WebSocket, String) throws -> ()' to non-throwing function type '(WebSocket, String) -> ()'
while I generally understand this error, and dependent on how I play with this it varies.
What generally I'm looking for is a simple pattern to take internal dictionary values, encode them into JSON, and send them. What am I not doing, or not doing right?
I see two problems with that code.
The first one, explained by the compiling error, it's actually telling that it will not handle any errors thrown. When you do encoder.encode(msgDict), this code can throw an Error, and you're not handling this possible error anywhere. If you handle that possible error, code you wrote in that closure will have the expected type. You have a few options to do so:
Wrap the code around a do-catch block
do {
let data = try encoder.encode(msgDict)
ws.send(data)
} catch let error {
// Handle error
}
Use try?, means you do not handle the error but get nil as a result, if an error occurred.
if let data = try? encoder.encode(msgDict) {
ws.send(data)
}
Use try!, means you force-unwrap the result (not advisable, but you can try, if you're 100% sure)
let data = try! encoder.encode(msgDict)
ws.send(data)
The second problem is how you're writing that response - "{'name' = 'bobby'}". This is an invalid JSON, you should use double quotes instead:
let msgDict = "{\"name\" = \"bobby\"}"
You can also use Dictionary, as long as the content is of type Encodable:
let msgDict = ["name": "bobby"]
You can also use JSONEncoder to encode any instances of classes that conform to Encodable.
So the whole thing, I'd write like this:
app.webSocket("respond") { req, ws in
ws.onText { ws, text in
print(text)
let msgDict = ["name": "bobby"]
let encoder = JSONEncoder()
do {
let data = try encoder.encode(msgDict)
ws.send(data)
}
catch let error {
print("An error occurred: \(error)")
}
}
}
The error tells you that a do - catch block is missing
do {
let encoder = JSONEncoder()
let data = try encoder.encode(msgDict)
ws.send(data)
} catch { print(error) }
However your approach to create JSON cannot work because msgDict is neither valid JSON nor a Swift dictionary.
To encode the dictionary with JSONEncoder it must be a Swift dictionary
let msgDict = ["name":"bobby"]
But JSONEncoder is overkill. A simpler way is to create the JSON dictionary literally
app.webSocket("respond") { req, ws in
ws.onText { ws, text in
print(text)
let msgDict = #"{"name":"bobby"}"#
ws.send(Data(msgDict.utf8))
}
}
I am using CocoaAsyncSocket to retrieve a message from a servers API that's using JSON. I am able to get the data and convert it to a printable string, what I am unable to do is retrieve a value (transactionId) from my attempts to parse the JSON string I already have, using SwiftyJSON. I know there are other posts that are similar to this one but none have solved my problem.
In ViewController:
func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) {
guard let msg = String(data: data, encoding: .utf8) else { return }
var response = " "
if msg.contains("Reset") {
transactionID = ParseJSON().parse(message: msg)
response = String(format: "{\"Response\":{\"transactionId\":\"%#%\",\"content\":{\"Reset\":{}}}}", transactionID)
socket.write((response.data(using: .utf8))!, withTimeout: -1, tag: 0)
}
socket?.readData(withTimeout: -1, tag: 0)
}
ParseJSON class:
func parse (message: String) -> String {
var parsedMessage = " "
let json = JSON(parseJSON: message)
let transactionId = json["Request"]["transactionId"].stringValue
parsedMessage = transactionId
print(parsedMessage)
return parsedMessage
}
The result that is displayed is an empty transactionId value. Nothing prints or anything.
If you spot any errors in my code or have a better approach then please let me know!
Edit:
Here is the string I am attempting to parse:
{"Request": {"content": {"Reset": {}}, "transactionId": "f7c4d630-552b-46d9-a37d-44450537b48d"}}
Here is my output:
{\"Response\":{\"transactionId\":\"\",\"content\":{\"Reset\":{}}}}
The problem is not the code. Consider:
func parse(message: String) -> String {
let json = JSON(parseJSON: message)
return json["Request"]["transactionId"].stringValue
}
let input = """
{"Request": {"content": {"Reset": {}}, "transactionId": "f7c4d630-552b-46d9-a37d-44450537b48d"}}
"""
let transactionID = parse(message: input)
print("transactionId:", transactionID)
let response = String(format: "{\"Response\":{\"transactionId\":\"%#\",\"content\":{\"Reset\":{}}}}", transactionID)
print("response:", response)
The result of the above, as you’d expect, is:
transactionId: f7c4d630-552b-46d9-a37d-44450537b48d
response: {"Response":{"transactionId":"f7c4d630-552b-46d9-a37d-44450537b48d","content":{"Reset":{}}}}
So I suspect that the input message is not quite what you expected. So I might suggest adding some error handling so you can diagnose precisely where it is going wrong:
func parse(message: String) -> String {
let json = JSON(parseJSON: message)
if let error = json.error {
fatalError("JSON parsing error: \(error)")
}
let request = json["Request"]
if let error = request.error {
fatalError("request error: \(error)")
}
let transactionId = request["transactionId"]
if let error = transactionId.error {
fatalError("transactionId error: \(error)")
}
return transactionId.stringValue
}
Now, in practice, you probably wouldn’t use fatalError, but rather would do some graceful error handling (e.g. change parse such that it throws, then throw the errors encountered, if any, and then catch the error where you call parse and handle any runtime issues gracefully). But during this diagnostic process, fatalError is useful because it will stop your debugger at the offending line, simplifying your diagnostic process.
Bottom line, the request must not be quite in the form you expect. Note, it’s going to be very sensitive to capitalization, malformed JSON, etc. So, by looking at the errors provided by SwiftyJSON, you should be able to narrow down the issue quite quickly.
Below, you tell us that the Data is:
<7b225265 71756573 74223a20 7b22636f 6e74656e 74223a20 7b225265 73657422
3a207b7d 7d2c2022 7472616e 73616374 696f6e49 64223a20 22643937 36303036
622d3130 38302d34 3864652d 39323232 2d623139 63363663 35303164 31227d7d
00>
The problem is that last byte, 0x00. If you remove that, it works.
FWIW, when I convert that hex string back to a Data and run it through JSONSerialization, it confirms the diagnosis:
Error Domain=NSCocoaErrorDomain
Code=3840 "Garbage at end."
UserInfo={NSDebugDescription=Garbage at end.}
You need to figure out why that 0x00 was included in the end of your payload and remove it.
I am trying to get learn how to use AlamoFire and I am having trouble.
My method so far is as follows:
func siteInfo()->String?{
var info:NSDictionary!
var str:String!
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {(request, response, JSON, error) in
info = JSON as NSDictionary
str = info["access_key"] as String
//return str
}
return str
}
This returns nil which is a problem. From what I have read here, this is because the request can take a while so the closure doesn't execute till after the return. The suggested solution of moving the return into the closure does not work for me and the compiler just yells (adding ->String after (request,response,JSON,error) which gives "'String' is not a subtype of void"). Same goes for the other solution provided.
Any ideas? Even some source code that is not related to this problem, that uses AlamoFire, would be helpful.
Thanks!
One way to handle this is to pass a closure (I usually call it a completionHandler) to your siteInfo function and call that inside Alamofire.request's closure:
func siteInfo(completionHandler: (String?, NSError?) -> ()) -> () {
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {
(request, response, JSON, error) in
let info = JSON as? NSDictionary // info will be nil if it's not an NSDictionary
let str = info?["access_key"] as? String // str will be nil if info is nil or the value for "access_key" is not a String
completionHandler(str, error)
}
}
Then call it like this (don't forget error handling):
siteInfo { (str, error) in
if str != nil {
// Use str value
} else {
// Handle error / nil value
}
}
In the comments you asked:
So how would you save the info you collect from the get request if you
can only do stuff inside the closure and not effect objects outside of
the closure? Also, how to keep track to know when the request has
finished?
You can save the result of the get request to an instance variable in your class from inside the closure; there's nothing about the closure stopping you from doing that. What you do from there really depends on, well, what you want to do with that data.
How about an example?
Since it looks like you're getting an access key form that get request, maybe you need that for future requests made in other functions.
In that case, you can do something like this:
Note: Asynchronous programming is a huge topic; way too much to cover here. This is just one example of how you might handle the data you get back from your asynchronous request.
public class Site {
private var _accessKey: String?
private func getAccessKey(completionHandler: (String?, NSError?) -> ()) -> () {
// If we already have an access key, call the completion handler with it immediately
if let accessKey = self._accessKey {
completionHandler(accessKey, nil)
} else { // Otherwise request one
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {
(request, response, JSON, error) in
let info = JSON as? NSDictionary // info will be nil if it's not an NSDictionary
let accessKey = info?["access_key"] as? String // accessKey will be nil if info is nil or the value for "access_key" is not a String
self._accessKey = accessKey
completionHandler(accessKey, error)
}
}
}
public func somethingNeedingAccessKey() {
getAccessKey { (accessKey, error) in
if accessKey != nil {
// Use accessKey however you'd like here
println(accessKey)
} else {
// Handle error / nil accessKey here
}
}
}
}
With that setup, calling somethingNeedingAccessKey() the first time will trigger a request to get the access key. Any calls to somethingNeedingAccessKey() after that will use the value already stored in self._accessKey. If you do the rest of somethingNeedingAccessKey's work inside the closure being passed to getAccessKey, you can be sure that your accessKey will always be valid. If you need another function that needs accessKey, just write it the same way somethingNeedingAccessKey is written.
public func somethingElse() {
getAccessKey { (accessKey, error) in
if accessKey != nil {
// Do something else with accessKey
} else {
// Handle nil accessKey / error here
}
}
}
i tried to save data from an API in my app.
But somehow it fails:
func getApiData() -> NSArray {
let ApiManager = RestApiManager(apiUrl:"http://localhost/api/", apiUsername:"user", apiPassword:"password", apiRequestedResource:"resource")
ApiManager.collectDataFromApi() { responseObject, error in
return responseObject! // Asynchronous data!! :/
}
}
My manager:
func collectDataFromApi(completionHandler: (responseObject: NSDictionary?, error: NSError?) -> ()) {
prepareHttpRequest(completionHandler)
}
func prepareHttpRequest(completionHandler: (responseObject: NSDictionary?, error: NSError?) -> ()) {
Alamofire.request(.GET, "\(self.apiUrl + self.apiRequestedResource)")
.authenticate(user: self.apiUsername, password: self.apiPassword)
.responseJSON { request, response, responseObject, error in
completionHandler(responseObject: responseObject as? NSDictionary, error: error)
}
}
Retrieving data works great!
When i print "responseObject" instead of returning it, it works.
But how to save it...
Anybody knows how i could save my asyncronous retrieved data, so i can work with it?
Greetings and thanks
Your getApiData function cannot return the API data, because when the function returns, the data is not ready yet. This is a concept at the heart of asynchronous programming. Your function starts the network operation and then returns so that program execution can continue while your network operation is working on a different thread.
Instead of returning a value from your getApiData function, you could consider passing a completion handler, like the other asynchronous functions in your example are doing. It would look something like this:
func getApiData(completion: (responseObject: NSDictionary?, error: NSError?) -> ()) {
let ApiManager = RestApiManager(apiUrl:"http://localhost/api/", apiUsername:"user", apiPassword:"password", apiRequestedResource:"resource")
ApiManager.collectDataFromApi() { responseObject, error in
completion(responseObject, error)
}
}
Whatever work you want to do to the data when it is ready, that goes in the completion handler. Instead of using it like this (like I think you might be trying to do):
let data = getApiData()
processData(data)
updateUserInterfaceWithData(data)
you would use it like this:
getApiData() { responseObject, error in
let data = responseObject!
processData(data)
updateUserInterfaceWithData(data)
}
Of course you would want to check for errors and unwrap the optional data in a safe way, but this is the general structure that you want, I think.
The User is on a map view. When doing a long press somewhere on the map the following function gets triggered to set a new annotation inclusively a proper title.
func action(gestureRecognizer: UIGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.Began {
var newTouch: CGPoint = gestureRecognizer.locationInView(self.mapView)
var newCoordinate: CLLocationCoordinate2D = mapView.convertPoint(newTouch, toCoordinateFromView: self.mapView)
var newLocation = CLLocation(latitude: newCoordinate.latitude, longitude: newCoordinate.longitude)
var newAnnotation = MKPointAnnotation()
newAnnotation.coordinate = newCoordinate
CLGeocoder().reverseGeocodeLocation(newLocation, completionHandler: {(placemarks, error) in
if error != nil { println(error) }
let p: CLPlacemark = placemarks[0] as CLPlacemark
var thoroughfare: String? = p.thoroughfare
var subThoroughfare: String? = p.subThoroughfare
if p.thoroughfare == nil || p.subThoroughfare == nil {
var date = NSDate()
newAnnotation.title = "Added \(date)"
} else {
newAnnotation.title = thoroughfare! + " " + subThoroughfare!
}
})
self.mapView.addAnnotation(newAnnotation)
self.mapView.selectAnnotation(newAnnotation, animated: true)
places.append(["name":"\(newAnnotation.title)", "lat":"\(newCoordinate.latitude)", "lon":"\(newCoordinate.longitude)"])
}
}
I know it is working fine when keeping the last three lines of code within the CLGeocoder block (closure?). But if I separate those and list them after the }) (or put some of the code to another thread) I'm facing the problem that its running asynchronous (as I don't understand how to control async vs sync) and by the time the annotation is added to the map and saved to places its title is not set yet by the CLGeocoder.
A beginner to programming is asking: What would be necessary to be implemented (disptach_sync...-something) so the last lines of code wait for the CLGeocoder block to finish? I haven't managed to implement this command in the right way yet...
You correctly point out that that it works when you put those three lines within the geocoder's closure. So, you should do precisely that: Leverage this asynchronous pattern and put everything dependent upon the geocode process inside the closure.
In answer to your question, yes, there are patterns which can make an asynchronous task behave in a synchronous manner. For example, you can use dispatch groups or dispatch semaphores. But you are calling this from an IBAction, which runs on the main thread. And you never want to block the main thread. So any attempt to make this geocode request run synchronously from the main thread is ill-advised.
Furthermore, the above process is complicated by a wrinkle of reverseGeocodeLocation: Specifically, the closure, itself, runs on the main thread, so if you block the main thread waiting for the closure to finish, the app will deadlock. So the above patterns won't even work with reverseGeocodeLocation (if done from the main thread).
Bottom line, you should embrace the asynchronous patterns, keeping dependent code inside the closure.
As an aside, your example was a simple one, where you'd just put the code you want to perform inside the geocoder's closure. But what if you wanted to have two separate functions, for example, one for geocoding which returned an annotation, and another function for adding the annotation to the map. Perhaps you were anticipating something like:
func handleLongPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == .Began {
let location = gesture.locationInView(self.mapView)
let annotation = geocodeLocationInMapView(self.mapView, location: location)
addAnnotationToMapView(self.mapView, annotation: annotation)
}
}
And then the question would be how would you have the second function, addAnnotationToMapView, wait for the first, geocodeLocationInMapView, to complete. Again, the answer is "you don't." Instead, just like Apple does with their asynchronous methods, you would employ a completion block pattern:
func handleLongPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == .Began {
let location = gesture.locationInView(self.mapView)
geocodeLocationInMapView(self.mapView, location: location) { annotation, error in
guard annotation != nil && error == nil else {
print("error = \(error)")
return
}
self.addAnnotationToMapview(self.mapView, annotation: annotation!)
}
}
}
In that case, the geocodeLocationInMapView might look like:
func geocodeLocationInMapView(mapView: MKMapView, location: CGPoint, completion: (MKAnnotation?, NSError?) -> ()) {
let coordinate = mapView.convertPoint(location, toCoordinateFromView: mapView)
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
guard placemarks != nil && error == nil else {
completion(nil, error)
return
}
if let placemark = placemarks!.first {
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
let thoroughfare = placemark.thoroughfare
let subThoroughfare = placemark.subThoroughfare
switch (thoroughfare, subThoroughfare) {
case (nil, nil):
annotation.title = "Added \(NSDate())"
case (_, nil):
annotation.title = thoroughfare
default:
annotation.title = thoroughfare! + " " + subThoroughfare!
}
completion(annotation, nil)
}
}
}
And the addAnnotationToMapview might look like:
func addAnnotationToMapview(mapView: MKMapView, annotation: MKAnnotation) {
mapView.addAnnotation(annotation)
mapView.selectAnnotation(annotation, animated: true)
places.append(["name":"\(annotation.title)", "lat":"\(annotation.coordinate.latitude)", "lon":"\(annotation.coordinate.longitude)"])
}
This is, admittedly, a contrived example, and I'd suggest you handle your IBAction like I described at the start of this answer. But in answer to the broader question of "how do I have function A wait until function B is finished", you might employ a completion block pattern as outlined here.