Generate Random UUIDs on iOS?

We are investigating porting one of our apps that is currently shipping for Mac and Windows to iOS, and hoping that Xojo’s iOS support is finally mature enough for the port. Today I started throwing some of the key classes from the desktop app into a new iOS project to see what it barfed up that needed to be changed. Much of what I’ve found is easy enough to convert (basically changing things to the new framework), but I’ve run into one item that I can’t seem to find any pre-built solutions for:

I need to generate UUIDs for any new record I want to add to the local database. On the desktop I’m using the MonkeyBread plugins for this, which works great. Unfortunately, I can’t find any information on MonkeyBread plugins for the iOS target. I’ve also looked at iOSLib (which looks AWESOME!) and while it can retrieve the GUID of the current device, I can’t find any way to create new UUIDs.

Am I going to just need to use declares for this?

Hot diggity! That’s great, thanks!

So, in the interest of teaching this man to fish rather than just feeding me for a day, I’ve been digging around on Apple’s documentation for the UUIDString and UUID functions, so that I can better understand why you’ve done things in this particular way, but I can’t find any reference for them in Foundation.

How can I learn to fish for things that I’ll need to use as declares for myself?

Thanks!

Could it be

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

Hi @Michel Bujardet - I thought at first that it was the NSUUID class, but that class defines a property called UUIDString, not a method or function. I’m no declares wizard by any stretch, but it looks to me like the declares provided by @jean-paul devulder are calling a function called UUIDString that receives a pointer and returns a CFStringRef.

Is that how you would typically get access to a property in an external library?

Basically it comes down to the fact that in Obj-C all properties can be accessed with their setter and getter functions instead of using the property names. This means that properties can be accessed in Xojo through declares instead of just functions which is important as new classes from Apple are increasingly using properties instead of methods for everything. All properties are accessed with just the property name as the selector unless they have a special getter (generally this is only for boolean properties and the name of the getter will be provided in the docs).

To explain the declares provided by @jean-paul devulder:
NSClassFromString is a function in Foundation that allows you to retrieve a pointer to the actual class by name.
UUID is a class function (notice the “+” in the docs, this means it’s a class function and not an insane function which is denoted by “-”) which returns a new NSUUID object. Since it is a class function, it must be passed a class pointer to the class as the first (and in this case only) parameter. You get this class pointer with the previously mentioned NSClassFromString function, and then pass it to the UUID function to get your new instance. Then you want to access the UUIDString property so you write a declare for the getter of the property (UUIDString). All instance functions must have the instance pointer passed as the first parameter (this is the “me” we are used to in Xojo and do not see in Obj-C unless we are using declares in Xojo) which in this case is the instance returned by the UUID function. After you have all of the declares written, then you just have to call them in the proper order or store all intermediate pointers in their own variables, and you will get the result.

In case you don’t understand where the CFStringRef comes from, it is a special type that automagically converts between string/text in Xojo and NSString/CFStringRef as found in Obj-C.

If you have more questions, please ask and I will try to answer them.

@Jason King - that is an awesome explanation. Thank you!

@Paul Lefebvre - any chance of updating the documentation for declares to include the details provided above by Jason? Would save a LOT of people some headaches…

If you actually want this to happen I can put together a much better and more clear explanation, I just wrote that quickly yesterday.

Hi Jason,
I am trying to get my head around declares. I took a look at the Xojo documentation and it lacks the kind of explanation you have displayed here. While it is possible to pull bits out of your iOSKit and other xojo code one can find on the web, this does not help me to understand how to find the Objective C bits I might need, and then convert them to useful functions in Xojo, especially when it involves declaring functions to concatenate pointers in order to get something working. If you have written more on this subject, could you please give me a pointer to where I can find it?
'Thanks in advance.

From my M_Text module:

Protected Function GenerateUUID(useSystemCalls As Boolean = True) as Text
  // Tries to use declares to let the native system functions handle this.
  // Otherwise, falls back to manual creation.
  
  dim result as text
  
  dim useDeclares as boolean = useSystemCalls
  
  if useDeclares then
    
    try
      
      #if TargetMacOS or TargetiOS
        
        soft declare function NSClassFromString lib "Foundation" ( clsName as CFStringRef ) as ptr
        soft declare function UUID lib "Foundation" selector "UUID" ( clsRef as ptr ) as ptr
        soft declare function UUIDString lib "Foundation" selector "UUIDString" ( obj_id as ptr ) as CFStringRef
        
        dim classPtr as Ptr = NSClassFromString( "NSUUID" ) 
        if classPtr = nil then
          useDeclares = false
        else
          dim NSUUID as ptr = UUID( classPtr )
          
          result = UUIDString( NSUUID )
        end if
        
      #elseif TargetWindows
        
        const kLibName = "rpcrt4"
        
        if not System.IsFunctionAvailable( "UuidCreate", kLibName ) then
          useDeclares = false
        elseif not System.IsFunctionAvailable( "UuidToStringA", kLibName ) then
          useDeclares = false
        else
          soft declare function UUIDCreate lib kLibName alias "UuidCreate" ( ByRef uuid as WindowsUUID ) as Integer
          soft declare function UUIDToString lib kLibName alias "UuidToStringA" ( ByRef inUUID as WindowsUUID, ByRef outString as CString ) As Integer
          
          dim uuid as WindowsUUID
          if UUIDCreate( uuid ) <> 0 then
            useDeclares = false
          else
            dim out as CString
            if UUIDToString( uuid, out ) <> 0 then
              useDeclares = false
            else
              dim outString as string = out
              outString = outString.DefineEncoding( Encodings.UTF8 )
              result = outString.ToText
              result = result.Uppercase
            end if
            
          end if
        end if
        
      #elseif TargetLinux
        
        const kLibName = "uuid"
        
        if not System.IsFunctionAvailable( "uuid_generate", kLibName ) then
          useDeclares = false
        elseif not System.IsFunctionAvailable( "uuid_unparse_upper", kLibName ) then
          useDeclares = false
        else
          soft declare sub UUIDGenerate lib kLibName alias "uuid_generate" ( ByRef uuid as LinuxUUID )
          soft declare sub UUIDUnparse lib kLibName alias "uuid_unparse_upper" ( ByRef uuid As LinuxUUID, ByRef out As LinuxUUIDString )
          
          dim uuid as LinuxUUID
          UUIDGenerate( uuid )
          
          dim out as LinuxUUIDString
          UUIDUnparse( uuid, out )
          
          result = out.Data
          result = result.DefineEncoding( Encodings.UTF8 )
        end if
        
      #else
        useDeclares = false
      #endif
      
    catch err as RuntimeException
      useDeclares = false
      if err IsA EndException or err IsA ThreadEndException then
        raise err
      end if
    end try
    
  end if
  
  if not useDeclares then
    //
    // Fallback to manual creation
    //
    // From http://www.cryptosys.net/pki/uuid-rfc4122.html
    //
    // Generate 16 random bytes (=128 bits)
    // Adjust certain bits according to RFC 4122 section 4.4 as follows:
    // set the four most significant bits of the 7th byte to 0100'B, so the high nibble is '4'
    // set the two most significant bits of the 9th byte to 10'B, so the high nibble will be one of '8', '9', 'A', or 'B'.
    // Convert the adjusted bytes to 32 hexadecimal digits
    // Add four hyphen '-' characters to obtain blocks of 8, 4, 4, 4 and 12 hex digits
    // Output the resulting 36-character string "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
    //
    
    #pragma BackgroundTasks False
    #pragma BoundsChecking False
    #pragma NilObjectChecking False
    
    System.DebugLog CurrentMethodName + ": Generating manually"
    'MsgBox "Manual!"
    
    dim randomBytes as Xojo.Core.MemoryBlock = Xojo.Crypto.GenerateRandomBytes( 16 )
    randomBytes.LittleEndian = false
    dim p as Ptr = randomBytes.Data
    
    //
    // Adjust seventh byte
    //
    dim value as byte = p.Byte( 6 )
    value = value and CType( &b00001111, Byte ) // Turn off the first four bits
    value = value or CType( &b01000000, Byte ) // Turn on the second bit
    p.Byte(6) = value
    
    //
    // Adjust ninth byte
    //
    value = p.Byte( 8 )
    value = value and CType( &b00111111, Byte ) // Turn off the first two bits
    value = value or CType( &b10000000, Byte ) // Turn on the first bit
    p.Byte( 8 ) = value
    
    dim unformatted as text = EncodeHex( randomBytes )
    result = unformatted.Left( 8 ) + "-" + unformatted.Mid( 8, 4 ) + "-" + unformatted.Mid( 12, 4 ) + "-" + unformatted.Mid( 16, 4 ) + _
    "-" + unformatted.Right( 12 )
    
    '#if DebugBuild
    'if result.ReplaceAll( "-", "" ) <> unformatted then
    'break
    'end if
    '#endif
  end if
  
  return result
  
End Function

[quote=425472:@Philip Cumpston]Hi Jason,
I am trying to get my head around declares. I took a look at the Xojo documentation and it lacks the kind of explanation you have displayed here. While it is possible to pull bits out of your iOSKit and other xojo code one can find on the web, this does not help me to understand how to find the Objective C bits I might need, and then convert them to useful functions in Xojo, especially when it involves declaring functions to concatenate pointers in order to get something working. If you have written more on this subject, could you please give me a pointer to where I can find it?
'Thanks in advance.[/quote]
There’s a bunch of scattered explanations from me, @Ulrich Bogun @jim mckay @Will Shank throughout the forum that will be useful but I don’t think there’s a centralized post. I’ve been wanting to put together a core comprehensive guide to encourage people to contribute to iOSKit but haven’t had the time. Maybe it will happen eventually…

Is anyone running a Xojo Wiki where topics like this could be centralized?

Common questions such as this suggest to me it should be a framework feature. Such as <https://xojo.com/issue/40358>, though I suspect this case will get closed now because I suggested a new framework class, and the new framework is dead now.

This page was added some time ago, which is a starting point at least.

https://documentation.xojo.com/topics/declares/calling_native_ios_apis.html

I might try and send you some more advanced details like using delegates and similar in the next couple weeks @Paul Lefebvre because that page really only has the basics.