main.swift 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import JavaScriptCore
  2. import Alamofire
  3. func downloadJSFile(url: URL, completion: @escaping (Result<String, Error>) -> Void) {
  4. AF.download(url).responseData { (response) in
  5. switch response.result {
  6. case .success(let data):
  7. if let jsString = String(data: data, encoding: .utf8) {
  8. completion(.success(jsString))
  9. } else {
  10. completion(.failure(NSError(domain: "Download Error", code: -1, userInfo: nil)))
  11. }
  12. case .failure(let error):
  13. completion(.failure(error))
  14. }
  15. }
  16. }
  17. func createJSContext() -> JSContext {
  18. let ctx = JSContext()
  19. // 注入 console.log
  20. ctx?.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
  21. let consoleLog: @convention(block) (String) -> Void = { message in
  22. print("JS 打印: \(message)")
  23. }
  24. ctx?.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as (NSCopying & NSObjectProtocol)?)
  25. // 注入AF
  26. ctx?.evaluateScript("var AF = { request: function(url, method, data, headers, callback) { _request(url, method, data, headers, callback) } }")
  27. let af: @convention(block) (String, String, String?, [String: String]?, JSValue?) -> Void = { url, method, data, headers, callback in
  28. var request = URLRequest(url: URL(string: url)!)
  29. request.httpMethod = method
  30. if method == "POST" {
  31. if let data = data {
  32. request.setValue("application/json", forHTTPHeaderField: "Content-Type")
  33. request.httpBody = data.data(using: .utf8)
  34. }
  35. }
  36. if let headers = headers {
  37. request.headers = HTTPHeaders(headers)
  38. }
  39. AF.request(request).response { response in
  40. if let data = response.data {
  41. callback?.call(withArguments: [String(data: data, encoding: .utf8), nil])
  42. }
  43. if let error = response.error {
  44. debugPrint(response)
  45. callback?.call(withArguments: [nil, error.localizedDescription])
  46. }
  47. }
  48. }
  49. ctx?.setObject(unsafeBitCast(af, to: AnyObject.self), forKeyedSubscript: "_request" as (NSCopying & NSObjectProtocol)?)
  50. // 捕捉JS运行异常
  51. ctx?.exceptionHandler = { context, exception in
  52. if let exception = exception {
  53. debugPrint(exception)
  54. print("JS 执行异常: \(exception)")
  55. }
  56. }
  57. return ctx!
  58. }
  59. func testDetail(url: String, ctx: JSContext) -> Void {
  60. if let detailFunction = ctx.objectForKeyedSubscript("detail") {
  61. let result = detailFunction.call(withArguments: [url])
  62. let completionHandler: @convention(block) (JSValue) -> Void = { result in
  63. print("详情结果!!: \(result.toDictionary())")
  64. }
  65. let completionFunction = unsafeBitCast(completionHandler, to: AnyObject.self)
  66. result?.invokeMethod("then", withArguments: [completionFunction])
  67. }
  68. }
  69. func testSearch(keyword: String, ctx: JSContext) -> Void {
  70. if let search = ctx.objectForKeyedSubscript("search") {
  71. let result = search.call(withArguments: [keyword])
  72. let completionHandler: @convention(block) (JSValue) -> Void = { result in
  73. print("搜索结果!!: \(result.toDictionary())")
  74. }
  75. let completionFunction = unsafeBitCast(completionHandler, to: AnyObject.self)
  76. result?.invokeMethod("then", withArguments: [completionFunction])
  77. }
  78. }
  79. let ctx = createJSContext()
  80. if let url = URL(string: "http://hubgit.cn/ben/be-ytb/raw/master/js/info.js") {
  81. downloadJSFile(url: url) { result in
  82. switch result {
  83. case .success(let jsString):
  84. print("下载远程JS成功")
  85. ctx.evaluateScript(jsString)
  86. testDetail(url: "https://www.youtube.com/watch?v=d0R-JyU4Btk", ctx: ctx)
  87. // testSearch(keyword: "周杰伦", ctx: ctx)
  88. case .failure(let error):
  89. print("Download Error: \(error)")
  90. }
  91. }
  92. }
  93. RunLoop.main.run()