Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Sources/ClerkKit/Domains/Environment/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extension Clerk {
public var userSettings: UserSettings
public var displayConfig: DisplayConfig
public var organizationSettings: OrganizationSettings
public var forceUpdate: ForceUpdate

public init(
authConfig: AuthConfig,
Expand All @@ -21,6 +22,7 @@ extension Clerk {
self.userSettings = userSettings
self.displayConfig = displayConfig
self.organizationSettings = organizationSettings
forceUpdate = .empty
}

public init(from decoder: Decoder) throws {
Expand All @@ -29,6 +31,7 @@ extension Clerk {
userSettings = try container.decode(UserSettings.self, forKey: .userSettings)
displayConfig = try container.decode(DisplayConfig.self, forKey: .displayConfig)
organizationSettings = try container.decodeIfPresent(OrganizationSettings.self, forKey: .organizationSettings) ?? .default
forceUpdate = try container.decodeIfPresent(ForceUpdate.self, forKey: .forceUpdate) ?? .empty
}
}
}
Expand Down
31 changes: 31 additions & 0 deletions Sources/ClerkKit/Domains/Environment/ForceUpdate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// ForceUpdate.swift
//

import Foundation

extension Clerk.Environment {
public struct ForceUpdate: Codable, Equatable, Sendable {
public var required: Bool
public var minimumAppVersion: String?
public var appStoreURL: URL?

public init(
required: Bool = false,
minimumAppVersion: String? = nil,
appStoreURL: URL? = nil
) {
self.required = required
self.minimumAppVersion = minimumAppVersion
self.appStoreURL = appStoreURL
}

public static let empty = Self()

enum CodingKeys: String, CodingKey {
case required
case minimumAppVersion
case appStoreURL = "appStoreUrl"
}
}
}
11 changes: 11 additions & 0 deletions Sources/ClerkKit/Domains/ForceUpdate/Clerk+ForceUpdate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// Clerk+ForceUpdate.swift
//

import Foundation

extension Clerk {
public var isForceUpdateRequired: Bool {
environment?.forceUpdate.required == true
}
}
93 changes: 93 additions & 0 deletions Sources/ClerkKitUI/Components/ForceUpdate/ForceUpdateView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// ForceUpdateView.swift
//

#if os(iOS) || os(macOS)

import ClerkKit
import SwiftUI

public struct ForceUpdateView: View {
@Environment(Clerk.self) private var clerk

private let title: LocalizedStringKey
private let subtitle: LocalizedStringKey

public init(
title: LocalizedStringKey = "Update required",
subtitle: LocalizedStringKey = "A newer version of this app is required to continue."
) {
self.title = title
self.subtitle = subtitle
}

public var body: some View {
if let forceUpdate = clerk.environment?.forceUpdate,
forceUpdate.required,
let appStoreURL = forceUpdate.validAppStoreURL
{
ForceUpdateContentView(
appStoreURL: appStoreURL,
title: title,
subtitle: subtitle
)
}
}
}

private struct ForceUpdateContentView: View {
@Environment(\.clerkTheme) private var theme
@Environment(\.openURL) private var openURL

let appStoreURL: URL
let title: LocalizedStringKey
let subtitle: LocalizedStringKey

var body: some View {
VStack(spacing: 24) {
AppLogoView()

VStack(spacing: 8) {
HeaderView(style: .title, text: title)
HeaderView(style: .subtitle, text: subtitle)
}

Button {
openURL(appStoreURL)
} label: {
Label("Update app", systemImage: "arrow.down.circle")
.frame(maxWidth: .infinity)
}
.buttonStyle(.primary(config: .init(emphasis: .high, size: .large)))
}
.padding()
.frame(maxWidth: 360)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(theme.colors.background)
}
}

#Preview {
ForceUpdateContentView(
appStoreURL: URL(string: "https://apps.apple.com/app/id123456789")!,
title: "Update required",
subtitle: "A newer version of this app is required to continue."
)
}

extension Clerk.Environment.ForceUpdate {
fileprivate var validAppStoreURL: URL? {
guard
let appStoreURL,
let scheme = appStoreURL.scheme,
["http", "https"].contains(scheme),
appStoreURL.host != nil
else {
return nil
}

return appStoreURL
}
}

#endif
Loading