I was working on a simple iOS app as a small side project and thought it would be fun to have a watch extension for it. Since the bulk of the project involved retrieving and parsing some JSON via a REST call, I wanted to share a package with Swift code between the app and the extension. It turned out to be complicated to figure out how to do that. It’s simple to do in practice so I wanted to document the process for the future.
Note: This is not the simplest way to share code. It is the simplest way to share code packaged as a unit.The simplest way is to select the file you want to share then select other targets in the Target Membership list for that file.
Create an iOS app project
In Xcode use File | New… | Project… then choose iOS from the platform list at the top and App in the Application section.
Click Next and give your app a name and organization identifier.
Click Next and save your new project. You should end up with something that looks roughly like this with a few file names changed.
Add a watch extension to the project
Use File | New… | Target… and choose WatchOS from the platform list then Watch App for iOS App in the Application section and click Next.
Now give the new watch app a name and click Next.
Click Finish and you should see something that looks roughly like this. In the version of Xcode I’m using (13.2.1), I get a modal dialog asking if it’s okay to activate the new target, which I accepted. You can see in this screenshot at the very top that the WatchPkgSample is that active target.
At this point you have a working iOS app with a watch app embedded in it. You can run the resulting watch app on a watch simulator by clicking run.
Create a package
The point of all of this setup is to create an environment where we can share some code between the iOS app and the watch app. There are a number of approaches discussed about how to do this that I found online but so far Swift Packages has been the most successful approach I’ve tried. One alternative is to create an embedded framework or a separate framework. I tried the framework approach first and it turned out to be painful for WatchOS extensions in particular. Creating a package however was painless and worked the first time. Here’s what I did.
Use File | New… | Package.. and give your new package a name and then choose to add the package to the project and project group that were created previously. You can see in the screenshot below that PkgSample is selected as both the Add to and Group option. Note that is not the default, you have to explicitly choose those options from the drop downs. If you don’t then the package is not automatically added to your existing project. That might be a good choice for some cases where you’re sharing code across projects but that’s not what I wanted in this case. If that is your goal, you likely already know what you’re doing.
Make sure you choose the right directory in the file dialog. It’s probably best to put the package into the project directory unless you plan on reusing it in other projects. After clicking the Create button you’ll see this – note that I expanded the Sources and Sample Model folders and then clicked the SampleModel.swift file in this screenshot. I wanted to show where the package source code.
Add the package to the extension
Finally we’re almost at the goal, using some code from the package in the watch extension. To do that you have to add the package to the extension. Start by selecting the extension in the project navigator.
Then right click it and choose Add Packages..
In the packages dialog you need to choose Add Local… because you want to use the package you just created, not one of the Apple published packages.
Now choose the directory that contains the package that you just created in the file dialog and then click Add Package. You can see in the screenshot below that the SampleModel package is selected. The first time I did this walk through I created the SampleModel in a different directory and had to search for it. It’s probably simpler to put the package into the project directory as is shown here. Just make sure you choose that directory when you create the package.
Like me you’re probably thinking, ‘hey, my package is now ready to use!’. Not so fast, there’s yet another step you have to go through to make it usable. What you’ve done up to now has made it possible to access the code in Xcode but it won’t actually run because the package isn’t available to the build. To make that happen you have to choose the watch target and then add the package to the list of Frameworks, Libraries and Embedded Content. You do that by first selecting the target.
Now find the Frameworks, Libraries and Embedded Content section
Then press the + button to add the package and select the newly created package from the dialog and click Add to add it to the target.
Use the package
Finally, after all that clicking, time to write some code! We’ll use the text exported by the package to set the text in the extension. To do that open the ContentView file in the watch extension and import the SampleModel package. Then you can reference the text from that package in your code.
And just like that (sarcasm) you can now add any common code you want to the package and use it in your watch extension and your main app. In my case I put some code that calls a REST endpoint and then parses the resulting JSON into some internal model classes for use.
Note that you will have to repeat the steps in Add the package to the extension above to add the package to your app.