skip to Main Content

I have multiple Widgets for my iOS/iPadOS/watchOS app. I have one widget with supported family .systemLarge.

struct MyWidget: Widget {

    var body: some WidgetConfiguration {
        StaticConfiguration(...)
            .xxx // some modifiers
            .supportedFamilies([.systemLarge])
    }

}

I want this widget to be supported on new stand-by location so I need to support family .systemSmall as well (I want to reuse body). But since I don’t want this .systemSmall widget in home screen’s gallery I need to use .disfavoredLocations([.homeScreen], for: [.systemSmall]) as well.

Unfortunetely, I’m supporting iOS 15 and 16 as well so I need to wrap this modifier somehow into #available(iOS 17.0, *). But… this can’t be done since there is no type-erased WidgetConfiguration.

Then I was thinking about something like this with the help of WidgetBundle:

@available(iOS 17.0, *)
struct MyWidgetIOS17: Widget {

    var body: some WidgetConfiguration {
        var widget = MyWidget()
        return widget.body.disfavoredLocations([.homeScreen], for: [.systemSmall])
    }

}

@main
struct Widgets: WidgetBundle {
    var body: some Widget {
        if #available(iOS 17.0, *) {
            MyWidgetIOS17()
        } else {
            MyWidget()
        }
    }
}

But… while

if #available(iOS 17.0, *) {
    MyWidgetIOS17()
}

works,

if #available(iOS 17.0, *) {
    MyWidgetIOS17()
} else {
    MyWidget()
}

does not.

Does anybody know how to use disfavoredLocations below iOS 17 while still reusing the Widgets body?

2

Answers


  1. Chosen as BEST ANSWER

    My solution is to add MyWidget for all versions with supported family .systemLarge as it used to support and then add MyWidgetIOS17 which supports just .systemSmall and is wrapped in #available(iOS 17.0, *) if statement. This way I can reuse MyWidget's body and still be able to use disfavoredLocations.

    Also note, that these two widgets have to have different kind.

    @available(iOS 17.0, *)
    struct MyWidgetIOS17: Widget {
    
        var body: some WidgetConfiguration {
            var widget = MyWidget()
            widget.supportedFamilies = [.systemSmall]
            widget.kind = "MyWidgetIOS17"
            return widget.body.disfavoredLocations([.homeScreen], for: [.systemSmall])
        }
    
    }
    
    struct MyWidget: Widget {
    
        var supportedFamilies: [WidgetFamily] = [.systemLarge]
        var kind = "MyWidget"
    
        var body: some WidgetConfiguration {
            StaticConfiguration(kind: kind, ...)
                .xxx // some modifiers
                .supportedFamilies(supportedFamilies)
        }
    
    }
    
    @main
    struct Widgets: WidgetBundle {
    
        var body: some Widget {
            if #available(iOS 17.0, *) {
                MyWidgetIOS17()
            }
            MyWidget()
        }
    
    }
    

  2. I solved this issue with an extension on WidgetConfiguration:

    extension WidgetConfiguration {
        func excludeOnHomeScreen(for families: [WidgetFamily]) -> some WidgetConfiguration {
            if #available(iOSApplicationExtension 17.0, *) {
                return self.disfavoredLocations([.homeScreen], for: families)
            } else {
                return self
            }
        }
        
        func excludeOnLockScreen(for families: [WidgetFamily]) -> some WidgetConfiguration {
            if #available(iOSApplicationExtension 17.0, *) {
                return self.disfavoredLocations([.lockScreen], for: families)
            } else {
                return self
            }
        }
    }
    

    The only downside is that you cannot pass WidgetLocation as a parameter because the symbol is only available on iOS 17+.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search