Sunday, May 31, 2015

Building iOS apps in comand line

Building iOS apps in command line.
1 Using xcodebuild command
Navigate to the directory of the Xcode in the terminal and issue the command xcodebuild. Then you will be able to build. In addition you can specify the build configuration using the –configuration option. For example, this is how to build with debug configuration.
xcodebuild –configuration Debug
2 Building using xctool
Also we can build using the command xctool. In fact xctool can be considered as a replacement for xcodebuild tool. Also xctool is created and maintained by the facebook. In order to install the xctool you need to clone it to some convenient location as follows
git clone git@github.com:facebook/xctool.git

In cloned project, you will see a script called xctool.sh. That is what we should be concentrating.Next you have to build the tool. Fortunately, when you call xctool for the first time, it builds itself. Invoke the the script with with option version as follows. This will give you the current version of the tool. Also it will build the tool.
./xctool.sh -version
Now you need to include xctool installation path to the installation path. So you can easily invoke the command from anywhere else. Now you have the xctool installed and path also correctly set. Now navigate to the folder where your project is residing and issue the xctool command as follows.
xctool.sh -workspace /Your workspace name/ -scheme /Your project name/
There are lot of advantages of using xctool over xcodebuild. One is, output of xctool is better readable. In addition xctool provides different reporters making it easier to integrate CI systems such as jenkins. Also it supports cofiguration file format. In order to use it, simply create a “.xctool-args” file with the following format
[
        "-workspace", "/workspace name/.xcworkspace",
        "-scheme", "/project name/",
        "-configuration", "Release"
]
Because of this file, now you can invoke xctool without other options. But since the xctool is created and maintained by a third party company, xctool may fail with newer xcode versions.
3 Building IPA files with command line
Now you know how to build a xcode project in command line. But in order to run it, you need to create an IPA file. It can be done in command line. It can be done using xcrun tool. xcrun is a xcode tool. It is impacted by the location of the developer directory and the value of $DEVELOPER_DIR environment variable. This tool does two tasks. That is finding development tools and executing them. If you want to find the location of you git installation, you can call this command as follows
xcrun --find git
As I mentioned earlier, this tool can execute tools as well. This is how to execute a git command via xcrun
xcrun git --version
Here you need to be bit carefull. Because xcrun can bypass the traditional $PATH enviroment variable to locate executable files. Now lets get back to our original topic. That is generating the IPA file. It can be achieved with PackageApplication tool. This tool can be invoked by xcrun tool. This is how to invoke it
xcrun PackageApplication
But unfortunately, you will get the following error message.
xcrun: error: unable to find utility "PackageApplication", not a developer tool or in PATH
This error can be caused by either of this issues.
  • It is simply not in your PATH
  • PackageApplication is an executable specific to the iOS SDK. Since we did not specify the SDK we are using and the SDKROOT environment variable was not set, the xcrun was unable to find the tool
We can specify the iOS sdk we are using with the SDK argument.
xcrun --sdk iphoneos --f PackageApplication
Package application only needs the path to the .app package generated at the end of the xcodebuild command line tool. In addition it will perform tasks such as resigning the application before packaging it into the IPA file. Path to the generated package can be found at the end of the command's output. This is how to use it
/usr/bin/xcrun -sdk iphoneos PackageApplication -v "${RELEASE_BUILDDIR}/${APPLICATION_NAME}.app" -o "${BUILD_HISTORY_DIR}/${APPLICATION_NAME}.ipa" --sign "${DEVELOPER_NAME}" --embed "${PROVISONING_PROFILE}”
Putting everything in a single script
Now you have scene how to build and generate IPA files in command line. In a CI environment it is better to have them in a single script. This is how to do it.
#!/bin/bash
WORKSPACE="/Your workspace name/"
SCHEME="/Your project name/"
BUILD_DIR="$(pwd)/build"
CONFIGURATION="Release"
ARCHIVE_FILENAME="/Your project name/.app"
IPA_FILENAME="/Your project name/.ipa"
security unlock-keychain -p $PASSWORD $HOME/Library/Keychains/login.keychain

xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -configuration
CONFIGURATION_BUILD_DIR="$BUILD_DIR" clean build | xcpretty -c
xcrun -sdk iphoneos PackageApplication "$BUILD_DIR/$ARCHIVE_FILENAME" -o "$BUILD_DIR/$IPA_FILENAME"

For any query, feel free to contact me via my linkedin profile.

Friday, May 1, 2015

Defining a service layer with AFNetworking

This example shows how to create a service layer for json based web services. This is how I developed it.
  1. Create a basic interface for calling web services
  2. Create a subclass of correct AFNetworking seralizer class(in this case AFJsonSerializer) and format the response
  3. For each web service create a singleton subclass of AFHttpSessionManager
  4. Create a Façade for service layer
For this example I am using free web service used used by raywenderlich afnetworking tutorial

1 Creating basic interface
In modern software engineering, we write code for the interface, not for the implementation. So create a protocol named Service as folows
@protocol Service <NSObject>

- (void)callService:(NSDictionary*)parameters withCompletionBlock:(void(^)(NSArray *resultArray, NSError *error))completionBlock;

@end

This has a method called callService and it is the method used to call the web services. Now we have an interface, which can be used to call web services. I will explain the importance of this when we actually call a web service.

2 Creating a custom json response serializer
Here, we get the response in json format. we need to process this json response in order to get the correct result out of it. For that purpose we can create a subclass of the AFJSONResponseSerializer class and override it's responseObjectForResponse as follows. This method is overridden according to the json response we get. This method directly create models from the response and add it to an array
@implementation WeatherServieJasonSerializer

- (id)responseObjectForResponse:(NSURLResponse *)response
                           data:(NSData *)data
                          error:(NSError *__autoreleasing *)error {
    NSMutableArray *retArray = [[NSMutableArray alloc] init];
    NSDictionary *json = [super responseObjectForResponse:response data:data error:error];
    NSDictionary *dictionary = json[@"data"];
    WeatherInfo *infoRet = [[WeatherInfo alloc] init];
    NSArray *arrData = dictionary[@"weather"];
    
    infoRet.tempMaxC = @([((NSString*)((NSDictionary*)arrData[0])[@"tempMaxC"]) integerValue]);
    infoRet.tempMaxF = @([((NSString*)((NSDictionary*)arrData[0])[@"tempMaxF"]) integerValue]);
    infoRet.tempMinC = @([((NSString*)((NSDictionary*)arrData[0])[@"tempMinC"]) integerValue]);
    infoRet.tempMinF = @([((NSString*)((NSDictionary*)arrData[0])[@"tempMinF"]) integerValue]);
    
    [retArray addObject:infoRet];
    
    return retArray;
}
@end
 
3 Creating a singleton subclass of AFHttpSessioManager to invoke web services.
For each web service you have, you need to create a separate class like this. In addition this class conform to the Service protocol and implements its callService method. Following is the code for the class.
.h file
#import "AFHTTPSessionManager.h"
#import "Service.h"

@interface WetherWebService : AFHTTPSessionManager<Service>

+ (WetherWebService*)getSharedInstance;

@end

.m file
#import "WetherWebService.h"
#import "WeatherServieJasonSerializer.h"

static NSString * const baseURLString = @"http://www.raywenderlich.com/demos/weather_sample/";

@implementation WetherWebService

+ (WetherWebService*)getSharedInstance {
    static WetherWebService *sharedInstance = nil;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        sharedInstance = [[WetherWebService alloc] initWithBaseURL:[NSURL URLWithString:baseURLString]];
        sharedInstance.responseSerializer = [WeatherServieJasonSerializer serializer];
    });
    
    return sharedInstance;
}

- (void)callService:(NSDictionary*)parameters withCompletionBlock:(void(^)(NSArray *resultArray, NSError *error))completionBlock {
    [self GET:@"weather.php" parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
        completionBlock(responseObject, nil);
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        completionBlock(nil, error);
    }];
}

@end
 
4 Creating the service layer façade.
This is an implementation of façade design pattern. This creates an abstraction layer between the web service layer and the rest of the application. This is the source of ServiceLayerFacade.h file
@interface ServiceLayerFacade : NSObject

+ (ServiceLayerFacade*)getSharedInstance;
- (id<Service>)getWeatherService;

@end
#import "ServiceLayerFacade.h"
#import "WetherWebService.h"

@implementation ServiceLayerFacade

+ (ServiceLayerFacade*)getSharedInstance {
    static ServiceLayerFacade *sharedInstance = nil;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        sharedInstance = [[ServiceLayerFacade alloc] init];
    });
    
    return sharedInstance;
}
- (id<service>)getWeatherService {
    return [WetherWebService getSharedInstance];
}
This is how these web services should be called.
NSDictionary *parameters = @{@"format": @"json"};
    ServiceLayerFacade *serviceLayer = [ServiceLayerFacade getSharedInstance];
    id<Service> weatherWebService = [serviceLayer getWeatherService];
    [weatherWebService callService:parameters withCompletionBlock:^(NSArray *resultArray, NSError *error) {
        if (resultArray[0] != nil) {
            id<Tablelayout> tableModel = resultArray[0];
            _tableArray = [tableModel getTableRepresentation];
            [_weatherTable reloadData];
        }
    }];
Here, since we are using interfaces and a facade, we can completely hide the implementation details of the web service layer. Web service caller only knows of the uniform interface method to call the web service. He does not know the classes used to call the web service, the way response is processed etc. What he gets is model classes that can be directly used in his code. Now I will explain the beauty of this approach. We use Service interface to access the web service. And we do not know how the web service is called behind the scene and how the response is processed. Because of this we can change everything related to web service calling and processing without affecting rest of the layers.

Presenting the data
This is our model class
#import 

@interface WeatherInfo : NSObject

@property NSNumber *tempMaxC;
@property NSNumber *tempMaxF;
@property NSNumber *tempMinC;
@property NSNumber *tempMinF;

@end
Now we have the data retrieved from the web service stored in our model classes. Now how to present it? You can add a method to the model class to get some sort of array and show it in a table. But this is an extremely bad idea. Because a model class should not know how the data is presented. So here our basic problem is adding behavior to the model object, without affecting behavior of other objects from the same class. What is the design pattern we can use to achieve this? The answer is decorator pattern
 
Implementation of decorator pattern
I normally use categories to implement decorator pattern in objective c. In addition I declare category methods in a protocol as well. This protocol acts as an interface
#import 

@protocol TableLayout <NSObject>

-(NSArray*)getTableRepresentation;

@end

This is the category of the model class.
.h file
#import "WeatherInfo.h"
#import "TableLayout.h"

@interface WeatherInfo (TableRepresentation)<TableLayout>

@end

This is the .m file
#import "WeatherInfo+TableRepresentation.h"

@implementation WeatherInfo (TableRepresentation)

-(NSArray*)getTableRepresentation {
    return [[NSArray alloc] initWithObjects:self.tempMaxC,self.tempMaxF,self.tempMinC,self.tempMinF, nil];
}

@end
Then in code via the TableLayout interface I am getting the table representation as follows.

id<Tablelayout> tableModel = resultArray[0];
            _tableArray = [tableModel getTableRepresentation];
            [_weatherTable reloadData];
This is how I implemented the service layer. The complete example of this tutorial is available in GitHub.

Alternative approaches
  • This stackoverflow question presents few ways of developing a service layer using AFNetworking and ReactiveCocoa
  • You can use RestKit to get Core data models directly after web service calls

For any query, feel free to contact me via my linkedin profile. Happy coding!!!!