Full string power with CFStringTransform

Powerful languages have great string manipulation capabilities, today we’ll have a look at what Objective-C offers.

CFStringTransform is part of the Core Foundation framework, it will let you manipulate strings of both NSString and CFString type.

So, in Objective-C we have
Boolean CFStringTransform ( CFMutableStringRef string, CFRange *range, CFStringRef transform, Boolean reverse );
(and in Swift)
func CFStringTransform(_ string: CFMutableString!, _ range: UnsafeMutablePointer<CFRange>, _ transform: CFString!, _ reverse: Boolean) -> Boolean

the arguments it requires are really straightforward: a string, a range in which to apply the transformation, the transformation itself and a boolean to specify whether or not use the inverse transformation.

The transformations are very powerful and range from classic Uppercase, Lowercase, Titlecase, Full/Halfwidth conversions to handling Unicode characters. We will look to a couple of examples, but for the whole list of available transformations, please check the official list

Give a name to Unicode Characters

Sometimes you need to normalize Unicode input to ascii, and since modern devices now support emoji, you can now transliterate emoji characters:
so this string: 🐝🌵💀👽
becomes “{HONEYBEE}{CACTUS}{SKULL}{EXTRATERRESTRIAL ALIEN}”

in swift we just have to do:
var str = NSMutableString(string: "🐝🌵💀👽")
CFStringTransform(str, nil, kCFStringTransformToUnicodeName, Boolean(0))

Transliterate non latin characters

From emoji to pictograms the step is easy, so we can also transliterate japanese hiragana or chinese characters to their phonetic equivalent:


var japaneseTroublesome = NSMutableString(string: "めんどくさい")
CFStringTransform(japaneseTroublesome, nil, kCFStringTransformLatinHiragana, Boolean(1))
japaneseTroublesome


var chineseHello = NSMutableString(string: "你好")
CFStringTransform(chineseHello, nil, kCFStringTransformMandarinLatin, Boolean(0))
chineseHello

for the japanese transformation note we had to use the inverse transformation since the available one is from Latin to Hiragana.

We used swift to show a couple of examples so you can easily play with it in a playground in XCode.

iOS/OS X network testing with XCTest

I recently worked on a OS X app which had to keep in sync with a database running on a remote server, to interact with a hardware device via serial port and to allow the user to edit said database with information coming from the hardware device. Since reinventing the wheel is never a good idea, I decided to use various frameworks for those tasks: ORSSerialPort to handle the serial communication, AFNetworking for the networking, Apple’s own Core Data for the local database and XCTest together with OHHTTPStubs for the testing. The networking part was by far the easiest to develop but also the hardest to test, due to its asynchronicity.

This is basically how XCTest works: you create subclasses of XCTestCase, and then define instance methods with their name starting with “test”. Inside these methods you can check for whatever condition you need to test. Here’s a very simple example:

- (void)testReverseString{ 

    NSString *originalString = @"test";
    NSString *reversedString = [self.controllerToTest reverseString:originalString];
    NSString *expectedReversedString = @"tset";

    XCTAssertEqualObjects(expectedReversedString, reversedString);
}

XCTAssert allows to easily compare variables and to let tests fail/succeed based on the result, but it is not enough when it comes to testing the networking code. For example, I needed to test a GET request that fetched the user database from the remote server and tried to do so this way:

- (void)testSuccessfulUserFetch
{

    // Perform fetch
    [self.networkManager fetchUsers];

    // Test if the number of users created in the local database is equal to the one in the remote database
    NSArray *localUserDatabase = [User usersInManagedObjectContext:self.moc];
    XCTAssertEqual([localUserDatabase count], 10);

}

The test failed even if the GET was performed with success, because the local database was tested right after the network request was started and not after its completion.

Luckily Xcode 6 introduced a new feature in XCTest that allows to test asynchronous code as well: XCTestExpectation. It’s as easy as declaring an expectation and setting a timeout for that expectation. The only other thing that’s needed is of course to check for whatever condition has to be met and to fulfill the expectation if that happens.

The obvious place to check and fulfill my network expectations was inside the success/failure blocks that my network manager used, but since that code was written way before I decided to write tests for it, there was no easy way to do so without refactoring. At first I felt like it was not a very good idea to refactor the network manager just to be able to test it, but I’m glad I did: while the existing code was working fine, refactoring it and being able to test it afterwards helped a lot while further developing it.

This is how I changed on of my NetworkManger.m methods to use optional success/failure blocks:

typedef void(^SuccessBlock)(AFHTTPRequestOperation *operation, id responseObject);
typedef void(^FailureBlock)(AFHTTPRequestOperation *operation, NSError *error);

// Perform network request for user database
- (void)fetchUsersWithSuccess:(SuccessBlock)optionalSuccessBlock
         withFailure:(FailureBlock)optionalFailureBlock
{
    // Create success block
    SuccessBlock successBlock = ^(AFHTTPRequestOperation *operation, id responseObject){   

        // Load users into the database
        NSArray *usersArray = (NSArray *)responseObject;
        [User loadUsersFromArray:usersArray inManagedObjectContext:self.managedObjectContext];

        // Optional success block, if given
        if (optionalSuccessBlock) optionalSuccessBlock(operation, responseObject);
    };  

    // Create failure block
    FailureBlock failureBlock = ^(AFHTTPRequestOperation *operation, NSError *error){

        // Optional failure block, if given
        if (optionalFailureBlock) optionalFailureBlock(operation, error);
    };  

    // Perform request to server
    [self.manager GET:self.userSyncURL parameters:nil success:successBlock failure:failureBlock];
}

And here’s what the test method looks like:

- (void)testSuccessfulUserFetch{
    // Create expectation
    XCTestExpectation *countExpectation = [self expectationWithDescription:@"User count expectation"];

    // Create custom success block
    SuccessBlock successBlock = ^(AFHTTPRequestOperation *operation, id responseObject){
        // Check and fulfill expectation
        [self checkUsersAndFullfillCountExpectation:countExpectation];
    };

  // Wait for expectations to be fulfilled. If timeout is met, test fails.
  [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *error) {
    if (error) {
      NSLog(@"Timeout Error: %@", error);
    }
  }];
}

// Check expectations for successful user fetch
- (void)checkUsersAndFullfillExpectation:(XCTestExpectation *)countExpectation{

  // Check the users count in the database
  NSArray *users = [User usersInManagedObjectContext:self.moc];
  if ([users count] == 10){
    [countExpectation fulfill];
  }
}