diff --git a/BusinessCard.xcodeproj/project.pbxproj b/BusinessCard.xcodeproj/project.pbxproj
index ef399cd..b6fa068 100644
--- a/BusinessCard.xcodeproj/project.pbxproj
+++ b/BusinessCard.xcodeproj/project.pbxproj
@@ -629,6 +629,7 @@
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ "INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -665,6 +666,7 @@
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ "INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -851,6 +853,7 @@
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ "INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
LD_RUNPATH_SEARCH_PATHS = (
@@ -888,6 +891,7 @@
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ "INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
LD_RUNPATH_SEARCH_PATHS = (
diff --git a/BusinessCard.xcodeproj/xcuserdata/mattbruce.xcuserdatad/xcschemes/xcschememanagement.plist b/BusinessCard.xcodeproj/xcuserdata/mattbruce.xcuserdatad/xcschemes/xcschememanagement.plist
index 2643284..849d755 100644
--- a/BusinessCard.xcodeproj/xcuserdata/mattbruce.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/BusinessCard.xcodeproj/xcuserdata/mattbruce.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -7,12 +7,12 @@
BusinessCard.xcscheme_^#shared#^_
orderHint
- 2
+ 1
BusinessCardClip.xcscheme_^#shared#^_
orderHint
- 1
+ 2
BusinessCardWatch Watch App.xcscheme_^#shared#^_
diff --git a/BusinessCard/Assets.xcassets/AppIcon.appiconset/AppIcon.png b/BusinessCard/Assets.xcassets/AppIcon.appiconset/AppIcon.png
new file mode 100644
index 0000000..70a2778
Binary files /dev/null and b/BusinessCard/Assets.xcassets/AppIcon.appiconset/AppIcon.png differ
diff --git a/BusinessCard/Assets.xcassets/AppIcon.appiconset/Contents.json b/BusinessCard/Assets.xcassets/AppIcon.appiconset/Contents.json
index 2305880..ce8e776 100644
--- a/BusinessCard/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/BusinessCard/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,6 +1,7 @@
{
"images" : [
{
+ "filename" : "AppIcon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
diff --git a/BusinessCard/Assets.xcassets/LaunchBackground.colorset/Contents.json b/BusinessCard/Assets.xcassets/LaunchBackground.colorset/Contents.json
new file mode 100644
index 0000000..d110017
--- /dev/null
+++ b/BusinessCard/Assets.xcassets/LaunchBackground.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.278",
+ "green" : "0.329",
+ "red" : "0.949"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.278",
+ "green" : "0.329",
+ "red" : "0.949"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/BusinessCard/BusinessCardApp.swift b/BusinessCard/BusinessCardApp.swift
index 1d31d8c..bbffc7e 100644
--- a/BusinessCard/BusinessCardApp.swift
+++ b/BusinessCard/BusinessCardApp.swift
@@ -72,9 +72,17 @@ struct BusinessCardApp: App {
var body: some Scene {
WindowGroup {
- RootTabView()
- .environment(appState)
- .preferredColorScheme(.light)
+ ZStack {
+ // Base background matching launch screen - prevents white flash
+ Color.Branding.primary
+ .ignoresSafeArea()
+
+ AppLaunchView(config: .businessCard) {
+ RootTabView()
+ .environment(appState)
+ .preferredColorScheme(.light)
+ }
+ }
}
.modelContainer(modelContainer)
}
diff --git a/BusinessCard/Design/BrandingConfig.swift b/BusinessCard/Design/BrandingConfig.swift
new file mode 100644
index 0000000..1edbabb
--- /dev/null
+++ b/BusinessCard/Design/BrandingConfig.swift
@@ -0,0 +1,63 @@
+//
+// BrandingConfig.swift
+// BusinessCard
+//
+// App-specific branding configurations for icons and launch screens.
+//
+
+import SwiftUI
+import Bedrock
+
+// MARK: - App Branding Colors
+
+extension Color {
+ /// BusinessCard branding colors for icon and launch screen.
+ enum Branding {
+ /// Primary gradient color (warm coral/red).
+ /// Must match LaunchBackground.colorset exactly to prevent flash.
+ static let primary = Color(red: 0.949, green: 0.329, blue: 0.278)
+
+ /// Secondary gradient color (darker red).
+ static let secondary = Color(red: 0.65, green: 0.18, blue: 0.15)
+
+ /// Accent color for icons and highlights.
+ static let accent = Color.white
+ }
+}
+
+// MARK: - App Icon Configuration
+
+extension AppIconConfig {
+ /// BusinessCard app icon configuration.
+ static let businessCard = AppIconConfig(
+ title: "CARDS",
+ subtitle: nil,
+ iconSymbol: "person.text.rectangle",
+ primaryColor: Color.Branding.primary,
+ secondaryColor: Color.Branding.secondary,
+ accentColor: Color.Branding.accent
+ )
+}
+
+// MARK: - Launch Screen Configuration
+
+extension LaunchScreenConfig {
+ /// BusinessCard launch screen configuration.
+ static let businessCard = LaunchScreenConfig(
+ title: "BUSINESS CARD",
+ tagline: "Share Your Professional Identity",
+ iconSymbols: ["person.text.rectangle"],
+ cornerSymbol: nil,
+ decorativeSymbol: "circle.fill",
+ patternStyle: .dots,
+ layoutStyle: .iconAboveTitle,
+ primaryColor: Color.Branding.primary,
+ secondaryColor: Color.Branding.secondary,
+ accentColor: Color.Branding.accent,
+ titleColor: .white,
+ iconSize: 52,
+ titleSize: 32,
+ iconSpacing: 12,
+ animationDuration: 0.6
+ )
+}
diff --git a/BusinessCard/Design/BusinessCardTheme.swift b/BusinessCard/Design/BusinessCardTheme.swift
new file mode 100644
index 0000000..2f5b043
--- /dev/null
+++ b/BusinessCard/Design/BusinessCardTheme.swift
@@ -0,0 +1,136 @@
+//
+// BusinessCardTheme.swift
+// BusinessCard
+//
+// App-specific theme conforming to Bedrock's color protocols.
+// This light theme uses warm, professional tones.
+//
+
+import SwiftUI
+import Bedrock
+
+// MARK: - Surface Colors
+
+/// Surface colors with warm off-white tones for a professional light theme.
+public enum BusinessCardSurfaceColors: SurfaceColorProvider {
+ /// Primary background - warm off-white base
+ public static let primary = Color(red: 0.97, green: 0.96, blue: 0.94)
+
+ /// Secondary/elevated surface
+ public static let secondary = Color(red: 0.95, green: 0.95, blue: 0.95)
+
+ /// Tertiary/card surface - most elevated
+ public static let tertiary = Color(red: 1.0, green: 1.0, blue: 1.0)
+
+ /// Overlay background (for sheets/modals)
+ public static let overlay = Color(red: 0.97, green: 0.96, blue: 0.94)
+
+ /// Card/grouped element background
+ public static let card = Color(red: 1.0, green: 1.0, blue: 1.0)
+
+ /// Subtle fill for grouped content sections
+ public static let groupedFill = Color(red: 0.95, green: 0.94, blue: 0.92)
+
+ /// Section fill for list sections
+ public static let sectionFill = Color(red: 0.93, green: 0.92, blue: 0.90)
+}
+
+// MARK: - Text Colors
+
+public enum BusinessCardTextColors: TextColorProvider {
+ public static let primary = Color(red: 0.14, green: 0.14, blue: 0.17)
+ public static let secondary = Color(red: 0.32, green: 0.34, blue: 0.40)
+ public static let tertiary = Color(red: 0.56, green: 0.58, blue: 0.62)
+ public static let disabled = Color(red: 0.70, green: 0.72, blue: 0.75)
+ public static let placeholder = Color(red: 0.60, green: 0.62, blue: 0.66)
+ public static let inverse = Color(red: 0.98, green: 0.98, blue: 0.98)
+}
+
+// MARK: - Accent Colors
+
+public enum BusinessCardAccentColors: AccentColorProvider {
+ /// Primary accent - warm red
+ public static let primary = Color(red: 0.95, green: 0.33, blue: 0.28)
+
+ /// Light variant
+ public static let light = Color(red: 0.98, green: 0.50, blue: 0.45)
+
+ /// Dark variant
+ public static let dark = Color(red: 0.75, green: 0.25, blue: 0.22)
+
+ /// Secondary accent - ink/dark
+ public static let secondary = Color(red: 0.12, green: 0.12, blue: 0.14)
+}
+
+// MARK: - Button Colors
+
+public enum BusinessCardButtonColors: ButtonColorProvider {
+ public static let primaryLight = Color(red: 0.98, green: 0.45, blue: 0.40)
+ public static let primaryDark = Color(red: 0.85, green: 0.28, blue: 0.24)
+ public static let secondary = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle)
+ public static let destructive = Color.red.opacity(Design.Opacity.heavy)
+ public static let cancelText = Color(red: 0.32, green: 0.34, blue: 0.40)
+}
+
+// MARK: - Status Colors
+
+public enum BusinessCardStatusColors: StatusColorProvider {
+ public static let success = Color(red: 0.2, green: 0.75, blue: 0.4)
+ public static let warning = Color(red: 0.95, green: 0.75, blue: 0.25)
+ public static let error = Color(red: 0.9, green: 0.3, blue: 0.3)
+ public static let info = Color(red: 0.3, green: 0.6, blue: 0.9)
+}
+
+// MARK: - Border Colors
+
+public enum BusinessCardBorderColors: BorderColorProvider {
+ public static let subtle = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle)
+ public static let standard = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.hint)
+ public static let emphasized = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.light)
+ public static let selected = BusinessCardAccentColors.primary.opacity(Design.Opacity.medium)
+}
+
+// MARK: - Interactive Colors
+
+public enum BusinessCardInteractiveColors: InteractiveColorProvider {
+ public static let selected = BusinessCardAccentColors.primary.opacity(Design.Opacity.selection)
+ public static let hover = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.subtle)
+ public static let pressed = Color(red: 0.14, green: 0.14, blue: 0.17).opacity(Design.Opacity.hint)
+ public static let focus = BusinessCardAccentColors.light
+}
+
+// MARK: - Combined Theme
+
+/// BusinessCard's complete color theme.
+/// Note: We use the individual color provider typealiases (AppSurface, AppThemeAccent, etc.)
+/// directly in views rather than going through the theme type.
+///
+/// This enum is not used directly but documents the theme structure.
+/// The AppColorTheme conformance is omitted to avoid MainActor isolation conflicts.
+public enum BusinessCardTheme {
+ public typealias Surface = BusinessCardSurfaceColors
+ public typealias Text = BusinessCardTextColors
+ public typealias Accent = BusinessCardAccentColors
+ public typealias Button = BusinessCardButtonColors
+ public typealias Status = BusinessCardStatusColors
+ public typealias Border = BusinessCardBorderColors
+ public typealias Interactive = BusinessCardInteractiveColors
+}
+
+// MARK: - Convenience Typealiases
+
+/// Short typealiases for cleaner usage throughout the app.
+/// These avoid conflicts with Bedrock's default typealiases by using unique names.
+///
+/// Usage:
+/// ```swift
+/// .background(AppSurface.primary)
+/// .foregroundStyle(AppThemeAccent.primary)
+/// ```
+typealias AppSurface = BusinessCardSurfaceColors
+typealias AppThemeText = BusinessCardTextColors
+typealias AppThemeAccent = BusinessCardAccentColors
+typealias AppButtonColors = BusinessCardButtonColors
+typealias AppStatus = BusinessCardStatusColors
+typealias AppBorder = BusinessCardBorderColors
+typealias AppInteractive = BusinessCardInteractiveColors
diff --git a/BusinessCard/Info.plist b/BusinessCard/Info.plist
index 2ec3aff..5b7c325 100644
--- a/BusinessCard/Info.plist
+++ b/BusinessCard/Info.plist
@@ -12,5 +12,10 @@
$(CLOUDKIT_CONTAINER_IDENTIFIER)
AppClipDomain
$(APPCLIP_DOMAIN)
+ UILaunchScreen
+
+ UIColorName
+ LaunchBackground
+
diff --git a/BusinessCard/Models/AppTab.swift b/BusinessCard/Models/AppTab.swift
index b243a36..1794e88 100644
--- a/BusinessCard/Models/AppTab.swift
+++ b/BusinessCard/Models/AppTab.swift
@@ -4,6 +4,7 @@ enum AppTab: String, CaseIterable, Hashable, Identifiable {
case cards
case contacts
case widgets
+ case settings
var id: String { rawValue }
}
diff --git a/BusinessCard/Resources/Localizable.xcstrings b/BusinessCard/Resources/Localizable.xcstrings
index 7f298c3..5753701 100644
--- a/BusinessCard/Resources/Localizable.xcstrings
+++ b/BusinessCard/Resources/Localizable.xcstrings
@@ -93,6 +93,10 @@
},
"Address" : {
+ },
+ "App Clip (includes photo)" : {
+ "comment" : "A title for a section that allows users to generate an App Clip link for their card, including a photo.",
+ "isCommentAutoGenerated" : true
},
"Apt, Suite, Unit (optional)" : {
@@ -115,6 +119,10 @@
},
"Card Label" : {
+ },
+ "Card not found" : {
+ "comment" : "Error message when a requested shared card is not found.",
+ "isCommentAutoGenerated" : true
},
"Card style" : {
"extractionState" : "stale",
@@ -197,6 +205,10 @@
},
"Contact" : {
+ },
+ "Could not load card: %@" : {
+ "comment" : "A description of an error that might occur when fetching a shared card. The argument is text describing the underlying error.",
+ "isCommentAutoGenerated" : true
},
"Country" : {
@@ -232,6 +244,10 @@
},
"Create your first card" : {
+ },
+ "Creates a link that opens a mini-app for recipients to preview and save your card with photo." : {
+ "comment" : "A description of the feature that generates an App Clip link.",
+ "isCommentAutoGenerated" : true
},
"Custom color" : {
@@ -297,9 +313,24 @@
},
"Email or Username" : {
+ },
+ "Expires in 7 days" : {
+ "comment" : "A notice displayed below an App Clip URL that indicates when it will expire.",
+ "isCommentAutoGenerated" : true
+ },
+ "Failed to create share URL" : {
+ "comment" : "Error message when the share URL for a shared card cannot be created.",
+ "isCommentAutoGenerated" : true
},
"First Name" : {
+ },
+ "Generate App Clip Link" : {
+
+ },
+ "Generate New Link" : {
+ "comment" : "A button label that resets the generation of an App Clip URL.",
+ "isCommentAutoGenerated" : true
},
"Header Layout" : {
@@ -377,6 +408,10 @@
},
"Maiden Name" : {
+ },
+ "Matt Bruce" : {
+ "comment" : "The name of the developer of the app.",
+ "isCommentAutoGenerated" : true
},
"Messaging" : {
@@ -782,6 +817,10 @@
},
"The default card is used for sharing and widgets." : {
+ },
+ "This card has expired" : {
+ "comment" : "Error message indicating that the shared card has expired.",
+ "isCommentAutoGenerated" : true
},
"This doesn't appear to be a business card QR code." : {
@@ -791,6 +830,13 @@
},
"Title (optional)" : {
+ },
+ "Upload failed: %@" : {
+ "comment" : "A description of an error that might occur when uploading a shared card. The argument is text describing the underlying error.",
+ "isCommentAutoGenerated" : true
+ },
+ "Uploading card" : {
+
},
"URL" : {
diff --git a/BusinessCard/State/SettingsState.swift b/BusinessCard/State/SettingsState.swift
new file mode 100644
index 0000000..5695107
--- /dev/null
+++ b/BusinessCard/State/SettingsState.swift
@@ -0,0 +1,52 @@
+//
+// SettingsState.swift
+// BusinessCard
+//
+// Observable state for app settings.
+//
+
+import Foundation
+import Observation
+
+@Observable
+@MainActor
+final class SettingsState {
+
+ // MARK: - App Info
+
+ /// The app's display name.
+ var appName: String {
+ Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
+ ?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
+ ?? "BusinessCard"
+ }
+
+ /// The app's version string (e.g., "1.0.0").
+ var appVersion: String {
+ Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "1.0"
+ }
+
+ /// The app's build number.
+ var buildNumber: String {
+ Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "1"
+ }
+
+ /// Combined version and build string for display.
+ var versionString: String {
+ "Version \(appVersion) (\(buildNumber))"
+ }
+
+ // MARK: - Debug Settings
+
+ #if DEBUG
+ /// Debug-only: Simulates premium being unlocked for testing.
+ var isDebugPremiumEnabled: Bool {
+ get { UserDefaults.standard.bool(forKey: "debugPremiumEnabled") }
+ set { UserDefaults.standard.set(newValue, forKey: "debugPremiumEnabled") }
+ }
+ #endif
+
+ // MARK: - Initialization
+
+ init() {}
+}
diff --git a/BusinessCard/Views/RootTabView.swift b/BusinessCard/Views/RootTabView.swift
index 930c123..86387fe 100644
--- a/BusinessCard/Views/RootTabView.swift
+++ b/BusinessCard/Views/RootTabView.swift
@@ -31,6 +31,10 @@ struct RootTabView: View {
Tab(String.localized("Widgets"), systemImage: "square.grid.2x2", value: AppTab.widgets) {
WidgetsView()
}
+
+ Tab(String.localized("Settings"), systemImage: "gearshape", value: AppTab.settings) {
+ SettingsView()
+ }
}
.sheet(isPresented: $showingShareSheet) {
ShareCardView()
diff --git a/BusinessCard/Views/SettingsView.swift b/BusinessCard/Views/SettingsView.swift
new file mode 100644
index 0000000..69acc83
--- /dev/null
+++ b/BusinessCard/Views/SettingsView.swift
@@ -0,0 +1,150 @@
+//
+// SettingsView.swift
+// BusinessCard
+//
+// App settings screen using Bedrock components.
+//
+
+import SwiftUI
+import Bedrock
+
+struct SettingsView: View {
+ @State private var settingsState = SettingsState()
+
+ var body: some View {
+ NavigationStack {
+ ScrollView {
+ VStack(spacing: Design.Spacing.large) {
+
+ // MARK: - About Section
+
+ aboutSection
+
+ // MARK: - Debug Section
+
+ #if DEBUG
+ debugSection
+ #endif
+
+ Spacer(minLength: Design.Spacing.xxxLarge)
+ }
+ .padding(.horizontal, Design.Spacing.large)
+ .padding(.top, Design.Spacing.medium)
+ }
+ .background(Color(.systemGroupedBackground))
+ .navigationTitle(String.localized("Settings"))
+ .navigationBarTitleDisplayMode(.large)
+ }
+ }
+
+ // MARK: - About Section
+
+ private var aboutSection: some View {
+ VStack(alignment: .leading, spacing: Design.Spacing.small) {
+ SettingsSectionHeader(
+ title: String.localized("About"),
+ systemImage: "info.circle",
+ accentColor: AppThemeAccent.primary
+ )
+
+ SettingsCard(
+ backgroundColor: Color(.secondarySystemGroupedBackground),
+ borderColor: .clear
+ ) {
+ VStack(alignment: .leading, spacing: Design.Spacing.medium) {
+ // App Name
+ HStack {
+ Text(settingsState.appName)
+ .font(.system(size: Design.BaseFontSize.title, weight: .semibold))
+ .foregroundStyle(.primary)
+
+ Spacer()
+ }
+
+ // Version
+ HStack {
+ Text(String.localized("Version"))
+ .font(.system(size: Design.BaseFontSize.body, weight: .medium))
+ .foregroundStyle(.primary)
+
+ Spacer()
+
+ Text(settingsState.versionString)
+ .font(.system(size: Design.BaseFontSize.body, design: .monospaced))
+ .foregroundStyle(.secondary)
+ }
+
+ // Developer
+ HStack {
+ Text(String.localized("Developer"))
+ .font(.system(size: Design.BaseFontSize.body, weight: .medium))
+ .foregroundStyle(.primary)
+
+ Spacer()
+
+ Text("Matt Bruce")
+ .font(.system(size: Design.BaseFontSize.body))
+ .foregroundStyle(.secondary)
+ }
+ }
+ }
+ }
+ }
+
+ // MARK: - Debug Section
+
+ #if DEBUG
+ private var debugSection: some View {
+ VStack(alignment: .leading, spacing: Design.Spacing.small) {
+ SettingsSectionHeader(
+ title: "Debug",
+ systemImage: "ant.fill",
+ accentColor: AppStatus.error
+ )
+
+ SettingsCard(
+ backgroundColor: Color(.secondarySystemGroupedBackground),
+ borderColor: .clear
+ ) {
+ VStack(spacing: Design.Spacing.medium) {
+ SettingsToggle(
+ title: "Enable Debug Premium",
+ subtitle: "Unlock all premium features for testing",
+ isOn: Binding(
+ get: { settingsState.isDebugPremiumEnabled },
+ set: { settingsState.isDebugPremiumEnabled = $0 }
+ ),
+ accentColor: AppStatus.warning
+ )
+
+ SettingsNavigationRow(
+ title: "Icon Generator",
+ subtitle: "Generate and save app icon to Files",
+ backgroundColor: Color(.tertiarySystemGroupedBackground)
+ ) {
+ IconGeneratorView(config: .businessCard, appName: "BusinessCard")
+ }
+
+ SettingsNavigationRow(
+ title: "Branding Preview",
+ subtitle: "Preview app icon and launch screen",
+ backgroundColor: Color(.tertiarySystemGroupedBackground)
+ ) {
+ BrandingPreviewView(
+ iconConfig: .businessCard,
+ launchConfig: .businessCard,
+ appName: "BusinessCard"
+ )
+ }
+ }
+ }
+ }
+ }
+ #endif
+}
+
+// MARK: - Preview
+
+#Preview {
+ SettingsView()
+}
diff --git a/BusinessCardClip/BusinessCardClip.entitlements b/BusinessCardClip/BusinessCardClip.entitlements
index c058543..1d8b984 100644
--- a/BusinessCardClip/BusinessCardClip.entitlements
+++ b/BusinessCardClip/BusinessCardClip.entitlements
@@ -6,17 +6,9 @@
appclips:$(APPCLIP_DOMAIN)
- com.apple.developer.icloud-container-identifiers
-
- $(CLOUDKIT_CONTAINER_IDENTIFIER)
-
- com.apple.developer.icloud-services
-
- CloudKit
-
com.apple.developer.parent-application-identifiers
- $(TeamIdentifierPrefix)$(APP_BUNDLE_IDENTIFIER)
+ $(AppIdentifierPrefix)$(APP_BUNDLE_IDENTIFIER)