Throwing errors from closure - swift

I have this piece of code in my app:
func saveContact2(contact: String) throws {
let contactStore = CNContactStore()
contactStore.requestAccess(for: .contacts, completionHandler: {(granted, error) in
if granted && error == nil {
//...
} else {
if !granted {
throw contactErrors.contactAccessNotGranted(["Error","Access to Contacts is not granted."])
}
}
})
}
I'd like to throw all errors raising in closure to calling function.
Compiler shows error:
Invalid conversion from throwing function of type '(_, _) throws -> ()' to non-throwing function type '(Bool, Error?) -> Void'
Could anyone help me please with the right syntax?

You cannot throw errors from an #escaping closure that is called asynchronously. And this makes sense because your app has carried on with its execution and there’s no where to catch the error.
So, instead, adopt completion handler pattern yourself:
func saveContact2(_ contact: String, completion: #escaping: (Result<Bool, Error>) -> Void) {
let contactStore = CNContactStore()
contactStore.requestAccess(for: .contacts) { (granted, error) in
guard granted else {
completion(.failure(error!)
return
}
//...
completion(.success(true))
}
}
And then you’d call it like:
saveContact2(contactName) { result in
switch result {
case .failure:
// handler error here
case .success:
// handle confirmation of success here
}
}
If you’re using an old compiler that doesn’t have the Result type, it’s basically:
enum Result<Success, Failure> where Failure: Error {
case success(Success)
case failure(Failure)
}

Related

Invalid conversion from throwing function of type 'xx' to non-throwing function type 'xx'

how fix it? How should I use do-try-catch correctly.
This is possible when I remove the error code from the catch segment, but I need the specific type of error, so how do I catch a specific error and use it?
static func request<T: HandyJSON>(
target: StatusAPI,
type: T.Type,
success successCallback: (T) -> Void,
error errorCallback: (_ statusCode: Int) -> Void,
failure failureCallback: #escaping (MoyaError) -> Void
) {
provider.request(target) { result in
switch result {
case let .success(response):
do {
try response.filterSuccessfulStatusCodes()
} catch {
print(error)
}
case let .failure(error):
failureCallback(error)
}
}
}

Invalid conversion from throwing function of type '(_, _) throws -> ()' to non-throwing function type '(Bool, Error?) -> Void

I have very simple class which fetches the contacts.
Now I need to create this function with throws.
As store.requestAccess is not throwing function so I can't throw any error from that clousure.
So I am getting this error
Invalid conversion from throwing function of type '(_, _) throws -> ()' to non-throwing function type '(Bool, Error?) -> Void'
class ContactFetcher {
enum ContactError:Error {
case permissionError
case fetchError
}
func fetchContacts(completion:#escaping(([CNContact]) -> ())) throws {
let keys = [CNContactPhoneNumbersKey] as [CNKeyDescriptor]
let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
let store = CNContactStore()
var results:[CNContact] = []
store.requestAccess(for: .contacts) { (grant, error) in
if grant{
do {
try store.enumerateContacts(with: fetchRequest, usingBlock: { (contact, stop) -> Void in
results.append(contact)
})
} catch {
// throw ContactError.fetchError
}
completion(results)
} else {
throw ContactError.permissionError
print("Error \(error)")
}
}
}
}
Is there any way to fix this ?
Thanks in advance
You cannot throw from within the non throwing completion handler. It is asynchronous! Just as you cannot return a value from within an asynchronous function, so too you cannot throw there. After all, to catch the throw, the throw would need to travel backwards in time to when the original function was called. And that is metaphysically impossible.
Instead you must provide another completion handler such that you can call it with an Error parameter from within the non throwing completion handler. That is exactly why the Result type was invented, to allow you to propagate an error through an asynchronous situation.

How to raise an Error for the completionHandler?

I'm in process of unittesting a case where the completionHandler is throwing an error. But I'm not sure how to raise that error.
class MockErrorSession: URLSessionProtocol {
var nextDataTask = MockURLSessionDataTask()
var nextData: Data?
var nextError: Error?
func dataTask(with request: NSURLRequest, completionHandler: #escaping DataTaskResult) -> URLSessionDataTaskProtocol {
nextError = ?
completionHandler(nextData, successHttpURLResponse(request: request), nextError)
return nextDataTask as URLSessionDataTaskProtocol
}
}
I need somehow to populate nextError
I tried to do this,
enum MyError : Error {
case RuntimeError(String)
}
func throwError(_ message: String) throws {
throw MyError.RuntimeError(message)
}
nextError = try throwError("test") as! Error
Any advice please?
Your function dataTask(with:completionHandler:) does not seem to be marked with throws/rethrows in which case it seems the error is simply being passed to the completion block.
Since neither the function nor the closure "throws", you can't throw the error & as a result neither will you be able to do-try-catch it later anyways.
So as per my assumptions, just the following should suffice:
nextError = MyError.RuntimeError("test")
i.e.
enum MyError : Error {
case RuntimeError(String)
}
func dataTask(with request: NSURLRequest,
completionHandler: #escaping DataTaskResult) -> URLSessionDataTaskProtocol {
nextError = MyError.RuntimeError("test")
completionHandler(nextData,
successHttpURLResponse(request: request),
nextError)
return nextDataTask as URLSessionDataTaskProtocol
}

Custom Serialization finds incorrect "response" in Alamofire 4.0

I'm attempting to define a custom model serialization for Alamofire 4.0. So far I'm following the model presented used by responseJson and friends. Specifically, what I have so far is:
extension Alamofire.Request {
public static func serializeResponseModel<T:ModelObject>(response:HTTPURLResponse?, data:Data?, error:Error?) -> Alamofire.Result<T> {
switch serializeResponseJSON(options: [], response: response, data: data, error: error) {
case .success(let jsonObject):
do {
return .success(try T(json:jsonObject as! JSONObject))
}
catch {
return .failure(error)
}
case .failure(let error):
return .failure(error)
}
}
}
extension Alamofire.DataRequest {
public static func serializeResponseModel<T:ModelObject>() -> DataResponseSerializer<T> {
return DataResponseSerializer { _, response, data, error in
return Request.serializeResponseConcierge(response: response, data: data, error: error)
}
}
#discardableResult
public func responseModel<T:ModelObject>(queue: DispatchQueue? = nil, completionHandler: #escaping (DataResponse<T>) -> Void) -> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.serializeResponseModel(),
completionHandler: completionHandler
)
}
}
Unfortunately, the framework is somewhat poorly implemented and the line return response( is finding the response property (defined in Request) and not the appropriate response method (defined in DataRequest), which leads to the compile error:
Cannot call value of non-function type 'HTTPURLResponse?'
What am I missing here that allows this to work in the responseJson case, but not in my case?
Apparently the problem arose from over-generalization, and the compiler not being able to generate an appropriate type for DataRequest.serializeResponseModel() When I changed responseModel to the following and specified the appropriate type, things work as expected:
#discardableResult
public func responseModel<T:ModelObject>(queue: DispatchQueue? = nil, completionHandler: #escaping (DataResponse<T>) -> Void) -> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.modelResponseSerializer() as DataResponseSerializer<T>,
completionHandler: completionHandler
)
}

Swift generics: "Generic parameter T could not be inferred"

I'm trying to make a generic request handler class based on Moya and Object Mapper.
Basically I want a method that performs a request to a Moya Target and performs some basic handling on both sucess and failure cases:
my class would be something like this based on this answer:
class APIRequestHandler<T: Mappable> {
let provider = APIManager().getAPIProvider() // Moya provider
func performRequest(target: Target, completionHandler: (response: T?, error: StoreError?) -> Void) {
provider.request(target) { result in
switch result {
case let .Success(response):
switch response.statusCode {
case 200..<300:
do {
let json = try NSJSONSerialization.JSONObjectWithData(response.data, options: .MutableContainers)
let contractualDocument = Mapper<T>().map(json)
completionHandler(response: contractualDocument!, error: nil)
} catch _ {
// Unexpected error when the response can't be parsed
completionHandler(response: nil, error: StoreError.UnexpectedError)
}
case 500:
completionHandler(response: nil, error: StoreError.ServerError)
default:
completionHandler(response: nil, error: StoreError.HTTPError(statusCode: response.statusCode))
}
case .Failure(_):
completionHandler(response: nil, error: StoreError.NetworkError)
}
}
}
}
My question is if I can get the same results using generics at a function level instead of class level:
func performRequest<T: Mappable>(target: Target, completionHandler: (response: T?, error: StoreError?) -> Void) {
provider.request(target) { result in
switch result {
case let .Success(response):
// blah, blah
case .Failure(_):
// bleh, bleh
}
}
}
I've tried it and I get a build error on my code:
Generic parameter "T" could not be inferred