FlutterMethodChannelManager.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //
  2. // FlutterMethodChannelManager.swift
  3. // Runner
  4. //
  5. // Created by Destiny on 2025/5/8.
  6. //
  7. import Flutter
  8. import UIKit
  9. import StoreKit
  10. import AdServices
  11. class FlutterMethodChannelManager: NSObject {
  12. // 单例模式
  13. static let shared = FlutterMethodChannelManager()
  14. // 方法通道
  15. var keyboardChannel: FlutterMethodChannel?
  16. var retryCount = 0
  17. // 私有初始化方法
  18. private override init() {
  19. super.init()
  20. }
  21. // 设置方法通道
  22. func setupMethodChannels(controller: FlutterViewController) {
  23. // 创建键盘相关的方法通道
  24. keyboardChannel = FlutterMethodChannel(
  25. name: "keyboard_ios",
  26. binaryMessenger: controller.binaryMessenger)
  27. // 设置方法调用处理器
  28. keyboardChannel?.setMethodCallHandler { [weak self] (call, result) in
  29. guard let self = self else { return }
  30. switch call.method {
  31. case "saveSystemKeyboardInfo":
  32. if let args = call.arguments as? [String: Any],
  33. let info = args["info"] as? String {
  34. KeyboardSharedDataManager.shared.saveSystemkeyboard(info)
  35. result(true)
  36. } else {
  37. result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
  38. }
  39. case "saveAuthToken":
  40. if let args = call.arguments as? [String: Any],
  41. let token = args["token"] as? String {
  42. KeyboardSharedDataManager.shared.saveToken(token)
  43. result(true)
  44. } else {
  45. result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
  46. }
  47. case "clearAuthToken":
  48. KeyboardSharedDataManager.shared.clearAuthToken()
  49. result(true)
  50. case "saveIDFV":
  51. if let args = call.arguments as? [String: Any],
  52. let idfv = args["idfv"] as? String {
  53. KeyboardSharedDataManager.shared.saveInitIDFV(idfv)
  54. result(true)
  55. } else {
  56. result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
  57. }
  58. case "saveIDFA":
  59. if let args = call.arguments as? [String: Any],
  60. let idfa = args["idfa"] as? String {
  61. KeyboardSharedDataManager.shared.saveInitIDFA(idfa)
  62. result(true)
  63. } else {
  64. result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
  65. }
  66. case "isKeyboardAdded":
  67. let isAdd = self.isKeyboardEnabled()
  68. result(isAdd)
  69. case "isDefaultKeyboard":
  70. let isDefault = self.isCustomKeybroad()
  71. result(isDefault)
  72. case "openKeyboardGuide":
  73. KeyboardSharedDataManager.shared.saveIsShowGuide()
  74. result(true)
  75. case "isHasDiscount":
  76. if let args = call.arguments as? [String: Any],
  77. let appleGoodId = args["appleGoodId"] as? String {
  78. self.checkProductDiscount(appleGoodId: appleGoodId, completion: { hasDiscount in
  79. result(hasDiscount)
  80. })
  81. } else {
  82. result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
  83. }
  84. case "initAttr":
  85. self.initAttribution()
  86. case "addASAPayReport":
  87. if let args = call.arguments as? [String: Any],
  88. let price = args["price"] as? Double {
  89. AsaManager.shared.addEventAttribution(eventDict: ["event_name": "pay", "event_val": price])
  90. } else {
  91. result(FlutterError(code: "INVALID_ARGUMENTS", message: "无效的参数", details: nil))
  92. }
  93. default:
  94. result(FlutterMethodNotImplemented)
  95. }
  96. }
  97. }
  98. // 是否添加键盘
  99. func isKeyboardEnabled() -> Bool {
  100. // 获取系统中已安装的键盘列表
  101. let keyboards = UserDefaults.standard.dictionaryRepresentation()
  102. .filter { $0.key.contains("Keyboard") }
  103. if let keyboardList = keyboards["AppleKeyboards"] as? [String] {
  104. let keyboardBundleId = "com.qihuan.zhuiaijianpan.AiKeyboard"
  105. let isAdded = keyboardList.contains(keyboardBundleId)
  106. return isAdded
  107. }
  108. return false
  109. }
  110. // 是否为默认键盘
  111. func isCustomKeybroad() -> Bool {
  112. let currentKeyboardName = (((UITextInputMode.activeInputModes as NSArray).filtered(using: NSPredicate(format: "isDisplayed = YES"))).last as? NSObject)?.value(forKey: "extendedDisplayName") as? String
  113. let infoDictionary = Bundle.main.infoDictionary!
  114. let appDisplayName = infoDictionary["CFBundleDisplayName"] as? String
  115. return currentKeyboardName == appDisplayName
  116. }
  117. }
  118. extension FlutterMethodChannelManager {
  119. func initAttribution() {
  120. if #available(iOS 14.3, *) {
  121. do {
  122. let appleAttributionToken = try AAAttribution.attributionToken()
  123. // 尝试获取归因信息
  124. getASA(token: appleAttributionToken)
  125. } catch let error {
  126. print("Error fetching attribution token: \(error.localizedDescription)")
  127. }
  128. }
  129. }
  130. // 开始获取归因结果
  131. func getASA(token: String) {
  132. guard retryCount < 3 else { return }
  133. let configuration = URLSessionConfiguration.default
  134. let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
  135. let asaUrl = URL(string: "https://api-adservices.apple.com/api/v1/")!
  136. var request = URLRequest(url: asaUrl)
  137. request.httpMethod = "POST"
  138. request.addValue("application/json", forHTTPHeaderField: "Content-Type")
  139. request.httpBody = token.data(using: .utf8)
  140. let task = session.dataTask(with: request) { data, response, error in
  141. if let error = error {
  142. print("sendASAToken error = \(error)")
  143. // 5秒后重试
  144. DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
  145. self.getASA(token: token)
  146. }
  147. return
  148. }
  149. guard let data = data, let payloadDic = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
  150. print("sendASAToken error payload = nil")
  151. // 5秒后重试
  152. DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
  153. self.getASA(token: token)
  154. }
  155. return
  156. }
  157. print("sendASAToken payload = \(payloadDic)")
  158. if let attribution = payloadDic["attribution"] {
  159. UserDefaults.standard.set(payloadDic, forKey: "ASA_ATTRIBUTION_PAYLOAD")
  160. UserDefaults.standard.synchronize()
  161. // 调用奇异果数据上报接口
  162. let appSaManager = AsaManager.shared
  163. appSaManager.addAttributionReport { isSuccess in
  164. print("asaReportWithCompletion = ", isSuccess)
  165. if isSuccess {
  166. let isRegister = UserDefaults.standard.bool(forKey: "isRegisterKey")
  167. if !isRegister {
  168. AsaManager.shared.addEventAttribution(eventDict: ["event_name": "active", "event_val": 1])
  169. AsaManager.shared.addEventAttribution(eventDict: ["event_name": "register", "event_val": 1])
  170. UserDefaults.standard.setValue(true, forKey: "isRegisterKey")
  171. }
  172. }
  173. }
  174. }
  175. }
  176. task.resume()
  177. retryCount += 1
  178. }
  179. }
  180. extension FlutterMethodChannelManager {
  181. private func checkProductDiscount(appleGoodId: String, completion: @escaping (Bool) -> Void) {
  182. if #available(iOS 15.0, *) {
  183. Task {
  184. do {
  185. // print("开始请求产品信息 (StoreKit 2)")
  186. let products = try await Product.products(for: [appleGoodId])
  187. print("成功获取产品信息: \(products.count) 个产品")
  188. if let product = products.first {
  189. // 检查是否有优惠
  190. var hasDiscount = await product.subscription?.isEligibleForIntroOffer ?? false
  191. DispatchQueue.main.async {
  192. completion(hasDiscount)
  193. }
  194. } else {
  195. print("未找到产品")
  196. DispatchQueue.main.async {
  197. completion(false)
  198. }
  199. }
  200. } catch {
  201. print("获取产品失败 (StoreKit 2): \(error)")
  202. DispatchQueue.main.async {
  203. completion(false)
  204. }
  205. }
  206. }
  207. }
  208. // // 使用StoreKit查询商品信息
  209. // let request = SKProductsRequest(productIdentifiers: [appleGoodId])
  210. // request.delegate = ProductRequestDelegate(completion: { product in
  211. // if let product = product {
  212. // // iOS 12.2及以上版本支持促销优惠
  213. // if #available(iOS 12.2, *) {
  214. // // 检查商品是否有促销优惠
  215. // let hasDiscount = product.discounts.count > 0
  216. // completion(hasDiscount)
  217. // } else {
  218. // completion(false)
  219. // }
  220. // } else {
  221. // completion(false)
  222. // }
  223. // })
  224. // request.start()
  225. }
  226. }
  227. // 处理StoreKit产品请求的代理
  228. class ProductRequestDelegate: NSObject, SKProductsRequestDelegate {
  229. private let completion: (SKProduct?) -> Void
  230. init(completion: @escaping (SKProduct?) -> Void) {
  231. self.completion = completion
  232. super.init()
  233. }
  234. func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
  235. if let product = response.products.first {
  236. completion(product)
  237. } else {
  238. completion(nil)
  239. }
  240. }
  241. func request(_ request: SKRequest, didFailWithError error: Error) {
  242. print("商品请求失败: \(error.localizedDescription)")
  243. completion(nil)
  244. }
  245. }