Lisp functions as UI callbacks in ECL/iOS

There's been a bit of recent interest in building "real" apps using ECL/iOS. While the `eclffi' sample code in the ecl-iphone-builder repo included examples of how to create/call Objective-C classes/methods from Lisp, it was missing the glue to go the other way, which was necessary to implement callbacks in Lisp. I've pushed an update to github which adds this functionality.

As an example I created a subclass of UIButton called UIButtonCB with one instance var called clickHandler, which can be set to a Lisp function pointer. The UIButtonCB class wires the UIControlEventTouchUpInside event to the specified handler function.

@implementation UIButtonCB

- (id) initWithFrame: (CGRect) aFrame
{
    [super initWithFrame: aFrame];
    clickHandler = Cnil;
    [self addTarget: self action: @selector(clicked:) forControlEvents: UIControlEventTouchUpInside];
    return self;
}

- (void) setClickHandler: (cl_object) fun
{
    register_cb(clickHandler, fun);
}

- (void) clicked: (id) sender
{
    if (Cnil != clickHandler) {
        cl_funcall(2, clickHandler, ecl_foreign_data_ref_elt(&sender,ECL_FFI_POINTER_VOID));
    }
}

- (void) dealloc
{
    remove_cb(clickHandler);
    clickHandler = Cnil;
    [super dealloc];
}

The call to register_cb() ensures that the callback function is protected from GC, and remove_cb() makes the function GC'able again. UIButtonCB can now be wrapped in Lisp with the following FFI glue:

(defun set-click-handler (view fun)
  (when fun
    (c-fficall ((view :pointer-void)
                ((make-callback-function fun) :object))
        :void
      "[#0 setClickHandler: #1];")))

(defun make-button (&rest init-view-args &key title title-color icon
                    on-click (enabled t) &allow-other-keys)
  (let ((view (make-view-instance "UIButtonCB" init-view-args)))
    (set-click-handler view on-click)
    (when icon (set-image view icon))
    (when title (set-title view title))
    (when title-color (set-title-color view title-color))
    (set-enabled view enabled)
    view))

Here's an example of the Lisp code to create a button with an on-click callback, which updates a label:

(let ((n 0)
      (label (make-label "" :frame '(10.0 50.0 300.0 35.0))))
  (add-subview (key-window) label)
  (add-subview (key-window)
               (make-button :frame '(140 230 40 20)
                            :title "click"
                            :background-color (color-argb 1 0.5 0.5 0.5)
                            :on-click (lambda (button)
                                        (declare (ignore button))
                                        (set-text label
                                                  (format nil
                                                          "~d click~:p"
                                                          (incf n)))))))

Obviously, this isn't a very sophisticated approach, but it's a start. It requires subclassing each UIControl or UIView to add the vars to store the Lisp callbacks. I'm sure there's ample room for abstracting or automating some of this boilerplate Objective C code.

ECL for iOS updated to work with SDK 4.2

I've pushed a new branch (centaur) of the ECL for iOS project to github, which contains a number of fixes to get ECL compiled under the latest 4.2 SDK from Apple. This release has been tested on iPhone and iPad devices running iOS 4.2 as well as the older 3.1.3. Please refer to the updated README for further details.

http://github.com/kriyative/ecl-iphone-builder/tree/centaur

Many thanks to Terje Norderhaug and Anthony Fairchild for their bug reports and feedback.