Skip to content

Commit e6aeeb9

Browse files
committed
add bridge base
1 parent 1887b58 commit e6aeeb9

1 file changed

Lines changed: 157 additions & 0 deletions

File tree

WKWebViewJavascriptBridge/WKWebViewJavascriptBridgeBase.swift

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,160 @@
77
//
88

99
import Foundation
10+
11+
@available(iOS 9.0, *)
12+
protocol WKWebViewJavascriptBridgeBaseDelegate: AnyObject {
13+
func evaluateJavascript(javascript: String)
14+
}
15+
16+
@available(iOS 9.0, *)
17+
class WKWebViewJavascriptBridgeBase: NSObject {
18+
typealias Callback = (_ responseData: Any?) -> Void
19+
typealias Handler = (_ parameters: [String: Any]?, _ callback: Callback?) -> Void
20+
typealias Message = [String: Any]
21+
22+
weak var delegate: WKWebViewJavascriptBridgeBaseDelegate?
23+
var startupMessageQueue = [Message]()
24+
var responseCallbacks = [String: Callback]()
25+
var messageHandlers = [String: Handler]()
26+
var uniqueId = 0
27+
28+
override init() {
29+
super.init()
30+
}
31+
32+
func reset() {
33+
self.startupMessageQueue = [Message]()
34+
self.responseCallbacks = [String: Callback]()
35+
self.uniqueId = 0
36+
}
37+
38+
func send(data: Any?, callback: Callback?, handlerName: String?) {
39+
var message = [String: Any]()
40+
41+
if data != nil {
42+
message["data"] = data
43+
}
44+
45+
if callback != nil {
46+
self.uniqueId += 1
47+
let callbackID = "native_iOS_cb_\(self.uniqueId)"
48+
self.responseCallbacks[callbackID] = callback
49+
message["callbackID"] = callbackID
50+
}
51+
52+
if handlerName != nil {
53+
message["handlerName"] = handlerName
54+
}
55+
56+
self.queue(message: message)
57+
}
58+
59+
func flush(messageQueueString: String) {
60+
guard let messages = self.deserialize(messageJSON: messageQueueString) else {
61+
log(messageQueueString)
62+
return
63+
}
64+
65+
for message in messages {
66+
log(message)
67+
68+
if let responseID = message["responseID"] as? String {
69+
let callback = self.responseCallbacks[responseID]
70+
callback!(message["responseData"]!)
71+
self.responseCallbacks.removeValue(forKey: responseID)
72+
} else {
73+
var callback: Callback?
74+
if let callbackID = message["callbackID"] {
75+
callback = { (_ responseData: Any?) -> Void in
76+
guard responseData != nil else {
77+
return
78+
}
79+
80+
let msg = ["responseID": callbackID, "responseData": responseData!] as Message
81+
self.queue(message: msg)
82+
}
83+
} else {
84+
callback = { (_ responseData: Any?) -> Void in
85+
// no logic
86+
}
87+
}
88+
89+
guard let handlerName = message["handlerName"] as? String else {
90+
return
91+
}
92+
guard let handler = self.messageHandlers[handlerName] else {
93+
log("NoHandlerException, No handler for message from JS: \(message)")
94+
return
95+
}
96+
handler(message["data"] as? [String : Any], callback)
97+
}
98+
}
99+
}
100+
101+
// MARK: - Private
102+
fileprivate func queue(message: Message) {
103+
if self.startupMessageQueue.isEmpty {
104+
self.dispatch(message: message)
105+
} else {
106+
self.startupMessageQueue.append(message)
107+
}
108+
}
109+
110+
fileprivate func dispatch(message: Message) {
111+
guard var messageJSON = self.serialize(message: message, pretty: false) else {
112+
return
113+
}
114+
115+
messageJSON = messageJSON.replacingOccurrences(of: "\\", with: "\\\\")
116+
messageJSON = messageJSON.replacingOccurrences(of: "\"", with: "\\\"")
117+
messageJSON = messageJSON.replacingOccurrences(of: "\'", with: "\\\'")
118+
messageJSON = messageJSON.replacingOccurrences(of: "\n", with: "\\n")
119+
messageJSON = messageJSON.replacingOccurrences(of: "\r", with: "\\r")
120+
// messageJSON = messageJSON.replacingOccurrences(of: "\f", with: "\\f")
121+
// messageJSON = messageJSON.replacingOccurrences(of: "\u2028", with: "\\u2028")
122+
// messageJSON = messageJSON.replacingOccurrences(of: "\u2029", with: "\\u2029")
123+
124+
let javascriptCommand = "WebViewJavascriptBridge._handleMessageFromObjC('\(messageJSON)');"
125+
if Thread.current.isMainThread {
126+
self.delegate?.evaluateJavascript(javascript: javascriptCommand)
127+
} else {
128+
DispatchQueue.main.async {
129+
self.delegate?.evaluateJavascript(javascript: javascriptCommand)
130+
}
131+
}
132+
}
133+
134+
// MARK: - JSON
135+
fileprivate func serialize(message: Message, pretty: Bool) -> String? {
136+
var result: String?
137+
do {
138+
let data = try JSONSerialization.data(withJSONObject: message, options: pretty ? .prettyPrinted : JSONSerialization.WritingOptions(rawValue: 0))
139+
result = String(data: data, encoding: .utf8)!
140+
} catch let error {
141+
log(error)
142+
}
143+
return result
144+
}
145+
146+
fileprivate func deserialize(messageJSON: String) -> [Message]? {
147+
var result = [Message]()
148+
guard let data = messageJSON.data(using: .utf8) else {
149+
return nil
150+
}
151+
do {
152+
result = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [WKWebViewJavascriptBridgeBase.Message]
153+
} catch let error {
154+
log(error)
155+
}
156+
return result
157+
}
158+
159+
// MARK: - Log
160+
fileprivate func log<T>(_ message: T, file: String = #file, function: String = #function, line: Int = #line) {
161+
#if DEBUG
162+
let fileName = (file as NSString).lastPathComponent
163+
print("\(fileName):\(line) \(function) | \(message)")
164+
#endif
165+
}
166+
}

0 commit comments

Comments
 (0)