Get the device ID in iOS 7.0 and earlier

In my previous article Get the device ID in iOS 6.0 and earlier I used [[UIDevice currentDevice] uniqueIdentifier] to retrieve a unique identifier for the user device when the current iOS version is 5.x or older.

But uniqueIdentifier method cannot be used anymore on Apple App Store apps. As mentioned by other developers (see MKStoreKit discussion on uniqueIdentifier and Stackoverflow identifierforvendor discussion) Apple added an automatic check which rejects all apps using this method. Moreover in iOS7 Apple removed uniqueIdentifier method.

Unfortunately Apple forces applications to remove the ability to get the UDID for privacy reasons. A universal identifier able to identify the device without requiring authorization from the user may allow to collect usage information from different applications and relate them to build a user profile based on applications usage (for example a third party vendor may buy informations from different app vendors…).

The following implementation avoids using uniqueIdentifier method in iOS 5.x or below.

  • For iOS6.0 or later I use the identifierForVendor new method.
  • For iOS5.x or below I use CFUUIDCreate method in association with storage into NSUserDefaults.

The second method will not ensure to have a unique identifier if the application is deleted and reinstalled.

You have to replace the @"app identifier" with your app identifier. The code uses ARC for memory management.

// Get the device id on iOS7.0 and previous versions
- (NSString *) getDeviceId {
    NSString *deviceID;
    UIDevice *device = [UIDevice currentDevice];
    if ([UIDevice instancesRespondToSelector:@selector(identifierForVendor)]) {
        deviceID = [[device identifierForVendor] UUIDString];
    } else {
        // In iOS5.x or older 'identifierForVendor' is not available
        deviceID = [[NSUserDefaults standardUserDefaults] objectForKey:@"app identifier"];
        if (!deviceID) {
            CFUUIDRef uuidRef = CFUUIDCreate(NULL);
            CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
            deviceID = [NSString stringWithString:(NSString *) CFBridgingRelease(uuidStringRef)];
            CFRelease(uuidRef);
            [[NSUserDefaults standardUserDefaults] setObject:deviceID forKey:@"app identifier"];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
    }
    return deviceID;
}

Tip: CFUUIDCreate and CFUUIDCreateString functions are Core Foundation functions that follows the Create Rule. So the uuidRef must be released with CFRelease whereas the uuidStringRef ownership is transferred to ARC with CFBridgingRelease and CFRelease is not needed.

JSON WebServices from iOS in Objective-C

In this article I present an effective way to call a webservice method from an iOS device.

Input and output are encoded to and decoded from a widely used format: JSON. For this I use the NSJSONSerialization class included in iOS starting from version 5.0.

First of all prepare input arguments by using an NSDictionary. This is an initialization valid for a fixed number of input arguments. The sample has 3 input arguments that may be any of the allowed NSJSONSerialization types (see NSJSONSerialization documentation).

    NSDictionary *inputData = [NSDictionary dictionaryWithObjectsAndKeys:in1, @"Input1", in2, @"Input2", in3, @"Input3", nil];

The conversion to a JSON string is a two step process . First to a binary format (NSData) by using dataWithJSONObject:options:error:, then to the JSON NSString. The error handling is not performed in this sample.

    NSError *error = nil;
    NSData *jsonInputData = [NSJSONSerialization dataWithJSONObject:inputData options:NSJSONWritingPrettyPrinted error:&error];
    NSString *jsonInputString = [[NSString alloc] initWithData:jsonInputData encoding:NSUTF8StringEncoding];

the synchronous WebService call , is a HTTP POST call to the server.

    NSURL *url = [NSURL URLWithString:@"http://host:port/Service.asmx/WebService1"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:[jsonInputString dataUsingEncoding:NSUTF8StringEncoding]];
    NSURLResponse *response;
    NSError *err;
    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];

The execution will block on sendSynchronousRequest:returningResponse:error: until the full response is read from the server.

The JSON (binary) response data from http call can be converted into the corresponding Objective-C object by using:

    // Parse response
    id jsonResponseData = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:nil];

I use ‘id‘ reference because usually we do not know which is the object type returned from the server. Here the sample may be considered completed, but I want to add additional tips to interpret the output and detect error messages that may come from the server.

Even if we know the type returned bye the server it is a better choice to check it because the server may return an error in a JSON response which will be encapsulated in a different object type.

The .NET server on which I tested this sample code always returns a collection of name/value pairs, decoded by NSJSONSerialization into an NSDictionary Objective-C object. This collection always contains a value named “d”. So I check for it and extract the value of “d”:

    NSDictionary *jsonResponseDict;
    if ([jsonResponseData isKindOfClass:[NSDictionary class]]) {
        jsonResponseDict = jsonResponseData;
    } else {
        // Error-handling code
    }
    jsonResponseData = [jsonResponseDict objectForKey:@"d"];
    if (jsonResponseData == nil) {
        // Server may have returned a response containing an error
        // The "ExceptionType" value is returned from my .NET server used in sample
        id jsonExceptioTypeData = [jsonResponseDict objectForKey:@"ExceptionType"];
        if (jsonExceptioTypeData != nil) {
            NSLog(@"%s ERROR : Server returned an exception", __func__);
            NSLog(@"%s ERROR : Server error details = %@", __func__, jsonResponseDict);
        }
    }

If the server does not return expected values you can debug by logging jsonResponseDict value.

Tip: By using “__func__” in NSLog you can easily track the class/method where logged message was written.

From now on you can iterate by examining the jsonResponseData object type and extracting data from it. JSON definition declares only two collection types: “A collection of name/value pairs”, which is mapped to NSDictionary, and “An ordered list of values” which is mapped to NSArray. You can iterate on items of these collections and extract basic types (NSString, NSNumber).

Get the device ID in iOS 6.0 and earlier

An updated version of this article for iOS7 is Get the device ID in iOS 7.0 and earlier

As you may know, starting from iOS 6.0 the method to retrieve the UDID ([[UIDevice currentDevice] uniqueIdentifier]) is deprecated.

This is due to privacy reasons. Allowing applications to detect the device will give ability to track the user without permission from him.

However is still possible to get a unique identifier for an AppStore publisher. A new method was introduced in iOS 6.0: [[[UIDevice currentDevice] identifierForVendor] UUIDString].

I’ve created the following code to get the identifier with compatibility in all versions of iOS. The code contains several tricks explained below.

// Get the device id on iOS6.0 and previous versions
- (NSString *) getDeviceId {
    NSString *deviceID;
    UIDevice *device = [UIDevice currentDevice];
    if ([UIDevice instancesRespondToSelector:@selector(identifierForVendor)]) {
        NSLog(@"%s DEBUG : iOS6.0 or later 'identifierForVendor' is available.", __func__);
        deviceID = [[device identifierForVendor] UUIDString];
    } else {
        NSLog(@"%s DEBUG : iOS5.x or older 'identifierForVendor' is not available.", __func__);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        deviceID = [device uniqueIdentifier];
#pragma clang diagnostic pop
    }
    return deviceID;
}

Three useful tricks contained in the code are explained below.

1) To check new iOS 6.0 method availability I use instancesRespondToSelector method, available for all objective-c classes.

2) To log debug messages with the current method I use __func__ variable, this allows fast identification of messages in debug console.

3) The use of deprecated method will give an annoying warning in xCode. To remove it I use the three pragma preprocessor directives that disables the warning only for the needed code line.

More informations on UDID APIs changes in iOS 6 can be found in this article UDID Replacement APIs in iOS 6