iOS: Identifying Your Users Devices – A Recipe How to Import iOS Classes

  

You might want to identify your users devices, for example to bind that information to some licensing mechanism. In earlier iOS days, you would have used UIDevice.uniqueIdentifier for that. That ID is unique for every iOS device out in the wild (well maybe except jail broken ones). Due to privacy concerns usage of that ID has been deprecated with iOS 5.0, and since May 1st, 2013 Apple imposes a no-usage policy. In other words if you use that id in your apps, apple will just reject your app from App Store  approval.
The new way to identify your users devices is to use UIDevice.identifierForVendor. That is still a unique identifier, but it is also unique per vendor. In other words your Apple Developer Account gets tied into that ID’s calculation, so that your own apps can identify all your users devices, even between apps. Other vendors/developer get different ids for the same device though.
Ok, so far so easy. If you want to use identifierForVendor from Delphi XE4 though, you will find, that it has forgotten to be imported. UIDevice as declared in iOSApi.UIKit.pas just doesn’t know about that function.
This made me try to import it on my own, and that worked pretty easy for me. There was an additional obstacle (the required class NSUUID does also not exist in XE4), but that was also easy to fix.
I started with a DX.Apple.Utils.pas helper unit, which will grow over time and for now does exactly two things:

Provide a Delphi NSLog2 function, which directly maps to Apple’s NSLog function, which will log to the console.
Provide a VendorIdentifier function which does what its name says

The most current version can be checked out anonymously from a SubVersion repository hosted at Google.
Below is the full source, which I will probably put into a public SVN –  eventually. That source basically contains the recipe how to import (even partial) classes from iOS into Delphi.

unit DX.Apple.Utils;

interface

/// <summary>
/// DX.Apple.Utils is a helper library to get easier acces to certain Apple Mac/iOS functions.
/// </summary>
/// <description>
/// This library also imports certain classes or parts of them which have been "forgotten" by EMBT
/// The code is intended to be used with Delphi XE4 only. Other versions may or may not work.
/// There is a dependency to AppleUtils.pas, which ships with XE4 and can usually be founc here:
/// C:\Users\Public\Documents\RAD Studio\11.0\Samples\Delphi\RTL\CrossPlatform Utils
/// Make sure use the most recent version – that samples folder above
/// is connected to an SVN repository of Embarcadero.
/// </description>
/// <remarks>
/// <para>
/// DX.Apple.Utils is Copyright 2013 Olaf Monien / Developer Experts, LLC.
/// </para>
/// <para>
/// All code comes "as is", without any guaranties granted. This code may contain bugs and my not work as advertised.
/// </para>
/// <para>
/// RESTClient is licensed under Mozilla Public License 2.0
/// In other words you are free to use this library in your projects, just leave this header intact.
/// </para>
/// </remarks>

// Logs to the console
// Named NSLog2 to avoid name clash
// iOS device: Xcode – Organizer -> Device – Console
// Mac / iOS Simulator: Mac – Console
procedure NSLog2(const AMessage: string);

// Retrieves the vendor specific device ID – DO NOT USE UIDevice.uniqueIdentifier – this would lead to AppStore rejection since May 1st, 2013!
function VendorIdentifier: string;

implementation

uses
System.SysUtils,
Apple.Utils,
{$IFDEF IOS}
Macapi.ObjectiveC,
iOSApi.Foundation,
iOSApi.UIKit,
iOSApi.QuartzCore,
iOSApi.CocoaTypes
{$ELSE}
{$IFDEF MACOS}
Macapi.ObjectiveC,
Macapi.Foundation
{$ENDIF MACOS}
{$ENDIF IOS}
;

{$IFDEF IOS}

// Hack to import forgotten classes/functions and properties
// Be careful – classes with same name may already exist in iOSApi!!
type

// **** NSUUID

NSUUIDClass = interface(NSObjectClass)
[‘{D9518F5E-DDBC-4702-A555-411D32B85340}’]
end;

// We just need the UUIDString here
NSUUID = interface(NSObject)
[‘{4C137FF5-E854-461F-B77E-8CD357FD4E9C}’]
function UUIDString: NSString; cdecl;
end;

TNSUUIDDX = class(TOCGenericImport<NSUUIDClass, NSUUID>)
end;

// **** UIDevice

UIDeviceClass = interface(NSObjectClass)
[‘{D5105207-FBA7-4F55-BC7B-1ADACE347ECA}’]
{ class } function currentDevice: Pointer; cdecl;
end;

UIDevice = interface(NSObject)
[‘{481E431F-2C02-4F2D-86C5-7728480ECF48}’]
function identifierForVendor: NSUUID; cdecl;
end;

TUIDeviceDX = class(TOCGenericImport<UIDeviceClass, UIDevice>)
end;

{$ENDIF}

procedure NSLog2(const AMessage: string);
var
LMessage: NSString;
begin
LMessage := NSSTR(FormatDateTime(‘hh:nn:ss,zzz’, now) + ‘ – ‘ + AMessage);
iOSApi.Foundation.NSLog(PtrForObject(LMessage));
end;

{$IFDEF IOS}
function currentDevice: DX.Apple.Utils.UIDevice;
begin
result := TUIDeviceDX.Wrap(TUIDeviceDX.OCClass.currentDevice);
end;

function VendorIdentifier: string;
var
LDevice: DX.Apple.Utils.UIDevice;
begin
LDevice := currentDevice;
result := NSStringToString(LDevice.identifierForVendor.UUIDString);
end;
{$ENDIF}

end.

Tweet This!

Share this on del.icio.us

Digg this!

Share this on Reddit

Get Shareaholic

Comments are closed.