Swift Xcode Asynchronous Testing

A while back I posted my tweak to some Xcode testing for asynchronous calls. Here’s an update I did in Swift for XCTestCase.

extension XCTestCase {
  /**
   For use testing async functions.
   Will wait for completion of the function.
   You need to provide a test function for when the call is completed.
   The ultimate timeout should be provided for cases of there not
   being a response.
   
   let _ = self.waitUntilReady(completionTest: { () -> Bool in
   return remoteInterface?.isBusy == false
   })
   
   or as a trailing closure:
   
   let _ = self.waitUntilReady() {
   return remoteInterface?.isBusy == false
   }
   
   optionally using your own timeout:
   
   let _ = self.waitUntilReady(timeout: 20) {
   return remoteInterface?.isBusy == false
   }
   
   */
  func waitUntilReady(timeout seconds:TimeInterval = 30, completionTest: () -> Bool) -> TimeInterval {
    let start = Date.timeIntervalSinceReferenceDate
    let loopUntil = Date(timeIntervalSinceNow: seconds)
    
    while completionTest() == false && loopUntil.timeIntervalSinceNow > 0 {
      RunLoop.current.run(mode: .defaultRunLoopMode, before: loopUntil)
    }
    
    let finish = NSDate.timeIntervalSinceReferenceDate
    let runtime = finish - start
    return runtime
  }
}

Asynchronous Testing with SenTestingKit

Xcode includes a testing library which is useful for unit testing.  Simple unit tests are easy to manage, but it tends to be (A) perform operation, and (B) test the state of your objects.

I was recently doing some asynchronous programming and wanted to build some tests against my code.  A little searching led me to a good idea by akisute on Github.  But I thought it could use some simplification.

It boils down to a single method that takes a block to determine if the expected action is completed. Simply implement it to return YES when it is.

- (NSTimeInterval)waitTillReady:(BOOL (^)())isReady timeout:(NSTimeInterval)seconds {
  NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
  NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:seconds];

  while (isReady() == NO && [loopUntil timeIntervalSinceNow] > 0) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:loopUntil];
  }

  NSTimeInterval finish = [NSDate timeIntervalSinceReferenceDate];
  return (finish - start);
}

In line 6 above, NSRunLoop performs the magic. In any iOS or OS X there is a run loop at the top of the call stack that gets things running in the event-driven applications. Instead of allowing it to drop out of our call stack back to the top level, we are creating a secondary run loop that allows anything the user causes or external events cause to be processed.

Once our event occurs or the timeout is reached, we return and let the normal execution of the main run loop eventually take back over.

And here’s an example use of it. Creating a message object which is simply loading a URL from the web. Here’s the code to test that it starts loading within 5 seconds, and that it completes within 20 seconds.

- (void)testMessage {
  PPPostMessage *message = [[PPPostMessage alloc] init];
  STAssertTrue(message.isRequesting, @"should automatically start requesting (currently)");

  NSTimeInterval delay = [self waitTillReady:^BOOL{
    return message.isLoading;
  } timeout:5];
  STAssertTrue(message.isLoading, @"expecting it to start loading in 5 second");
  NSLog(@"[%@ %@] load statup delay = %f", NSStringFromClass([self class]), NSStringFromSelector(_cmd), delay );

  delay = [self waitTillReady:^BOOL{
      return message.isLoaded;
    } timeout:20 - delay];
  
  STAssertTrue(message.isLoaded, @"expecting it to be loaded in 20 seconds");
  NSLog(@"[%@ %@] load time = %f", NSStringFromClass([self class]), NSStringFromSelector(_cmd), delay );
}

You’ll notice that the waitTillReady: method returns the time that it actually took and we are using that value to adjust the time we allow on the second test.

New Provisioning Profile Not Working: Fix

My Provisioning Profile for my iPhone app expired, so I renewed it, downloaded it.  Added it into Xcode.  Manually added it to ~/Library/Mobile Devices.  It would build and install onto the device, but when launched via the debugger I received the following output:

Error launching remote program: security policy error.

The app would terminate, the debugger was running but not attached to any process.  The fix that finally resolved it was going to Settings > General > Profiles on the device and removing all of the expired Profiles in there.  (Which were not related to the app in question.)

SQLitePersistentObjects

I was on another iPhone project that had several thousand simple data objects that I would need to keep and search through. SQLite again. I’ve seen Jeff Lamarche mention his open source project, SQLitePersistenObjects a few times, so I decided to give it a whirl.

You can get the code at it’s google source site. There’s not too much documentation available, but it seems simple enough (as any SQLite wrapper should aim to be). There is a ReadMe.txt with a brief introduction, and some sample code (which I haven’t yet looked at).

I went about including it a different way than the “drop the zip file in” method it suggests. My project is in SVN and I’m using Versions, so I just added an svn:extern statement and now have a subdirectory with the source in it.

In Xcode I created a new group, then edited it’s information to point to the subdirectory naturally, then added all the files in there to the group. Then right-click on frameworks, add existing framework, and dig through the /Developer/Platforms et al. until you get to the sqlite dynamic library to link against.

It built and compiled fine for me. Now off to put it through its paces.