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())")
}
}
Related
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
I am using the Kentico-cloud Swift SDK to grab a bunch of elements from the CMS using the Delivery API in the background.
One of the Swift SDK methods allows me to get a ContentType for a certain element on the CMS so I can then map it to an object in my code. Here's the code:
self.client.getContentType(name: codename, completionHandler: { (isSuccess, contentType, error) in
guard error == nil else {
print(error!)
return
}
if isSuccess {
if let type = contentType {
print(type)
self.client.getItem(modelType: type, itemName: codename, completionHandler: { (isSuccess, deliveryItem, error) in
if isSuccess {
// save this Element
print(deliveryItem)
} else {
if let error = error {
print(error)
}
}
})
}
}
})
the attribute codename is the name of the object I am trying to find the ContentType for. The call succeeds and I get my ContentType object, unfortunately, it does not have any properties in it that aren't nil.
I assume it should give me the name of the type as a String so I can then map it to my class.
Could you verify you have valid content type codename in the name parameter? I've tried to reproduce it (see attached screenshot) and everything works on my side (there is also test for this feature which passes as well in GetContentType.swift).
Could you post the value of a requestUrl property from DeliveryClient.swift getContentType() method line 176?
Edit: Oh, from your screen on the GitHub issue I can see you are trying to get the content type with the codename of the item which in wrong. You should use the codename of the content type.
From the docs for getContentType() method:
/**
Gets single content type from Delivery service.
- Parameter name: The codename of a specific content type.
- Parameter completionHandler: A handler which is called after completetion.
- Parameter isSuccess: Result of the action.
- Parameter contentTypes: Received content type response.
- Parameter error: Potential error.
*/
You can learn more about content types here.
I also had the same thought usps tracking but thanks that you have provided a question.
Thanks and Regards,
Shane.
When I try to run a Firebase Function from Swift, I get an error saying:
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
This is the Firebase Function:
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!")
})
This is my Swift code:
Functions.functions().httpsCallable("helloWorld").call { (result, error) in
if error == nil {
print("Success: \(result?.data)")
} else {
print(error.debugDescription)
}
}
You may see this error if you haven't specified the correct region on the client side. For example:
lazy var functions = Functions.functions(region: "europe-west1")
My issue was that I used http://localhost:5000 instead of the correct http://localhost:5001. The emulator by default emulates the hosted website at port 5000 and the functions at port 5001.
I realized this by evaluating the response in FIRFunctions.m#261. It was the html of the hosted website which obviously cannot be parsed as json.
response.send("Hello from Firebase!")
You are not returning a JSON TEXT here but you are returning a string.
You need to return something like
response.send('{"message": "Hello from Firebase!"}')
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.
I am quite new to swift and got a pretty weird compiler error:
Command
/Applications/Xcode6-Beta5.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc
failed with exit code 254
The Error occours when I ad the following lines to my code:
var response = HoopsClient.instance().collections["posts"]
response = response["_id"]
when I remove the second line everything compiles fine. I don't get it ^^
edit: The reason is probably that "response" is of type "AnyObject" according to the compiler... but shouldn't that be detected by xcode or give me a runtime error instead of this compiler error?
Try casting response as the type you're expecting. From what you're trying to do, instance().collections I would assume that it should return some type of dictionary.
var response = HoopsClient.instance().collections["posts"] as NSDictionary
That way, response now can handle subscripts so you could now (in theory) do:
response = response["_id"]
However
The error you get is regarding bad access to an array. This makes me think that instance().collections actually returns an array of some sort, containing Post objects.
Arrays in Swift can only handle Integer subscripts. If you want to access the information of a Post in the array, you can do something like this:
for post: Post in response {
println(post._id)
}
I know this is a long shot, but hope it helps.
Swift tends to throw error when it cant infer the type of an object, what you could probably do is add a conditional cast as follows
Im assuming that HoopsClient.instance().collections["posts"] is either a Dictionary or an Array
var response = HoopsClient.instance().collections["posts"]
if response is NSArray {
let item = response.objectAtIndex(0)
let reponseId: Post = item
}
if response is NSDictionary {
let item = response.objectForKet("_id")
let reponseId: Post = item
}
Any way, in my experience you should try to cast your variables when assigning from types that return AnyObject, xcode doesn't handle very well type inferring and when it's unable to infer the type the interface starts to throw error, like text editor uncoloring the code.