RunnerApp.swift
3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//
// RunnerApp.swift
// Runner
//
// Created by 权海 on 2026/6/11.
//
import SwiftUI
import UIKit
@main
struct RunnerApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
RunnerLaunchView(appDelegate: appDelegate)
}
.onChange(of: scenePhase, initial: true) { _, phase in
guard phase == .active else { return }
appDelegate.setup()
HealthKitService.shared.startBackgroundObserversIfNeeded()
Task {
await HealthKitService.shared.uploadHealthDataAfterForeground()
}
}
}
}
private struct RunnerLaunchView: View {
let appDelegate: AppDelegate
@State private var showFlutter = false
@State private var didStart = false
var body: some View {
Group {
if showFlutter {
if let flutterEngine = appDelegate.flutterEngine{
FlutterRootView(engine: flutterEngine)
.ignoresSafeArea()
}else{
VStack{
Text("Engin init error: \(appDelegate.enginInitError ?? "-")")
Button {
appDelegate.warmUpFlutterEngineIfNeeded()
} label: {
Text("Engin 重载")
}
}
}
} else {
SplashView()
.onAppear(perform: startIfNeeded)
}
}
.onAppear {
appDelegate.setup()
}.onOpenURL { url in
if WeChatLoginBridge.shared.handleOpenURL(url) {
return
}
appDelegate.handleFlutterUrl(url.absoluteString)
}
}
private func startIfNeeded() {
guard !didStart else {
return
}
didStart = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
appDelegate.warmUpFlutterEngineIfNeeded()
withAnimation(.easeInOut(duration: 0.2)) {
showFlutter = true
}
}
}
}
private struct SplashView: View {
var body: some View {
ZStack(alignment: .top) {
Color.white
.ignoresSafeArea()
AppLogoView()
.padding(.top, 96)
}
}
}
private struct AppLogoView: View {
var body: some View {
if let image = Bundle.main.appIconImage {
Image(uiImage: image)
.resizable()
.scaledToFit()
.frame(width: 96, height: 96)
.clipShape(RoundedRectangle(cornerRadius: 22, style: .continuous))
} else {
Text("DoubleFeel")
.font(.system(size: 28, weight: .semibold))
.foregroundStyle(.primary)
}
}
}
private extension Bundle {
var appIconImage: UIImage? {
if let image = UIImage(named: "AppIcon") {
return image
}
guard let icons = infoDictionary?["CFBundleIcons"] as? [String: Any],
let primaryIcon = icons["CFBundlePrimaryIcon"] as? [String: Any],
let iconFiles = primaryIcon["CFBundleIconFiles"] as? [String],
let iconName = iconFiles.last else {
return nil
}
return UIImage(named: iconName)
}
}