In my previous article, I show you how to optimize network calls for widgets.
Challenge â which widget is the most popular?
After minimizing network calls in my widgets, the server team was satisfied, and I felt good about solving the problem technically. đ
However, now our PM wants to know which widget is the most popular. Yikes! I searched for an API to detect the widgetâs lifecycle but found none.
Fortunately, the article âLessons From Building iOS Widgetsâ on Shopify Engineering was a big help. Thank you guys!
To determine the addition or removal of a Widget, the required API is WidgetCenterâs getCurrentConfigurations(_:)
method.
Using this method, you can retrieve [WidgetInfo]. Inside the WidgetInfo type, thereâs a widgetFamily property we need. This means you can detect all the WidgetFamilies in use.
From Shopifyâs article in the [Detecting, Adding, and Removing Widgets] section, it is evident that the getTimeline
method calls getCurrentConfigurations
.
In the section marked with a red under line, it states that widget removals are not immediately reflected; theyâre only detected after several getTimeline
calls.
In my testing, I found that widget removals are only detected upon the next getTimeline
call. This issue was more annoying with the Lock Screen Widget (iOS 16+). Accessing just the Widget Gallery can trigger multiple getTimeline
calls, risking false detection of widget additions.
If you add three different types (small, medium, large) of widgets to your Home screen and then access the Lock Screen Widget Gallery, youâll observe an unexpectedly high number of getTimeline
calls, way beyond five.
Iâve been grappling with widgets for months, but there are still mysteries to unravel. Moreover, logging through the logEvent method of Firebase Analytics doesnât work in Widget Extensions. đ€Ż
WidgetDetector â for reliable logging
My requirements for the âwidgets in useâ logging:
- Logging doesnât need to be immediate upon adding/removing a widget, but it must be accurate.
- Since Firebase Analytics doesnât work in Widget Extensions, logging must be done in the hosting app.
- I want to know the types of WidgetFamily in use by users.
I found a solution that meets these requirements and want to share it with developers struggling with widgets. Unlike Shopify, I chose not to call getCurrentConfigurations
in the getTimeline
method.
My trick is â when the hosting app enters the foreground, call getCurrentConfigurations
to retrieve current [WidgetInfo] and compare it with the old [WidgetInfo] stored in UserDefaults.
Let me show you WidgetDetector code. Itâs very simple.
Looking at the code, youâll notice I store [WidgetFamily] in UserDefaults instead of a Singleton property. The reason is that a Singleton property acts as a short-lived cache.
Therefore, if a user updates the app from the App Store while some widgets are in use, the cache in the Singleton will reset and cause duplicate widget add logging.
Storing [WidgetFamily] in UserDefaults ensures data persistence unless the app is entirely deleted, safeguarding the data from app updates.
In the code, I access UserDefaults using KeyPath. For more details, feel free to check out my repository.
It only takes a single line of code. Iâll call detect()
when the hosting app enters the foreground. This way, we can log the addition or removal of widgets whenever the user launches the app.
Letâs look at how it works in a gif. First, Iâll add a small widget.
When a small widget is added and the app enters the foreground, you can see that WidgetDetector detects it.
I also tested adding/removing lock screen widgets, but creating a gif for this was too heavy, so I made a video and uploaded it to YouTube. Please refer to the video below.
Now, you can remove print()
and call logEvent()
as you see fit. Whew, I think our PM will be happy now!