Back to Blog Home
← all posts

How to use Swift or Objective C Delegates with NativeScript

May 22, 2023 — by Nandee Tjihero

This blog post will show you how to create and use iOS delegates in NativeScript.

From What is a delegate in iOS?:

A delegate is any object that should be notified when something interesting has happened. What that "something interesting" means depends on the context: for example, a table view's delegate gets notified when the user taps on a row, whereas a navigation controller's delegate gets notified when the user moves between view controllers.

ColorPicker Example

Let's take a look at UIColorPickerViewController which provides a way to present color choices a user can select. When the controller detects the user color selection, it needs a way to inform your app about the color picked. The way it does that is through a delegate. Apple provides different protocols for different purposes. For the UIColorPickerViewController, it provides the UIColorPickerViewControllerDelegate protocol.

Using this delegate, let's update the backgroundColor of a StackLayout in NativeScript. To receive the selected color from the picker, we conform to the UIColorPickerViewControllerDelegate protocol.

We can always approach controller/delegate setup in 2 stages.

Note: Be sure to understand the brief but important best practice usage of delegates in NativeScript: Delegates, delegates, DELEGATES!!.

Stage 1: Create a Delegate Implementation

Create a delegate implementation class, let's call it ColorPickerDelegateImpl, that extends NSObject and conforms (aka implements) to the delegate protocol UIColorPickerViewControllerDelegate.

@NativeClass()
class ColorPickerDelegateImpl
  extends NSObject
  implements UIColorPickerViewControllerDelegate {

    // informs NativeScript to wire up the protocol
    static ObjCProtocols = [UIColorPickerViewControllerDelegate];

    // best practice to have an owner weak reference
    owner: WeakRef<HelloWorldModel>;

    // common pattern with statically initializing an implementation
    static initWithOwner(owner: WeakRef<HelloWorldModel>) {
      const delegate = <ColorPickerDelegateImpl>ColorPickerDelegateImpl.new();
      delegate.owner = owner;
      return delegate;
    }

    // implement delegate methods here...
}

Understanding highlights:

  • When creating platform class implementations with NativeScript, we always decorate them with @NativeClass(). The @NativeClass() decorator ensures compliance with the NativeScript runtime which you can learn more about here.
  • We extend NSObject most commonly because it provides all the common baseline iOS behavior our implementation needs and also because our delegate is just a protocol, aka an interface, which cannot be extended (just conformed to by an implementation).
  • The static ObjCProtocols Array can contain any number of protocols we want our implementation to use and informs NativeScript to wire up the specified protocols on our behalf.
  • A common best practice is to allow delegate implementations to have an owner weak reference. An owner is the class that is being communicated with from this delegate. You can learn more about WeakRef here.
  • There's many ways to instantiate an implementation class but it's become quite common to use the static initWithOwner(owner: WeakRef<HelloWorldModel>) pattern since it allows you to pass in other references if needed as additional method arguments without interfering with a platform (super) constructor up chain. Of note, the ColorPickerDelegateImpl.new() is a convenient simple constructor that NativeScript adds to all platform classes avoiding particular init arguments which you may want to handle later and returns an NSObject which is why we simply cast it to our type <ColorPickerDelegateImpl>.

The owner weak reference comes into play when wanting to pass events and data back/forth between the delegate and it's owner. Let's do exactly that by implementing several methods provided by the UIColorPickerViewControllerDelegate:

import { Color } from '@nativescript/core';

@NativeClass()
class ColorPickerDelegateImpl
  extends NSObject
  implements UIColorPickerViewControllerDelegate
{
  // ...

  // all delegate methods come from Apple documentation:
  // https://developer.apple.com/documentation/uikit/uicolorpickerviewcontrollerdelegate#3635512

  colorPickerViewControllerDidFinish(
    viewController: UIColorPickerViewController
  ) {
    // did close/finish event
    this.owner?.deref()
      .changeColor(Color.fromIosColor(viewController.selectedColor));
  }

  colorPickerViewControllerDidSelectColorContinuously(
    viewController: UIColorPickerViewController,
    color: UIColor,
    continuously: boolean
  ) {
    // selecting colors event
  }
}

Stage 2: Use the Delegate

Create the controller which will use your delegate:

const picker = UIColorPickerViewController.alloc().init();

Init the delegate implementation we created above while assigning it to an instance property as mentioned in the best practices:

this.colorDelegate = ColorPickerDelegateImpl.initWithOwner(
  new WeakRef(this)
);

Set the delegate property the controller that needs it:

picker.delegete = this.colorDelegate;

Watch the platform come to life.

Try it out right now in your hand via StackBlitz

Bonus surprise 🎉

Want even more neat delegate examples? Here's another implementing table view delegates from scratch to sort list views!

Credits