From cc30f59ef9111fadec38e0ab5d927e23c978edb0 Mon Sep 17 00:00:00 2001 From: ddisfriend Date: Thu, 23 Jul 2026 14:45:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=85=E7=94=B5=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OrderScheduling.xcodeproj/project.pbxproj | 8 +- .../Common/WebView/ScanViewController.swift | 224 ++++++++++++++++ .../Common/WebView/WebViewController.swift | 252 +++++++++++++++++- OrderScheduling/Info.plist | 15 ++ OrderScheduling/Main/AppDelegate.swift | 17 ++ OrderScheduling/Main/SceneDelegate.swift | 5 + 6 files changed, 509 insertions(+), 12 deletions(-) create mode 100644 OrderScheduling/Common/WebView/ScanViewController.swift diff --git a/OrderScheduling.xcodeproj/project.pbxproj b/OrderScheduling.xcodeproj/project.pbxproj index 77f0c9d..52316c9 100644 --- a/OrderScheduling.xcodeproj/project.pbxproj +++ b/OrderScheduling.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 56046A0096E81BD9D3480337 /* ScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E98591BF58FB3CB969F8A277 /* ScanViewController.swift */; }; 5C5E148BC19DCC3A1F56E894 /* libPods-OrderSchedulingNotificationService.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F4D22CCB18DAC27B34DA947D /* libPods-OrderSchedulingNotificationService.a */; }; 791887422A7CCCCD007EA0C1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 791887402A7CCCCD007EA0C1 /* AppDelegate.swift */; }; 791887432A7CCCCD007EA0C1 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 791887412A7CCCCD007EA0C1 /* SceneDelegate.swift */; }; @@ -267,6 +268,7 @@ BD034FA1DD7C17CCBA6B0E5D /* Pods-OrderScheduling.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OrderScheduling.release.xcconfig"; path = "Target Support Files/Pods-OrderScheduling/Pods-OrderScheduling.release.xcconfig"; sourceTree = ""; }; BEFB1D0033356A04B31FA263 /* Pods-OrderScheduling.prerelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OrderScheduling.prerelease.xcconfig"; path = "Target Support Files/Pods-OrderScheduling/Pods-OrderScheduling.prerelease.xcconfig"; sourceTree = ""; }; DF9CE87B72CAB01C1BFFDCF9 /* Pods-OrderSchedulingNotificationService.prerelease.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OrderSchedulingNotificationService.prerelease.xcconfig"; path = "Target Support Files/Pods-OrderSchedulingNotificationService/Pods-OrderSchedulingNotificationService.prerelease.xcconfig"; sourceTree = ""; }; + E98591BF58FB3CB969F8A277 /* ScanViewController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ScanViewController.swift; sourceTree = ""; }; F4D22CCB18DAC27B34DA947D /* libPods-OrderSchedulingNotificationService.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OrderSchedulingNotificationService.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -426,6 +428,7 @@ children = ( 791887942A80C361007EA0C1 /* WebViewController.swift */, 794FBB1E2A92F7C300D57BB8 /* WebViewTool.swift */, + E98591BF58FB3CB969F8A277 /* ScanViewController.swift */, ); path = WebView; sourceTree = ""; @@ -1086,14 +1089,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-OrderScheduling/Pods-OrderScheduling-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-OrderScheduling/Pods-OrderScheduling-resources-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-OrderScheduling/Pods-OrderScheduling-resources.sh\"\n"; @@ -1212,6 +1211,7 @@ 79CE24AA2EF52EAF007FCF90 /* IdentityAlertView.swift in Sources */, 79D964E02E77FE2100309946 /* AppealInputViewController.swift in Sources */, 791887A62A80D50C007EA0C1 /* ParametersList.swift in Sources */, + 56046A0096E81BD9D3480337 /* ScanViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/OrderScheduling/Common/WebView/ScanViewController.swift b/OrderScheduling/Common/WebView/ScanViewController.swift new file mode 100644 index 0000000..82dd60a --- /dev/null +++ b/OrderScheduling/Common/WebView/ScanViewController.swift @@ -0,0 +1,224 @@ +// +// ScanViewController.swift +// OrderScheduling +// +// Created by 中道 on 2026/7/23. +// + +import Foundation +import UIKit +import AVFoundation +import SnapKit +import DDAutoUIKit_Private +import DDControlsKit_Private +import DDToastKit_Private + +class ScanViewController: ZDViewController { + + private lazy var session = AVCaptureSession() + private var previewLayer: AVCaptureVideoPreviewLayer? + private var scanCompletion: ((String?) -> Void)? + + private let scanRectView = UIView() + private let tipLabel = UILabel() + private let backButton = UIButton(type: .custom) + private let torchButton = UIButton(type: .custom) + + init(completion: @escaping (String?) -> Void) { + self.scanCompletion = completion + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + checkPermissionAndSetupCamera() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.navigationBar.isHidden = true + if session.isRunning == false { + DispatchQueue.global(qos: .background).async { [weak self] in + self?.session.startRunning() + } + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.navigationBar.isHidden = false + if session.isRunning == true { + session.stopRunning() + } + } + + private func setupUI() { + view.backgroundColor = .black + + scanRectView.layer.borderColor = UIColor.hex("00FF00").cgColor + scanRectView.layer.borderWidth = 1.0 + view.addSubview(scanRectView) + + tipLabel.text = "将二维码/条码放入框内,即可自动扫描" + tipLabel.textColor = .white + tipLabel.font = UIFont.systemFont(ofSize: 14) + tipLabel.textAlignment = .center + view.addSubview(tipLabel) + + backButton.setImage(UIImage(named: "nav_back_white") ?? UIImage(systemName: "chevron.left"), for: .normal) + backButton.tintColor = .white + backButton.addTarget(self, action: #selector(backAction), for: .touchUpInside) + view.addSubview(backButton) + + torchButton.setTitle("手电筒", for: .normal) + torchButton.setTitleColor(.white, for: .normal) + torchButton.titleLabel?.font = UIFont.systemFont(ofSize: 14) + torchButton.addTarget(self, action: #selector(torchAction), for: .touchUpInside) + view.addSubview(torchButton) + + scanRectView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.width.height.equalTo(auto(260)) + } + + tipLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(scanRectView.snp.bottom).offset(auto(20)) + make.left.right.equalToSuperview().inset(auto(20)) + } + + backButton.snp.makeConstraints { make in + make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(auto(10)) + make.left.equalTo(auto(15)) + make.width.height.equalTo(auto(44)) + } + + torchButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(tipLabel.snp.bottom).offset(auto(30)) + } + } + + private func checkPermissionAndSetupCamera() { + let status = AVCaptureDevice.authorizationStatus(for: .video) + switch status { + case .authorized: + setupCamera() + case .notDetermined: + AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in + DispatchQueue.main.async { + if granted { + self?.setupCamera() + } else { + self?.showPermissionAlert() + } + } + } + default: + showPermissionAlert() + } + } + + private func setupCamera() { + guard let device = AVCaptureDevice.default(for: .video) else { + showErrorAndBack("无法获取相机设备") + return + } + + do { + let input = try AVCaptureDeviceInput(device: device) + let output = AVCaptureMetadataOutput() + output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) + + session.beginConfiguration() + if session.canAddInput(input) { + session.addInput(input) + } + if session.canAddOutput(output) { + session.addOutput(output) + } + output.metadataObjectTypes = [.qr, .ean13, .ean8, .code128, .code39, .code93, .pdf417] + session.commitConfiguration() + + let previewLayer = AVCaptureVideoPreviewLayer(session: session) + previewLayer.videoGravity = .resizeAspectFill + previewLayer.frame = view.bounds + view.layer.insertSublayer(previewLayer, at: 0) + self.previewLayer = previewLayer + + updateScanRect() + + DispatchQueue.global(qos: .background).async { [weak self] in + self?.session.startRunning() + } + } catch { + showErrorAndBack("相机初始化失败") + } + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + previewLayer?.frame = view.bounds + updateScanRect() + } + + private func updateScanRect() { + guard let previewLayer = previewLayer else { return } + let rect = scanRectView.frame + let scanRect = previewLayer.metadataOutputRectConverted(fromLayerRect: rect) + if let output = session.outputs.first as? AVCaptureMetadataOutput { + output.rectOfInterest = scanRect + } + } + + @objc private func backAction() { + scanCompletion?(nil) + navigationController?.popViewController(animated: true) + } + + @objc private func torchAction() { + guard let device = AVCaptureDevice.default(for: .video), device.hasTorch else { return } + do { + try device.lockForConfiguration() + device.torchMode = device.torchMode == .on ? .off : .on + device.unlockForConfiguration() + } catch {} + } + + private func showPermissionAlert() { + let alert = UIAlertController(title: "相机权限未开启", message: "请在设置中开启相机权限以使用扫码功能", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "取消", style: .cancel) { [weak self] _ in + self?.scanCompletion?(nil) + self?.navigationController?.popViewController(animated: true) + }) + alert.addAction(UIAlertAction(title: "去设置", style: .default) { _ in + if let url = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(url) + } + }) + present(alert, animated: true) + } + + private func showErrorAndBack(_ message: String) { + view.dd_makeToast(message) + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in + self?.scanCompletion?(nil) + self?.navigationController?.popViewController(animated: true) + } + } +} + +extension ScanViewController: AVCaptureMetadataOutputObjectsDelegate { + func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + guard let object = metadataObjects.first as? AVMetadataMachineReadableCodeObject, + let code = object.stringValue else { return } + session.stopRunning() + scanCompletion?(code) + navigationController?.popViewController(animated: true) + } +} diff --git a/OrderScheduling/Common/WebView/WebViewController.swift b/OrderScheduling/Common/WebView/WebViewController.swift index fb342a3..805e726 100644 --- a/OrderScheduling/Common/WebView/WebViewController.swift +++ b/OrderScheduling/Common/WebView/WebViewController.swift @@ -13,15 +13,34 @@ import DDLogKit_Private import DDToastKit_Private import DDControlsKit_Private import DDUIKit +import SafariServices +import CoreLocation +import DDMAMapKit_Private + +let WebViewPayCallbackNotification = NSNotification.Name(rawValue: "WebViewPayCallbackNotification") class WebViewController : ZDViewController { - lazy var webView = WKWebView.init() + lazy var webView: WKWebView = { + let config = WKWebViewConfiguration() + config.userContentController = WKUserContentController() + + // 注入环境标识 + let script = WKUserScript(source: "window.__IN_APP_WEBVIEW__ = true;", injectionTime: .atDocumentStart, forMainFrameOnly: false) + config.userContentController.addUserScript(script) + + let webView = WKWebView(frame: .zero, configuration: config) + return webView + }() + lazy var statusBarBackgroundView = DDView.init() var showNavBar : Bool public var vcTitle : String? var url : String? var screenEdgePanGestureRecognizerEnable : Bool public var disappearHandler: (() -> Void)? + + private let locationManager = CLLocationManager() + private var locationCallback: String? public init(showNavBar:Bool = true,title:String?,url:String,screenEdgePanGestureRecognizerEnable:Bool = true) { self.showNavBar = showNavBar @@ -67,6 +86,11 @@ class WebViewController : ZDViewController { } view.dd_showHUD() + + locationManager.delegate = self + locationManager.desiredAccuracy = kCLLocationAccuracyBest + + NotificationCenter.default.addObserver(self, selector: #selector(handlePayCallback(_:)), name: WebViewPayCallbackNotification, object: nil) } override func viewSafeAreaInsetsDidChange() { @@ -102,7 +126,7 @@ class WebViewController : ZDViewController { let nav = navigationController as? DDNavigationController nav?.screenEdgePanGestureRecognizerEnable(false) } - webView.configuration.userContentController.add(self, name: "nativeObject") + addScriptMessageHandlers() } override func viewWillDisappear(_ animated: Bool) { @@ -114,7 +138,7 @@ class WebViewController : ZDViewController { let nav = navigationController as? DDNavigationController nav?.screenEdgePanGestureRecognizerEnable(true) } - webView.configuration.userContentController.removeScriptMessageHandler(forName: "nativeObject") + removeScriptMessageHandlers() } override func dd_backActionPop(_ isAnimated: Bool) { @@ -124,12 +148,80 @@ class WebViewController : ZDViewController { super.dd_backActionPop(isAnimated) } } + + private func addScriptMessageHandlers() { + let handlers = ["nativeObject", "scan", "getLocation", "openMapNavigation", "callPhone", "openDocument"] + handlers.forEach { name in + webView.configuration.userContentController.add(self, name: name) + } + } + + private func removeScriptMessageHandlers() { + let handlers = ["nativeObject", "scan", "getLocation", "openMapNavigation", "callPhone", "openDocument"] + handlers.forEach { name in + webView.configuration.userContentController.removeScriptMessageHandler(forName: name) + } + } + + @objc private func handlePayCallback(_ notification: Notification) { + guard let orderNo = notification.userInfo?["orderNo"] as? String else { return } + let js = """ + (function() { + if (window.onPayCallback) { + window.onPayCallback({ orderNo: '\(orderNo)' }); + return true; + } + return false; + })() + """ + webView.evaluateJavaScript(js) { [weak self] result, _ in + if let handled = result as? Bool, handled == true { + return + } + // 兜底:刷新当前页面 + self?.webView.reload() + } + } + + private func evaluateCallback(_ callback: String?, with result: [String: Any]) { + guard let callback = callback, callback.isEmpty == false else { return } + guard let data = try? JSONSerialization.data(withJSONObject: result, options: []), + let jsonString = String(data: data, encoding: .utf8) else { + return + } + let js = "window['\(callback)'](\(jsonString));" + webView.evaluateJavaScript(js, completionHandler: nil) + } } extension WebViewController : WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { let dict = message.body as? [String:Any] - let action = dict?["action"] as? String + let name = message.name + + if name == "nativeObject" { + handleNativeObject(dict) + return + } + + switch name { + case "scan": + handleScan(dict) + case "getLocation": + handleGetLocation(dict) + case "openMapNavigation": + handleOpenMapNavigation(dict) + case "callPhone": + handleCallPhone(dict) + case "openDocument": + handleOpenDocument(dict) + default: + break + } + } + + private func handleNativeObject(_ dict: [String: Any]?) { + let action = dict?["action"] as? String if action == "goBack" { navigationController?.popViewController(animated: true) }else if action == "goTraining" { @@ -189,6 +281,124 @@ extension WebViewController : WKScriptMessageHandler { } } } + + // MARK: - scan + private func handleScan(_ dict: [String: Any]?) { + let callback = dict?["callback"] as? String + let vc = ScanViewController { [weak self] code in + if let code = code { + self?.evaluateCallback(callback, with: ["success": true, "code": code]) + } else { + self?.evaluateCallback(callback, with: ["success": false]) + } + } + navigationController?.pushViewController(vc, animated: true) + } + + // MARK: - getLocation + private func handleGetLocation(_ dict: [String: Any]?) { + locationCallback = dict?["callback"] as? String + let status = CLLocationManager.authorizationStatus() + switch status { + case .authorizedWhenInUse, .authorizedAlways: + locationManager.requestLocation() + case .notDetermined: + locationManager.requestWhenInUseAuthorization() + default: + evaluateCallback(locationCallback, with: ["success": false, "error": "location_denied"]) + locationCallback = nil + } + } + + // MARK: - openMapNavigation + private func handleOpenMapNavigation(_ dict: [String: Any]?) { + let name = dict?["name"] as? String ?? "" + let address = dict?["address"] as? String ?? "" + let latitude = dict?["latitude"] as? Double ?? dict?["lat"] as? Double + let longitude = dict?["longitude"] as? Double ?? dict?["lng"] as? Double + + guard let latitude = latitude, let longitude = longitude else { + view.dd_makeToast("缺少导航坐标") + return + } + + // 优先高德地图 + let amapUrlString = "iosamap://route?dlat=\(latitude)&dlon=\(longitude)&dname=\(name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")&dev=0&t=0" + if let amapUrl = URL(string: amapUrlString), UIApplication.shared.canOpenURL(amapUrl) { + UIApplication.shared.open(amapUrl, options: [:], completionHandler: nil) + return + } + + // 回退 Apple Maps + let appleUrlString = "http://maps.apple.com/?daddr=\(latitude),\(longitude)&q=\(name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" + if let appleUrl = URL(string: appleUrlString) { + UIApplication.shared.open(appleUrl, options: [:], completionHandler: nil) + } + } + + // MARK: - callPhone + private func handleCallPhone(_ dict: [String: Any]?) { + let callback = dict?["callback"] as? String + guard let phone = dict?["phone"] as? String, phone.isEmpty == false else { + evaluateCallback(callback, with: ["success": false, "error": "phone_empty"]) + return + } + let tel = phone.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "-", with: "") + guard let url = URL(string: "tel://\(tel)") else { + evaluateCallback(callback, with: ["success": false, "error": "phone_invalid"]) + return + } + UIApplication.shared.open(url, options: [:]) { [weak self] success in + self?.evaluateCallback(callback, with: ["success": success]) + } + } + + // MARK: - openDocument + private func handleOpenDocument(_ dict: [String: Any]?) { + guard let urlString = dict?["url"] as? String, let url = URL(string: urlString) else { + view.dd_makeToast("文档地址无效") + return + } + let vc = SFSafariViewController(url: url) + vc.modalPresentationStyle = .fullScreen + present(vc, animated: true) + } +} + +extension WebViewController : CLLocationManagerDelegate { + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + guard let location = locations.last else { return } + let result: [String: Any] = [ + "success": true, + "latitude": location.coordinate.latitude, + "lat": location.coordinate.latitude, + "y": location.coordinate.latitude, + "longitude": location.coordinate.longitude, + "lng": location.coordinate.longitude, + "x": location.coordinate.longitude, + "lon": location.coordinate.longitude, + "coordType": "wgs84", + "accuracy": location.horizontalAccuracy + ] + evaluateCallback(locationCallback, with: result) + locationCallback = nil + } + + func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { + evaluateCallback(locationCallback, with: ["success": false, "error": error.localizedDescription]) + locationCallback = nil + } + + func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { + if status == .authorizedWhenInUse || status == .authorizedAlways { + if locationCallback != nil { + locationManager.requestLocation() + } + } else if status != .notDetermined, locationCallback != nil { + evaluateCallback(locationCallback, with: ["success": false, "error": "location_denied"]) + locationCallback = nil + } + } } private func unwrapEscapedJSONString(_ raw: String) -> String { @@ -245,11 +455,37 @@ extension WebViewController : WKNavigationDelegate,WKUIDelegate { DDLog(message: "didFailProvisionalNavigation--------------------------------\(String(describing: webView.url?.absoluteString))") } public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void) { - let urlString = navigationAction.request.url?.absoluteString - let prefix = "tel:" - if let urlString, urlString.hasPrefix(prefix) == true, let tel = urlString.components(separatedBy: prefix).last { - UIApplication.shared.dd_openUrl(type: .phone, appending: tel) + guard let url = navigationAction.request.url else { + decisionHandler(.allow) + return } + let urlString = url.absoluteString + let scheme = url.scheme?.lowercased() ?? "" + + // tel: 拨号 + if scheme == "tel", let tel = urlString.components(separatedBy: "tel:").last { + UIApplication.shared.dd_openUrl(type: .phone, appending: tel) + decisionHandler(.cancel) + return + } + + // 微信 / 支付宝 / 高德等外部 App Scheme,交给系统打开 + let externalSchemes = ["weixin", "wechat", "alipay", "alipays", "iosamap", "baidumap", "qqmap"] + if externalSchemes.contains(scheme) { + UIApplication.shared.open(url, options: [:], completionHandler: nil) + decisionHandler(.cancel) + return + } + + // 允许微信支付 H5 页面直接跳转 + if scheme == "https", let host = url.host { + let wxHosts = ["wx.tenpay.com", "payapp.wechatpay.com", "mclient.alipay.com", "mobile.alipay.com"] + if wxHosts.contains(host) { + decisionHandler(.allow) + return + } + } + decisionHandler(.allow) } } diff --git a/OrderScheduling/Info.plist b/OrderScheduling/Info.plist index 53d7e04..b0153c2 100644 --- a/OrderScheduling/Info.plist +++ b/OrderScheduling/Info.plist @@ -17,6 +17,10 @@ NSAllowsArbitraryLoads + NSCameraUsageDescription + 扫码充电需要访问相机 + NSLocationWhenInUseUsageDescription + 获取附近充电站需要您的位置信息 NSLocationTemporaryUsageDescriptionDictionary FORCE_FULL_ACCURACY @@ -39,6 +43,17 @@ + CFBundleURLTypes + + + CFBundleURLName + com.zhongdao.orderscheduling.paycallback + CFBundleURLSchemes + + myappscheme + + + UIBackgroundModes remote-notification diff --git a/OrderScheduling/Main/AppDelegate.swift b/OrderScheduling/Main/AppDelegate.swift index cccf156..90915b1 100644 --- a/OrderScheduling/Main/AppDelegate.swift +++ b/OrderScheduling/Main/AppDelegate.swift @@ -39,6 +39,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return .allButUpsideDown } + func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + handlePayCallbackURL(url) + return true + } + func applicationDidBecomeActive(_ application: UIApplication) { JPUSHService.resetBadge() UIApplication.shared.applicationIconBadgeNumber = 0 @@ -166,3 +171,15 @@ extension AppDelegate : JPUSHRegisterDelegate { } } + +func handlePayCallbackURL(_ url: URL) { + guard url.scheme?.lowercased() == "myappscheme" else { return } + guard url.host?.lowercased() == "paycallback" else { return } + let components = URLComponents(url: url, resolvingAgainstBaseURL: false) + let orderNo = components?.queryItems?.first(where: { $0.name == "orderNo" })?.value + var userInfo: [String: Any] = [:] + if let orderNo = orderNo { + userInfo["orderNo"] = orderNo + } + NotificationCenter.default.post(name: WebViewPayCallbackNotification, object: nil, userInfo: userInfo) +} diff --git a/OrderScheduling/Main/SceneDelegate.swift b/OrderScheduling/Main/SceneDelegate.swift index 2dd1f0a..cdaed8b 100644 --- a/OrderScheduling/Main/SceneDelegate.swift +++ b/OrderScheduling/Main/SceneDelegate.swift @@ -73,6 +73,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { MCOUNT.newestMessage() } } + + func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { + guard let url = URLContexts.first?.url else { return } + handlePayCallbackURL(url) + } func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background.