Using ProvisioningProfile and Firebase to monitor profile expiries remotely

Chris Mash
6 min readApr 30, 2023

Previously I wrote about my Swift Package ProvisioningProfile, which extracts when your (iOS/watchOS/tvOS/macOS) app’s provisioning profile is going to expire.

If you’ve got a load of your own apps on your device(s) (or other people’s devices) but they’re not on the App Store or TestFlight then you‘ll have experienced the struggle of the provisioning profile expiring and the app not launching, potentially at a crucial time when it was needed and there’s no dev machine nearby to reinstall it!

I’ve got (too many) personal apps on my iPhone, iPad and also some on my the iPads belonging to my kids, which is quite a lot to consider if you want those apps to always be available, especially with them all expiring at different times throughout the year!

The original plan for ProvisioningProfile

Originally I created the ProvisioningProfile package as an easy way to show the expiry date in an app and even show alerts or notifications when the expiry date was nearing. That would work ok for most of my regularly used apps on my iPhone, but there are some that are used less regularly and I don’t use my iPad much at all so those apps would expire without me realising (and my kids would probably miss/ignore any notifications on their iPads).

So I started thinking about what the next option would be and I figured I needed a cloud database to capture this information and allow me to keep an eye on it myself. So that’s what I did next!

Whenever I see people talking about cloud databases I see iCloud and Firebase as the forerunners. iCloud would be pretty good for this use case as all my apps are for Apple devices, but I’ve heard there can be issues with it and having dabbled with Firebase’s realtime database previously (and found it to be super straightforward for simple stuff like this) I figured I’d stick to what I know!

Firebase & ProvisioningProfile (and yet another app)

So the basic idea is that you create a single project in Firebase that houses the realtime database and you register all your app bundle IDs to it so that they can update the database.

I made a simple “profile logging” Swift package that would use the ProvisioningProfile package to extract the expiry time and insert it into the Firebase database, along with some useful information such as the device name, app name and version. If you follow the Firebase realtime database tutorial it’s fairly straightforward to update it. This is basically what my package does:

I then made yet another iOS “tracking” app (but who doesn’t love creating new apps??) to read from the Firebase database and display the information, as well as scheduling notifications for each app expiring, which of course I’ll actually notice and deal with appropriately! And of course this “tracking” app also logs its profile expiry to the database!

This has proven to be a pretty nice solution for my use case. It’s not too awkward to integrate it into a new app and then it silently keeps the database up to date:

  • Setup your new app on the Firebase console (simply by adding the bundle ID to the existing Firebase project, super quick)
  • Download the GoogleService-Info.plist from Firebase and add to the app
  • Add the “profile logging” Swift Package to the app
  • Call the package’s API to log the expiry when the app launches

No need to add any UI to display the expiry date or to mess around with scheduling notifications, that’s all handled by the “tracking” app separately!

And here’s a look at the “tracking” app”

A screenshot of the tracking app showing a list of devices and the apps installed on those devices. Each app lists when its provisioning profile is going to expire.

Some things to bear in mind

Potentially missing changes

With this solution, if I install a new app on a device (or the profile is updated for an existing app) and I don’t open up the “tracking” app to pull down its expiry date then it’s unaware of that app so it could then miss the expiry and not notify me. It’s not a likely issue for my use case but it could be for some. I think Firebase might be able to send out database update notifications to apps such that it could deal with this scenario though.

False alarms

If I install one of my apps from the App Store over the top of a dev version, with my current approach, the “tracking app” will still warn me the app is going to expire, even though it won’t. This is because the App Store versions of my apps don’t update the database. Not a huge issue for me to manage but your mileage may vary. One solution could be to keep the database updated from App Store apps too, removing the entry for the app (or flagging it) if there’s no profile found when running the app (though simulators also don’t have profiles available so keep that in mind).

Firebase app limits

Firebase has a limit of 30 apps per project on free plans, which could be problematic for those with many apps. You can open your wallet (though I’m not sure how much the limit goes up to for paid plans) or you might be able to find a solution that batches up your apps into multiple projects and then get the app to pull down from each project?

Firebase already in use?

If you’re already using Firebase in your app for something else then I’m not sure whether this will create an issue. You’d have a specific Firebase project configured in your app for the main functionality but would need a different Firebase project to receive the profile expiry details. I imagine there’s probably a way you could setup your app to start with one configuration, log the expiry and then switch to the main configuration, or maybe you could log it to your main Firebase project and then get that to pass it on to the tracking project (some custom server magic, which might not be everyone’s cup of tea!).

Firebase security

Security of the database could be a concern. In my setup I need the database to be open to modification by anyone so the plethora of apps can make their updates silently. So I imagine an attacker that found my database could update/delete the data fairly easily. I did look into this a bit and Firebase does offer various solutions but I’m not sure any of them really help my situation much. I did limit reading of the database to my own account and integrated Firebase authentication into the “tracking” app, but I didn’t want to require authentication in all my apps as that would affect the UX and integration complexity too much.

For my use case I only need this in development builds. All my non-App Store builds are debug and the App Store builds are release, so I’ve skipped the logging in release builds and also excluded the GoogleService-Info.plist (which lists everything you need to access the database) from release builds. So at least that keeps the database a bit more secret.

A curveball from Apple

In iOS 16 Apple changed UIDevice.name to no longer return the user’s chosen name for the device (which defaults to something like “Chris’ iPhone”) but instead a generic name, unless you jumped through some hoops to get approval for accessing the user’s device name. That was a shame because it was very useful for me to differentiate devices, but I totally understand why Apple changed it.

I looked into whether I’d be able to jump through Apple’s hoops but it didn’t seem like I would so I had to come up with an alternative. If you’ve looked at the gist for the logging code above then you’ll have already seen what I did. I basically swapped out the devices name for a combination of UIDevice.identifierForVendor and the model of the device model (which I get from DeviceGuru). My “tracking” app has the ability to give a recognisable name to a device in the database so I can re-label it to something more meaningful.

The device model gives me a clue which device it might be (I have a different iPad model to my kids, though they both have the same model) and then I have to use some logic to figure out which vendor ID identifies which similar device based on the apps that have been logged or the time that the logging happened (e.g. I just installed app X on my son’s iPad so I can tell that this vendor ID in the database is for his iPad).

With my small data set this is manageable, but if you’ve got a lot of different devices you’re deploying to then you might need something a bit better!

--

--

Chris Mash

iOS developer since 2012, previously console games developer at Sony and Activision. Twitter: @CJMash