multiple return values in callback blocks - ios

people I am working with don't wanna use delegate methods for a couple of reasons and they want me to come up with a solution;
basically they want a callback block with multiple return values, and when the first return value arrived block should still wait for the upcoming return values
something like
[parserobject parsefile:file withCallback {
if (started) {
//file parsing started
//this should get executed first
}
//if file parsing succesfully started also check if it
//succesfully finished or failed in the same callback
if (finished) {
//file parsing finished
}
if(failed)
{
//this represents if parsing failed any time from start to finish
//if so exit the callback
}
}];
I have seen people using structs or nsintegers to return different values but usually only one result is returned....
is that above code block possible to written in objective-c?

This is your function
typedef void(^VOIDCALLBACK)(void); //Just a type here
typedef void(^BOLLCALLBACK)(bool succeed);//Just a type here
-(void)parserobjectWithFile:(NSString *)file StartCallBack:(VOIDCALLBACK) startCallBack completionBlock:(BOLLCALLBACK) completion{
// When start
dispatch_async(dispatch_get_main_queue(), ^{
startCallBack();//Make call back run in main queue
});
//When finisehd set succeed to yes,otherwise set to no
BOOL succeed = YES;//Pass in to indicator if succeed
dispatch_async(dispatch_get_main_queue(), ^{
completion(succeed); //Make call back run in main queue
});
}
Then you call it like this
[self parserobjectWithFile:#""
StartCallBack:^{
//Here task is start
}
completionBlock:^(bool succeed) {
//Here task is done.Check succeed to figure out if succeed
}];
A simple example
This is the function part
#implementation ViewController
-(void)viewDidAppear:(BOOL)animated{
[self parserobjectWithFile:#"" StartCallBack:^{
NSLog(#"start");
} completionBlock:^(bool succeed) {
NSLog(#"finished");
}];
}
typedef void(^VOIDCALLBACK)(void); //Just a type here
typedef void(^BOLLCALLBACK)(bool succeed);//Just a type here
-(void)parserobjectWithFile:(NSString *)file StartCallBack: (VOIDCALLBACK) startCallBack completionBlock:(BOLLCALLBACK) completion{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Start
dispatch_async(dispatch_get_main_queue(), ^{
startCallBack();//Make call back run in main queue
});
sleep(2.0);
BOOL succeed = YES;//Pass in to indicator if succeed
dispatch_async(dispatch_get_main_queue(), ^{
completion(succeed); //Make call back run in main queue
});
});
}
#end
This will output
2015-06-11 23:39:25.426 OCTest[767:144615] start
2015-06-11 23:39:27.426 OCTest[767:144615] finished

It is possible to write a block that does different things depending on the state reached so far. In order for that to work, declare variables that represent block's state with the __block modifier:
__block BOOL started = NO;
__block BOOL finished = NO;
__block BOOL failed = NO;
[parserobject parsefile:file withCallback:^ {
if (started) {
//file parsing started
//this should get executed first
} else {
...
// Conditionally set started to YES
started = YES;
}
//if file parsing succesfully started also check if it
//succesfully finished or failed in the same callback
if (finished) {
//file parsing finished
}
if(failed) {
//this represents if parsing failed any time from start to finish
//if so exit the callback
}
}];

Related

Core Data Performing Fetch Request with Completion Handler or execute in some other thread other than Main thread

In my iOS application I am using Core Data.
For table View listing I use NSFetchedResultsController and
Connecting to Remote store I use NSIncrementalStore.
My FetchedResultsController Context is having MainQueue Cuncurrency type.(I couldn't do it with a PrivateQueueCurrencyTYpe).
For resolving Fault, for a many relationship, the executeFetchResultsCall:withContext:error method is executed from my IncrementalStore subclass.
Inside the executeFetchResults method, I will invoke the API (connecting to remote server) if it is not available in my local database.
myarray = [object representationsForRelationship:#"manyconnection" withParams:nil];
Now I need the results array in return synchronously to be returned to the ExecuteFetchResultsMethod. Also this operation should be executed on Main thread.
So I am having only one option to fetch the results from server which causes the UI to unresponsive for the specified sleep time.
-(RequestResult*)makeSyncJsonRequest{
__block RequestResult *retResult = [[RequestResult alloc] init];
__block BOOL block = YES;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
void (^resultBlock)(RequestResult*) = ^(RequestResult* result){
if(!retResult.error)
retResult = result;
block = NO;
dispatch_group_leave(group);
};
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
dispatch_group_enter(group);
[self makeAsyncJsonRequestWithBlock:resultBlock];
});
// Do some other work while the tasks execute.
// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return retResult;
}
As the above operation is being executed on main thread,the UI hangs.
Inorder to make the UI smoother, I need to carry out the executeFetchrequest in some other thread which is not possible.
It also expects the results array in return.
Is there any option to carry out this something like in a completion handler manner?
or
Any alternate methods or design to work this proper.
Any Help is highly appreciated.
This is a skeleton, using a dispatch_group, assuming you are using an NSFetchedResultsController to update your UITableView:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// do your setup (FetchedResultsController and such)
[self syncData];
}
- (void)syncData
{
NSArray<Entity*> *results = [self fetchData];
BOOL needsUpdateFromServer = YES; // check your results and set this bool accordingly
if (!needsUpdateFromServer) {
return;
}
__block ServerResponse *fromServer = nil;
__block dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self loadDataFromServer:^(ServerResponse *response) {
fromServer = response;
dispatch_group_leave(group);
}];
dispatch_group_notify(group,dispatch_get_main_queue(),^{
[self persistData:fromServer];
/*
According to our discussion, you are using an NSFetchedResultsController.
So your tableView should update automatically after persisting the data.
*/
});
}
- (void)loadDataFromServer:(void (^)(ServerResponse *response))completion
{
// [someDownloadService downloadDataFromServerInBackgroundWithCompletion:^(ServerResponse* response){
dispatch_async(dispatch_get_main_queue(), ^{
completion(response);
});
// }];
}
- (NSArray<Entity*>*)fetchData
{
NSArray<Entity*> *results = nil;
// fetch from core data and return it
return results;
}
- (void)persistData:(NSArray<ServerResponse*> *)serverResponses
{
// parse whatever you get from server
// ... and persist it using Core Data
}
#end

Objective c- How to make sure block executes before executing any other code further?

Overview : I am using Amazon DynamoDB for my login service. And my login method looks like this in some UserAccount.m. And I am calling this class method in some LoginViewController.m on login button tap:
+ (BOOL)loginWithUsername:(NSString *)username password:(NSString *)password{
AWSDynamoDBObjectMapper *dynamoDBObjectMapper = [AWSDynamoDBObjectMapper defaultDynamoDBObjectMapper];
BOOL __block isLoginSuccessful = NO;
[[dynamoDBObjectMapper load:[User class] hashKey:username rangeKey:#"key"]
continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"The request failed. Error: [%#]", task.error);
}
if (task.exception) {
NSLog(#"The request failed. Exception: [%#]", task.exception);
}
if (task.result) {
//Do something with the result.
User *user = task.result; // User is a model I'm using
NSLog(#"pass: %#", user.password);
// Check returned password from DynamoDB with user-supplied password.
if ([user.password isEqualToString:password]) {
isLoginSuccessful = YES;
}
}
return nil;
}];
return isLoginSuccessful; // <-- Issue: function returns before block executes and isLoginSuccessful value is changed.
}
The issue is function returns before block executes. What I tried:
i) I read up about using dispatch groups on this SO question
ii) Tried to execute method - (AWSTask *)load:(Class)resultClass hashKey:(id)hashKey rangeKey:(id)rangeKey on main thread like this but still function returns before block execution finishes.:
dispatch_async(dispatch_get_main_queue(), ^{
[[dynamoDBObjectMapper load:[User class]
hashKey:username
rangeKey:#"key"] continueWithExecutor:[AWSExecutor mainThreadExecutor] withBlock:^id(AWSTask *task) {
if (!task.error) {
User *user = task.result;
NSLog(#"pass: %#", user.password);
//Do something with the result.
if ([user.password isEqualToString:password]) {
isLoginSuccessful = YES;
}
} else {
NSLog(#"Error: [%#]", task.error);
}
return nil;
}];
});
Am I missing out something here/ doing wrong in my approach? Any suggestion towards right direction would be really helpful. Thank you!
Edit 1: Including function from where loginWithUsername class method is being called:
#IBAction func login(sender: AnyObject) {
if(UserAccount.loginWithUsername(txtEmail.text, password: txtPassword.text)){
println("SUCCESS")
lblIncorrect.hidden = true
}
else{
println("Incorrect username/password")
lblIncorrect.hidden = false
}
}
It is the basic idea of GCD that blocks are executed "in background", so the control flow can return to the sender of the message before the task is finished. This is, because the task is a potential long runner and you do not want to block the sending control flow, esp. if it is the main thread.
If you want to execute code after finishing the operation, simply add it to the block. This is the code inside login(): Add the if to the block and remove the block variable and the return value. Make the method void.
To have a general pattern:
-(IBAction)doSomething:(id)sender
{
[self executeOperation]; // Method becomes void
// Remove code processing on result
}
-(void)executeOperation // Method becomes void
{
[receiver longRunnerWithBlock:^(id result)
{
…
// Add code processing on result
}
}
Please take care, that the block is potentially run on a different thread than the main thread, so you have to dispatch it on the main thread, if it is UI related.
OR you could just take a page from Googles iOS framework code and do it the easy way like this:
- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {
if (signInDoneSel) {
[self performSelector:signInDoneSel];
}
}

iOS - How to increase variable in block using cycle

I need to create an entity on the server and then upload few images to the server.
So first block display success creating entity on the server then I starting upload 10 images one by one in cycle, but the app send the notification not after last 10 image was uploaded, so 'i' variable can be 10 even not be a 10 in the order. I am not sure but seems iteration in the block is not right. So I just want to be sure that the 10 images was uploaded and just then invoke sending notification.
So I skip some blocks parameters and failure options, array that I use for getting images to upload and etc. Just think about my blocks as an example that display success invocation after '{'.
// success first block
block1
{
// cycle from 0 to 10
for (NSInteger i = 0; i <=10; i++)
{
// success upload image to the server block
block2
{
// if I did 10 uploads I need to send notification.
if (i == 10)
{
// send notification here when last block returned success....
}
}
}
}
If you're creating a bunch of AFHTTPRequestOperation objects, then the most logical approach would be to create a completion operation and make it dependent upon the request operations:
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// code to be run when the operations are all done should go here
}];
for (...) {
// create `operation` AFHTTPRequestOperation however you want and add it to some queue
// but just make sure to designate the completion operation dependency
[completionOperation addDependency:operation];
}
// now that all the other operations have been queued, you can now add the completion operation to whatever queue you want
[[NSOperationQueue mainQueue] addOperation:completionOperation];
You can use Dispatch Group as the following.
// success first block
block1
{
dispatch_group_t group = dispatch_group_create();
// cycle from 0 to 10
__block NSUInteger successCount = 0;
for (NSInteger i = 0; i <=10; i++)
{
dispatch_group_enter(group);
// upload image to the server block
successOrErrorBlock
{
if (success)
successCount++;
dispatch_group_leave(group);
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (successCount == 10) {
// send notification here when last block returned success....
}
});
}
The pattern I follow when invoking more than a couple blocks asynchronously is to create a to-do list (array) and perform that list recursively, like this:
// say this is our asynch operation. presume that someParameter fully
// describes the operation, like a file to be uploaded
- (void)performSomeAsynchOperationDefinedBy:(id)someParameter completion:(void (^)(BOOL, NSError *))completion {
// this could wrap any operation, like anything from AFNetworking
}
- (void)doOperationsWithParameters:(NSArray *)parameters completion:(void (^)(BOOL, NSError *))completion {
if (!parameters.count) return completion(YES, nil);
id nextParameter = someParameters[0];
NSArray *remainingParameters = [parameters subarrayWithRange:NSMakeRange(1, parameters.count-1)];
[self performSomeAsynchOperationDefinedBy:nextParameter completion:^(BOOL success, NSError *error)) {
if (!error) {
[self doManyOperations:remainingParameters completion:completion];
} else {
completion(NO, error);
}
}];
}
Now, to do several operations, place the parameters in an array, like:
NSArray *parameters = #[#"filename0", #"filename1", ...];
[self doOperationsWithParameters:parameters completion:(BOOL success, NSError *error) {
NSLog(#"ta-da!");
}];
A variant on the above with a progress block invoked up front and after each recursion (with a progress percentage initialCount/remainingCount) should be straight forward from the example provided.
Another variation would be to wrap both the param array and the completion block in a single NSObject, so the recursive call could be made with performSelector:, the benefit of doing this would be to not wind up the stack on a long recursion.

Testing background save of Core Data entity with Kiwi

I'm struggling to figure out the best method to test interacting with Core Data in a background thread. I have the following class method:
+ (void)fetchSomeJSON
{
// Download some json then parse it in the block
[[AFHTTPClient sharedClient] fetchAllThingsWithCompletion:^(id results, NSError *error) {
if ([results count] > 0) {
NSManagedObjectContext *backgroundContext = //... create a new context for background insertion
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, ^{ // If I comment this out, my test runs just fine
//... insert and update some entities
for (NSString *str in results) {
NSManagedObject *object = //...
}
});
}
}];
}
I'm currently testing this method with the following Kiwi code:
describe(#"MyAction", ^{
__block void (^completionBlock)(NSArray *array, NSError *error);
beforeEach(^{
// Stub the http client
id mockClient = [AFHTTPClient mock];
[WRNAPIClient stub:#selector(sharedClient) andReturn:mockClient];
// capture the block argument
KWCaptureSpy *spy = [mockClient captureArgument:#selector(fetchAllThingsWithCompletion:) atIndex:0];
[MyClass fetchSomeJSON]; // Call the method so we can capture the block
completionBlock = spy.argument;
// run the completion block
completionBlock(#[#"blah"], nil);
})
// If I remove the dispatch_async block, this test passes fine.
// If I add it in again the test fails, probably because its not waiting
it(#"should return the right count", ^{
// entityCount is a block that performs a fetch request count
NSInteger count = entityCount(moc, #"Task");
[[theValue(count) should] equal:theValue(4)];
})
// This works fine, but obviously I don't want to wait a second
it(#"should return the right count after waiting for a second", ^{
sleep(1);
NSInteger count = entityCount(moc, #"Task");
[[theValue(count) should] equal:theValue(4)];
});
};
If I remove the dispatch_async line, then I can get my test to run quickly. The only way I can get my test suite to run when using dispatch_async is to sleep(1) after calling the completion block. Using sleep() makes me think that I'm not approaching it in the right way. I have tried using shouldEventually but this doesn't seem to re-fetch my count value.
Have you tried these asynchronous block macros?
#define TestNeedsToWaitForBlock() __block BOOL blockFinished = NO
#define BlockFinished() blockFinished = YES
#define WaitForBlock() while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) && !blockFinished)
I have tried several approaches to solving this, none feel right.
1) Move the dispatch_async to its own class
+ (void)dispatchOnMainQueue:(Block)block
{
if ([NSThread currentThread] == [NSThread mainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}
+ (void)dispatchOnBackgroundQueue:(Block)block
{
dispatch_queue_t background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(background, block);
}
Then during test execution, swizzle the background dispatch to occur on the main queue. This worked, but was unpredictable. It also felt so wrong!
2) Move the setup code to Kiwi's beforeAll block, then sleep the main thread. This works as the Kiwi tests are run on the main thread, so we're effectively saying "let the background operations happen before carrying on with the tests". I think this is what I'm going to use. Yes it makes my unit tests run slower, but they pass when they should do, and fail when they should
describe(#"MyAction", ^{
__block void (^completionBlock)(NSArray *array, NSError *error);
beforeAll(^{
// Stub the http client
id mockClient = [AFHTTPClient mock];
[WRNAPIClient stub:#selector(sharedClient) andReturn:mockClient];
// capture the block argument
KWCaptureSpy *spy = [mockClient captureArgument:#selector(fetchAllThingsWithCompletion:) atIndex:0];
[WRNTaskImporter importAllTasksFromAPI];
completionBlock = spy.argument;
// run the completion block
completionBlock(#[#"blah"], nil);
// Wait for background import to complete
[NSThread sleepForTimeInterval:0.1];
})
// This works
it(#"should return the right count", ^{
// entityCount is a block that performs a fetch request count
NSInteger count = entityCount(moc, #"Task");
[[theValue(count) should] equal:theValue(4)];
})
};
The caveat of this approach is that it only works when you aren't changing any data before a test. Say for example I insert 4 entities, and want to check each entity was inserted as expected. This option would work here. If I needed to re-run the import method and check that the count hadn't increased, I would need to add another [NSThread sleepForTimeInterval:0.1] after calling the insertion code.
For normal block based Kiwi tests you should probably use either the expectFutureValue shouldEventually method, or KWCaptureSpy to test your code, but this may not help when calling nested blocks.
If anyone has a more appropriate method for testing cases like these I'm happy to hear it!

Testing asynchronous call in unit test in iOS

I am facing a problem while unit testing an asynchronous call in iOS. (Although it is working fine in view controllers.)
Has anyone faced this issue before? I have tried using a wait function but I'm still facing the same problem.
Please suggest an example of a good way to do this.
You'll need to spin the runloop until your callback is invoked. Make sure that it gets invoked on the main queue, though.
Try this:
__block BOOL done = NO;
doSomethingAsynchronouslyWithBlock(^{
done = YES;
});
while(!done) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
You can also use a semaphore (example below), but I prefer to spin the runloop to allow asynchronous blocks dispatched to the main queue to be processed.
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
doSomethingAsynchronouslyWithBlock(^{
//...
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
Here is Apple's description of native support for async testing.
TL;DR manual:
Look at XCTextCase+AsynchronousTesting.h
There is special class XCTestExpectation with only one public method: - (void)fulfill;
You should init instance of this class and in success case call fulfill method. Otherwise your test will fail after timeout that you specify in that method:
- (void)waitForExpectationsWithTimeout:(NSTimeInterval)timeout handler:(XCWaitCompletionHandler)handlerOrNil;
Example:
- (void)testAsyncMethod
{
//Expectation
XCTestExpectation *expectation = [self expectationWithDescription:#"Testing Async Method Works Correctly!"];
[MyClass asyncMethodWithCompletionBlock:^(NSError *error) {
if(error)
NSLog(#"error is: %#", error);
else
[expectation fulfill];
}];
//Wait 1 second for fulfill method called, otherwise fail:
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
if(error)
{
XCTFail(#"Expectation Failed with error: %#", error);
}
}];
}
I think many of the suggested solutions in this post has the problem that if the asynchronous operation does not complete the "done" flag is never set, and the test will hang forever.
I have successfully used this approach in many of my test.
- (void)testSomething {
__block BOOL done = NO;
[obj asyncMethodUnderTestWithCompletionBlock:^{
done = YES;
}];
XCTAssertTrue([self waitFor:&done timeout:2],
#"Timed out waiting for response asynch method completion");
}
- (BOOL)waitFor:(BOOL *)flag timeout:(NSTimeInterval)timeoutSecs {
NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeoutSecs];
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeoutDate];
if ([timeoutDate timeIntervalSinceNow] < 0.0) {
break;
}
}
while (!*flag);
return *flag;
}
Since Xcode 6 this built in to XCTest as a category:
See https://stackoverflow.com/a/24705283/88164
Here's another alternative, XCAsyncTestCase, that works well with OCMock if you need to use it. It's based on GHUnit's async tester, but is uses the regular XCTest framework instead.
Fully compatible with Xcode Bots.
https://github.com/iheartradio/xctest-additions
Usage is the same, just import and subclass XCAsyncTestCase.
#implementation TestAsync
- (void)testBlockSample
{
[self prepare];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){
sleep(1.0);
[self notify:kXCTUnitWaitStatusSuccess];
});
// Will wait for 2 seconds before expecting the test to have status success
// Potential statuses are:
// kXCTUnitWaitStatusUnknown, initial status
// kXCTUnitWaitStatusSuccess, indicates a successful callback
// kXCTUnitWaitStatusFailure, indicates a failed callback, e.g login operation failed
// kXCTUnitWaitStatusCancelled, indicates the operation was cancelled
[self waitForStatus:kXCTUnitWaitStatusSuccess timeout:2.0];
}
AGAsyncTestHelper is a C macro for writing unit tests with asynchronous operations and works with both SenTestingKit and XCTest.
Simple and to the point
- (void)testAsyncBlockCallback
{
__block BOOL jobDone = NO;
[Manager doSomeOperationOnDone:^(id data) {
jobDone = YES;
}];
WAIT_WHILE(!jobDone, 2.0);
}
Sam Brodkin already gave the right answer.
Just to make the answer looks better at first sight, I bring the sample code here.
Use XCTestExpectation.
// Test that the document is opened. Because opening is asynchronous,
// use XCTestCase's asynchronous APIs to wait until the document has
// finished opening.
- (void)testDocumentOpening
{
// Create an expectation object.
// This test only has one, but it's possible to wait on multiple expectations.
XCTestExpectation *documentOpenExpectation = [self expectationWithDescription:#"document open"];
NSURL *URL = [[NSBundle bundleForClass:[self class]]
URLForResource:#"TestDocument" withExtension:#"mydoc"];
UIDocument *doc = [[UIDocument alloc] initWithFileURL:URL];
[doc openWithCompletionHandler:^(BOOL success) {
XCTAssert(success);
// Possibly assert other things here about the document after it has opened...
// Fulfill the expectation-this will cause -waitForExpectation
// to invoke its completion handler and then return.
[documentOpenExpectation fulfill];
}];
// The test will pause here, running the run loop, until the timeout is hit
// or all expectations are fulfilled.
[self waitForExpectationsWithTimeout:1 handler:^(NSError *error) {
[doc closeWithCompletionHandler:nil];
}];
}
you can use async api calling in swift like this
private let serverCommunicationManager : ServerCommunicationManager = {
let instance = ServerCommunicationManager()
return instance
}()
var expectation:XCTestExpectation?
func testAsyncApiCall() {
expectation = self.expectation(description: "async request")
let header = ["Authorization":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImQ4MmY1MTcxNzI4YTA5MjI3NWIzYWI3OWNkOTZjMGExOTI4MmM2NDEyZjMyYWQzM2ZjMzY4NmU2MjlhOWY2YWY1NGE0MDI4MmZiNzY2NWQ3In0.eyJhdWQiOiIxIiwianRpIjoiZDgyZjUxNzE3MjhhMDkyMjc1YjNhYjc5Y2Q5NmMwYTE5MjgyYzY0MTJmMzJhZDMzZmMzNjg2ZTYyOWE5ZjZhZjU0YTQwMjgyZmI3NjY1ZDciLCJpYXQiOjE1MDg4MjU1NTEsIm5iZiI6MTUwODgyNTU1MSwiZXhwIjoxNTQwMzYxNTUxLCJzdWIiOiIiLCJzY29wZXMiOltdfQ.osoMQgiY7TY7fFrh5r9JRQLQ6AZhIuEbrIvghF0VH4wmkqRUE6oZWjE5l0jx1ZpXsaYUhci6EDngnSTqs1tZwFTQ3srWxdXns2R1hRWUFkAN0ri32W0apywY6BrahdtiVZa9LQloD1VRMT1_QUnljMXKsLX36gXUsNGU6Bov689-bCbugK6RC3n4LjFRqJ3zD9gvkRaODuOQkqsNlS50b5tLm8AD5aIB4jYv3WQ4-1L74xXU0ZyBTAsLs8LOwvLB_2B9Qdm8XMP118h7A_ddLo9Cyw-WqiCZzeZPNcCvjymNK8cfli5_LZBOyjZT06v8mMqg3zszWzP6jOxuL9H1JjBF7WrPpz23m7dhEwa0a-t3q05tc1RQRUb16W1WhbRJi1ufdMa29uyhX8w_f4fmWdAnBeHZ960kjCss98FA73o0JP5F0GVsHbyCMO-0GOHxow3-BqyPOsmcDrI4ay006fd-TJk52Gol0GteDgdntvTMIrMCdG2jw8rfosV6BgoJAeRbqvvCpJ4OTj6DwQnV-diKoaHdQ8vHKe-4X7hbYn_Bdfl52gMdteb3_ielcVXIaHmQ-Dw3E2LSVt_cSt4tAHy3OCd7WORDY8uek4Paw8Pof0OiuqQ0EB40xX5hlYqZ7P_tXpm-W-8ucrIIxgpZb0uh-wC3EzBGPjpPD2j9CDo"]
serverCommunicationManager.sendServerRequest(httpMethodType: .get, baseURL: "http://192.168.2.132:8000/api/v1/user-role-by-company-id/2", param: nil, header: header) { (isSuccess, msg , response) in
if isSuccess
{
let array = response as! NSArray
if array.count == 8
{
XCTAssertTrue(true)
self.expectation?.fulfill()
}
else
{
XCTAssertFalse(false)
XCTFail("array count fail")
}
}
}
waitForExpectations(timeout: 5) { (error) in
if let error = error{
XCTFail("waiting with error: \(error.localizedDescription)")
}
}
}
I suggest you should have a look on the tests of Facebook-ios-sdk. It's a good example of how to test async unit test on iOS, though personally I think async tests should be break into sync tests.
FBTestBlocker: a blocker that prevent current thread exits with specified timeout. You can drag and drop this to your project, but you need to remove OCMock related stuff if you don't have that in you project.
FBTestBlocker.h
FBTestBlocker.m
FBURLConnectionTests: test examples you should look at.
FBURLConnectionTests.h
FBURLConnectionTests.m
This code snippet should give you some idea
- (void)testExample
{
FBTestBlocker *_blocker = [[FBTestBlocker alloc] initWithExpectedSignalCount:1];
__block BOOL excuted = NO;
[testcase test:^(BOOL testResult) {
XCTAssert(testResult, #"Should be true");
excuted = YES;
[_blocker signal];
}];
[_blocker waitWithTimeout:4];
XCTAssertTrue(excuted, #"Not executed");
}
Try KIWI framework. It's powerful and might help you with other kinds of tests.
I recommend you connection semaphore + runloop, i also wrote method which take block:
// Set the flag to stop the loop
#define FLEND() dispatch_semaphore_signal(semaphore);
// Wait and loop until flag is set
#define FLWAIT() WAITWHILE(dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
// Macro - Wait for condition to be NO/false in blocks and asynchronous calls
#define WAITWHILE(condition) \
do { \
while(condition) { \
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]; \
} \
} while(0)
method:
typedef void(^FLTestAsynchronousBlock)(void(^completion)(void));
void FLTestAsynchronous(FLTestAsynchronousBlock block) {
FLSTART();
block(^{
FLEND();
});
FLWAIT();
};
and call
FLTestAsynchronous(^(void(^completion)()){
[networkManager signOutUser:^{
expect(networkManager.currentUser).to.beNil();
completion();
} errorBlock:^(NSError *error) {
expect(networkManager.currentUser).to.beNil();
completion();
}];
});
If you are using XCode 6, you can test async network calls like this:
XCTest and asynchronous testing in Xcode 6

Resources