Calling native iOS APIs
You can call into Cocoa Touch APIs to use methods and properties that are not built into the framework by using the Declare command. To create a Declare statement you first need to track down the API you want to use using Apple's documentation: Apple Developer Documentation. Most of the time you will reference the Foundation and UIKit libraries, but there are many other libraries as well. Xojo Declares use the Objective-C names so be sure to refer to those in the documentation rather than the Swift naming.
When you call Cocoa methods you supply the method name using the Selector part of the Declare command. The selector name has to end in a ":" if there are any parameters that are passed, an oddity of how Objective-C works. Unlike with Xojo methods, the case of the name also has to match exactly. For the Xojo Declare, the first parameter is always required and must be a reference to the class containing the method you are calling.
Give focus to a text field
To start with a simple example, consider that you may want to set focus to a text control such as an MobileTextField. Xojo does not provide a method for this. Looking at the Summary on the iOSTextField page you'll see that the actual UIKit control is called "UITextField". Click the link to open the Apple doc page. On the doc page you'll find information about calling becomeFirstResponder to show the keyboard, which is what happens when a text field gets focus. When you click on becomeFirstResponder you'll get to its doc page with this declaration:
The above is Objective-C code that indicates that this is a function that returns a Boolean value. You should also note on the page that this is part of the UIKit library. With this information you can now create a Xojo Declare command to call it:
Declare Function becomeFirstResponder Lib "UIKit" Selector "becomeFirstResponder" (controlHandle As Ptr) As Boolean
The important thing to note here is that you have to always have a parameter (the first parameter) that is a reference to an instance of the class (or control in this case).
You can call this method just as you would any other Xojo method, but remember you have to pass it a reference to the control. Assuming you have a control called TextField1 on the view you can call the method like this:
The Call command is used to ignore the return value, which is not relevant in this situation.
If you want to use this function often you can wrap it in an Extension method so that it is available everywhere. To do this, create a module and add a global method to it with this declaration:
SetFocus(Extends c As MobileTextField) As Boolean
To the method, add the Declare code as follows:
Declare Function becomeFirstResponder Lib "UIKit" Selector "becomeFirstResponder" (controlHandle As Ptr) As Boolean Return becomeFirstResponder(c.Handle)
Now you can call the method like this:
Use the Clipboard
As a more advanced example, this is how you can add some text to the system clipboard. On iOS the clipboard is actually called the pasteboard and is managed by the UIPasteboard class. Looking a Apple's docs for UIPasteboard you'll see two methods that are useful. First is the generalPasteboard shared method that gets the instance of the system pasteboard. The other method is setValue which is used to put content, such as text, onto the pasteboard.
To get started, in Xojo create a public method on the App object and call it AddTextToPasteboard with this declaration:
AddTextToPasteboard(value As String)
The first bit of code you need has to get a reference to the UIPasteboard class and then call the generalPasteboard method to get an instance of the system UIPasteboard.
In order to do this, you'll need to use a common utility function, NSClassFromString, that gets a reference to an iOS class. So this is the first line of code:
Declare Function NSClassFromString Lib "Foundation" (className As CFStringRef) As Ptr
You can now use this function to get a reference to the UIPasteboard class:
Var uiPasteboardClass As Ptr = NSClassFromString("UIPasteboard")
Now you can call the generalPasteboard method by passing in the reference to the UIPasteboard class (UIPasteboard is part of the UIKit library):
Declare Function sharedApplication Lib "UIKit" Selector "sharedApplication" (classRef As Ptr) As Ptr Var sharedApp As Ptr = sharedApplication(uiAppClass)
For reference, here is the declaration for the setValue method from the Apple docs:
- (void)setValue:(id)value forPasteboardType:(NSString *)pasteboardType;
This declaration tells you the method is a Sub (the void at the beginning indicates it does not return a value) and that it takes two parameters: a value and the pasteboardType (as Text). Since this example is for putting text in the pasteboard the setValue method can be mapped to a Xojo Declare command like this:
Declare Sub setValue Lib "UIKit" Selector "setValue:forPasteboardType:" (pasteboardReference As Ptr, _ value As CFStringRef, _ pasteboardType As CFStringRef)
Remember, the first parameter is always a reference to the class, which is the pasteboard in this case. Another thing to note is that the name for the selector is not just the method name of "setValue". Objective-C uses names that include the parameter so the selector is actually called "setValue:forPasteboardType" (the proper case is important). This is the full name you'll see on the Apple doc page for the method. Lastly, when you see NSString as an Objective-C type you can instead substitute the Xojo type of CFStringRef which will automatically convert a Text value to the correct type needed for the Declare.
With this Declare in place you can now call the method to put the text onto the pasteboard:
setValue(generalPasteboard(NSClassFromString("UIPasteboard")), value, "public.text")
The pasteboardType takes a UTI which for text is "public.text".
Combining all this code together results in this method:
Public Sub AddTextToPasteboard(value As String) // Get a reference to the system pasteboard Declare Function NSClassFromString Lib "Foundation" (className As CFStringRef) As Ptr Var uiPasteboardClass As Ptr = NSClassFromString("UIPasteboard") Declare Function generalPasteboard Lib "UIKit" Selector "generalPasteboard" (classRef As Ptr) As Ptr Var pasteboard As Ptr = generalPasteboard(uiPasteboardClass) // Put the value onto the pasteboard Declare Sub setValue Lib "UIKit" Selector "setValue:forPasteboardType:" (pasteboardReference As Ptr, _ value As CFStringRef, _ pasteboardType As CFStringRef) setValue(generalPasteboard(NSClassFromString("UIPasteboard")), value, "public.text") End Sub
You can now use your new method to put text into the pasteboard with this code:
For reference, these two utility functions are commonly used with Declares. You've already seen how to use NSClassFromString. The alloc method can be used to create a new instance of a class when there is otherwise no factory method that returns an instance. It is equivalent to using "New" with a Xojo class.
// Gets a reference to an iOS class Declare Function NSClassFromString Lib "Foundation" (classname As CFStringRef) As Ptr // Usage: Var NSClass As Ptr = NSClassFromString("NSClassName") // Gets an instance of an iOS class Declare Function alloc Lib "Foundation" Selector "alloc" (classRef As Ptr) As Ptr // Usage: Var instance As Ptr = alloc(NSClass)
When dealing with properties in the Apple frameworks you have to alter the property name to include "set" at the beginning (for the setter). Apparently this is because the computed property is really a method behind the scenes. So for a property called "borderStyle", the actual name would be "setBorderStyle" (note the slight change to the case as well because it matters).
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).