Thursday, September 30, 2010

Creating a GUID or UUID in Objective-C

I recently needed to generate unique IDs to be sent from an iPhone app to a web service. Rather than come up with my own scheme, I looked around for a way to generate a UUID (or GUID if you're from the Windows world) on iOS.

Turns out that Core Foundation includes a set of functions for working with UUIDs naturally called CFUUID. I needed a string representation of the UUID so I quickly rolled this method to generate it:
// return a new autoreleased UUID string
- (NSString *)generateUuidString
{
  // create a new UUID which you own
  CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);

  // create a new CFStringRef (toll-free bridged to NSString)
  // that you own
  NSString *uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid);
  
  // transfer ownership of the string
  // to the autorelease pool
  [uuidString autorelease];

  // release the UUID
  CFRelease(uuid);

  return uuidString;
}
Not too hard to do if you're familiar with Core Foundation conventions.

5 comments:

Eimantas said...

there's also Cocoa way:
[[NSProcessInfo processInfo] globallyUniqueString];

Don McCaughey said...

Cool, I wasn't aware of that method. I'm not sure it would necessarily work correctly for my use case though, but the documentation isn't clear on that.

A GUID / UUID is generally based on the network adapter's MAC address, which (barring devices that are impersonating another network interface) is supposed to be unique to each network interface, thus unique across the internet.

Apple's docs state that globallyUniqueString is based on the host name, process ID and time stamp. I'm not sure how host name is assigned to iOS devices, but process ID and time stamps can easily be collide. If host name is derived from the device's IP address, then it also is likely to collide for devices on private subnets.

This is definitely works for devices on the same subnet, and might be good enough across the internet if the rare collision of IDs isn't important to you (or if iOS devices assign host names in some unique way -- dunno about that).

Dragon said...

Thank you so much, sir. Worked perfectly for me. This is a great article!

Don McCaughey said...

Great, glad to hear it!

Basil.Bourque said...

Your example code does not seem to work in Xcode 4.4.1, apparently because of ARC.

Error:
Cast of C pointer type 'CFStringRef' (aka 'const struct _CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast

Two fix-its offered. I chose this one:
Use CFBridgingRelease call to transfer ownership of a +1 'CFStringRef' (aka 'const struct _CFString *' into ARC

So my revision to your code:

=================
// Return a new UUID string. Built for ARC in iOS 4 and later.
- (NSString *)generateUuidString
{
// create a new UUID which you own
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);

// create a new CFStringRef.
NSString *uuidString = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid));

// release the UUID
CFRelease(uuid);

return uuidString;
}
=================

This revision seems to be working. But is memory managed correctly? I'm new to C & Objective-C.

--Basil Bourque