WatchKit openParentApplication:reply - ios

I have currently a problem.
My wanted behaviour: If I open my WatchKit App, I call "openParentApplication". I receive my wanted data.
But if I tested on real devices, it doesnt work since I open the parent app in the iPhone.
But when I'm testing in simulator it works without to open the parent app.
My Xcode Version is 6.3.2 and iOS 8.3.
What could be the problem?
InterfaceController.m
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
NSDictionary *userInfo = #{#"request":#"refreshData"};
[WKInterfaceController openParentApplication:userInfo reply:^(NSDictionary *replyInfo, NSError *error)
{
entries = replyInfo;
NSLog(#"Reply: %#",replyInfo);
[self reloadTable];
[self.city setText:[entries valueForKey:#"city"][0] ];
}];
}
AppDelegate.m
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply
{
NSString *refresh = [userInfo valueForKey:#"request"];
if([refresh isEqualToString:#"refreshData"])
{
NSString *city = [[NSUserDefaults standardUserDefaults] stringForKey:#"City"];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:[NSString stringWithFormat:#"http://blackdriver.adappter.de/api/retrieve.php?city=%#",[city stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject)
{
reply(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %#", error);
}];
}
}
EDIT - Correct answer:
See the link from mohammed alwaili in the comments

A openParentApplication:reply request must return immediately, so you'll have to request extra time for your asynchronous request to finish (alternately run a synchronous request, but this is terrible practice).
From the Apple WatchKit Developer Tips and Best Practices:
If your app on Apple Watch needs to perform longer running background
tasks, such as networking calls, you should rely on your iPhone app to do the work. Use the openParentApplication:reply: method in WKInterfaceController to wake up your iPhone app in the background and return the data that your WatchKit extension needs. The UIApplicationDelegate method that handles the WatchKit request must return immediately. If an asynchronous call is required, to perform networking for example, use a background task to make sure your app is not suspended before it has a chance to send its reply.

I had a similar issue and had to work through a few problems to have my WatchKit app successfully call iOS app which made an asynchronous API call.
Here's a working code snippet
func application(application: UIApplication, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]?, reply: (([NSObject : AnyObject]!) -> Void)!) {
let backgroundProcessingToken = application.beginBackgroundTaskWithName("backgroundApiCall", expirationHandler: { () -> Void in
reply(["response":["error":"SOME_ERROR_CODE_INDICATING_TIMEOUT"]])
})
request(.GET, "https://api.forecast.io/forecast/[INSERT DARK SKY API CODE]/37.8267,-122.423").responseJSON(options: NSJSONReadingOptions.AllowFragments, completionHandler:{request, response, data, error in
if(error != nil || data == nil){
reply(["response":["error":"SOME_ERROR_CODE_INDICATING_FAILURE"]])
}
if let json = data as? NSDictionary {
reply(["response":["data":json]])
}
application.endBackgroundTask(backgroundProcessingToken)
})
}
Ultimately, you need to register as a background task to ensure your app doesn't get killed by the operating system.
I also have a working example here on github FWIW

Related

how can I use WCSessionUserInfoTransfer in watchos?

is anywhere a simple tutorial, how I can use the WCSessionUserInfoTransfer to change data between my watch and iOS ?
And what must be written in the delegate of the iOS -App?
in my old programming I used:
[WKInterfaceController openParentApplication:#{#"Kommando":#"Radius"} reply:^(NSDictionary *replyInfo, NSError *error) {
if (error) {
NSLog(#"Error from parent: %#", error);
} else {
NSLog(#"Radius from parent: %#", [replyInfo objectForKey:#"Radius"]);
}
}];
The first thing you need to do is to define a WCSession. This must be defined and activated in every class that you're planning on transferring data to and receiving data from. To use WCSession, make sure it's supported, and then activate the default session as shown below.
#import <WatchConnectivity/WatchConnectivity.h>
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
From here, you can use transferUserInfo where ever you need to send data (from the watch or the iOS app):
[[WCSession defaultSession] transferUserInfo:#{#"Kommando":#"Radius"}];
On the receiving end, you would use session:didReceiveUserInfo. Note that this does NOT need to be in the app delegate anymore on the iOS app side, unlike handleWatchKitExtensionRequest. You can use this where ever you need to receive the data. Make sure to activate WCSession, as shown above, in the class where you have didReceiveUserInfo also.
- (void)session:(nonnull WCSession *)session didReceiveUserInfo:(nonnull NSDictionary<NSString *,id> *)userInfo {
NSLog(#"Radius from parent: %#", [userInfo objectForKey:#"Radius"]);
}

Apple watch app does not work when iPhone app is in terminated state?

I am working on an apple watch app, but facing a weird issue, i.e. my watch app works only when I manually open the iPhone app or when iPhone app is in background. When I terminate my iPhone app and test apple watch app then it does not work any more.
Here I am mentioning watch app flow:
When apple watch app starts, I call a web api to fetch response from server.
I used openParentApplication:reply: method to call web api from parent app
I understand that, I will have to call web api method in a background thread because, openParentApplication:reply: method automatically open the parent app in iPhone and suspends in a mean time, so if we are processing a time taking task using this method then we should use a background thread as mentioned under WatchKit Development Tips. So I am using a background thread to call web api.
When I get response I pass it to watch app.
Here is the attached snippet:
Watch App - InitialInterfaceController.m
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
}
- (void)willActivate {
[super willActivate];
[self getDetails];
}
- (void)getDetails{
//Open parent app
[WKInterfaceController openParentApplication:#{#“request”:#“details”}
reply:^(NSDictionary *replyInfo, NSError *error) {
if (!error) {
NSLog(#“Success”);
[self parseKPI:replyInfo];
}
else{
NSLog(#"Error - %#", error.localizedDescription);
}
}];
}
iPhone App - AppDelegate.m
- (void)application:(UIApplication *)application
handleWatchKitExtensionRequest:(NSDictionary *)userInfo
reply:(void (^)(NSDictionary *))reply{
NSString *request = [userInfo objectForKey:#“request”];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Get Details
if ([request isEqualToString:#"details"]) {
APIHandler *api = [[APIHandler alloc] init];
[api getDetailsUsername:#“my_user_name”
onSuccess:^(NSDictionary *details) {
dispatch_async(dispatch_get_main_queue(), ^{
reply(details);
});
} onFailure:^(NSString *message) {
dispatch_async(dispatch_get_main_queue(), ^{
reply(#{#"Error":message});
});
}];
}
}
iPhone App - APIHandler.m
- (void) getDetailsUsername:(NSString *)username
onSuccess:(void(^)(NSDictionary * details))success
onFailure:(void(^)(NSString *message))failure{
NSString *urlString = [NSString stringWithFormat:#"%#%#", HOST, DETAILS_API];
urlString = [urlString stringByAppendingFormat:#"?username=%#",username];
urlString = [urlString stringByAppendingFormat:#"&%#", self.APIKeyParameter];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *mutableURL = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:mutableURL
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!connectionError) {
NSError *error = nil;
NSDictionary *details = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableLeaves
error:&error];
success(details);
}
else{
failure(#"Connection Error!");
}
}];
}
But this approach is not working for me.
I found one more issue in watch app simulator i.e.
- (void)awakeWithContext:(id)context for my initial view controller is called, but - (void)willActivate method is not being called sometimes and I just see watch app spinner. Some times it works. It’s quite strange. I have around 15 controls (including all groups) in initial interface controller added using storyboard.
I have also referred Watchkit not calling willActivate method and modified my code but still facing same issue.
Can any one let me know why is this issue persisting in my app?
You'll need to handle the request in the iPhone app a bit differently in order for your app to not be killed off by the OS. I've shared a similar answer here: https://stackoverflow.com/a/29848521/3704092

Parse Query in WatchKit

I do a Parse query in my iPhone app, but I am getting errors trying to do the same Parse query in my Watch app.
This is the query in my iPhone app:
- (void)viewDidLoad {
// GMT Date from Phone
NSDate *gmtNow = [NSDate date];
NSLog(#"GMT Now: %#", gmtNow);
// Query Parse
PFQuery *query = [self queryForTable];
[query whereKey:#"dateGame" greaterThanOrEqualTo:gmtNow];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableArray *localMatchup = [#[] mutableCopy];
for (PFObject *object in objects) {
// Add objects to local Arrays
[localMatchup addObject:[object objectForKey:#"matchup"]];
// App Group
NSString *container = #"group.com.me.off";
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:container];
// Matchup
[defaults setObject:localMatchup forKey:#"KeyMatchup"];
NSArray *savedMatchup = [defaults objectForKey:#"KeyMatchup"];
NSLog(#"Default Matchup: %#", savedMatchup);
savedMatchup = matchupArray;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}];
}
This is the query I tried in my WatchKit app...
- (void)awakeWithContext:(id)context {
// GMT Date from Phone
NSDate *gmtNow = [NSDate date];
NSLog(#"GMT Now: %#", gmtNow);
// Query Parse
PFQuery *query = [self queryForTable];
[query whereKey:#"dateGame" greaterThanOrEqualTo:gmtNow];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableArray *localMatchup = [#[] mutableCopy];
for (PFObject *object in objects) {
// Add objects to local Arrays
[localMatchup addObject:[object objectForKey:#"matchup"]];
// App Group
NSString *container = #"group.com.me.off";
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:container];
// Matchup
[defaults setObject:localMatchup forKey:#"KeyMatchup"];
NSArray *savedMatchup = [defaults objectForKey:#"KeyMatchup"];
NSLog(#"Default Matchup: %#", savedMatchup);
savedMatchup = self.matchupArray;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}];
}
But I get errors on these two lines...
`PFQuery *query = [self queryForTable];`
`[self.tableView reloadData];`
because I can't do the same exact code related to a table I'm guessing, but I'm just not sure what to change it to.
EDIT: Adding code per #cnoon answer
WatchKit InterfaceController.m:
How would I ask for my query to run here?
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
[WKInterfaceController openParentApplication:nil reply:^(NSDictionary *replyInfo, NSError *error) {
// What to put here?
NSLog(#"Open Parent Application");
}];
-and-
iPhone AppDelegate.h
How would I ask for my PFQuery to run?
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply {
// What to put here?
}
There's no such thing as a UITableView in a WatchKit application. Instead, you have to work with WKInterfaceTable. Before you continue, I'd also suggest you read through the documentation in the WatchKit Programming Guide. It will give you a MUCH better understanding of all the toolsets available to you as an aspiring Apple Watch developer.
WKInterfaceTable
Once you know the ins and outs of a WKInterfaceTable, you'll quickly see why your approach is flawed for two reasons. First off, you don't have a reloadData method. The alternative in WatchKit is to setNumberOfRows(_:withRowTypes:). Then you need to iterate through each row and configure it.
PFQuery in WatchKit Extension
The second reason you are going to have issues is due to your use of PFQuery.
This is a bit of side advice, so take it or leave it. I'm speaking from experience here having already built a very large page-based Watch App that heavily communicates with the iOS App.
I would advise you to stop making PFQuerys in your WatchKit Extension. The reason is that users using your Watch App are only going to have the app open for a second or two. Everything will happen extremely fast. Because of this, it is extremely difficult to guarantee the success of network calls before the Watch App is terminated by the user. This makes things MUCH more difficult, but is simply the way it is.
Instead, you want to run your PFQuery calls on the iOS App and return that information back to the Watch Extension through the following calls:
WKInterfaceController - openParentApplication(_:reply:)
UIApplicationDelegate - handleWatchKitExtensionRequest(_:reply:)
You can also cache the PFQuery into the shared app group using MMWormhole or a similar approach alternative. Below is an example of how you can have your Watch Extension request the iOS Application to run a PFQuery, cache the data in MMWormhole and notify the Watch Extension once it is finished. By always reading the data out of the cache, you have a consistent mechanism whether the Watch Extension was still running as well as closed and re-opened.
Objective-C
InterfaceController.m
- (void)willActivate {
[super willActivate];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[WKInterfaceController
openParentApplication:#{#"pfquery_request": #"dumm_val"}
reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"User Info: %#", replyInfo);
NSLog(#"Error: %#", error);
if ([replyInfo[#"success"] boolValue]) {
NSLog(#"Read data from Wormhole and update interface!");
}
}];
});
}
AppDelegate.m
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
if (userInfo[#"pfquery_request"]) {
NSLog(#"Starting PFQuery"); // won't print out to console since you're running the watch extension
// 1. Run the PFQuery
// 2. Write the data into MMWormhole (done in PFQuery completion block)
// 3. Send the reply back to the extension as success (done in PFQuery completion block)
reply(#{#"success": #(YES)});
}
reply(#{#"success": #(NO)});
}
Swift
InterfaceController.swift
override func willActivate() {
super.willActivate()
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Float(NSEC_PER_SEC))), dispatch_get_main_queue()) {
WKInterfaceController.openParentApplication(["pfquery_request": "dummy_val"]) { userInfo, error in
println("User Info: \(userInfo)")
println("Error: \(error)")
if let success = (userInfo as? [String: AnyObject])?["success"] as? NSNumber {
if success.boolValue == true {
println("Read data from Wormhole and update interface!")
}
}
}
return
}
}
AppDelegate.swift
func application(
application: UIApplication!,
handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!,
reply: (([NSObject : AnyObject]!) -> Void)!)
{
if let pfqueryRequest: AnyObject = (userInfo as? [String: AnyObject])?["pfquery_request"] {
println("Starting PFQuery") // won't print out to console since you're running the watch extension
// 1. Run the PFQuery
// 2. Write the data into MMWormhole (done in PFQuery completion block)
// 3. Send the reply back to the extension as success (done in PFQuery completion block)
reply(["success": true])
}
reply(["success": false])
}
Hopefully that helps break down the complexity of having a consistent way to read data from the cache as well as offload network requests (or PFQueries) to the iOS App.

openParentApplication:reply: error with asynchronous network call in containing app

I'm getting stuck with an error when using my Watchkit Application. When I launch it, I ask the containing iOS app to get some data from network. The problem is that I get an error saying the containing app never calls 'reply()' :o But looking at my code, it should call it.
I tried to debug every step from openParentApplication to the 'reply()' call, and it seems to work well =X
Here is my code in the Watchkit extension
- (void)initDiaporamasWithSuccess:(void (^)())success andFailure:(void (^)(NSError*))failure {
NSLog(#"Ask to load diapos");
__weak typeof(self) weakSelf = self;
[WKInterfaceController openParentApplication:#{#"watchKit": #"watchKit.initDiapos"} reply:^(NSDictionary *replyInfo, NSError *error) {
if (error) {
NSLog(#"%#", error);
if (failure) {
failure(error);
}
return;
}
NSLog(#"got items : %#", replyInfo[#"diapos"]);
weakSelf.diaporamas = replyInfo[#"diapos"];
[weakSelf setDiaporama:replyInfo[#"firstDiapo"] AtIndex:0];
if (success) {
success();
}
}];
}
The result should be an NSDictionary containing an NSArray with some diaporamas basic informations, and an object (Diapo) containing the full informations of the first diaporama (e.g. self.diaporamas[0])
And here is the code in the containing app's AppDelegate :
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
// Maybe we could handle multiple watchKit extension calls that way ?
// Something like a key-value 'protocol' to run the right block of code
NSString *watchKitCall = userInfo[#"watchKit"];
NSLog(#"watchKit handled");
if ([watchKitCall isEqualToString:#"watchKit.initDiapos"]) {
[AppDelegate watchInitialObjects:^(NSDictionary *info) {
NSLog(#"Managed to get initial infos");
reply(info);
} failure:^(NSError *error) {
NSLog(#"Fail : %#", error);
reply(#{#"error": error});
}];
}
}
+ (void) watchInitialObjects:(void (^)(NSDictionary *info))success failure:(void (^)(NSError *error))failure {
NSDictionary *parameters = #{#"site" : #(14), #"limit" : #(10)};
[AppDelegate requestDiapoListWithParams:parameters success:^(NSArray *items) {
if ([items count] == 0)
{
NSError *error = [NSError errorWithDomain:#"com.domain.app" code:404 userInfo:nil];
failure(error);
return;
}
Diapo *firstDiapo = [items firstObject];
[AppDelegate requestDiapoDetailWithDiapo:firstDiapo success:^(Diapo *diapo) {
if (!diapo)
{
NSError *error = [NSError errorWithDomain:#"com.domain.app" code:404 userInfo:nil];
failure(error);
return;
}
NSDictionary *result = #{
#"firstDiapo" : diapo,
#"diapos" : items
};
success(result);
} failure:^(NSError *error) {
failure(error);
}];
} failure:^(NSError *error) {
failure(error);
}];
}
In the watchKitHandler, I call watchInitialObjects to get the diaporamas array and the first diaporama's informations.
In the watchInitialObjects, I make a first network call to get the array, and on success, I make an other network call to get the firs diaporama informations.
To make the calls and map the JSON into objects, I use RESTKit
I really don't get what could be the error =x
UPDATE
I forgot to write the error I get, here it is :
Error Domain=com.apple.watchkit.errors Code=2 "The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]" UserInfo=0x7fcb53e12830 {NSLocalizedDescription=The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]}
And I kept trying to know why I get this error, and I think I found it:
It seems that there is a (very little) timeout to do the work in the containing app. But I mapped the JSON data I received directly in the containing app and then, send those custom objects in the reply(). But when I removed the mapping part, it worked well !
So...that's why I think that was the problem =X
Does anybody could approve my thoughts or corrects me ?
After hours of searching and testing different codes, I finally found my problem...and it's obvious when we read the Apple documentation about 'application:handleWatchKitExtensionRequest:reply:' seriously...
here is the answer : (it's in the documentation)
The contents of the dictionary must be serializable to a property list file.
Which means that objects can ONLY be dictionaries, arrays, strings, numbers (integer and float), dates, binary data, or Boolean values
...I feel dumb ><

How to wait for asyn operation in iOS unit test using NSConditionLock

I have a unit test in which I need to wait for an async task to finish. I am trying to use NSConditionLock as it seems to be a pretty clean solution but I cannot get it to work.
Some test code:
- (void)testSuccess
{
loginLock = [[NSConditionLock alloc] init];
Login login = [[Login alloc] init];
login.delegate = self;
// The login method will make an async call.
// I have setup myself as the delegate.
// I would like to wait to the delegate method to get called
// before my test finishes
[login login];
// try to lock to wait for delegate to get called
[loginLock lockWhenCondition:1];
// At this point I can do some verification
NSLog(#"Done running login test");
}
// delegate method that gets called after login success
- (void) loginSuccess {
NSLog(#"login success");
// Cool the delegate was called this should let the test continue
[loginLock unlockWithCondition:1];
}
I was trying to follow the solution here:
How to unit test asynchronous APIs?
My delegate never gets called if I lock. If I take out the lock code and put in a simple timer it works fine.
Am I locking the entire thread and not letting the login code run and actually make the async call?
I also tried this to put the login call on a different thread so it does not get locked.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[login login];
});
What am I doing wrong?
EDIT adding login code. Trimmed do the code for readability sake. Basically just use AFNetworking to execute a POST. When done will call delegate methods.
Login make a http request:
NSString *url = [NSString stringWithFormat:#"%#/%#", [_baseURL absoluteString], #"api/login"];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (_delegate) {
[_delegate loginSuccess];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (_delegate) {
[_delegate loginFailure];
}
}];
The answer can be found in https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFHTTPRequestOperation.m.
Since you are not setting the completionQueue property of the implicitly created AFHTTPRequestOperation, it is scheduling the callbacks on the main queue, which you are blocking.
Unfortunately, many answers (not all) in the given SO thread ("How to unit test asynchronous APIs?") are bogus and contain subtle issues. Most authors don't care about thread-safity, the need for memory-barriers when accessing shared variables, and how run loops do work actually. In effect, this leads to unreliable and ineffective code.
In your example, the culprit is likely, that your delegate methods are dispatched on the main thread. Since you are waiting on the condition lock on the main thread as well, this leads to a dead lock. One thing, the most accepted answer that suggests this solution does not mention at all.
A possible solution:
First, change your login method so that it has a proper completion handler parameter, which a call-site can set in order to figure that the login process is complete:
typedef void (^void)(completion_t)(id result, NSError* error);
- (void) loginWithCompletion:(completion_t)completion;
After your Edit:
You could implement your login method as follows:
- (void) loginWithCompletion:(completion_t)completion
{
NSString *url = [NSString stringWithFormat:#"%#/%#", [_baseURL absoluteString], #"api/login"];
[manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completion) {
completion(responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (completion) {
completion(nil, error);
}
}];
Possible usage:
[self loginWithCompletion:^(id result, NSError* error){
if (error) {
[_delegate loginFailure:error];
}
else {
// Login succeeded with "result"
[_delegate loginSuccess];
}
}];
Now, you have an actual method which you can test. Not actually sure WHAT you are trying to test, but for example:
-(void) testLoginController {
// setup Network MOCK and/or loginController so that it fails:
...
[loginController loginWithCompletion:^(id result, NSError*error){
XCTAssertNotNil(error, #"");
XCTAssert(...);
<signal completion>
}];
<wait on the run loop until completion>
// Test possible side effects:
XCTAssert(loginController.isLoggedIn == NO, #""):
}
For any other further steps, this may help:
If you don't mind to utilize a third party framework, you can then implement the <signal completion> and <wait on the run loop until completion> tasks and other things as described here in this answer: Unit testing Parse framework iOS

Resources