8

I have several iOS14 Home screen Widgets ready for my app and return them all in WidgetBundle like this:

@main
struct WidgetsBudle: WidgetBundle {
    @WidgetBundleBuilder
    var body: some Widget {
        Widget1()
        Widget2()
        Widget3()
    }
}

Widget2 depends on location services to work properly, but if the user hasn't granted location permissions, it doesn't really make sense to even show this widget. Can I have some logic involved in returning the widgets inside WidgetBundle body ?

pawello2222
  • 46,897
  • 22
  • 145
  • 209
sabius
  • 704
  • 7
  • 10
  • 4
    AFAIK you can't. `@WidgetBundleBuilder` body doesn't allow control flow statements (yet) and you don't have other tools like in Views (Group, VStack etc). – pawello2222 Sep 22 '20 at 07:23
  • I use a different perspective. By not showing the widget to the ungranted users kills the opportunity to encourage them to give access.What I meant is maybe a user can give access after seeing this widget requires location. So, I am showing a different view by checking isLocationEnabled in widgetEntryView. In this view, it simply says "To use this widget you should grand access". So users can see and maybe give access to you. – mzdogan Nov 03 '22 at 10:26

2 Answers2

6

If you want to select a widget bundle based on some logic you can do the following:

@main
struct WidgetLauncher {
    static func main() {
        if isLocationEnabled {
            WidgetBundle1.main()
        } else {
            WidgetBundle2.main()
        }
    }
    
    static var isLocationEnabled: Bool {
        /// Put your logic here
        true
    }
}

struct WidgetBundle1: WidgetBundle {
    var body: some Widget {
        Widget1()
        Widget2()
        Widget3()
    }
}

struct WidgetBundle2: WidgetBundle {
    var body: some Widget {
        Widget1()
        Widget2()
    }
}
Community
  • 1
  • 1
ryanlintott
  • 79
  • 1
  • 6
  • Isn't `var isLocation`should be `static var isLocation` too? Otherwise how we can access a non-static variable from a static func – mzdogan Oct 31 '22 at 08:45
  • Also, I have a question. With this approach, the widgets are not updating on the home screen until I re-run from Xcode. I want them to change immediately after the logic part is changed. Do you know what to do? – mzdogan Nov 02 '22 at 07:33
  • 1
    I didn't think about that aspect. I'm only using these if statements to show different widget groups based on @available iOS versions so it wouldn't really change. You can use `WidgetCenter.shared.reloadAllTimelines()` to force widgets to reload but I'm not sure if that would make iOS change the bundle. I also think it might be a strange user experience if they installed a widget then some logic change meant the widget is no longer valid. – ryanlintott Nov 02 '22 at 17:44
0

You can also control availability from within the widget configuration itself by passing an empty list to supportedFamilies. You can then include these widgets unconditionally in the WidgetBundleBuilder, but only those returning a valid configuration with a non-empty list of supported families will actually show. Something like this:

struct ExampleWidget: Widget {
    var isAvailable: Bool = true
    var body: some WidgetConfiguration {
        return StaticConfiguration(kind: "something", provider: SomeProvider()) { _ in
           return SomeView()
        }
        .configurationDisplayName("Some name")
        .description("Some description")
        .supportedFamilies(supportedFamilies)
    } 

    var supportedFamilies: [WidgetFamily] {
        isAvailable ? [.systemMedium] : []
    }
}

Then in the widget bundle you can do something like this:

@main
struct WidgetsBundle: WidgetBundle {
    @WidgetBundleBuilder
    var body: some Widget {
        ExampleWidget(isAvailable: /* whatever condition you like */)
    }
}
tenuki
  • 101
  • 7