Swift capture value with same name - swift

In a closure can we use the same name somehow inside as well as a value that is captured by the closure.
func load(withResource resource: Resource) {
var data: A?
var error: Error?
load(resource: resource) { (result, error) in
data = result // Ok!
error = error // error!
}
print("data: \(data), error: \(error)")
}
I am thinking if there is something like using self if we were talking about stored properties but these vars are are declared in the function scope.
The easiest way would just to rename error but I was wondering if there is another way.

First, If your load method is asynchronous, error and data will always be nil when it's printed.
The direct answer to your question is that within the scope of the closure, the value of "error" is the value from the parameter and there's no way to access the error from the function.
Obviously there are a lot of options to get around this, but one clean option would be to make your information into a Tuple:
func load(withResource resource: Resource) {
var closureData: (data: A?, error: Error?)?
load(resource: resource) { (result, error) in
closureData = (result, error)
}
//print(closureData) if you don't need to format it)
print("data: \(closureData.data), error: \(closureData.error)")
}

Related

Swift: use inOut parameter in escaping closure

I have the following method:
private func retrieveFromAPIAndMapToCD<T: Decodable & CoreDataMappable, R: NSManagedObject>(endpoint: Endpoint, object: T.Type, cdObjectType: R.Type, storedObjectList: inout [R]) {
API.client.call(endpoint, expecting: [T].self) { (result, objects) in
switch result {
case .failure:
print("Unable to retrieve objects")
case .success:
guard let objectResponse = objects else { return }
DispatchQueue.main.async {
for object in objectResponse {
object.mapToCoreData()
}
self.appDelegate.saveContext()
self.updateLastSavedDate(object: T.self)
self.fetchObjectsFromCD(object: cdObjectType.self, objectList: &storedObjectList)
}
}
}
}
Without pasting too much unnecessary code, my issue is that within this API call (which comes from a library helper method we use to handle API responses) I need to set storedObjectList which is a variable defined within the scope of the current class.
However, when I do this I receive an error stating:
Escaping closure captures 'inout' parameter 'storedObjectList'
I'm trying to find a way around this so that I can still pass in storedObjectList here. I hit this method for 3 different objects, hence why I am trying to make it generic to avoid code repetition. This is the last part that I cannot get to work. I would like to avoid having some kind of a switch statement based on the object type passed in as R because this will limit the reusability of this method in the future.

Initialize a placeholder empty variable of generic type

I'm new to Swift.
My ultimate goal is to return the value level (which contains decoded JSON) but I am struggling with returning data from my function.
This is my code:
func decode<T: Decodable>(_ type: T.Type, from file: String) -> T {
let decoder = JSONDecoder()
let urlString = "https://drive.google.com/u/0/uc?id=1WMjoHM2iaKjfx9wZhE_6jmKlcMi_OzGT&export=download"
var level = type;
self.loadJson(fromURLString: urlString) { (result) in
switch result {
case .success(let data):
level = try! decoder.decode(T.self, from: data)
case .failure(let error):
print(error)
}
}
return level
}
Current problem
The current problem is when I try to assign a value to level I get the following error Cannot assign value of type 'T' to type 'T.Type'
What I've tried
My assumption is the error is happening because initializing a variable with T.type is trying to initialize with the name of the Class instead of the Class itself. I tried to call type.self with no luck.
I also tried to get creative to not have to initialize an empty variable. I tried returning decoder.decode(T.self, from: data) but I was getting a weird Unexpected non-void return value in void function error. I have no idea what that is all about.
Is there an easy way to initialize an empty variable to return data later when given T.Type

Unwrapping an Optional value on callback function swift

I made this question because even though there are multiple ones with similar titles I was not able to find one with the same issue I have. I have a function that calls on a function that has a call back. This second function makes another call to another function that has a callback. My issue is, the third function that is being called is inside a .m file which through a bridge I am able to access it. This function returns nil if there is no error. The problem is that I am unable to allow nil in the callback function and it return the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I am new to swift and from what I have read using optionals is a good way but it still giving issues. Any ideas?
Code:
mainViewController.swift
func makeACall(){
var service:Service!
service = Service()
// error happens on this next line I believe is the error variable
service.lookup(item: itemTextField.text, callback: { (details:String?, error: serviceError?) -> Void in
if (error != nil){
//do something
}
})
}
Service.swift
func lookup(item:String!, callback: (_ details:String?, _ error: serviceError?) -> Void){
super.fetchItem(item, callback: { (details:String?, error: serviceError?) -> Void in
callback(details, error!)
}) // in objective C .m file fetchItem returns the call back as callback(details, nil) if no error
}
If your callback has a optional, why are you force-unwrapping it?
You didn't for details.
Both of these are optionals, yet you force unwrapped the error one. Use callback like this:
callback(details, error)
As for the text going in. Just do this before the fetch:
guard let itemText = itemTextField.text else { return }
Or if you want to call the function even with an empty string you can do this
let itemText = itemTextField.text ?? ""
And then use itemText like so:
service.lookup(item: itemText, callback: { (details:String?, error: serviceError?) -> Void in
if (error != nil){
//do something
}
})

Asynchronous error handling in swift 2

So I tried error handling thing in swift 2. But one thing that I am not sure about is how to get it work for asynchronous callback functions. Suppose I am loading a resource from backend. I defined my error type like the following:
enum NetworkError: ErrorType {
case NoConnection
case InvalidJSON
case NoSuccessCode(code: Int)
}
I am planning to throw one of these cases when something wrong. Here is the function that makes network call:
func loadRequest<T: Decodable>(request: NSURLRequest, callback:T -> Void) throws {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { data, response, error in
// Other code that parses json and at somewhere it throws
throw NetworkError.NoConnection
}
}
But here compiler gives error:
Cannot invoke dataTaskWithRequest with an argument list of type
(NSURLRequest, (_,_,_) throws) -> Void)
From here it is obvious that same closure type is considered as a different type when it is declared with throws.
So how is this do-try-catch thing works in these situations?
An error cannot be thrown asynchronously because the the function will already have returned when the error occurs, you have to handle the error within the closure by calling back to some function with an ErrorType parameter to decide what you want to do with it. Example:
import Foundation
enum NetworkError: ErrorType {
case NoConnection
case InvalidJSON
case NoSuccessCode(code: Int)
}
func getTask() -> NSURLSessionDataTask? {
let session = NSURLSession.sharedSession()
let urlRequest = NSURLRequest(URL: NSURL(string: "www.google.com")!)
return session.dataTaskWithRequest(urlRequest) { data, response, error in
if let error = error {
asyncError(error)
} else {
// Do your stuff while calling asyncError when an error occurs
}
}
}
func asyncError(error: ErrorType) {
switch error {
case NetworkError.NoConnection:
// Do something
break
default:
break
}
}
Nothing in NSURLSession.h seems to throw exceptions. So I wonder if that class has been converted to use this new functionality yet.

Function call is requiring an argument but the function takes no arguments

I have this code in a file called makeRequest.swift in a class makeRequest:
class makeRequest {
func callAPI () {
let url = NSURL(string: "http://APIServer.com)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
println(NSString(data: data, encoding: NSUTF8StringEncoding))
}
task.resume()
}
}
And I call it with makeRequest.callAPI(). However, it is requiring an argument:
Missing argument for parameter #1 in call
When I tried to configure callAPI to take a string:
class makeRequest {
func callAPI(urlEnd: String) {
...
}
}
And call it with
makeRequest.callAPI("ending")
It errors with
Cannot invoke 'callAPI' with an argument list type of '(String)'
Sorry about how confusing Swift's error messages are. (There is a cool reason for the nature of this error message, but never mind that right now.) The problem is really that you are calling this method as a class method but it is declared as an instance method.