Unable to access variable within a function in Swift - ios

In the function I'm building a socket method but I can't access pCount after the socket method in the function
Use of unresolved identifier 'pCount'
Why?

psCount is async function so you can't return in it. In case, socket.on have't finished but psCount is still return
You should code:
func psCount(handleFinish:((pCount:Int)->())){
socket.on("reply") { data, act in
let json = Json(data)
let pCount:Int = json[0].count
handleFinish(pCount: pCount:Int)
}
}
and call it:
self.psCount { (pCount) -> () in
print(pCount)
}

Related

Who passed the arguments to the parameters of the complition handler closure?

func startUpdates(from start: Date,
withHandler handler: #escaping CMPedometerHandler)
typealias CMPedometerHandler = (CMPedometerData?, Error?) -> Void
The above function retrieves the pedometer data from your iOS device. When I called the function the only argument I need passed to is the parameter from start.
Who actually initialized the parameter list of the completion handler closure? The startUpdates function I've called?
When I called the function the only argument I need to passed to is the parameter from start
That's not true.
You have to pass also the closure as second parameter. The closure itself is called by the startUpdates function after doing its work and passes two parameters back, an optional Data and an optional Error instance.
The functional programming is a very convenient way to be able to run arbitrary code (in the closure).
You can declare the closure separately
let result : CMPedometerHandler = { data, error in
if let error = error { print(error); return }
// do something with the data
}
startUpdates(from: Date(), withHandler: result)
or inline
startUpdates(from: Date(), withHandler: { data, error in
if let error = error { print(error); return }
// do something with the data
})
or with trailing closure syntax
startUpdates(from: Date()) { data, error in
if let error = error { print(error); return }
// do something with the data
}

In Swift, how to set a function's return value in a closure?

I am now making a UITextField with AutoComplete that users will get a place name and zip code list after they have tapped one or some letters.
I got an Autocomplelt (https://github.com/cjcoax/Autocomplete) and there is a delegate function:
func autoCompleteItemsForSearchTerm(term: String) -> [AutocompletableOption]
I have to send a http request with term to server and wait for a json response as return.
For network connenction, I used Moya lib and its Method like:
CredentialProvider.request(.Autocomplete(term, 10)) { (result) -> () in }
Now, my question is: How can I make a return value after getting the response from server?
Thanks
Declare a function with completion block:
class func authenticateUser(userName:String?,password:String?,completionHandler: (response:NSDictionary?) -> (Void)) ->Void
{
completionHandler(response: nil(or)dict)
}
Call a function :
authenticateUser(emailId, password: password, completionHandler: { (response) in
print(response)
})

Performing an asynchronous task within a synchronous call

I would like to register a user which is performed asynchronous. However, the calling function behaves synchronous since the program should only continue when a user is created successfully.
The current implementation is:
class SignUp: NSObject {
// ...
func signUpUser() throws -> Bool {
guard hasEmptyFields() else {
throw CustomErrorCodes.EmptyField
}
guard isValidEmail() else {
throw CustomErrorCodes.InvalidEmail
}
createUser( { (result) in
guard result else {
throw CustomErrorCodes.UserNameTaken
}
return true // Error: cannot throw....
})
}
func createUser( succeeded: (result: Bool) -> () ) -> Void {
let newUser = User()
newUser.username = username!
newUser.password = password!
// User is created asynchronously
createUserInBackground(newUser, onCompletion: {(succeed, error) -> Void in
if (error != nil) {
// Show error alert
} else {
succeeded(result: succeed)
}
})
}
}
and in a ViewController the signup is initiated as follows:
do {
try signup.signUpUser()
} catch let error as CustomErrorCodes {
// Process error
}
However, this does not work since createUser is not a throwing function. How could I ensure that signUpUser() only returns true when an new user is created successfully?
You say:
and in a ViewController the signup is initiated as follows:
do {
try signup.signUpUser()
} catch let error as CustomErrorCodes {
// Process error
}
But don't. That's not how asynchronous works. The whole idea is that you do not wait. If you're waiting, it's not asynchronous. That means you're blocking, and that's just what you mustn't do.
Instead, arrange to be called back at the end of your asynchronous process. That's when you'll hear that things have succeeded or not. Look at how a download task delegate is structured:
https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSessionDownloadTask_class/
The download task calls back into the delegate to let it know whether we completed successfully or not. That is the relationship you want to have with your asynchronous task. You want to be like that delegate.
You need to adjust your thinking. Instead of trying to write a synchronous method that we need to wait for an asynchronous event, write a method that takes a completion closure. The method will return immediately, but once the asynchronous process is complete it wild invoke the completion closure. When you call such a method you pass in code in the incompletion closure that gets called once the job is done.

Parse iOS - How to capture the findObjects() error being thrown?

I am coding in Swift and attempting to use the findObjects() function for the Parse iOS SDK. However, I can't seem to figure out what type of error is being thrown by Parse if this function call fails. I'm a novice in Swift so that may be my issue. I attempt to call the function in a do->catch block and use the try keyword on the function call however I'm not sure what to catch. I can catch the error using the _ but I would like to grab the description from the error. Thanks!
P.S. I don't want to use the findObjectsInBackground() method.
do {
let object = try query.getFirstObject()
// do something with the object
} catch _ {
// this is where I would like to print out the error description
}
In Obj-C, which I assume will be similar, I print out the error.userInfo[#"error"] parameter of the NSError that is returned.
All you need is print(error). An example here:
func getReferenceNumberAsStringSync() -> String? {
let query = PFQuery(className: "PropertyCount")
do {
let object = try query.getFirstObject()
if let referenceNumber = object["count"] as? Int {
return String(referenceNumber)
}
} catch {
print(error)
}
return nil
}

Using a Swift closure to capture a variable

I have this bit of code and it obviously errors out because when I use FOO in the return statement it's outside of the scope of the function. I know (I think I know) I need to use a closure to capture the variable but I can't figure out how to do that. Using Alamofire & SwiftyJSON. Any help would be great! Thanks!
func getPlayerID(named: String) -> String {
Alamofire.request(.GET, "URL", headers: headers)
.responseJSON { response in
let json = JSON.self(response.result.value!)
for var index = 0; index < json.count; index++ {
if json[index]["Name"].stringValue == named {
var FOO = json[index]["FOO"].stringValue
} // If Statement End
} // For Loop End
} // Alamofire Request End
// Return Statement for getPLayerID Function
return FOO
} // getPlayerID Function End
} // Player Struct End
The basic idea is that getPlayerID should not return anything, but rather should just have a parameter which is a closure, and once you retrieve the value you want to "return", you call the closure using that value as a parameter.
But, I'd suggest all sorts of other refinements here:
Build an array of the strings and return that
Check to see if result is a .Failure because you have no control over what various server/network issues may arise
Change the closure to detect and report errors
But hopefully this illustrates the basic idea:
Personally, I'd (a) make the String parameter to the completionHandler optional; (b) add another optional error parameter; and (c) add error handling to the getPlayerID:
func getPlayerID(completionHandler: ([String]?, ErrorType?) -> Void) {
Alamofire.request(.GET, "URL", headers: headers)
.responseJSON { request, response, result in
switch (result) {
case .Success(let value):
let json = JSON.self(value)
// variable to hold all of the results
var strings = [String]()
// populate the array of strings
for var index = 0; index < json.count; index++ {
if json[index]["Name"].stringValue == named {
strings.append(json[index]["FOO"].stringValue)
}
}
// call the completion handler with the strings
completionHandler(strings, nil)
case .Failure(_, let error):
completionHandler(nil, error)
}
}
}
And then, when you want to call it:
getPlayerID() { strings, error in
// use `strings` here
}
// but not here
If you make an asynchronous request you can not return a value received in response in the same function cause it needs time for request to be sent over network to the server and back. The best way to solve this out is to add callback parameter to your function instead of return value.
func getPlayerID(named: String, callback:(foo:String)->()) {
Alamofire.request(.GET, "URL", headers: headers)
.responseJSON { response in
let json = JSON.self(response.result.value!)
for var index = 0; index < json.count; index++ {
if json[index]["Name"].stringValue == named {
var FOO = json[index]["FOO"].stringValue
callback(foo: FOO) // you fire callback here..
} // If Statement End
} // For Loop End
} // Alamofire Request End
} // getPlayerID Function End
Callback is a block object that will be fired when your response will be received. So if response is not coming (for example, internet connection went down) callback will never fired.
Example how to use this:
self.getPlayerID("ototo") { (foo) -> () in
print("foo received = \(foo)")
}
Also there is a time span between sending the request and receiving the response. So it is a good practice to add UIActivityIndicatorView in UI of your app until response is arrived (and handle timeout if internet connection suddenly went down).

Resources