import JavaScriptCore import Alamofire import Alamofire import Foundation //// 扩展SessionManager以支持多个Cookies //extension SessionManager { // static let multipleCookiesSessionManager: SessionManager = { // let configuration = URLSessionConfiguration.default // configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders // // let manager = SessionManager(configuration: configuration) // // // 自定义处理Cookies的方式 // manager.delegate.taskWillPerformHTTPRedirection = { session, task, response, request in // if let headers = response.allHeaderFields as? [String: String], // let setCookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: request.url!) { // for cookie in setCookies { // HTTPCookieStorage.shared.setCookie(cookie) // } // } // return request // } // // return manager // }() //} // //// 使用扩展的SessionManager发送请求 //SessionManager.multipleCookiesSessionManager.request("https://www.youtube.com/watch?v=5GJWxDKyk3A").response { response in // debugPrint(response) //} func downloadJSFile(url: URL, completion: @escaping (Result) -> Void) { AF.download(url).responseData { (response) in switch response.result { case .success(let data): if let jsString = String(data: data, encoding: .utf8) { completion(.success(jsString)) } else { completion(.failure(NSError(domain: "Download Error", code: -1, userInfo: nil))) } case .failure(let error): completion(.failure(error)) } } } func createJSContext() -> JSContext { let ctx = JSContext() // 注入 console.log ctx?.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }") let consoleLog: @convention(block) (String) -> Void = { message in print("JS 打印: \(message)") } ctx?.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as (NSCopying & NSObjectProtocol)?) // 注入AF ctx?.evaluateScript("var AF = { request: function(url, method, data, headers, callback) { _request(url, method, data, headers, callback) } }") let af: @convention(block) (String, String, String?, [String: String]?, JSValue?) -> Void = { url, method, data, headers, callback in var request = URLRequest(url: URL(string: url)!) request.httpMethod = method if method == "POST" { if let data = data { request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpBody = data.data(using: .utf8) } } if let headers = headers { request.headers = HTTPHeaders(headers) } AF.request(request).response { response in if let data = response.data { var headerJsonString = "" if let headers = response.response?.allHeaderFields as? [String: String] { do { let jsonData = try JSONSerialization.data(withJSONObject: headers, options: []) if let jsonString = String(data: jsonData, encoding: .utf8) { headerJsonString = jsonString } } catch { } } callback?.call(withArguments: [String(data: data, encoding: .utf8), headerJsonString, nil]) } if let error = response.error { debugPrint(response) callback?.call(withArguments: [nil, nil, error.localizedDescription]) } } } ctx?.setObject(unsafeBitCast(af, to: AnyObject.self), forKeyedSubscript: "_request" as (NSCopying & NSObjectProtocol)?) // 捕捉JS运行异常 ctx?.exceptionHandler = { context, exception in if let exception = exception { debugPrint(exception) print("JS 执行异常: \(exception)") } } return ctx! } func testDetail(url: String, ctx: JSContext) -> Void { let startTime = DispatchTime.now() if let detailFunction = ctx.objectForKeyedSubscript("detail") { let result = detailFunction.call(withArguments: [url]) let completionHandler: @convention(block) (JSValue) -> Void = { result in print("详情结果!!: \(result.toDictionary())") let endTime = DispatchTime.now() let nanoTime = endTime.uptimeNanoseconds - startTime.uptimeNanoseconds let timeInterval = Double(nanoTime) / 1_000_000_000 print("耗时: \(timeInterval) seconds") } let completionFunction = unsafeBitCast(completionHandler, to: AnyObject.self) result?.invokeMethod("then", withArguments: [completionFunction]) } } func testSearch(keyword: String, ctx: JSContext) -> Void { if let search = ctx.objectForKeyedSubscript("search") { let result = search.call(withArguments: [keyword]) let completionHandler: @convention(block) (JSValue) -> Void = { result in print("搜索结果!!: \(result.toDictionary())") } let completionFunction = unsafeBitCast(completionHandler, to: AnyObject.self) result?.invokeMethod("then", withArguments: [completionFunction]) } } let ctx = createJSContext() let remote = "https://d3crpuooyqht8f.cloudfront.net/e76b32a6-42a0-4682-a30c-0b5c2d75e5b9" let local = "file:///Users/ben/Desktop/app/be/be-ytb/js/bundle.js" if let url = URL(string: remote) { downloadJSFile(url: url) { result in switch result { case .success(let jsString): print("下载远程JS成功") ctx.evaluateScript(jsString) testDetail(url: "https://www.youtube.com/watch?v=JByDbPn6A1o", ctx: ctx) // testSearch(keyword: "周杰伦", ctx: ctx) case .failure(let error): print("Download Error: \(error)") } } } RunLoop.main.run()