React Native App with Apple Watch & Widget Support
Extending your app’s reachability to your users’ wrists
We all love developing mobile apps using React Native because it provides cross-platform integration which is widely used for iOS as well as Android applications. But do you know what is more exciting?
You guessed it right! It is a React Native application which also supports smart watch devices.
In this article, we will learn how to integrate an Apple watchOS application with our React Native application. Moreover, we will also create a widget which can be set as a complication in watch faces.
Prerequisites
- Basic knowledge of React Native, Xcode, and SwiftUI
- Xcode with watchOS-supported device simulator
Setting up your development environment
To build a watchOS app for our React Native app, we will use Xcode, which is an IDE provided by Apple.
Moreover, we will use WatchKit, which is a framework provided by Apple to build watchOS apps. With WatchKit, we can create watchOS apps and program them to connect and work with apps on other Apple devices.
Installing necessary dependencies for watchOS and widgets
To implement communication between iOS and watchOS apps, we will use a library named react-native-watch-connectivity.
- To install this package, you may use yarn or npm based on your project:
npm install react-native-watch-connectivity - save
yarn add react-native-watch-connectivity
Don’t forget to install cocoapods!
cd ios && pod install && cd ..
Next up, we will create a very basic React Native screen which will have a count variable with plus and minus buttons.
Adding support for watchOS app
Now that you have set up your iOS app, let’s move forward and start developing our watchOS app.
Open your iOS project using Xcode. From the toolbar, select File -> New -> Target.
You will see the following window: From the top tabs, select watchOS, and under that tab, select App.
Next up, you will see another window to provide more details about the watchOS app.
Provide an app name and bundle identifier for the watchOS app.
From the window shown above, you can select whether you already have an existing iOS app or not. In our case, we have an iOS app project setup. So we will select Watch App for Existing iOS App, and from the drop-down below, select our iOS app’s target.
After clicking the Finish button, you will notice that a new folder has been added to our Xcode project. That folder contains our watchOS project files.
Designing the watchOS app’s UI
Now, we will start designing the UI for our watchOS application. To begin, open the ContentView.swift file, which contains SwiftUI code.
In our SwiftUI code, we will display a simple text element that will show the value of the counter.
Our ContentView should look like this:
Implementing functionality
Now, let’s set up a mechanism for two-way communication between the iOS and the watchOS apps.
Sending messages from the iOS app to the watchOS app
- iOS app as sender
To send messages to the watchOS app, we will need to use the package we have installed in the React Native app.
Let’s import sendMessage and getReachability function from the react-native-watch-connectivity and use them to determine the watch’s reachability to send the count variable to the watchOS app.
We will modify our button actions as shown below:
- watchOS app as receiver
To set up the watchOS app to receive messages sent from the iOS app, let’s create a new Swift file and name it ConnectionHelper.swift. We will use this class to capture messages and events sent from the iOS app.
To begin, let’s create a class named ConnectionHelper and inside that, we will set up our watch session and events.
This code will give you an error, which forces us to implement the required methods in our class for the watch session delegate.
To keep the code clean, create an extension of our class ConnectionHelper and implement WCSessionDelegate into it. The extension must also implement the method shown below, as it is a required method.
Info: An extension in Swift allows you to add new functionality to an existing class.
This method is useful for indicating when there are any changes in the activation state of WCSession. It means we can see that communication between your iOS app and the paired Apple watch is now active. If not, it sends an error.
The next step is getting events and messages that are sent from our iOS app into our watchOS app, for which we will implement the didReceiveMessage method in the extension. Our ConnectionHelper extension should look like this now:
Using the above-declared method, we will update the value of our counter. Let’s first declare it inside our ConnectionHelper class. Our class should now look as follows:
We declared a variable count, annotated it as Published and implemented ObservableObject protocol in our class because we want to track whenever this count variable is changed.
Now, we need to implement a logic that updates the count value whenever we receive an event from the iOS app. To achieve this, we will use the didReceiveMessage method defined in our ConnectionHelper extension.
Info: Whenever you get a message into the watchOS app from iOS app, you can send a reply by using the replyHandler callback.
Now that we have created our counter variable, it’s time to use it inside our watchOS app’s UI. Let’s instantiate the ConnectionHelper class in our ContentView and use the count property to display it under the text element.
Now, our one-way communication from the iOS app to the watchOS app is complete.
Sending messages from the watchOS app to the iOS app
- watchOS app as sender
To implement sending messages from the watchOS app to the iOS app, we will first create a helper function inside the ConnectionHelper class that will send the count variable as a message from the watchOS to our iOS app using the watch session that we previously created.
In the next step, we will modify our ContentView file to add buttons with text + and - followed by adding actions to them. As shown in the code snippet below, we will modify the count variable and use the sendNewCount function to send it to the iOS app.
But wait! We still need to set up our iOS app to listen to the messages sent from the watchOS app.
- iOS app as receiver
To handle these incoming messages, we first need to use watchEvents to listen to the messages, which are imported from the react-native-watch-connectivity package.
Voila! That’s it. Let’s run both apps and give them a try.
Adding widgets to the watchOS application
Setting up the Widget Extension target
To introduce widget support for our watchOS app, we will need to create a new target in the same Xcode project.
We will add a new target named Widget Extension to our project, similar to the watchOS target.
After clicking Next, provide the product name for your widget target.
Once you have set up your Widget Extension target, you will see a new folder in your project, named CounterWidget in this case. This folder will initially contain two Swift files, AppIntent and CounterWidget.
Designing and implementing the widget UI and its functionality
Before modifying the UI, let’s first set up our count variable in the Widget Extension.
To accomplish this, open the AppIntent file, declare a variable named currentCount, and set up the constructors as shown in the code snippet below:
To start designing our widget’s UI, let’s open the CounterWidget file. Scroll down to the EntryView, which was automatically generated by Xcode when we created the Widget Extension. We will create a very simple text component and, inside it, show our current count variable as shown below:
Communicate and share data between the watchOS app and the Widget Extension
Now, we want to show our current count variable, declared in the watchOS app, in our widget’s UI. But Apple does not provide a direct way to exchange data between the watchOS app and a widget. So, we will store our count variable in UserDefaults, which is local storage provided by Apple.
More info on UserDefaults: https://developer.apple.com/documentation/foundation/userdefaults
To share a single property across our watchOS app and Widget Extension, we have to add a new capability called App Groups for both watchOS and Widget Extension.
To do so, open Project Settings -> select your target, and then select the tab Signing & Capabilities. Click the + Capability button to your target in that window.
After adding App Groups as capabilities, you will see this section inside that window. Now, tap the + button and add an identifier.
Now, provide a valid identifier for the container (which will also be used as a suite name for UserDefaults) where our data will be stored and used by multiple targets in our project.
Note: Make sure to do this step for both the watchOS app and the Widget Extension.
The CounterWidget file will contain all the logic that manages the user interface (UI) and its functionality. However, the main function is the timeline function, which is responsible for refreshing the widget’s UI at specified intervals.
For more info about it, read: https://developer.apple.com/documentation/widgetkit/appintenttimelineprovider
Moving forward, now that we have our app groups ready, let’s set up the mechanism for sharing our data between the watchOS app and the Widget Extension.
We will declare a new variable called appCount inside our ConnectionHelper class, which is declared in the watchOS target’s folder, and it will be annotated with AppStorage using UserDefaults, as shown below:
In addition to that, to make the appCount variable functional, we will assign a new value to it whenever the count is changed.
In the next step, to display it inside the widget’s UI, we want to modify the Timeline Provider of the widget. For that, navigate to the pre-generated Provider structure inside the CounterWidget file.
First, we want to declare the appCount variable in that structure’s scope and modify the provider functions so that they look as shown below:
Hallelujah! We have successfully set up our watchOS app’s Widget Extension to show the count value. Now let’s run and give it a shot.
Let’s try modifying the count from the iOS application.
The full project is available on this GitHub repo. 🚀
Conclusion
We successfully integrated the React Native app with watchOS, as well as widget support for the watch faces.
Now, it’s your turn to create awesome apps using WatchKit and WidgetKit. Share your ideas and projects in the comments below.
Happy coding! 😉
For more updates on the latest tools and technologies, follow the Simform Engineering blog.