How to use React Native to control Live Activities | iOS 16.1 | POC

A proof of concept for a React Native delivery app that will display the status, driver name, and expected delivery time of the order, on the soon to come new iOS 16.1 feature: “Live Activities”.

How to use React Native to control Live Activities | iOS 16.1 | POC
iOS16.1 Live Activities

Live Activities weren’t included in the initial version of iOS 16, but will be available to the public in an update (iOS 16.1) later this year. This new feature will display and update an app’s most current data on the iPhone Lock Screen, which will allow people to see the live information they care about the most at a glance: it will allow users, for example, to keep track of sports’ game scores, food or package deliveries’ current status, and more, right from their Lock Screen.

Same day delivery apps are ubiquitous since the not-so-long-ago lock-downs precipitated by the pandemic. So it comes to no surprise I’ve been working for the past year on a delivery app project!

When Apple announced Live Activities at the WWDC as a way for developers to add real-time, up-to-date information to the iPhone Lock Screen I immediately thought: “Cool, I am in the perfect project to test that!”.

Since we are developing the app in React Native I decided to develop a Proof-of-Concept (POC) experiment to understand how we could integrate this new feature in RN.

Conditions / Constraints

  • A Live Activity can only be active for eight hours, after that the system will automatically end it. After the end, the Live Activity remains on the Lock screen for up to four hours before the system removes it.
  • Each Live Activity runs in its own sandbox, and can’t access the network or receive location updates.
  • The updated dynamic data for ActivityKit or remote push notifications can’t exceed 4KB.

Since Live Activities must work on top of the ActivityKit how can we implement it in a React Native App?

This API owns the responsibility of handling the life cycle of each Live Activity so we need to use it to request, update and end the Live Activity.

Live Activities receive updated data from your app with ActivityKit or by receiving remote push notifications. — Apple

Implementing Live Activities on React Native

The POC’s main goal is to create a React Native app that is able to control Live Activities. The app should be able to:

  • Start a Live Activity
  • Define the content of that Live Activity
  • Stop the Live Activity
  • Update the Live Activity
  • List all Live Activities

To enable React Native to use the WidgetKit’s functions and methods I created a native module using Create React Native Library which will create a native module and a React Native app (see here on how to create a RN native module).

We can start by implementing a struct for the Live Activity state:

struct MyActivityAttributes: ActivityAttributes {
  public struct ContentState: Codable, Hashable {
      var status: String
      var driverName: String
      var expectedDeliveryTime: String
  }
}

Then we need to build 4 functions on Swift or Objective-C:

  • start Live Activity
  • update Live Activity
  • end Live Activity
  • list all active Live Activity

These functions will control and manage Live Activities by interacting with the ActivityKit. The example below will start a new Live Activity:

@objc(startActivity:withDriverName:withExpectingDeliveryTime:withResolver:withRejecter:)
func startActivity(status: String, driverName: String, expectingDeliveryTime: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {

  if #available(iOS 16.1, *) {
    var activity: Activity<MyActivityAttributes>?
    let initialContentState = MyActivityAttributes
      .ContentState(status: status,
            driverName: driverName,
            expectedDeliveryTime: expectingDeliveryTime
            )
    let activityAttributes = MyActivityAttributes()
    do {
      activity = try Activity.request(attributes:
              activityAttributes,
              contentState: initialContentState)
      resolve("Requested Live Activity \(String(describing: activity?.id)).")
    } catch (let error) {
      reject("Error requesting Live Activity \(error.localizedDescription).", "", error)
    }
  } else {
    reject("Not available", "", NSError())
  }
}

After that, we need to expose these functions to the Native Module.

RCT_EXTERN_METHOD(startActivity:(NSString)status withDriverName:(NSString)driverName
                  withExpectingDeliveryTime:(NSString)expectingDeliveryTime
                 withResolver:(RCTPromiseResolveBlock)resolve
                 withRejecter:(RCTPromiseRejectBlock)reject);

RCT_EXTERN_METHOD(listAllActivities:(RCTPromiseResolveBlock)resolve
                 withRejecter:(RCTPromiseRejectBlock)reject);

RCT_EXTERN_METHOD(endActivity:(NSString)status withResolver:(RCTPromiseResolveBlock)resolve
                 withRejecter:(RCTPromiseRejectBlock)reject);

RCT_EXTERN_METHOD(updateActivity:(NSString)id withStatus:(NSString)status withDriverName:(NSString)driverName
                  withExpectingDeliveryTime:(NSString)expectingDeliveryTime
                 withResolver:(RCTPromiseResolveBlock)resolve
                 withRejecter:(RCTPromiseRejectBlock)reject);

And so the native part of the module is done.

Now, in React Native, let’s create the 4 functions in JavaScript:

function startActivity(
      status: string,
      driverName: string,
      expectingDeliveryTime: string);

function listAllActivities();

function endActivity(id: string);

function updateActivity(
      id: string,
      status: string,
      driverName: string,
      expectingDeliveryTime: string);

After installing the module in React Native, we then create the Live Activity itself on the iOS project.

For that let’s create a widget extension (documentation here) on Xcode and also add NSSupportsLiveActivities as YES to the info.plist of the project.

Then we can create the Live Activity UI in SwiftUI using the struct created on the Native Module:

@main
@available(iOS 16.1, *)
struct LiveActivityDynamicIsland: Widget {
  var body: some WidgetConfiguration {
    ActivityConfiguration(for: MyActivityAttributes.self) { context in
      HStack {
        Image(systemName: "bicycle")
          .foregroundColor(.blue)
          .padding(8)
        VStack(alignment: .leading) {
          Text(context.state.status)
            .font(.body)
          Text(context.state.driverName)
        }
        Spacer()
        VStack(alignment: .trailing) {
          Text("Deliver at")
            .font(.body)
          Text(context.state.expectedDeliveryTime)
            .font(.footnote)
        }
        .padding(8)
      }
      .padding(8)
    } dynamicIsland: { context in
      DynamicIsland {
        DynamicIslandExpandedRegion(.leading) {
          Image(systemName: "bicycle")
            .foregroundColor(.blue)
            .padding(8)
        }
        DynamicIslandExpandedRegion(.trailing) {
          VStack(alignment: .leading) {
            Text("Deliver at")
              .font(.body)
            Text(context.state.expectedDeliveryTime)
              .font(.footnote)
          }
        }
        DynamicIslandExpandedRegion(.center) {
          Text(context.state.status)
            .font(.body)
        }
        DynamicIslandExpandedRegion(.bottom) {
          Text(context.state.driverName)
        }
      } compactLeading: {
        Image(systemName: "bicycle")
          .foregroundColor(.blue)
      } compactTrailing: {
        Text(context.state.expectedDeliveryTime)
      } minimal: {
        Text(context.state.expectedDeliveryTime)
      }
      .keylineTint(.yellow)
    }
  }

Finally, in the React Native project, create an activity:

import { startActivity } from 'react-native-live-activity';
await startActivity("Packing", "Jhon", "12 PM")

All the code for this POC here.

Video showing an example of a React Native app using Live Activities. You can download it here.

Conclusion #Live Activities on React Native

Although we need to create a native module to give RN access to the ActivityKit’s functions and aside from some simple Swift coding for the Live Activities UI, it is possible and fairly straightforward to create a stable RN app that can use and manage the activities from within React Native.

Thanks for reading. If you enjoyed our content you can you can stay up to date by following us on TwitterFacebook and LinkedIn 👋