Contextual closure type firebase / swift - swift

Somewhat confused by this error, very new to Firebase but I think this is more a swift programming issue on my part. A lot of the swift coding I'm trying to implement with firebase is new to me.
"Contextual closure type '(Result<StorageListResult, any Error>) -> Void' expects 1 argument, but 2 were used in closure body"
Starting with:
textRef.listAll { (result, error) in
for item in result!.items {
If I use 'item' within this closure alone such as:
let downloadTask = item.write(toFile: localURL) { url, error in
if let error = error {
print ("UNABLE TO DOWNLOAD FILES")
} else {
print("NEW FILE DOWNLOADED")
print(item)
}
}
Works perfectly.
But if I try and use item a 2nd time within the closure, such as:
let serverTimestamp = dateFormatter.string(from: itemTemp.getMetadata.updated())
I get the above error.
The aim of my code is to gain a list all items on my Firebase storage, then against each storage item firstly check its metadata updated date before deciding whether to download or not. However within the closure I can't seem to use item more than once. I either check its metadata or download...not both.
I've tried looking at closures, but struggling to see how I could potential expand the closure to incorporate what I want.
Any advice apprecaited

Related

Watson Assistant in Swift 4 Producing Error

I'm trying to connect an iOS project, coded in Swift, to an instance of a Watson Assistant that I've already created. The issue is, I can't get past a "Contextual Closure type" error in compiling.
I'm relatively new to Swift and I have yet to come across a solution.
The error arises in the block of code provided by IBM themselves (here's the link: https://console.bluemix.net/docs/swift/machine_learning/conversation.html#before-you-begin). The one thing I could find was that this is likely a result of the update to Swift 4. Below is the function that's producing the error; more specifically, it's the "response in" statement, after the third comment.
func assistantExample() {
// Assistant credentials
let username = "shawn.soneja85#gmail.com"
let password = "Shawn8135!"
let workspace = "199b1f99-b28c-4b3f-b610-5933328141d5"
// instantiate service
let assistant = Assistant(username: username, password: password, version: "2018-03-01")
// start a conversation
assistant.message(workspaceID: workspace) { response in
print("Conversation ID: \(response.context.conversationID!)")
print("Response: \(response.output.text.joined())")
// continue assistant
print("Request: turn the radio on")
let input = InputData(text: "turn the radio on")
let request = MessageRequest(input: input, context: response.context)
assistant.message(workspaceID: workspace, request: request) { response in
print("Response: \(response.output.text.joined())")
}
}
}
Here's the error itself:
Contextual closure type '(RestResponse?, WatsonError?) -> Void' (aka '(Optional>, Optional) -> ()') expects 2 arguments, but 1 was used in closure body
Update:
I've tried replacing "response" with "(response, error)", but that leads to the following error messages:
Value of type 'RestResponse?' has no member 'context'
Value of type 'RestResponse?' has no member 'output'
And with "(error, response)", it leads to the following error:
Value of type 'WatsonError?' (aka 'Optional') has no member 'output'
Value of type 'WatsonError?' (aka 'Optional') has no member 'context'
Here is documentation on .message function:
first screenshot
second screenshot
Thanks in advance for the help!
RestResponse contains a result of the .message call in a field called result. So to access the output or context, use response.result.output and response.result.context.
It would be helpful if you gave more description like:
Swift version you are using to compile your target
Line number of the error
That being said, I think your problem is in the nested .message method, i've copied your code above but deleted some lines to try and make it clear (at least what I am proposing), you may have added the second parameter to the callback in the first call to .message but had forgotten to do so in the second (nested) call, the one where you pass in the request. So you might have added the second, error parameter in the first call but got the same exact error message for a missing parameter because you forgot to do the same thing for the nested call. I hope this helps :)
// start a conversation
assistant.message(workspaceID: workspace) { response, error in
let input = InputData(text: "turn the radio on")
let request = MessageRequest(input: input, context: response.context)
assistant.message(workspaceID: workspace, request: request) { response, error in
print("Response: \(response.output.text.joined())")
}
}

Need clarification regarding closures/completion handlers

I have been following a video tutorial, and have written the following code:
func downloadWeatherDetails(completed: ()->() ) {
let currentWeatherURL = URL(string: CURRENT_WEATHER_URL)!
Alamofire
.request(currentWeatherURL)
.responseJSON(completionHandler: { response in
let result = response.result
print(result)
})
completed()
}
So basically, my understanding is as follows. The .responseJSON handler lets you call code after the request has been fired. It allows you to specify a completionHandler, which in my case, is the closure:
{ response in
let result = response.result
print(result)
}
However, what I don't understand is what the "response" keyword actually signifies. I researched the usage of closures and saw that the syntax is:
{(param) -> returnType in { code here }
Thus, is the "response" keyword a parameter? If so, how is it being declared and where is the data coming from? How is the data passed into the "response" object? Also, why is only one parameter allowed? The code did not work if I made it as follows, for example:
{ (response, test) in
let result = response.result
print(result)
}
I would really appreciate a thorough explanation on this as I've found no help elsewhere online. I've gone through Apple's "The Swift Programming Language", a multitude of different explanations, and similar questions, but still do not understand completely.
Just to clarify, I do not believe my question is a duplicate since my question revolves primarily on the captured value stored in response rather than the syntax of closures as a whole. I went through the linked question while trying to figure out my own problem, but it did not help me sufficiently.
Minor clarification needed:
Is it always the case that when a method takes a closure as one of its parameters, for example, .testMethod(testParam: (String) -> ()) and would thus in practice be used: .testMethod(testParam: { (capturedVar) in statements} (correct me if im wrong), is it always the case that the parameter of the closure ((String) in this case) will be captured and stored in capturedVar? Will there always be data passed into the variable you define? Or is this cycle specific to alamofire?
Swift closures are defined as:
{ (parameters) -> return_type in
statements
}
That is, the names in parenthesis are the variables the closure has captured, and the -> type is the optional return type (optional because the compiler can usually infer it). Alamofire's responseJSON method captures a DataResponse<Any> parameter, which you can name whatever you want, but which is usually just named response. You can then access it inside that closure.
Also, your completed() call should be inside the responseJSON call, not outside, otherwise it just gets called immediately.

ReactiveKit Bond KVO observe UserDefaults

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)
...

Completion closure within a closure

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)

Swift OSX Firebase app cannot read data

I am trying to create an OSX Swift app with Firebase. I can write to Firebase following the examples, but is not able to read the data back correctly. I have tried using the .Value listener and println(snapshot.value), it appears that the value returned is not correct.
let userRoot = "https://inkcloud.firebaseio.com/users/" + user
ref = Firebase(url:userRoot)
ref!.observeEventType(.ChildChanged, withBlock: { snapshot in
let result = snapshot.value.objectForKey("test") as? String
println("the result is \(result)")
}, withCancelBlock: { error in
println(error.description)
})
I get compiler error: '() -> AnyObject!' does not have a member named 'objectForKey'
Any help would be appreciated.
Ben
Found the soluiton, need to use functions calls instead of property.