Camlkit provides OCaml bindings to the following Cocoa frameworks:
- Foundation (all platforms)
- AppKit (macOS and GNUStep)
- UIKit (iOS, macOS on Arm, and Mac Catalyst)
- WebKit (iOS and macOS)
- Vision (iOS and macOS)
- CoreImage (iOS and macOS)
- Photos (iOS and macOS)
- Using the classes and objects from these Cocoa frameworks, defining new Cocoa classes, and accessing the functionality of other Cocoa frameworks (via the low-level Objective-C runtime API bindings) can be done from the comfort of OCaml. No need to write wrappers manually in C or Objective-C.
- Cocoa object lifetimes can be managed by the OCaml GC.
- GUI object hierarchies can be created either programmatically or visually using Xcode's Interface Builder.
- An Xcode project is not required. A complete macOS or iOS application can be developed entirely in OCaml.
The fastest way to get started developing an iOS app is to use the
starter project template.
Follow the instructions there to set up your environment. Build the
library with make
and open the Xcode project with make open
.
A few sample programs are provided in the examples repository. To give you a taste of what a program in Camlkit looks like, here is a "Hello World" iOS application:
open Foundation
open Uikit
open Uikit_globals
open Runtime
module AppDelegate = struct
let show_hello _self _cmd _app _opts =
let screen_bounds =
UIScreen._class_ |> UIScreen.C.mainScreen |> UIScreen.bounds
in
let win =
UIWindow._class_ |> NSObject.C.alloc
|> UIWindow.initWithFrame screen_bounds
and vc = UIViewController._class_ |> NSObject.C.new_
and label = UILabel._class_ |> NSObject.C.new_
in
let view = vc |> UIViewController.view in
view |> UIView.setFrame screen_bounds;
view |> UIView.setBackgroundColor (UIColor._class_ |> UIColor.C.systemBackgroundColor);
label |> UILabel.setText (new_string "Hello from OCaml!");
label |> UILabel.setTextColor (UIColor._class_ |> UIColor.C.systemBlackColor);
label |> UILabel.setTextAlignment _UITextAlignmentCenter;
label |> UIView.setFrame screen_bounds;
view |> UIView.addSubview label;
win |> UIWindow.setRootViewController vc;
win |> UIWindow.makeKeyAndVisible;
true
let _class_ = Define._class_ "AppDelegate"
~superclass: UIResponder._class_
~methods:
[ Define._method_ show_hello
~cmd: (selector "application:didFinishLaunchingWithOptions:")
~args: Objc_t.[id; id]
~return: Objc_t.bool
]
end
let main () =
let _ = NSObject.C.new_ NSAutoreleasePool._class_
and argc = Array.length Sys.argv
and argv =
Sys.argv
|> Array.to_list
|> Objc.(CArray.of_list string)
|> Objc.CArray.start
in
_UIApplicationMain argc argv nil (new_string "AppDelegate") |> exit
;;
let () = main ()
A more substantial example is available in the starter project template.
If you are familiar with Cocoa development in Objective-C or Swift, transferring your knowledge to Camlkit should be straightforward. Let's introduce some constructs by comparing the equivalent Objective-C and OCaml code.
-
Creating basic objects
Objective-C:
[NSObject new]; [[NSString alloc] initWithUTF8String: "Hello"]; [NSString stringWithUTF8String: "Hello"];
OCaml (showing multiple equivalent constructs):
NSObject.C.new_ NSObject._class_ _new_ NSObject._class_ NSString._class_ |> NSObject.C.alloc |> NSString.initWithUTF8String "Hello" alloc NSString._class_ |> NSString.initWithUTF8String "Hello" NSString._class_ |> NSString.C.stringWithUTF8String "Hello" new_string "Hello"
To print a NSString in utop:
myNSStr |> NSString._UTF8String |> print_string
-
Defining a new Cocoa class
Objective-C:
@interface MyClass : NSObject { id myVar; } - (void)myMethodWithArg1:(id)arg1 arg2:(id)arg2; @end @implementation MyClass - (void)myMethodWithArg1:(id)arg1 arg2:(id)arg2 { // method implementation } @end
OCaml:
Define._class_ "MyClass" ~ivars: [ Define.ivar "myVar" Objc_t.id ] ~methods: [ Define._method_ ~cmd: (selector "myMethodWithArg1:arg2:") ~args: Objc_t.[id; id] ~return: Objc_t.void (fun self cmd arg1 arg2 -> (* method implementation *)) ]
NOTE: The
~args
parameter includes only the explicit argument types. The number of arguments is the same as the number of:
in the selector. If your method does not accept arguments, the~args
parameter looks like this:Objc_t.[]
-
Memory management
A newly allocated object has a reference count of 1. When you want to keep an object around, you send it the
retain
message. This increments the reference count. When you no longer need an object, you send it therelease
message. This decrements the reference count. When the reference count reaches 0, the object is sent thedealloc
message and its memory is reclaimed. See alsoautorelease
and theNSAutoreleasePool
class.Since OCaml has a garbage collector, you can leverage it to help manage the lifetimes of Cocoa objects. To this effect, we provide the
gc_autorelease
function, which ensures the object will be sent therelease
message when the OCaml reference to it is garbage collected. -
Using objects from frameworks when bindings are not available
The Objective-C runtime provides functions which enable you to get a hold of an arbitrary class by name and send it an arbitrary message, eg:
let a_class = Objc.get_class "AClassThatINeed" in let an_instance = alloc a_class |> init in an_instance |> msg_send (selector "anArbitrarySelector") ~args: Objc_t.[] ~return: Objc_t.void
-
Sending a message to the superclass
Eg,
viewDidLoad
:Objective-C:
- (void)viewDidLoad { [super viewDidLoad] ... }
OCaml:
let viewDidLoad self cmd = self |> msg_super cmd ~args: Objc_t.[] ~return: Objc_t.void; ...
NOTE: Method implementations in OCaml always include two implicit parameters: self - a pointer to the object; cmd - the current selector
At this time, the documentation of the project is lacking. The framework bindings follow a regular naming pattern, so if you know the Objective-C method you want to call, figuring the name of the OCaml function should be easy. Read the Apple documentation for the classes and methods of interest. All books on iOS and macOS development in Objective-C are directly applicable.
Some usefull sources you may wish to examine include:
- Objective-C runtime bindings and basic functionality
- Representation of Objective-C types in OCaml
- Usage examples
- The Ctypes documentation
The project is in active development and is still experimental. It can be considered at the alpha stage. If you are an early adopter, keep in mind that the API is subject to change.
For iOS and Mac Catalyst development you will need to set up a cross-toolchain from opam-cross-ios.