23

I need to generate some int value that would never repeat (at least theoretically). I know there is arc4random() fnc but I'm not sure how to use it with some current date or smth :(

tshepang
  • 12,111
  • 21
  • 91
  • 136
Centurion
  • 14,106
  • 31
  • 105
  • 197
  • 3
    What would be wrong with `return counter++`? Will generate the full range of ints before it has to repeat. – Dunes Aug 10 '11 at 19:11
  • 2
    How often will you be generating this int value that would theoretically never repeat? Once the first time app is launched? Once per launch, or multiple times each time the application is ran. – Joe Aug 10 '11 at 19:12
  • I need to generate unique ID for each played game. After game over, I'm updating highscore array with 10 top score objects (player, score, mode and gameID). There is Highscore UIViewController that reads that data (not gets as argument) from NSUserDefaults. I need to highlight current user gameplay. So for the sake of code clearity and simplicity it would be better to have such ID. – Centurion Aug 10 '11 at 19:36

9 Answers9

54

This returns a unique key very similar to UUID generated in MySQL.

+ (NSString *)uuid
{
    CFUUIDRef uuidRef = CFUUIDCreate(NULL);
    CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
    CFRelease(uuidRef);
    return [(NSString *)uuidStringRef autorelease];
}

ARC version:

+ (NSString *)uuid
{
    CFUUIDRef uuidRef = CFUUIDCreate(NULL);
    CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
    CFRelease(uuidRef);
    return (__bridge_transfer NSString *)uuidStringRef;
}
Airsource Ltd
  • 32,379
  • 13
  • 71
  • 75
Nandakumar R
  • 1,404
  • 11
  • 17
  • 1
    Is this generated key guaranteed to be unique through the lifetime of the computer? Through quits and openings of the app, restarts and shutdowns? (basically to the end of time? :) What I mean is can I use this function as many times as I want and keep the returned value as long as I want on my app (and still have it unique of course)? – Alex Apr 24 '13 at 15:47
  • @Alex Has there been any update one way or the other on the guaranteed uniqueness of these generated UUIDs? – Dan F Aug 15 '13 at 15:37
  • @DanF Nope, not that I know of. The only thing I know is that this method seems to be working for me (generating completely unique IDs, that is) – Alex Aug 16 '13 at 19:35
  • 1
    Should if be `__bridge_transfer` instead of `__bridge`? – derpoliuk Sep 30 '13 at 08:36
  • Hi, I want to use this method to let my app automatically create unique product ID's, but I think the ID that is created by this method is quite long. Is there a way to limit the amount of characters in the uuid? Thanks! – Joris416 Mar 21 '14 at 10:37
  • Nothing is guaranteed to be unique without either a database or a sequence, so probabilistic approaches are used instead. UUIDs were created with this in mind. As you can see [here](https://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates) random UUIDs have 122 random bits, which is a lot. – André Fratelli Aug 19 '15 at 08:00
  • Quoting the same link: "the annual risk of a given person being hit by a meteorite is estimated to be one chance in 17 billion, which means the probability is about 0.00000000006 (6 × 10−11), equivalent to the odds of creating a few tens of trillions of UUIDs in a year and having one duplicate. In other words, only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%." – André Fratelli Aug 19 '15 at 08:01
  • @AndréFratelli Would it not be possible to just store what ever UDIDs this creates in an ```NSArray```, then if it creates a duplicate, you can just keep calling the method until it creates a new unique ID string? – Supertecnoboff Mar 20 '16 at 11:24
  • @Supertecnoboff it would, but if you are to use such storage methods then you do not need UUIDs. UUIDs use 16 bytes (128 bits, 122 of which are random) - that's a lot. UUIDs are meant for generating random values with really (_really_) low probability of collision. If you implement a collision detection mechanisms then they lose their purpose and you are better off with much smaller values. – André Fratelli Mar 21 '16 at 03:59
  • @AndréFratelli But really really low does not equal nothing. Its still possible. I don't see why a simple array or database of some sort can't be used to store the values you already have. Then you know for a fact that there will never be any collisions. – Supertecnoboff Mar 27 '16 at 10:09
  • 1
    @Supertecnoboff I didn't say that. What I said is that if you are going to use a storage mechanism to keep track of generated identifiers, then don't use UUIDs, as you can use something way smaller (thus saving memory), like a simple integer. Consider how many bytes you actually need, as it probably won't be 16. UUIDs are useful when you don't have a centralized means of generating an identifier, such as a server, but still need to avoid collisions. They won't give you 100% certainty, but 1/17 billion is usually good enough. – André Fratelli Mar 27 '16 at 16:23
41

A simple version to generate UUID (iOS 6 or later).

Objective-C:

NSString *UUID = [[NSUUID UUID] UUIDString];

Swift 3+:

let uuid = UUID().uuidString

It will generate something like 68753A44-4D6F-1226-9C60-0050E4C00067, which is unique every time you call this function, even across multiple devices and locations.

Reference: https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSUUID_Class/Reference/Reference.html

MaikonFarias
  • 696
  • 5
  • 15
3

If you are using CoreData to save the played games, NSManagedObject's objectID should serve your purpose without any extra effort.

Akshay
  • 5,747
  • 3
  • 23
  • 35
2

You can create a category of UIApplication , UIDevice or as you prefere like this (ARC example)

@interface UIApplication (utilities)
- (NSString*)getUUID;
@end

@implementation UIApplication (utilities)

- (NSString*)getUUID {

    NSUserDefaults *standardUserDefault = [NSUserDefaults standardUserDefaults];

    static NSString *uuid = nil;

    // try to get the NSUserDefault identifier if exist
    if (uuid == nil) {

        uuid = [standardUserDefault objectForKey:@"UniversalUniqueIdentifier"];
    }

    // if there is not NSUserDefault identifier generate one and store it
    if (uuid == nil) {

        uuid = UUID ();
        [standardUserDefault setObject:uuid forKey:@"UniversalUniqueIdentifier"];
        [standardUserDefault synchronize];
    }

    return uuid;
}

@end

UUID () is this function

NSString* UUID () {

    CFUUIDRef uuidRef = CFUUIDCreate(NULL);
    CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
    CFRelease(uuidRef);
    return (__bridge NSString *)uuidStringRef;
}

this generate an unique identifier stored into the NSUserDefault to be reused whenever the application need it - This identifier will unique related to the application installs not to the device, but can be used for example to take trace about the number devices subscribed the APN service etc...

After that you can use it in this way:

    NSString *uuid = [[UIApplication sharedApplication] getUUID];
Manu
  • 788
  • 5
  • 10
2

A simple timestamp (milliseconds * 10) should do the trick:

self.uid = [NSNumber numberWithInteger:[NSDate timeIntervalSinceReferenceDate] * 10000];
bob
  • 7,539
  • 2
  • 46
  • 42
2

You can use the time in milliseconds or a more advanced way GUID.

Justin
  • 2,960
  • 2
  • 34
  • 48
1

If you have a NSDictionary, you could generate a progressive id from the last item:

NSInteger maxKey = -1;
for(NSString *key in [YOUR_DICTIONARY allKeys])
{
    NSInteger intKey = [key integerValue];
    if(intKey > maxKey)
    {
        maxKey = intKey;
    }
}
NSString *newKey = [NSString stringWithFormat:@"%d", maxKey + 1];
Enrique
  • 59
  • 3
1

You did not say it must be random. So why not start with some number, and then just add by 1 to the last number you generated.

This method should give you at lest 4 billion unique numbers to start with:

-(NSInteger)nextIdentifies;
{
    static NSString* lastID = @"lastID";
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    NSInteger identifier = [defaults integerForKey:lastID] + 1;
    [defaults setInteger:identifier forKey:lastID];
    [defaults synchronize];
    return identifier;
}
PeyloW
  • 36,742
  • 12
  • 80
  • 99
0

You have to be careful, especially if you use the increment by 1 routines, that if your app is deleted and reloaded on the iDevice, that you won't have your saved default number anymore. It will start over from the beginning. If you're storing user's scores, you might want to save their highest number too. Better to check the time routines for seconds (or milliseconds) after a certain date. The GUID mentioned above is good too, if you need that kind of uniqueness.

Owen Hartnett
  • 5,925
  • 2
  • 19
  • 35