Lock widget for pro-users

Hi,

I have added widgets to my iOS app and I would like to make this feature only accessible to "pro" users that have made a non-consumable in-app purchase.

Currently, I am doing the following:
  • I store an "isUnlocked" property in the Keychain after the purchase is made

  • I read data to be displayed in the widget and here I also query the Keychain and store whether the widget is unlocked

  • I have no refresh policy, but only change the widget data on a significant time change

  • a different view is displayed when the app is locked

Some dummy code snippets:

Code Block
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
let entry = readContents()
let timeline = Timeline(entries: [entry], policy: .never)
completion(timeline)
}

Code Block
struct WidgetEntryView: View {
let entry: Provider.Entry
    @Environment(\.widgetFamily) var family
    @ViewBuilder
    var body: some View {
        switch family {
        case .systemSmall:
            if !entry.isUnlocked {
                LockedWidgetView()
            } else if let event = entry.event {
                SmallWidgetEventView(event: event)
            } else {
                NoDataWidgetView()
            }
...

Code Block
func applicationSignificantTimeChange(_ application: UIApplication) {
if #available(iOS 14.0, *) {
WidgetCenter.shared.reloadAllTimelines()
}
...


However, 2 unexpected things happen:
  • the view is refreshed intraday (not only at midnight i.e. at significant time change)

  • sometimes the LockedWidgetView is displayed.

Especially the latter is problematic, because it gives false information to a user that has already made the in-app purchase.

How can I achieve my goal of only displaying info when the user has made the in-app purchase?

Thanks in advance.

P.S. Although it would not have my preference, I would also find it acceptable if the widget is only shown as option to add once the purchase is made. In other words, I was considering changing the Widget itself:

Code Block
struct MyWidget: Widget {
    private var supportedFamilies: [WidgetFamily] = isUnlocked() ? [.systemSmall, .systemMedium] : []

but I believe I cannot re-initialise the widget from the app when the user makes the in-app purchase, because the only refresh option that I have is

Code Block
WidgetCenter.shared.reloadAllTimelines()

Replies

Apple will reject this update. Your idea violates Guidelines. It's like you are going to limit ability to receive Push notifications or e.g. disable Camera, just because user is not PRO. All widgets should be accessible without limiting by Purchase attribute.
As a followup to @tetiana160's answer, here's the link to the App Store review guidelines
You won't be able to change supportedFamilies based on an in-app purchase, but there are already many apps that have unlockable widgets and widget features based on subscriptions / in-app purchase. You'll have to show all the widgets, but can show the locked view if they haven't unlocked it yet. It sounds like maybe the widget isn't getting the correct value from keychain? Might have to find another way to verify.
@tetiana160 My app has a few free widgets, but also a few widgets that are tailored around the paid features of my app. We can't make those widgets available to all users as they don't have the underlying feature so those widgets won't even have any data to display. What's the approach there? Currently, we display a "Sorry, this is only available to premium users" but ideally I would like to be able to hide those widgets entirely for non-premium users. Is that possible?

Hi @maxencehnnr, I face the same situation these days. Could you share some solutions?

Hi @maxencehnnr @tetiana160, I am same requirement where I don't want to show the widget to some of the users. And have a control in my app to enable the widget later. Is it possible? if yes, is it acceptable by apple? Any help would be appreciated.