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.
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?
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…
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.
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]
Theres a bunch of scattered explanations from me, @Ulrich Bogun @jim mckay @Will Shank throughout the forum that will be useful but I dont think theres a centralized post. Ive been wanting to put together a core comprehensive guide to encourage people to contribute to iOSKit but havent had the time. Maybe it will happen eventually…
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.
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.