PlatformHostApiImpl.swift 10.4 KB
import Foundation
import AuthenticationServices
import StoreKit
import UIKit
import WebKit

class PriceFormatter{
    static func formatPrice(_ price: Decimal, currencyCode: String) -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.currencyCode = currencyCode
        formatter.minimumFractionDigits = 2
        formatter.maximumFractionDigits = 2

        return formatter.string(from: price as NSDecimalNumber) ?? "\(price)"
    }

    static func currencySymbol(for currencyCode: String) -> String {

        let formatter = NumberFormatter()

        formatter.numberStyle = .currency

        formatter.currencyCode = currencyCode

//        formatter.locale = Locale(identifier: "zh_CN")

        return formatter.currencySymbol

    }
}

extension WatchAppOtherInfo{
    func toParams() -> [String: Any]{
        var json: [String: Any] = [:]
        json["userId"] = userId
        json["nickname"] = nickname
        json["markName"] = markName
        return json
    }
}

/**
 * PlatformApi iOS implementation.
 *
 * 组装完整 User-Agent格式与 Android 端保持一致
 * `{systemWebViewUA} doublefeel/{versionCode}({versionName})(Apple##Apple##{model}; iOS{osVersion}; {height}x{width})(huawei)`
 */
final class PlatformHostApiImpl: PlatformHostApi {
    func isDebugEnvoriment() throws -> Bool {
        #if DEBUG || CI_ENV
        return true
        #else
        return false
        #endif
    }

    func nativeHandleUrl(urlString: String) throws -> Bool {
        if let url = URL(string: urlString){
            UIApplication.shared.open(url)
            return true
        }
        return false
    }

    func requestUnhandedUrl() throws -> String? {
        let unhandedUrl = AppShared.shared.unhandedUrl
        AppShared.shared.unhandedUrl = nil
        return unhandedUrl
    }

    func jumpAppSetting() throws -> Bool {
        if let url = URL(string: UIApplication.openSettingsURLString){
            UIApplication.shared.open(url)
            return true
        }
        return false
    }

    func requestAppReview(completion: @escaping (Result<Bool, any Error>) -> Void) {
//        Task { @MainActor in
//            guard let windowScene = UIApplication.shared.connectedScenes
//                .compactMap({ $0 as? UIWindowScene })
//                .first(where: { $0.activationState == .foregroundActive }) else {
//                completion(.success(false))
//                return
//            }
//
//            // App Store controls whether the prompt is actually displayed.
//            AppStore.requestReview(in: windowScene)
//            completion(.success(true))
//        }
        let urlString = "itms-apps://itunes.apple.com/app/id\(kAppId)?action=write-review"
        guard let url = URL(string: urlString) else{
            return
        }
        if UIApplication.shared.canOpenURL(url){
            UIApplication.shared.open(url)
        }
    }

    func uploadFile(filePath: String, resourceType: HResourceType, completion: @escaping (Result<String?, any Error>) -> Void) {
        let obsResourceType: HuaweiOBSResourceType
        switch resourceType {
        case .image:
            obsResourceType = .image
        case .video:
            obsResourceType = .video
        }

        Task {
            do {
                let fileURL = try await HuaweiOBSUploader.shared.upload(
                    filePath: filePath,
                    resourceType: obsResourceType
                )
                completion(.success(fileURL))
            } catch {
                DebugLogger.log(desc: "uploadFile error: \(error.localizedDescription)")
                completion(.failure(error))
            }
        }
    }

    // 登录
    func updateLoginInfo(jsonString: String, baseUrl: String) throws {
        if let data = jsonString.data(using: .utf8){
            do{
                let model = try JSONDecoder().decode(FlutterUserSummary.self, from: data)
                if let token = model.accessToken{
                    // 刷新用户信息
                    let watchUser = UserInfoModel(userId: model.meUserInfo?.id, token: token, baseUrl: baseUrl)
                    do{
                        let data = try JSONEncoder().encode(watchUser)
                        AppGroupConstants.defaults?.set(data, forKey: AppGroupConstants.Key.myUserInfo)
                        AppShared.shared.login(userInfo: watchUser)
                        _ = WatchConnectivityService.shared.sendLoginInfoMessage(userInfo: watchUser)

                    }catch{}

                    Task{
                        await AppShared.shared.reportDeviceInfo()
                    }
                }
            }catch{
                DebugLogger.log(desc: error.localizedDescription)
            }
        }
    }

    // 退出登录
    func logout() throws {
        // 清除所有数据
        AppShared.shared.logout()
        _ = WatchConnectivityService.shared.sendLoginInfoMessage(userInfo: nil)
    }
    // 刷新会员
    func refreshVip() throws {
        _ = WatchConnectivityService.shared.sendCommandMessage(AppGroupMessageKey.reloadVip)
    }
    // 刷新watch app 和表盘
    func refreshWatchAppAndWidgets() throws {
        _ = WatchConnectivityService.shared.sendCommandMessage(AppGroupMessageKey.reloadAll)
    }
    // 设置/移除 好友显示到表盘
    func updateWatchOtherUserInfo(otherInfo: WatchAppOtherInfo?) throws {
        do{
            try refreshWatchAppAndWidgets()
        }catch{

        }
    }

    func performRestore(completion: @escaping (Result<Bool, any Error>) -> Void) {
        Task{
            let success = await AppShared.shared.payment.restore()
            completion(.success(success))
        }
    }

    func requestAppleProductInfo(productId: String, baseUnit: Int64, completion: @escaping (Result<AppleProductInfo?, any Error>) -> Void) {
        Task {
            do {
                let product = try await AppShared.shared.payment.requestProducts(productId)
                let isTrialPeriod = await AppShared.shared.payment.isFreeTrail(product: product)
                let originPrice = product.price
                var displayPrice = product.displayPrice
                var price = originPrice
                if let introductoryOffer = product.subscription?.introductoryOffer{
                    price = introductoryOffer.price
                    displayPrice = introductoryOffer.displayPrice
                }else if let offer = product.subscription?.promotionalOffers.first(where: { $0.id == product.id }){
                    price = offer.price
                    displayPrice = offer.displayPrice
                }

                let unitPrice = originPrice/Decimal(baseUnit)
                let currencyCode = product.priceFormatStyle.currencyCode

                let productInfo = AppleProductInfo(
                    productId: product.id,
                    originPriceDescription: product.displayPrice,
                    priceDescription: displayPrice,
                    originPrice: NSDecimalNumber(decimal: originPrice * 100).doubleValue,
                    price: NSDecimalNumber(decimal: price * 100).doubleValue,
                    unitPrice: PriceFormatter.formatPrice(unitPrice, currencyCode: currencyCode),
                    currencyCode: PriceFormatter.currencySymbol(for: currencyCode),
                    isTrialPeriod: isTrialPeriod
                )
                print("[PlatformHostApiImpl.requestAppleProductInfo] return: \(productInfo)")
                completion(.success(productInfo))
            } catch StoreKitError.productNotFound {
                print("[PlatformHostApiImpl.requestAppleProductInfo] return: nil")
                completion(.success(nil))
            } catch {
                print("[PlatformHostApiImpl.requestAppleProductInfo] error: \(error.localizedDescription)")
                completion(.failure(error))
            }
        }
    }

    func performApplePayment(productId: String, uuid: String, completion: @escaping (Result<AppleProductPaymentResult?, any Error>) -> Void) {
        Task {
            let result = await AppShared.shared.payment.purchase(productId, uuidString: uuid)
            print("[PlatformHostApiImpl.performApplePayment] return: \(result)")
            completion(.success(result))
        }
    }

    private var appleSignInCoordinator: AppleSignInCoordinator?
//    private var authorizationController: ASAuthorizationController?

    func requestAppleSignIn(completion: @escaping (Result<AppleSignInModel?, any Error>) -> Void) {
        DispatchQueue.main.async { [weak self] in
            guard let self else {
                print("[PlatformHostApiImpl.requestAppleSignIn] error: \(PlatformHostApiError.deallocated.localizedDescription)")
                completion(.failure(PlatformHostApiError.deallocated))
                return
            }

            let provider = ASAuthorizationAppleIDProvider()
            let request = provider.createRequest()
            request.requestedScopes = [.fullName, .email]

            guard let presentationAnchor = AppleSignInCoordinator.currentPresentationAnchor() else {
                print("[PlatformHostApiImpl.requestAppleSignIn] error: \(PlatformHostApiError.missingPresentationAnchor.localizedDescription)")
                completion(.failure(PlatformHostApiError.missingPresentationAnchor))
                return
            }

            let coordinator = AppleSignInCoordinator(presentationAnchor: presentationAnchor) { [weak self] result in
                self?.appleSignInCoordinator = nil
                switch result {
                case .success(let model):
                    print("[PlatformHostApiImpl.requestAppleSignIn] return: \(String(describing: model))")
                case .failure(let error):
                    print("[PlatformHostApiImpl.requestAppleSignIn] error: \(error.localizedDescription)")
                }
                completion(result)
            }
            appleSignInCoordinator = coordinator

            let controller = ASAuthorizationController(authorizationRequests: [request])
            controller.delegate = coordinator
            controller.presentationContextProvider = coordinator
            controller.performRequests()
//            authorizationController = controller
        }
    }

    func getFullUserAgent() throws -> String {
        let userAgent = AppShared.shared.agent.finalUA
        print("[PlatformHostApiImpl.getFullUserAgent] return: \(userAgent)")
        return userAgent
    }
}