No callback for Google Maps panorama request - ios

I am new to the Google Maps SDK. I wrote a little test app that is able to show a map with routing, and a streetView panorama. This works fine, i.e. everything (the Google account, the API key, etc.) is probably setup correctly.
I now tried to get streetView pano metadata using
DispatchQueue.main.async {
let panoramaService = GMSPanoramaService()
panoramaService.requestPanoramaNearCoordinate(start) { pano, error in
guard error == nil else { fatalError() }
}
}
where start ist some coordinate.
I set breakpoints at the requestPanoramaNearCoordinate call, and at the guard statement in the callback. The 1st breakpoint is reached, but the 2nd not, i.e. there is no callback.
What could be the reason?

This was my fault:
I initialized panoramaService as a variable local to the async block.
As soon as requestPanoramaNearCoordinate was executed, this variable was deallocated, and the completion block was thus not called.
The correct way is to declare let panoramaService as a property that is not deallocated. Then, the completion block is called, as expected.

Related

GoogleSignIn (iOS) getTokensWithHandler doesn't call its closure after token has expired

I am using Google Sign-In 5.0.2 with a Swift 4 iOS app. Here is my code to get a current id Token:
public static func getJwtToken(completion: #escaping (Result<String, Error>) -> Void) {
assert(Config.isAuthEnabled())
GIDSignIn.sharedInstance()?.currentUser?.authentication.getTokensWithHandler({ gidAuth, error in
if let error = error {
completion(.failure(error))
} else if let token = gidAuth?.idToken {
completion(.success(token))
} else {
assertionFailure("shouldn't have come here")
}
})
}
This works fine when I launch the app.
But if I leave the app running for an hour (to let the id token expire), then the next time getTokensWithHandler is called, it will not call my closure. Subsequent calls to getTokensWithHandler will once again call my closure.
I would like it to behave consistently so that my closure is always called, even if the token needs to be refreshed.
Anyone have any ideas on what I need to do to achieve this?
I figured out the answer to this problem after spending some more time on it.
My problem was I was trying to hack the Google SDK's closure to be synchronous instead of just letting it call the closure when it was ready. The function call that I claimed never called its closure would've called it if I had let the program flow naturally instead of trying to make it call the closure when I expected it to be called. I was using a semaphore somewhere in the call stack that was waiting until the closure got called and I just needed stop making assumptions about when Google's SDK would perform its work.
I'm still trying to get used to this async-first paradigm of Swift.

How do I wait for an asynchronous call in Swift?

So I've recently come back to Swift & iOS after a hiatus and I've run into an issue with asynchronous execution. I'm using Giphy's iOS SDK to save myself a lot of work, but their documentation is pretty much nonexistent so I'm not sure what might be happening under the hood in their function that calls their API.
I'm calling my function containing the below code from the constructor of a static object (I don't think that's the problem as I've also tried calling it from a cellForItemAt method for a Collection View).
My issue is that my function is returning and execution continues before the API call is finished. I've tried utilizing DispatchQueue.main.async and removing Dispatch entirely, and DispatchGroups, to no avail. The one thing that worked was a semaphore, but I think I remember reading that it wasn't best practice?
Any tips would be great, I've been stuck on this for waaaaaay too long. Thanks so much in advance
GiphyCore.shared.gifByID(id) { (response, error) in
if let media = response?.data {
DispatchQueue.main.sync {
print(media)
ret = media
}
}
}
return ret
My issue is that my function is returning and execution continues before the API call is finished.
That's the whole point of asynchronous calls. A network call can take an arbitrary amount of time, so it kicks off the request in the background and tells you when it's finished.
Instead of returning a value from your code, take a callback parameter and call it when you know the Giphy call has finished. Or use a promise library. Or the delegate pattern.
The one thing that worked was a semaphore, but I think I remember reading that it wasn't best practice?
Don't do this. It will block your UI until the network call completes. Since you don't know how long that will take, your UI will be unresponsive for an unknown amount of time. Users will think your app has crashed on slow connections.
You could just add this inside a method and use a completion handler and therefore do you not need to wait for the response. You could do it like this:
func functionName(completion: #escaping (YOURDATATYPE) -> Void) {
GiphyCore.shared.gifByID(id) { (response, error) in
if let media = response?.data {
completion(media)
return
}
}
}
Call your method like this
functionName() { response in
DispatchQueue.main.async {
// UPDATE the UI here
}
}

What does it mean to call an escaping closure after the function returns? [duplicate]

This question already has answers here:
Escaping Closures in Swift
(8 answers)
Closed 5 years ago.
I was reading the apple developer documentation's definition of escaping closures. It says "a closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns"
I am not sure what the last part is supposed to mean, what does it mean by "after the function returns"? Does it mean "after the function returns a value"?
Take for example a call to an API. This call is going to take time, but we are going to want to do something after the call is completed. For example, say we want to refresh a UITableView with the new data that we pull. If we were to do it immediately, the data wouldn't have been received yet:
ApiObject.getObjects(completion: { (error, objects) in })
tableView.reloadData()
If we were to reload the data here, the table view will refresh immediately (before we actually have received the data). By doing it in the completion block, we are saying, run the code when we have completed the function, not when the function actually returns:
ApiObject.getObjects(completion: {(error, objects) in
self.tableView.reloadData()
})
Here we are running it once the objects have been fetched, not once the function itself has reached the end.
Edit
Maybe this will make it easier; I have the following code:
let comeInAnimation = POPBasicAnimation(propertyNamed: kPOPLayoutConstraintConstant)!
comeInAnimation.toValue = 0
comeInAnimation.completionBlock = { (anim, success) -> Void in
self.loginButton.enabled = true
self.signupButton.enabled = true
}
signUpContainingViewLeftConstraint.pop_add(comeInAnimation, forKey: AnimationString.EnterExit.identifier)
This is using the POP animation framework. In this case, I have a login and signup button, but I also have an animation for them to appear. I dont want the buttons to be clicked while they are appearing so i have their enabled set to false originally. Now you can see they get set to enabled in the completionBlock. This means, when the animation is completed, the completion block gets called and I know now is the time to set them to be enabled. Had I done it like so:
let comeInAnimation = POPBasicAnimation(propertyNamed: kPOPLayoutConstraintConstant)!
comeInAnimation.toValue = 0
signUpContainingViewLeftConstraint.pop_add(comeInAnimation, forKey: AnimationString.EnterExit.identifier)
self.loginButton.enabled = true
self.signupButton.enabled = true
Even though the enabled properties are set after the animation is called, the properties are actually being set before the animation is complete. Because the program runs line by line, the animation gets added and then immediately the properties are set (this is too early).
Note: This is an issue because these functions run asynchronously. That is, it allows the program to keep running while it is doing its thing. If this line of code blocked (stopped the program until it was compete) then putting the code in a completion block and putting it immediately after would be the same thing. In real life though, we dont want to block because it gives the appearance that the program has frozen.

In Firebase, retrieved data doesn't get out from the .observeEventType method

I defined a class in a project, to manage my database set up in Firebase. The following is what I've done with the class so far.
import Foundation
import Firebase
class db{
class func getPrim() -> [String]{
var ret = [String]()
let ref = FIRDatabase.database().reference()
ref.child("bunya1").observeEventType(FIRDataEventType.Value, withBlock: {
s in
ret = s.value! as! [String]
})
print("ret: \(ret)")
return ret
}
}
And the method is called in a print() method, like print(db.getPrim()). But what the console(or terminal? anyway the screen on the bottom of xcode..) says is only an empty array. I embraced the statement above with print("-----------------------").
-----------------------
ret: []
[]
-----------------------
2016-09-07 20:23:08.808 이모저모[36962:] <FIRAnalytics/INFO> Successfully created Firebase Analytics App Delegate Proxy automatically. To disable the proxy, set the flag FirebaseAppDelegateProxyEnabled to NO in the Info.plist
2016-09-07 20:23:08.815 이모저모[36962:] <FIRAnalytics/INFO> Firebase Analytics enabled
Seems like ret in .observeEventType() method does not take its value out of the method block. As far as I know the data is supposed to be kept.. Can anyone give me a hint? I still don't understand how the code block as a method parameter works. Thnx!!
All firebase operations are by definition asynchronous which means your program doesn't wait for the data from firebase before going to the next statement in your code. So by the the time your print statements are called the data from firebase hasnt been fetched yet.
Take a look at this answer for more information.
André (and the link to Vikrum's answer) are indeed why this is happening. But it's usually easiest to understand if you add a few log statements to your code:
class func getPrim() -> [String]{
let ref = FIRDatabase.database().reference()
print("Before observer");
ref.child("bunya1").observeEventType(FIRDataEventType.Value, withBlock: {
s in
print("In observer block");
})
print("After observer");
return "..."
}
When you run this code, the logging will be in this order:
Before observer
After observer
In observer callback
This is probably not the order in which you expected them to appear. But it definitely explains why your returning an empty array in your snippet. If the code inside the block hasn't run yet, the item hasn't been added to the array yet.
The reason this order is inverted is as André and Vikrum say: the call to Firebase happens asynchronously. Since it can take some time (especially if this is the first time you're accessing the database), the Swift code continues executing to ensure the app stays responsive. Once the data comes back from Firebase, your block is called (for that reason it's sometimes referred to as a "callback") and you get the data.

XCTestExpectation - Calling an async method twice causes API violation

I’m writing unit tests in swifts, and testing a unique workflow.
In methodA(), I load an object incorrectly (say with incorrect credentials) using an async method. Also kick off an expectation
func methodA(withCred credential: NSURLCredential) {
var objA = ObjectA()
// Set objA.a, objA.b, objA.c,
objA.credential = credential //Incorrect credential First time, Correct Credential second time
objA.delegate = self
expectation = expectationWithDescription(“Aync”)
objA.callAsyncMethod() //This fires successDelegate() or failureDelegate()}
When FailureDelegate() is fired, I reload the object, correctly this time. In order do so, I need to call MethodA() again (so I can reuse all the other stuff there).
func failureDelegate(error: NSError!) {
XCTAssertTrue(error.localizedDescription == “Invalid Credentials“)
//Now that I’ve verified correct error is returned, I need to reload objA
methodA(withCred:correctCredential)
}
func successDelegate(obj : ObjectA) {
XCTAssert(“Object is loaded”)
expectation.fulfill()
}
3.This kicks off the same expectation again in methodA, and results in the following error:
API violation - creating expectations while already in waiting mode.
I understand this is not permitted by swift. Is there a workaround or better way to test these kinds of async methods looping with Swift using XCTest?
Thanks!
Don't share instances of expectation across tests. You should be declaring expectation (i.e. with let) in the body of each test, not as a property on XCTestCase. If you really need to use the delegation pattern (closures would be much, much simpler and more conventional), you can pass that as an additional parameter to your delegate method.
I think your code exemple is incomplete, could you provide the full code?
As #mattt said each test should preferably be unique and should not reuse other test variable.
Regarding your issue, you should declare all your expectation first before the triggering waitForExpectationsWithTimeout:handler:. You can't not create a new expectation after you've start waiting for another one.

Resources