I use the Alamofire swift library to do some networking in my App.
I'm currently building my own ResponseSerializer.
According to Alamofire's docs you throw errors in the serialization process like this
let failureReason = "Data could not be serialized. Input data was nil."
let error = Error.errorWithCode(.DataSerializationFailed, failureReason: failureReason)
return .Failure(data, error)
So my thought was to use this method to throw some more errors not related to Alamofire that can appear in my serialization. For example if there is a problem on the server-side, the response includes an error description as a string.
I want to add some custom error codes like .ServerErrorInJson that I can use in the errorWithCode(Alamofire.Error.Code, String) -> NSError method.
Is there a way I can add those cases to the enum Alamofire.Error.Code?
Related
In my previous project using older versions of Alamofire and SwiftyJSON, using JSON(response.result.value) worked just fine. I've tried to implement code in my new project but getting some errors I haven't seen before. I did see some questions asked about it here on SO, but none of the solutions seemed to resolve my issue (unless there's something I missed).
AF.request(self.apiEndpoint,
method: .get,
parameters: self.parameters,
headers: self.headers,
interceptor: nil).validate().responseJSON { (response) in
switch response.result {
case .success(let value):
let json = JSON(value)
print(json)
case .failure(_):
print(response)
}
}
This returns the following error:
Cannot invoke 'JSON' with an argument list of type '((Any))'
This is a strange situation. However, what I managed to identify was that if I call
JSON(value)
This fails with the error of not being able invoke the constructor with type (Any).
However, when I explicitly name the module as follows:
let json = SwiftyJSON.JSON(value)
Xcode no longer complains about that error.
Very strange indeed. I did go back and check that JSON() referred to SwiftyJSON when attempting to use it without specifically naming the module - just to verify there wasn't some sort of method naming clash with something else - and it did. So I'm not entirely sure why this works, but hopefully it helps someone else out. Or someone can give me a better answer if I've done something incorrectly.
So... I want to launch an application at a given URL using this function. The problem is, the developer documentation says:
Parameters
url
The application URL.
options
Options to use when launching the application. See
NSWorkspace.LaunchOptions for possible values.
configuration
A dictionary containing the configuration options. This
dictionary can be used to pass additional options to the app. Possible
key-value pairs are described in Workspace Launch Configuration
Options. The configuration dictionary may be empty, in which case
default behavior applies.
error
Returns, by-reference, the error if the application was unable
to be launched. You may specify nil for this parameter if you do not
want the error information.
Return Value
If the app is already running, and newInstance is not
specified in the options dictionary, then a reference to the existing
app is returned; otherwise a new application reference is returned. If
the application could not be launched, nil is returned and the error
is specified in error.
The problem is, that this doesn't match with the function signature at all, which is:
func launchApplication(at url: URL, options: NSWorkspace.LaunchOptions = [], configuration: [NSWorkspace.LaunchConfigurationKey : Any]) throws -> NSRunningApplication
Where the hell am I supposed to specify said error?
Except for the error handling, the parameters mentioned in the documentation exactly match the actual method. Both have url, options, and configuration. Though, thanks to Swift's ability to have argument labels, you use at with the url parameter when actually calling the method.
Most Objective-C APIs that have a last parameter of NSError **error are translated in Swift to an API that has no error parameter. Instead, they declare that they throw.
If you read the rest of the documentation for the method you quoted, you will see:
Handling Errors In Swift:
In Swift, this method returns a nonoptional result and is marked with the throws keyword to indicate that it throws an error in cases of failure.
You call this method in a try expression and handle any errors in the catch clauses of a do statement, as described in Error Handling in The Swift Programming Language and About Imported Cocoa Error Parameters.
You handle the error as follows:
do {
// pass real options and configuration as needed
try someWorkspace.launcApplication(at: someURL, options: [], configuration: [:])
} catch {
// handle error here as needed
print(error)
}
I am using a .playground file and I can't seem to add my CoreML model to it. I drag it into the Resources folder and this is my code:
func predict(image: CGImage) {
let model = try! VNCoreMLModel(for: Inceptionv3().model)
let request = VNCoreMLRequest(model: model, completionHandler: results)
let handler = VNSequenceRequestHandler()
try! handler.perform([request], on: image)
}
However, I get the error saying:
Use of Undeclared Type Inceptionv3
Can someone please help me out?
The compiler raises this error, because it cannot find a declaration of the class Inceptionv3, that you try to instantiate an object of.
This class is automatically created for you as long as you have a regular Xcode project. If you want to work with it inside a Swift playground, you will need to add this file manually:
First, create a regular Xcode project (an iOS app for example) and integrate your mlmodel there. Xcode will then generate an interface for that model automatically. These are exactly the files that are missing in your project and contain the definition of the Inceptionv3 class.
The same problem has been asked and answered here. There you can also find an image showing how to find the automatically generated classes.
Im used to typical try catch blocks to handle exceptions of any function or block of code like objective C.
However, after making leap of faith to Swift and reading about Swift’s error handling; it’s not much what I expected. You must handle your exceptions by throwing errors and guard and checking if else everything.
I need general Swift error handling tips and best practices, and especially for the following cases:
Im using Alamofire for calling services, which has closure, and calling it through another function with closure too. AFAIK I can’t throw error inside async code, so what is best practice for such case? will try to update with code sample
Is it favourable to have every function in the app throw errors? Just in case ? Like checking every single value or result.
Can I have a Singleton Error handling module?
Thanks in advance
You must handle your exceptions by throwing errors and guard and checking if else everything.
This is an assumption that doesn't have to be true. If your code is structured correctly, you shouldn't have to check everything with if lets and throws.
I need general Swift error handling tips and best practices ...
Before you look at anything else, read the following pages in order. They should give you a good background on best practices.
Error Protocol Apple Developer Documentation
Error Handling - Apple's Swift Programming Language Guide
Magical Error Handling in Swift - Ray Wenderlich
Im using Alamofire for calling services, which has closure, and calling it through another function with closure too. AFAIK I can’t throw error inside async code, so what is best practice for such case?
You can. A good practice is to pass a closure as a parameter to your service-calling function, then call the closure when the asynchronous operation is complete, like so:
functionThatCallsAService(completion: #escaping (Data?, NetworkingErrors?) -> ()) {
session.dataTask(with: request) { data, response, error in
guard error == nil else {
completion(nil, NetworkingErrors.returnedError(error!))
return
}
completion(data, nil)
}.resume()
}
enum NetworkingErrors: Error {
case errorParsingJSON
case noInternetConnection
case dataReturnedNil
case returnedError(Error)
case invalidStatusCode(Int)
case customError(String)
}
Is it favourable to have every function in the app throw errors? Just in case? Like checking every single value or result.
If you know for sure that a function or value won't be nil/cause a runtime error/throw an error, then don't check for it! But generally, according to the web pages above, you should check for nil and/or errors from calling a web service, interacting with the file system, creating a complex object, etc.
Can I have a Singleton Error handling module?
You technically can, but I don't see any reason to do so besides logging.
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: 17)
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 ")
}
try checkValidAgeForGovernmentJob(age: 17) OutPut : You are underage for government job
try checkValidAgeForGovernmentJob(age: 26) OutPut : You are overrage for government job
try checkValidAgeForGovernmentJob(age: 18) OutPut : You are valid for government job
Scenario: Networking app based on Alamofire.
I'm encountering deprecated-code notices in my latest project build. I traced it to the following statement within Alamofire. I don't see any mention of the alternatives.
#available(*, deprecated=3.4.0)
public static func errorWithCode(code: Int, failureReason: String) -> NSError {
let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
return NSError(domain: Domain, code: code, userInfo: userInfo)
}
What's the replacement?
And... how do I determine other replacements of deprecated codes?
You now need to build your own errors with your own custom domain. It was unwise of us to originally expose those convenience methods because it led users to create their own errors using the Alamofire error domain which is not correct.
All of this is going to be easier in Swift 3 with the new AFError type being introduced.