Search code examples
ioswidgetkit

How to use WidgetConfiguration.promptsForUserConfiguration() with a deployment target earlier than iOS 18


I've implemented my widget configuration like so in my app that supports iOS 17+

@main
struct MyWidget: Widget {
    let kind: String = "MyWidget"

    var body: some WidgetConfiguration {
        if #available(iOS 18.0, *) {
            AppIntentConfiguration(kind: kind, intent: MyIntent.self, provider: Provider()) { entry in
                MyEntryView(entry: entry)
            }
            .configurationDisplayName("Display Name")
            .description("Description")
            .promptsForUserConfiguration()
        } else {
            AppIntentConfiguration(kind: kind, intent: MyIntent.self, provider: Provider()) { entry in
                MyEntryView(entry: entry)
            }
            .configurationDisplayName("Display Name")
            .description("Description")
        }
    }
}

This results in a compile-time error

Branches have mismatching types 'some WidgetConfiguration' (result of 'Self.promptsForUserConfiguration()') and 'some WidgetConfiguration' (result of 'Self.description')

How are you supposed to use this when you can't change the deployment target to iOS 18?


Solution

  • You can create two different Widgets and move the availability check up to the body of your WidgetBundle.

    1. Add a WidgetBundle if you don't have one already
    2. Remove @main from your Widget if you did not have a WidgetBundle
    3. Duplicate your Widget and rename it
    4. Add @available(iOS 18.0, *) to the widget that contains .promptsForUserConfiguration()
    @main
    struct MyAppWidgetBundle: WidgetBundle {
        var body: some Widget {
            if #available(iOS 18.0, *) {
                return MyWidget()
            } else {
                return MyLegacyWidget()
            }
        }
    }
    
    @available(iOS 18.0, *)
    struct MyWidget: Widget {
        let kind: String = "MyWidget"
    
        var body: some WidgetConfiguration {
            AppIntentConfiguration(kind: kind, intent: MyIntent.self, provider: Provider()) { entry in
                MyEntryView(entry: entry)
            }
            .configurationDisplayName("Display Name")
            .description("Description")
            .promptsForUserConfiguration()
        }
    }
    
    struct MyLegacyWidget: Widget {
        let kind: String = "MyWidget"
    
        var body: some WidgetConfiguration {
            AppIntentConfiguration(kind: kind, intent: MyIntent.self, provider: Provider()) { entry in
                MyEntryView(entry: entry)
            }
            .configurationDisplayName("Display Name")
            .description("Description")
        }
    }