Commit 571eac67f5752379049a5d0279a65788cb06fef4

Authored by 权海
1 parent cd30be80

feat(ui):同步部分代码

@@ -27,24 +27,24 @@ final class HealthDataReader { @@ -27,24 +27,24 @@ final class HealthDataReader {
27 startDate: startDate, 27 startDate: startDate,
28 endDate: endDate 28 endDate: endDate
29 ) 29 )
30 - async let activeEnergy = fetchQuantitySamples( 30 + async let activeEnergy = fetchDailyCumulativeSamples(
31 identifier: .activeEnergyBurned, 31 identifier: .activeEnergyBurned,
32 dataType: .activeEnergy, 32 dataType: .activeEnergy,
33 unit: .kilocalorie(), 33 unit: .kilocalorie(),
34 startDate: startDate, 34 startDate: startDate,
35 endDate: endDate 35 endDate: endDate
36 ) 36 )
37 - async let exercise = fetchQuantitySamples( 37 + async let exercise = fetchDailyCumulativeSamples(
38 identifier: .appleExerciseTime, 38 identifier: .appleExerciseTime,
39 dataType: .exercise, 39 dataType: .exercise,
40 - unit: .minute(), 40 + unit: .second(),
41 startDate: startDate, 41 startDate: startDate,
42 endDate: endDate 42 endDate: endDate
43 ) 43 )
44 - async let stand = fetchQuantitySamples( 44 + async let stand = fetchDailyCumulativeSamples(
45 identifier: .appleStandTime, 45 identifier: .appleStandTime,
46 dataType: .stand, 46 dataType: .stand,
47 - unit: .minute(), 47 + unit: .second(),
48 startDate: startDate, 48 startDate: startDate,
49 endDate: endDate 49 endDate: endDate
50 ) 50 )
@@ -203,37 +203,40 @@ final class HealthDataReader { @@ -203,37 +203,40 @@ final class HealthDataReader {
203 } 203 }
204 204
205 func fetchActiveEnergyData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] { 205 func fetchActiveEnergyData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] {
206 - try await fetchQuantitySamples(  
207 - identifier: .activeEnergyBurned,  
208 - dataType: .activeEnergy,  
209 - unit: .kilocalorie(),  
210 - startDate: startDate,  
211 - endDate: endDate  
212 - ) 206 + try await fetchDailyCumulativeSamples(identifier: .activeEnergyBurned, dataType: .activeEnergy, unit: .kilocalorie(), startDate: startDate, endDate: endDate)
  207 +// try await fetchQuantitySamples(
  208 +// identifier: .activeEnergyBurned,
  209 +// dataType: .activeEnergy,
  210 +// unit: .kilocalorie(),
  211 +// startDate: startDate,
  212 +// endDate: endDate
  213 +// )
213 } 214 }
214 215
215 func fetchExerciseData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] { 216 func fetchExerciseData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] {
216 - try await fetchQuantitySamples(  
217 - identifier: .appleExerciseTime,  
218 - dataType: .exercise,  
219 - unit: .minute(),  
220 - startDate: startDate,  
221 - endDate: endDate  
222 - ) 217 + try await fetchDailyCumulativeSamples(identifier: .appleExerciseTime, dataType: .exercise, unit: .second(), startDate: startDate, endDate: endDate)
  218 +// try await fetchQuantitySamples(
  219 +// identifier: .appleExerciseTime,
  220 +// dataType: .exercise,
  221 +// unit: .minute(),
  222 +// startDate: startDate,
  223 +// endDate: endDate
  224 +// )
223 } 225 }
224 226
225 func fetchStandData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] { 227 func fetchStandData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] {
226 - try await fetchQuantitySamples(  
227 - identifier: .appleStandTime,  
228 - dataType: .stand,  
229 - unit: .minute(),  
230 - startDate: startDate,  
231 - endDate: endDate  
232 - ) 228 + try await fetchDailyCumulativeSamples(identifier: .appleStandTime, dataType: .stand, unit: .second(), startDate: startDate, endDate: endDate)
  229 +// try await fetchQuantitySamples(
  230 +// identifier: .appleStandTime,
  231 +// dataType: .stand,
  232 +// unit: .minute(),
  233 +// startDate: startDate,
  234 +// endDate: endDate
  235 +// )
233 } 236 }
234 237
235 func fetchStepCountData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] { 238 func fetchStepCountData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] {
236 - try await fetchDailyCumulativeSamples( 239 + try await fetchDailyCumulativeSamples(
237 identifier: .stepCount, 240 identifier: .stepCount,
238 dataType: .steps, 241 dataType: .steps,
239 unit: .count(), 242 unit: .count(),
@@ -336,6 +339,7 @@ final class HealthDataReader { @@ -336,6 +339,7 @@ final class HealthDataReader {
336 guard let type = NativeHealthTypeCatalog.quantity(identifier) else { 339 guard let type = NativeHealthTypeCatalog.quantity(identifier) else {
337 throw NativeHealthKitError.invalidType(identifier.rawValue) 340 throw NativeHealthKitError.invalidType(identifier.rawValue)
338 } 341 }
  342 +
339 let predicate = HKQuery.predicateForSamples( 343 let predicate = HKQuery.predicateForSamples(
340 withStart: startDate, 344 withStart: startDate,
341 end: endDate, 345 end: endDate,
@@ -143,20 +143,7 @@ final class HealthKitService { @@ -143,20 +143,7 @@ final class HealthKitService {
143 } 143 }
144 144
145 func refreshSharedWatchValues() async { 145 func refreshSharedWatchValues() async {
146 - do {  
147 - if let hrv = try await reader.fetchLatestHRV() {  
148 - AppGroupConstants.defaults?.set(hrv, forKey: AppGroupConstants.Key.latestHRV)  
149 - }  
150 - } catch {  
151 - // Keep the previous widget value when a single read fails.  
152 - }  
153 -  
154 - do {  
155 - let steps = try await reader.fetchTodayStepCount()  
156 - AppGroupConstants.defaults?.set(steps, forKey: AppGroupConstants.Key.latestStepCount)  
157 - } catch {  
158 - // Keep the previous widget value when a single read fails.  
159 - } 146 + _ = WatchConnectivityService.shared.sendCommandMessage(AppGroupMessageKey.statusPulseRefresh)
160 } 147 }
161 148
162 func fetchHrvData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] { 149 func fetchHrvData(startDate: Date, endDate: Date) async throws -> [NativeHealthDataPoint] {
@@ -58,7 +58,7 @@ final class NativeHealthDataUploader { @@ -58,7 +58,7 @@ final class NativeHealthDataUploader {
58 var sleepUploadSuccess = true 58 var sleepUploadSuccess = true
59 var errorMessages: [String] = [] 59 var errorMessages: [String] = []
60 60
61 - debugLog("uploadAll started, userId=\(AppShared.shared.userId ?? "<nil>")") 61 + debugLog("uploadAll started ")
62 do { 62 do {
63 let uploadTimeList = try await processLastUploadTime() 63 let uploadTimeList = try await processLastUploadTime()
64 64
@@ -512,10 +512,7 @@ struct NativeHealthUploadTimeRecorder { @@ -512,10 +512,7 @@ struct NativeHealthUploadTimeRecorder {
512 } 512 }
513 513
514 private static func cacheKeyForCurrentUser() -> String { 514 private static func cacheKeyForCurrentUser() -> String {
515 - let userId = AppShared.shared.userId  
516 - ?? AppShared.shared.userSummary?.meUserInfo?.id.map(String.init)  
517 - ?? AppGroupConstants.defaults?.string(forKey: AppGroupConstants.Key.userId)  
518 - ?? "" 515 + let userId = "\(AppShared.shared.userId ?? 0)"
519 return cacheKey + userId 516 return cacheKey + userId
520 } 517 }
521 } 518 }
@@ -54,7 +54,7 @@ final class PlatformHostApiImpl: PlatformHostApi { @@ -54,7 +54,7 @@ final class PlatformHostApiImpl: PlatformHostApi {
54 return false 54 return false
55 #endif 55 #endif
56 } 56 }
57 - 57 +
58 func nativeHandleUrl(urlString: String) throws -> Bool { 58 func nativeHandleUrl(urlString: String) throws -> Bool {
59 if let url = URL(string: urlString){ 59 if let url = URL(string: urlString){
60 UIApplication.shared.open(url) 60 UIApplication.shared.open(url)
@@ -62,7 +62,7 @@ final class PlatformHostApiImpl: PlatformHostApi { @@ -62,7 +62,7 @@ final class PlatformHostApiImpl: PlatformHostApi {
62 } 62 }
63 return false 63 return false
64 } 64 }
65 - 65 +
66 func requestUnhandedUrl() throws -> String? { 66 func requestUnhandedUrl() throws -> String? {
67 let unhandedUrl = AppShared.shared.unhandedUrl 67 let unhandedUrl = AppShared.shared.unhandedUrl
68 AppShared.shared.unhandedUrl = nil 68 AppShared.shared.unhandedUrl = nil
@@ -124,26 +124,23 @@ final class PlatformHostApiImpl: PlatformHostApi { @@ -124,26 +124,23 @@ final class PlatformHostApiImpl: PlatformHostApi {
124 124
125 // 登录 125 // 登录
126 func updateLoginInfo(jsonString: String, baseUrl: String) throws { 126 func updateLoginInfo(jsonString: String, baseUrl: String) throws {
127 - AppShared.shared.baseUrl = baseUrl  
128 if let data = jsonString.data(using: .utf8){ 127 if let data = jsonString.data(using: .utf8){
129 do{ 128 do{
130 let model = try JSONDecoder().decode(FlutterUserSummary.self, from: data) 129 let model = try JSONDecoder().decode(FlutterUserSummary.self, from: data)
131 - AppShared.shared.userSummary = model  
132 -  
133 - AppGroupConstants.defaults?.set(baseUrl, forKey: AppGroupConstants.Key.baseUrl)  
134 - if let userId = model.meUserInfo?.id.map(String.init), !userId.isEmpty {  
135 - AppGroupConstants.defaults?.set(userId, forKey: AppGroupConstants.Key.userId)  
136 - AppShared.shared.userId = userId  
137 - }  
138 if let token = model.accessToken{ 130 if let token = model.accessToken{
139 - AppGroupConstants.defaults?.set(token, forKey: AppGroupConstants.Key.token)  
140 // 刷新用户信息 131 // 刷新用户信息
141 - _ = WatchConnectivityService.shared.sendTextMessage(AppGroupMessageKey.login) 132 + let watchUser = UserInfoModel(userId: model.meUserInfo?.id, token: token, baseUrl: baseUrl)
  133 + do{
  134 + let data = try JSONEncoder().encode(watchUser)
  135 + AppGroupConstants.defaults?.set(data, forKey: AppGroupConstants.Key.myUserInfo)
  136 + AppShared.shared.login(userInfo: watchUser)
  137 + _ = WatchConnectivityService.shared.sendLoginInfoMessage(userInfo: watchUser)
  138 +
  139 + }catch{}
142 140
143 Task{ 141 Task{
144 await AppShared.shared.reportDeviceInfo() 142 await AppShared.shared.reportDeviceInfo()
145 } 143 }
146 - AppShared.shared.token = token  
147 } 144 }
148 }catch{ 145 }catch{
149 DebugLogger.log(desc: error.localizedDescription) 146 DebugLogger.log(desc: error.localizedDescription)
@@ -151,68 +148,24 @@ final class PlatformHostApiImpl: PlatformHostApi { @@ -151,68 +148,24 @@ final class PlatformHostApiImpl: PlatformHostApi {
151 } 148 }
152 } 149 }
153 150
154 - private static func extractUserId(from data: Data) -> String? {  
155 - guard let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {  
156 - return nil  
157 - }  
158 -  
159 - if let userId = stringify(json["userId"]) ?? stringify(json["user_id"]) {  
160 - return userId  
161 - }  
162 -  
163 - if let meUserInfo = json["me_user_info"] as? [String: Any] {  
164 - return stringify(meUserInfo["id"]) ?? stringify(meUserInfo["userId"]) ?? stringify(meUserInfo["user_id"])  
165 - }  
166 -  
167 - return nil  
168 - }  
169 -  
170 - private static func stringify(_ value: Any?) -> String? {  
171 - switch value {  
172 - case let value as String:  
173 - return value  
174 - case let value as Int:  
175 - return String(value)  
176 - case let value as Int64:  
177 - return String(value)  
178 - case let value as NSNumber:  
179 - return value.stringValue  
180 - default:  
181 - return nil  
182 - }  
183 - }  
184 -  
185 // 退出登录 151 // 退出登录
186 func logout() throws { 152 func logout() throws {
187 // 清除所有数据 153 // 清除所有数据
188 - AppGroupConstants.defaults?.dictionaryRepresentation().keys.forEach({  
189 - AppGroupConstants.defaults?.removeObject(forKey: $0)  
190 - })  
191 AppShared.shared.logout() 154 AppShared.shared.logout()
192 - _ = WatchConnectivityService.shared.sendTextMessage(AppGroupMessageKey.logout) 155 + _ = WatchConnectivityService.shared.sendLoginInfoMessage(userInfo: nil)
193 } 156 }
194 // 刷新会员 157 // 刷新会员
195 func refreshVip() throws { 158 func refreshVip() throws {
196 - _ = WatchConnectivityService.shared.sendTextMessage(AppGroupMessageKey.reloadVip) 159 + _ = WatchConnectivityService.shared.sendCommandMessage(AppGroupMessageKey.reloadVip)
197 } 160 }
198 // 刷新watch app 和表盘 161 // 刷新watch app 和表盘
199 func refreshWatchAppAndWidgets() throws { 162 func refreshWatchAppAndWidgets() throws {
200 - _ = WatchConnectivityService.shared.sendTextMessage(AppGroupMessageKey.reloadAll) 163 + _ = WatchConnectivityService.shared.sendCommandMessage(AppGroupMessageKey.reloadAll)
201 } 164 }
202 // 设置/移除 好友显示到表盘 165 // 设置/移除 好友显示到表盘
203 func updateWatchOtherUserInfo(otherInfo: WatchAppOtherInfo?) throws { 166 func updateWatchOtherUserInfo(otherInfo: WatchAppOtherInfo?) throws {
204 - defer{  
205 - try? refreshWatchAppAndWidgets()  
206 - }  
207 -  
208 - guard let otherInfo else{  
209 - AppGroupConstants.defaults?.removeObject(forKey: AppGroupConstants.Key.showOtherUserInfo)  
210 - return  
211 - }  
212 -  
213 do{ 167 do{
214 - let data = try JSONSerialization.data(withJSONObject: otherInfo.toParams())  
215 - AppGroupConstants.defaults?.set(data, forKey: AppGroupConstants.Key.showOtherUserInfo) 168 + try refreshWatchAppAndWidgets()
216 }catch{ 169 }catch{
217 170
218 } 171 }
@@ -37,7 +37,7 @@ final class WearEngineHostApiImpl: WearEngineHostApi { @@ -37,7 +37,7 @@ final class WearEngineHostApiImpl: WearEngineHostApi {
37 } 37 }
38 38
39 func sendWatchSyncPayload(jsonPayload: String) throws -> Bool { 39 func sendWatchSyncPayload(jsonPayload: String) throws -> Bool {
40 - WatchConnectivityService.shared.sendWatchThemeChangedMessage() 40 + WatchConnectivityService.shared.sendWatchThemeChangedMessage(json: jsonPayload)
41 } 41 }
42 42
43 func removeBackground(originImagePath: String, completion: @escaping (Result<String?, any Error>) -> Void) { 43 func removeBackground(originImagePath: String, completion: @escaping (Result<String?, any Error>) -> Void) {
@@ -6,48 +6,74 @@ enum AppGroupConstants { @@ -6,48 +6,74 @@ enum AppGroupConstants {
6 static let identifier = "group.com.luiz.doublefeel.watchkitapp" 6 static let identifier = "group.com.luiz.doublefeel.watchkitapp"
7 7
8 enum Key { 8 enum Key {
9 - static let deviceInfo = "deviceInfo"  
10 - static let appDeviceToken = "appDeviceToken"  
11 - static let watchDeviceToken = "watchDeviceToken" 9 + static let deviceInfo = "deviceInfo"
  10 + static let appDeviceToken = "appDeviceToken"
  11 + static let watchDeviceToken = "watchDeviceToken"
12 12
13 static let latestHealthData = "latestHealthData" 13 static let latestHealthData = "latestHealthData"
14 static let acitivyTarget = "acitivyTarget" 14 static let acitivyTarget = "acitivyTarget"
15 - static let latestHRV = "latestHRV"  
16 - static let latestHRVBaseline = "latestHRVBaseline"  
17 - static let latestStepCount = "latestStepCount"  
18 static let myWatchTheme = "myWatchTheme" 15 static let myWatchTheme = "myWatchTheme"
19 static let otherWatchTheme = "otherWatchTheme" 16 static let otherWatchTheme = "otherWatchTheme"
20 17
21 static let myTodayStandTime = "myTodayStandTime" 18 static let myTodayStandTime = "myTodayStandTime"
22 - static let myTodayExerciseTime = "myTodayExerciseTime"  
23 - static let myTodayActiveEnergy = "myTodayActiveEnergy"  
24 - static let myTodayStepCount = "myTodayStepCount"  
25 - static let myLatestHeartRate = "myLatestHeartRate" 19 + static let myTodayExerciseTime = "myTodayExerciseTime"
  20 + static let myTodayActiveEnergy = "myTodayActiveEnergy"
  21 + static let myTodayStepCount = "myTodayStepCount"
  22 + static let myLatestHeartRate = "myLatestHeartRate"
26 23
27 - static let myUserCharacter = "myUserCharacter"  
28 static let myUserInfo = "myUserInfo" 24 static let myUserInfo = "myUserInfo"
29 -  
30 - static let baseUrl = "baseURL"  
31 - static let userId = "userId"  
32 - static let token = "accessToken" 25 + /// flutter登录信息
  26 + static let userSummary = "flutterUserSummary"
  27 + /// 显示好友信息
33 static let showOtherUserInfo = "showOtherUserInfo" 28 static let showOtherUserInfo = "showOtherUserInfo"
34 - static let vipInfo = "vipInfo"  
35 - static let showRealtimeHRV = "showRealtimeHRV" 29 + /// 会员信息
  30 + static let vipInfo = "vipInfo"
  31 + static let showRealtimeHRV = "showRealtimeHRV"
36 } 32 }
37 33
38 static var defaults: UserDefaults? { 34 static var defaults: UserDefaults? {
39 UserDefaults(suiteName: identifier) 35 UserDefaults(suiteName: identifier)
40 } 36 }
  37 + static func clearAllData(){
  38 + defaults?.removePersistentDomain(forName: identifier)
  39 + defaults?.synchronize()
  40 + }
  41 +
  42 + static func clearUserSessionData() {
  43 + [
  44 + Key.latestHealthData,
  45 + Key.acitivyTarget,
  46 + Key.myWatchTheme,
  47 + Key.otherWatchTheme,
  48 + Key.myTodayStandTime,
  49 + Key.myTodayExerciseTime,
  50 + Key.myTodayActiveEnergy,
  51 + Key.myTodayStepCount,
  52 + Key.myLatestHeartRate,
  53 + Key.myUserInfo,
  54 + Key.userSummary,
  55 + Key.showOtherUserInfo,
  56 + Key.vipInfo,
  57 + Key.showRealtimeHRV
  58 + ].forEach { key in
  59 + defaults?.removeObject(forKey: key)
  60 + }
  61 + defaults?.synchronize()
  62 + }
41 } 63 }
42 64
43 -struct AppGroupMessageKey {  
44 - static let login = "login"  
45 - static let logout = "logout"  
46 65
47 - static let reloadAll = "reloadAll"  
48 - static let reloadVip = "reloadVip"  
49 - static let reloadTheme = "reloadTheme"  
50 66
51 - static let statusPulseRefresh = "statusPulseRefresh"  
52 - static let watchDeviceToken = "watchDeviceToken" 67 +struct AppGroupMessageKey{
  68 + static let login = "login"
  69 + static let logout = "logout"
  70 + static let requestLoginState = "requestLoginState"
  71 +
  72 + static let reloadAll = "reloadAll"
  73 + static let reloadVip = "reloadVip"
  74 + static let reloadTheme = "reloadTheme"
  75 +
  76 + // 手表通知app刷新连接状态
  77 + static let statusPulseRefresh = "statusPulseRefresh"
  78 + static let watchDeviceToken = "watchDeviceToken"
53 } 79 }
@@ -11,43 +11,60 @@ let kAppId = "6747254434" @@ -11,43 +11,60 @@ let kAppId = "6747254434"
11 11
12 class AppShared{ 12 class AppShared{
13 static let shared = AppShared() 13 static let shared = AppShared()
14 - 14 +
15 var isLogin: Bool{ 15 var isLogin: Bool{
16 - baseUrl.hasPrefix("http") && (userSummary?.accessToken?.count ?? 0 > 0) 16 + baseUrl.hasPrefix("http") && (token?.count ?? 0 > 0)
17 } 17 }
18 - 18 +
19 private(set) var agent = UserAgent() 19 private(set) var agent = UserAgent()
20 private(set) var payment = ApplePayment() 20 private(set) var payment = ApplePayment()
21 -  
22 - var baseUrl: String = "https://api.doublefeel.cn"  
23 - var token: String?  
24 - var userId: String?  
25 - var userSummary: FlutterUserSummary?  
26 - 21 +
  22 + var baseUrl: String{
  23 + myUserInfo?.baseUrl ?? "https://api.doublefeel.cn"
  24 + }
  25 + var token: String?{
  26 + myUserInfo?.token
  27 + }
  28 + var userId: Int?{
  29 + myUserInfo?.userId
  30 + }
  31 + private(set) var myUserInfo: UserInfoModel?{
  32 + didSet{
  33 + WatchConnectivityService.shared.transferLoginInfo(userInfo: myUserInfo)
  34 + }
  35 + }
  36 +
27 /// 冷启动待处理的urlstring 37 /// 冷启动待处理的urlstring
28 var unhandedUrl: String? 38 var unhandedUrl: String?
29 - 39 +
30 private init(){ 40 private init(){
31 - login() 41 + login(userInfo: nil)
32 } 42 }
33 43
34 - func login(){  
35 - token = AppGroupConstants.defaults?.string(forKey: AppGroupConstants.Key.token)  
36 - userId = AppGroupConstants.defaults?.string(forKey: AppGroupConstants.Key.userId)  
37 - if let url = AppGroupConstants.defaults?.string(forKey: AppGroupConstants.Key.baseUrl){  
38 - baseUrl = url 44 + func login(userInfo: UserInfoModel?){
  45 + if let userInfo{
  46 + myUserInfo = userInfo
  47 + return
  48 + }
  49 +
  50 + guard let data = AppGroupConstants.defaults?.data(forKey: AppGroupConstants.Key.myUserInfo) else{
  51 + return
  52 + }
  53 + do{
  54 + myUserInfo = try JSONDecoder().decode(UserInfoModel.self, from: data)
  55 + }catch{
  56 +
39 } 57 }
40 } 58 }
41 59
42 func logout(){ 60 func logout(){
43 - token = nil  
44 - userId = nil  
45 - userSummary = nil 61 + myUserInfo = nil
  62 + AppGroupConstants.defaults?.removeObject(forKey: AppGroupConstants.Key.myUserInfo)
46 } 63 }
47 64
48 func reportDeviceInfo() async{ 65 func reportDeviceInfo() async{
49 guard AppShared.shared.isLogin else { 66 guard AppShared.shared.isLogin else {
50 - print("not login") 67 + print("reportDeviceInfo not login")
51 return 68 return
52 } 69 }
53 70
@@ -56,7 +73,7 @@ class AppShared{ @@ -56,7 +73,7 @@ class AppShared{
56 print("error invalid baseUrl \(AppShared.shared.baseUrl)") 73 print("error invalid baseUrl \(AppShared.shared.baseUrl)")
57 return 74 return
58 } 75 }
59 - 76 + /// 推送平台 choices: [(1, '小米'), (2, '华为'), (3, 'oppo'), (4, 'vivo'), (5, '荣耀'), (100, '苹果')]
60 var params: [String: String] = ["push_platform": "100"] 77 var params: [String: String] = ["push_platform": "100"]
61 params["device_info"] = AppShared.shared.agent.deviceInfo 78 params["device_info"] = AppShared.shared.agent.deviceInfo
62 params["device_token"] = AppGroupConstants.defaults?.string(forKey: AppGroupConstants.Key.appDeviceToken) 79 params["device_token"] = AppGroupConstants.defaults?.string(forKey: AppGroupConstants.Key.appDeviceToken)
@@ -79,8 +96,13 @@ class AppShared{ @@ -79,8 +96,13 @@ class AppShared{
79 96
80 let bodyText = String(data: data, encoding: .utf8) ?? "" 97 let bodyText = String(data: data, encoding: .utf8) ?? ""
81 print("upload device info status=\(httpResponse.statusCode), body=\(bodyText)") 98 print("upload device info status=\(httpResponse.statusCode), body=\(bodyText)")
  99 +
  100 + guard (200..<300).contains(httpResponse.statusCode) else {
  101 + return
  102 + }
82 } catch { 103 } catch {
83 print("upload device info failed: \(error.localizedDescription)") 104 print("upload device info failed: \(error.localizedDescription)")
84 } 105 }
  106 +
85 } 107 }
86 } 108 }
@@ -197,18 +197,18 @@ class ApplePayment { @@ -197,18 +197,18 @@ class ApplePayment {
197 } 197 }
198 198
199 var params: [String: String] = [ 199 var params: [String: String] = [
200 - "productId": productId,  
201 - "appAccountToken": appAccountToken 200 + "product_id": productId,
  201 + "app_account_token": appAccountToken
202 ] 202 ]
203 - params["originalTransactionId"] = originalTransactionId  
204 - params["transactionId"] = transactionId 203 + params["original_transaction_id"] = originalTransactionId
  204 + params["transaction_id"] = transactionId
205 205
206 var request = URLRequest(url: url) 206 var request = URLRequest(url: url)
207 request.httpMethod = "POST" 207 request.httpMethod = "POST"
208 request.timeoutInterval = 30 208 request.timeoutInterval = 30
209 request.setValue("application/json", forHTTPHeaderField: "Content-Type") 209 request.setValue("application/json", forHTTPHeaderField: "Content-Type")
210 request.setValue("application/json", forHTTPHeaderField: "Accept") 210 request.setValue("application/json", forHTTPHeaderField: "Accept")
211 - request.setValue(AppShared.shared.userSummary?.accessToken, forHTTPHeaderField: "access_token") 211 + request.setValue(AppShared.shared.token, forHTTPHeaderField: "access_token")
212 request.setValue(AppShared.shared.agent.finalUA, forHTTPHeaderField: "User-Agent") 212 request.setValue(AppShared.shared.agent.finalUA, forHTTPHeaderField: "User-Agent")
213 213
214 do { 214 do {
@@ -249,7 +249,7 @@ class ApplePayment { @@ -249,7 +249,7 @@ class ApplePayment {
249 request.timeoutInterval = 30 249 request.timeoutInterval = 30
250 request.setValue("application/json", forHTTPHeaderField: "Content-Type") 250 request.setValue("application/json", forHTTPHeaderField: "Content-Type")
251 request.setValue("application/json", forHTTPHeaderField: "Accept") 251 request.setValue("application/json", forHTTPHeaderField: "Accept")
252 - request.setValue(AppShared.shared.userSummary?.accessToken, forHTTPHeaderField: "access_token") 252 + request.setValue(AppShared.shared.token, forHTTPHeaderField: "access_token")
253 request.setValue(AppShared.shared.agent.finalUA, forHTTPHeaderField: "User-Agent") 253 request.setValue(AppShared.shared.agent.finalUA, forHTTPHeaderField: "User-Agent")
254 254
255 do { 255 do {
@@ -343,8 +343,8 @@ extension ApplePayment { @@ -343,8 +343,8 @@ extension ApplePayment {
343 return Task.detached { 343 return Task.detached {
344 for await result in Transaction.updates { 344 for await result in Transaction.updates {
345 do { 345 do {
346 - let transaction = try await self.checkVerified(result)  
347 - if await AppShared.shared.isLogin{ 346 + let transaction = try self.checkVerified(result)
  347 + if AppShared.shared.isLogin{
348 let success = await self.verifyWithServer(appAccountToken: transaction.appAccountToken?.uuidString ?? "", originalTransactionId: String(transaction.originalID), transactionId: String(transaction.id), productId: transaction.productID) 348 let success = await self.verifyWithServer(appAccountToken: transaction.appAccountToken?.uuidString ?? "", originalTransactionId: String(transaction.originalID), transactionId: String(transaction.id), productId: transaction.productID)
349 // 更新客户产品状态 349 // 更新客户产品状态
350 if success{ 350 if success{
@@ -6,7 +6,6 @@ @@ -6,7 +6,6 @@
6 // 6 //
7 7
8 import Foundation 8 import Foundation
9 -import UIKit  
10 import WebKit 9 import WebKit
11 10
12 extension UIApplication { 11 extension UIApplication {
@@ -40,10 +39,10 @@ class UserAgent { @@ -40,10 +39,10 @@ class UserAgent {
40 func customUA() -> String { 39 func customUA() -> String {
41 let version = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey) as! NSString 40 let version = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), kCFBundleVersionKey) as! NSString
42 let bundle = Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? "" 41 let bundle = Bundle.main.infoDictionary?["CFBundleShortVersionString"] ?? ""
43 - let hardware = Self.hardwareString() 42 + let hardware = hardwareString()
44 deviceInfo = hardware 43 deviceInfo = hardware
45 AppGroupConstants.defaults?.set(hardware, forKey: AppGroupConstants.Key.deviceInfo) 44 AppGroupConstants.defaults?.set(hardware, forKey: AppGroupConstants.Key.deviceInfo)
46 - 45 +
47 let systemVersion = UIDevice.current.systemVersion 46 let systemVersion = UIDevice.current.systemVersion
48 let height = UIApplication.shared.currentScreen?.bounds.height ?? 0 47 let height = UIApplication.shared.currentScreen?.bounds.height ?? 0
49 let width = UIApplication.shared.currentScreen?.bounds.width ?? 0 48 let width = UIApplication.shared.currentScreen?.bounds.width ?? 0
@@ -55,7 +54,7 @@ class UserAgent { @@ -55,7 +54,7 @@ class UserAgent {
55 return ua 54 return ua
56 } 55 }
57 56
58 - private static func hardwareString() -> String { 57 + private func hardwareString() -> String {
59 var systemInfo = utsname() 58 var systemInfo = utsname()
60 uname(&systemInfo) 59 uname(&systemInfo)
61 60
  1 +//
  2 +// UserInfoModel.swift
  3 +// hippo
  4 +//
  5 +// Created by 权海 on 2026/6/26.
  6 +//
  7 +
  8 +import Foundation
  9 +
  10 +struct UserInfoModel: Codable, Equatable{
  11 + var userId: Int?
  12 + var token: String?
  13 + var baseUrl: String?
  14 +
  15 + init(userId: Int? = nil, token: String? = nil, baseUrl: String? = nil) {
  16 + self.userId = userId
  17 + self.token = token
  18 + self.baseUrl = baseUrl
  19 + }
  20 +
  21 + func toJson() -> [String: Any]{
  22 + do{
  23 + let data = try JSONEncoder().encode(self)
  24 + return try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as! [String : Any]
  25 + }catch{
  26 + return [:]
  27 + }
  28 + }
  29 +
  30 + var jsonString: String? {
  31 + guard let data = try? JSONEncoder().encode(self) else {
  32 + return nil
  33 + }
  34 + return String(data: data, encoding: .utf8)
  35 + }
  36 +}
@@ -20,7 +20,9 @@ final class WatchConnectivityService: NSObject { @@ -20,7 +20,9 @@ final class WatchConnectivityService: NSObject {
20 guard WCSession.isSupported() else { return false } 20 guard WCSession.isSupported() else { return false }
21 let session = WCSession.default 21 let session = WCSession.default
22 session.delegate = self 22 session.delegate = self
23 - session.activate() 23 + if session.activationState == .notActivated {
  24 + session.activate()
  25 + }
24 refreshState() 26 refreshState()
25 return true 27 return true
26 } 28 }
@@ -55,72 +57,62 @@ final class WatchConnectivityService: NSObject { @@ -55,72 +57,62 @@ final class WatchConnectivityService: NSObject {
55 ) 57 )
56 } 58 }
57 59
58 - func sendTextMessage(_ message: String) -> Bool {  
59 - activate()  
60 - guard WCSession.default.isReachable else { return false }  
61 - WCSession.default.sendMessage(["message": message], replyHandler: nil) { error in  
62 - print("Watch message failed: \(error.localizedDescription)") 60 + func transferLoginInfo(userInfo: UserInfoModel?){
  61 + guard activate() else { return }
  62 + let params = loginPayload(userInfo: userInfo)
  63 + do{
  64 + try WCSession.default.updateApplicationContext(params)
  65 + print("Send Watch updateApplicationContext: \(params)")
  66 + }catch{
  67 + print("Error Send Watch updateApplicationContext: \(params)")
  68 + }
63 } 69 }
64 - return true  
65 - }  
66 70
67 - func sendCommandMessage(_ message: String) -> Bool {  
68 - activate()  
69 - guard WCSession.default.isReachable else { return false }  
70 - WCSession.default.sendMessage(["command": message], replyHandler: nil) { error in  
71 - print("Watch message send failed: \(error.localizedDescription)")  
72 - }  
73 - return true  
74 - } 71 + func sendLoginInfoMessage(userInfo: UserInfoModel?) -> Bool {
  72 + guard let userInfo else {
  73 + return sendCommandMessage(AppGroupMessageKey.logout)
  74 + }
75 75
76 - func syncPayload(jsonPayload: String) -> Bool {  
77 - activate()  
78 - guard let data = jsonPayload.data(using: .utf8),  
79 - let object = try? JSONSerialization.jsonObject(with: data),  
80 - let payload = object as? [String: Any] else {  
81 - return false 76 + guard let json = userInfo.jsonString else {
  77 + return false
  78 + }
  79 + return sendCommandMessage(AppGroupMessageKey.login, json: json)
82 } 80 }
83 81
84 - persistThemeIfPresent(payload: payload, originalJSON: jsonPayload)  
85 -  
86 - guard WCSession.isSupported(), WCSession.default.isPaired, WCSession.default.isWatchAppInstalled else {  
87 - return false 82 + private func loginPayload(userInfo: UserInfoModel?) -> [String: Any] {
  83 + guard var params = userInfo?.toJson(), !params.isEmpty else {
  84 + return ["command": AppGroupMessageKey.logout]
  85 + }
  86 + params["command"] = AppGroupMessageKey.login
  87 + return params
88 } 88 }
89 89
90 - WCSession.default.transferUserInfo(payload)  
91 - try? WCSession.default.updateApplicationContext(payload)  
92 - sendWatchThemeChangedMessageIfNeeded(payload: payload) 90 + func sendTextMessage(_ message: String) -> Bool {
  91 + activate()
  92 + guard WCSession.default.isReachable else { return false }
  93 + WCSession.default.sendMessage(["message": message], replyHandler: nil) { error in
  94 + print("Watch message failed: \(error.localizedDescription)")
  95 + }
93 return true 96 return true
94 } 97 }
95 98
96 - func sendWatchThemeChangedMessage() -> Bool {  
97 - sendCommandMessage(AppGroupMessageKey.reloadTheme)  
98 - } 99 + func sendCommandMessage(_ message: String, json: String? = nil) -> Bool {
  100 + activate()
  101 + guard WCSession.default.isReachable else { return false }
  102 + var params: [String: Any] = [
  103 + "command": message
  104 + ]
  105 + params["json"] = json
99 106
100 - private func sendWatchThemeChangedMessageIfNeeded(payload: [String: Any]) {  
101 - if payload["myWatchTheme"] != nil || payload["watchTheme"] != nil || payload["theme"] != nil {  
102 - sendWatchThemeChangedMessage()  
103 - }  
104 - }  
105 -  
106 - private func persistThemeIfPresent(payload: [String: Any], originalJSON: String) {  
107 - if let theme = payload["myWatchTheme"] as? String {  
108 - WatchThemeStore.shared.saveThemeJSONString(theme)  
109 - return 107 + WCSession.default.sendMessage(params, replyHandler: nil) { error in
  108 + print("Watch message send failed: \(error.localizedDescription)")
  109 + }
  110 + return true
110 } 111 }
111 112
112 - let themeObject = payload["watchTheme"] ?? payload["theme"]  
113 - guard let themeObject,  
114 - JSONSerialization.isValidJSONObject(themeObject),  
115 - let data = try? JSONSerialization.data(withJSONObject: themeObject),  
116 - let json = String(data: data, encoding: .utf8) else {  
117 - if payload["isWatchTheme"] as? Bool == true {  
118 - WatchThemeStore.shared.saveThemeJSONString(originalJSON)  
119 - }  
120 - return 113 + func sendWatchThemeChangedMessage(json: String) -> Bool{
  114 + sendCommandMessage(AppGroupMessageKey.reloadTheme, json: json)
121 } 115 }
122 - WatchThemeStore.shared.saveThemeJSONString(json)  
123 - }  
124 } 116 }
125 117
126 extension WatchConnectivityService: WCSessionDelegate { 118 extension WatchConnectivityService: WCSessionDelegate {
@@ -132,6 +124,8 @@ extension WatchConnectivityService: WCSessionDelegate { @@ -132,6 +124,8 @@ extension WatchConnectivityService: WCSessionDelegate {
132 refreshState() 124 refreshState()
133 if let error { 125 if let error {
134 print("WatchConnectivity activation failed: \(error.localizedDescription)") 126 print("WatchConnectivity activation failed: \(error.localizedDescription)")
  127 + }else{
  128 + transferLoginInfo(userInfo: AppShared.shared.myUserInfo)
135 } 129 }
136 } 130 }
137 131
@@ -146,12 +140,17 @@ extension WatchConnectivityService: WCSessionDelegate { @@ -146,12 +140,17 @@ extension WatchConnectivityService: WCSessionDelegate {
146 140
147 func sessionReachabilityDidChange(_ session: WCSession) { 141 func sessionReachabilityDidChange(_ session: WCSession) {
148 refreshState() 142 refreshState()
  143 + if session.isReachable {
  144 + _ = sendLoginInfoMessage(userInfo: AppShared.shared.myUserInfo)
  145 + }
149 } 146 }
150 147
151 func session(_ session: WCSession, didReceiveMessage message: [String: Any]) { 148 func session(_ session: WCSession, didReceiveMessage message: [String: Any]) {
152 - if message["command"] as? String == "statusPulseRefresh" { 149 + if message["command"] as? String == AppGroupMessageKey.watchDeviceToken,
  150 + let token = message["token"] as? String {
  151 + AppGroupConstants.defaults?.set(token, forKey: AppGroupConstants.Key.watchDeviceToken)
153 Task { 152 Task {
154 - await HealthKitService.shared.refreshSharedWatchValues() 153 + await AppShared.shared.reportDeviceInfo()
155 } 154 }
156 } 155 }
157 } 156 }
@@ -161,11 +160,22 @@ extension WatchConnectivityService: WCSessionDelegate { @@ -161,11 +160,22 @@ extension WatchConnectivityService: WCSessionDelegate {
161 didReceiveMessage message: [String: Any], 160 didReceiveMessage message: [String: Any],
162 replyHandler: @escaping ([String: Any]) -> Void 161 replyHandler: @escaping ([String: Any]) -> Void
163 ) { 162 ) {
164 - if message["command"] as? String == "statusPulseRefresh" { 163 + if message["command"] as? String == AppGroupMessageKey.requestLoginState {
  164 + let userInfo = AppShared.shared.myUserInfo
  165 + var reply = loginPayload(userInfo: userInfo)
  166 + if let userInfo, let json = userInfo.jsonString {
  167 + reply["json"] = json
  168 + }
  169 + replyHandler(reply)
  170 + } else if message["command"] as? String == AppGroupMessageKey.watchDeviceToken,
  171 + let token = message["token"] as? String {
  172 + AppGroupConstants.defaults?.set(token, forKey: AppGroupConstants.Key.watchDeviceToken)
165 Task { 173 Task {
166 - await HealthKitService.shared.refreshSharedWatchValues()  
167 - replyHandler(["success": true]) 174 + await AppShared.shared.reportDeviceInfo()
168 } 175 }
  176 + replyHandler(["success": true])
  177 + } else if message["command"] as? String == AppGroupMessageKey.statusPulseRefresh {
  178 + replyHandler(["success": true])
169 } else { 179 } else {
170 replyHandler(["success": true]) 180 replyHandler(["success": true])
171 } 181 }