also looking at this
fix(ios): let native screens choose interface appearance
#398
8 files
+218
-5
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -790,7 +790,7 @@ describe('Craft iOS builder', () => { | ||
| 790 | 790 | const launch = JSON.parse(readFileSync(join(output, 'Assets.xcassets', 'LaunchBackground.colorset', 'Contents.json'), 'utf8')) |
| 791 | 791 | expect(plist).toContain('<key>UIUserInterfaceStyle</key>\n <string>Automatic</string>') |
| 792 | 792 | expect(plist).toContain('<string>UIStatusBarStyleDefault</string>') |
| 793 | expect(swift).toContain('.preferredColorScheme(appState.config.colorScheme)') | |
| 793 | expect(swift).toContain('.preferredColorScheme(appState.appearanceScheme)') | |
| 794 | 794 | expect(launch.colors).toHaveLength(2) |
| 795 | 795 | expect(launch.colors[1].appearances).toEqual([{ appearance: 'luminosity', value: 'dark' }]) |
| 796 | 796 | expect(launch.colors[1].color.components.red).toBe('0.008') |
| @@ -834,6 +834,8 @@ describe('Craft iOS builder', () => { | ||
| 834 | 834 | const screen = readFileSync(join(output, 'Sources', 'CraftNativeScreen.swift'), 'utf8') |
| 835 | 835 | expect(swift).toContain('CraftInterfaceStyleController.configure(config)') |
| 836 | 836 | expect(swift).toContain('CraftInterfaceStyleController.set(appearance)') |
| 837 | expect(swift).toContain('.preferredColorScheme(appState.appearanceScheme)') | |
| 838 | expect(swift).toContain('CraftInterfaceStyleController.colorScheme(for: self.config)') | |
| 837 | 839 | expect(swift).toContain('case "getAppearance":') |
| 838 | 840 | expect(screen).toContain('CraftInterfaceStyleController.apply(to: self, config: config)') |
| 839 | 841 | expect(screen).toContain('craftNativeSetAppearance') |
| @@ -134,6 +134,14 @@ enum CraftInterfaceStyleController { | ||
| 134 | 134 | runtimeSelection ?? configuredStyle(for: config) |
| 135 | 135 | } |
| 136 | 136 | |
| 137 | static func colorScheme(for config: CraftConfig) -> ColorScheme? { | |
| 138 | switch style(for: config) { | |
| 139 | case .light: return .light | |
| 140 | case .dark: return .dark | |
| 141 | default: return nil | |
| 142 | } | |
| 143 | } | |
| 144 | ||
| 137 | 145 | /// Returns false rather than silently accepting a typo from JavaScript. |
| 138 | 146 | @discardableResult |
| 139 | 147 | static func set(_ value: String) -> Bool { |
| @@ -689,7 +697,7 @@ struct CraftApp: App { | ||
| 689 | 697 | .ignoresSafeArea(.keyboard, edges: .bottom) |
| 690 | 698 | } |
| 691 | 699 | } |
| 692 | .preferredColorScheme(appState.config.colorScheme) | |
| 700 | .preferredColorScheme(appState.appearanceScheme) | |
| 693 | 701 | .environmentObject(appState) |
| 694 | 702 | .onOpenURL { url in |
| 695 | 703 | // Handle deep links and universal links |
| @@ -1601,6 +1609,8 @@ class DeepLinkManager { | ||
| 1601 | 1609 | // MARK: - App State |
| 1602 | 1610 | class AppState: ObservableObject { |
| 1603 | 1611 | @Published var config: CraftConfig |
| 1612 | @Published private(set) var appearanceScheme: ColorScheme? | |
| 1613 | private var appearanceObserver: NSObjectProtocol? | |
| 1604 | 1614 | |
| 1605 | 1615 | init() { |
| 1606 | 1616 | if let configURL = Bundle.main.url(forResource: "craft.config", withExtension: "json"), |
| @@ -1609,11 +1619,26 @@ class AppState: ObservableObject { | ||
| 1609 | 1619 | } else { |
| 1610 | 1620 | self.config = CraftConfig() |
| 1611 | 1621 | } |
| 1622 | self.appearanceScheme = CraftInterfaceStyleController.colorScheme(for: config) | |
| 1623 | appearanceObserver = NotificationCenter.default.addObserver( | |
| 1624 | forName: .craftColorSchemeChanged, | |
| 1625 | object: nil, | |
| 1626 | queue: .main, | |
| 1627 | ) { [weak self] _ in | |
| 1628 | guard let self else { return } | |
| 1629 | self.appearanceScheme = CraftInterfaceStyleController.colorScheme(for: self.config) | |
| 1630 | } | |
| 1612 | 1631 | // Before the first frame: the hybrid start screen, and the tab bar |
| 1613 | 1632 | // from the config, are decided here, if the app delegate has not |
| 1614 | 1633 | // already (it does at launch; the first call decides). |
| 1615 | 1634 | CraftHybrid.shared.configure(config) |
| 1616 | 1635 | } |
| 1636 | ||
| 1637 | deinit { | |
| 1638 | if let appearanceObserver { | |
| 1639 | NotificationCenter.default.removeObserver(appearanceObserver) | |
| 1640 | } | |
| 1641 | } | |
| 1617 | 1642 | } |
| 1618 | 1643 | |
| 1619 | 1644 | extension CraftConfig { |