Skip to content

Instantly share code, notes, and snippets.

@LukasCZ
Created September 6, 2023 15:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LukasCZ/1e1da01102b22e112b9c34cbb3db16b8 to your computer and use it in GitHub Desktop.
Save LukasCZ/1e1da01102b22e112b9c34cbb3db16b8 to your computer and use it in GitHub Desktop.
Supporting iOS 17 widget environment variables on previous releases
//
// WidgetEnvironmentBackporting.swift
// Widget Extension
//
// Created by Lukas Petr on 06.09.2023.
// Copyright © 2023 Glimsoft. All rights reserved.
//
// This solution is heavily inspired by https://developer.apple.com/forums/thread/733780?answerId=759064022#759064022
// All credit should go to him.
import SwiftUI
struct MyWidgetShowsContainerBackgroundKey: EnvironmentKey {
static let defaultValue: Bool = true
}
struct MyWidgetRenderingModeIsVibrant: EnvironmentKey {
static let defaultValue: Bool = false
}
struct MyWidgetContentMargins: EnvironmentKey {
static let defaultValue = EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
}
struct MyWidgetContentMarginsAreAvailable: EnvironmentKey {
static let defaultValue: Bool = false
}
extension EnvironmentValues {
var myWidgetShowsContainerBackground: Bool {
set { self[MyWidgetShowsContainerBackgroundKey.self] = newValue }
get { self[MyWidgetShowsContainerBackgroundKey.self] }
}
var myWidgetRenderingModeIsVibrant: Bool {
set { self[MyWidgetRenderingModeIsVibrant.self] = newValue }
get { self[MyWidgetRenderingModeIsVibrant.self] }
}
var myWidgetContentMargins: EdgeInsets {
set { self[MyWidgetContentMargins.self] = newValue }
get { self[MyWidgetContentMargins.self] }
}
var myWidgetContentMarginsAreAvailable: Bool {
set { self[MyWidgetContentMarginsAreAvailable.self] = newValue }
get { self[MyWidgetContentMarginsAreAvailable.self] }
}
}
// Magic happens here
struct WidgetEnvironmentReader<Content: View>: View {
var content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
if #available(iOS 17, *) {
WidgetEnvironmentViewBuilder(content: content)
} else {
content()
}
}
}
extension WidgetEnvironmentReader {
@available(iOS 17, *)
struct WidgetEnvironmentViewBuilder<C: View>: View {
@Environment(\.showsWidgetContainerBackground) var showsWidgetContainerBackground
@Environment(\.widgetRenderingMode) var widgetRenderingMode
@Environment(\.widgetContentMargins) var widgetContentMargins
var content: () -> C
init(@ViewBuilder content: @escaping () -> C) {
self.content = content
}
var body: some View {
content()
.environment(\.myWidgetShowsContainerBackground, self.showsWidgetContainerBackground)
.environment(\.myWidgetRenderingModeIsVibrant, self.widgetRenderingMode == .vibrant)
.environment(\.myWidgetContentMargins, self.widgetContentMargins)
.environment(\.myWidgetContentMarginsAreAvailable, true)
}
}
}
@LukasCZ
Copy link
Author

LukasCZ commented Sep 6, 2023

To use it, you can just wrap your widget entry view in the WidgetEnvironmentReader, like this:

struct TrackingWidget: Widget {
    let kind: String = "TrackingWidget"
    
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: self.kind, provider: Provider()) { entry in
            WidgetEnvironmentReader {
                TrackingWidgetEntryView(entry: entry)
            }
        }
        .configurationDisplayName("Tracking Widget")
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment