How we built MealPrepPro interactive widgets for iOS 17
Updating MealPrepPro widgets for iOS 17 and sharing data between the widget and the main iOS app.
With interactive widgets coming to iOS 17 we decided it was a great opportunity for us to update our widgets.
We started by going through the 2023 WWDC sessions:
We also signed up to Apple’s developer labs.
Widgets in MealPrepPro
In MealPrepPro you can log meals by tapping the Eaten button. This also logs the calorie and nutritional information to Apple Health.
Our widgets give you an easy way to see your upcoming meal, log it and track your calories and macros.
Widgets are independent mini apps
iOS Widgets operate independently from iOS apps. They run on their own as mini apps, and often the main iOS app won’t be running at all. You might have quite a few widgets visible all the time on your home screen, and the parent iOS apps aren’t running in the background at all.
For widgets to run independently, they need their own supply of data. Some widgets get data from the main iOS app. Other apps get data directly from the internet.
How the MealPrepPro widgets get data
In iOS 16 and earlier, our iOS app writes data to a file in a shared area. The widget then picks up the data from that shared area when it runs.
To prevent the widget from running out of data, we give the widget enough data to show meals for the week ahead.
With iOS 17, widgets are no longer limited to just displaying data. You can now wire up buttons to trigger actions that might do things such as tick off items in a to do list.
Widget Interactivity in iOS 17
To add interactivity to widgets you use App Intents. The most well known example of app intents is the Shortcuts app.
Intents are pieces of an app's functionality that are exposed to the iOS system.
🤔 Challenge 1: How do we hookup the widget’s eaten button?
For MealPrepPro, we wanted the Widget to have an Eaten button for a meal. This would:
Mark the meal as Eaten inside the iOS app
Optionally log the calories and nutritional information to Apple Health
However, this task proved to be more challenging than we initially anticipated. We mistakenly assumed that the App Intents feature for Widgets would work similarly to the Shortcuts app. We believed that tapping a button in the widget would trigger code inside the main iOS app to update the app's database. However, this was not the case.
The rules for what a Widget App Intent can do are:
Can trigger code inside the Widget
Can trigger code that’s shared between the main iOS app and Widget
Cannot trigger code inside the iOS app that is not shared with the Widget
In our case, we wanted to trigger code that was exclusively inside the iOS app and NOT shared with the widget. Specifically we wanted to update the iOS app’s database to update the Eaten (true/false) for a specific meal. The iOS app database is not visible to the widget.
Option 1 - Add the iOS database to the widget
The first solution we considered - could we make the iOS database visible to the widget? This would mean sharing the necessary database and model code with the widget.
It would mean moving the app’s database where the meals are logged to the shared area between the iOS app and widget. Our app’s database is based on SQLite (using GRDB), and sharing a database between the main app and a widget extension is particularly challenging and error-prone. The documentation recommends avoiding this approach.
Option 2 - Get Firebase to update the widget
We also considered using Firebase (via the network) to update the widget. However, we wanted to avoid adding complexity to the widget.
Option 3 - Create a new mini “database”
In the end we felt the least complicated solution would be to create a new mini database that would live inside the area that’s shared between the iOS app and the Widget Extension.
When the iOS app changes the meal plan, it updates this new database so that the widget knows about the change
If the widget marks a meal as Eaten, it also updates this new database so that the iOS app can pick up the change.
🤔 Challenge 2: Why is my widget button not working?
We hooked up our Eaten button in our widget, put in place our new code for our new mini “database”… and found that it worked sometimes… but not always. Tapping the Eaten button in our widget didn’t always seem to work which was strange.
We assumed this was a bug in a beta version of iOS, so set the problem aside for a while and waited for a new beta. But with the new beta the problem was still there, so we were a bit stuck thinking that we were doing something wrong.
And then on 1st August we saw an interesting post on Mastodon from Michael Gorbach, the Engineering Manager for App Intents, Shortcuts and Proactive Intelligence at Apple:
https://mastodon.social/@mgorbach/110812346248732071
Michael explained that what happens when you tap a button or toggle in a Widget depends on whether the iOS app is running at the time or not.
In our case:
If the iOS app is running, the App Intent triggered by the Widget runs in the iOS app
If the iOS app isn’t running, the App Intent triggered by the Widget runs in the Widget
This explanation by Michael explained the inconsistent behaviour we were seeing.
We updated our code to take in to consideration if the app is running or not.
Ready to go 🚀
We’re happy with how MealPrepPro widgets turned out for iOS 17 and looking forward to launching them in September.




