Skip to content

Commit 9897832

Browse files
committed
Fix passing value type error
1 parent 35ca471 commit 9897832

3 files changed

Lines changed: 125 additions & 36 deletions

File tree

WKJavaScriptController-Demo/WKJavaScriptController-Demo/ViewController.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import WKJavaScriptController
1212

1313
@objc protocol JavaScriptInterface {
1414
func onSubmit(dictonary: [String: AnyObject])
15-
func onSubmit(dictonary: [String: AnyObject], clear: Bool)
16-
func onSubmit(email: String, firstName: String, lastName: String, address1: String, address2: String, zipCode: Int, phoneNumber: String)
15+
func onSubmit(dictonary: [String: AnyObject], clear: JSBool)
16+
func onSubmit(email: String, firstName: String, lastName: String, address1: String, address2: String, zipCode: JSInt, phoneNumber: String)
1717
func onCancel()
1818
}
1919

@@ -54,15 +54,15 @@ extension ViewController: JavaScriptInterface {
5454
NSLog("onSubmit \(dictonary)")
5555
}
5656

57-
func onSubmit(dictonary: [String: AnyObject], clear: Bool) {
57+
func onSubmit(dictonary: [String: AnyObject], clear: JSBool) {
5858
NSLog("onSubmit \(dictonary)")
59-
if clear {
59+
if clear.value {
6060
webView.evaluateJavaScript("clearAll()", completionHandler: nil)
6161
}
6262
}
6363

64-
func onSubmit(email: String, firstName: String, lastName: String, address1: String, address2: String, zipCode: Int, phoneNumber: String) {
65-
NSLog("onSubmit \(email), \(firstName), \(lastName), \(address1), \(address2), \(zipCode), \(phoneNumber)")
64+
func onSubmit(email: String, firstName: String, lastName: String, address1: String, address2: String, zipCode: JSInt, phoneNumber: String) {
65+
NSLog("onSubmit \(email), \(firstName), \(lastName), \(address1), \(address2), \(zipCode.value), \(phoneNumber)")
6666
}
6767

6868
func onCancel() {

WKJavaScriptController-Demo/WKJavaScriptController-Demo/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function submit() {
88
if (isChecked('input_json')) {
99
native.onSubmit(values);
1010
} else if (isChecked('input_literal')) {
11-
native.onSubmitWithFirstnameAndLastnameAndAddress1AndAddress2AndZipcodeAndPhonenumber(values['mail'], values['first_name'], values['last_name'], values['address_line_1'], values['address_line_2'], values['zip_code'], values['phone_number']);
11+
native.onSubmitWithFirstnameAndLastnameAndAddress1AndAddress2AndZipcodeAndPhonenumber(values['mail'], values['first_name'], values['last_name'], values['address_line_1'], values['address_line_2'], parseInt(values['zip_code']), values['phone_number']);
1212
}
1313
}
1414

WKJavaScriptController/WKJavaScriptController.swift

Lines changed: 118 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,32 @@ public extension WKWebView {
3232
}
3333
}
3434

35+
public class JSValueType: NSObject {
36+
private var _value: NSNumber
37+
38+
private init(value: AnyObject) {
39+
_value = value as! NSNumber
40+
}
41+
}
42+
43+
public class JSBool: JSValueType {
44+
public var value: Bool {
45+
return _value.boolValue
46+
}
47+
}
48+
49+
public class JSInt: JSValueType {
50+
public var value: Int {
51+
return _value.integerValue
52+
}
53+
}
54+
55+
public class JSFloat: JSValueType {
56+
public var value: Float {
57+
return _value.floatValue
58+
}
59+
}
60+
3561
public class WKJavaScriptController: NSObject {
3662
private let name: String
3763
private weak var target: AnyObject?
@@ -72,7 +98,7 @@ public class WKJavaScriptController: NSObject {
7298
}
7399
}
74100

75-
private var numberOfArguments: Int {
101+
private var argumentLength: Int {
76102
return max(NSStringFromSelector(nativeSelector).componentsSeparatedByString(":").count - 1, 0)
77103
}
78104

@@ -97,21 +123,62 @@ public class WKJavaScriptController: NSObject {
97123
if methodList != nil, var list = Optional(methodList) {
98124
let limit = argumentLengthLimit
99125
while list.memory.name != nil {
100-
let bridge = MethodBridge(nativeSelector: list.memory.name)
101-
if bridge.numberOfArguments > limit {
102-
NSLog("[WKJavaScriptController:Warning] The length of argument was longer than \(limit), so it was excluded. (selector: \(bridge.nativeSelector))")
103-
} else {
104-
// Using ObjC style naming if have a method with the same name.
105-
let list = bridgeList.filter({ $0.jsSelector == bridge.jsSelector })
106-
if !list.isEmpty {
107-
bridge.extendJsSelector = true
108-
}
109-
for bridge in list {
110-
bridge.extendJsSelector = true
111-
}
112-
bridgeList.append(bridge)
126+
defer { list = list.successor() }
127+
128+
let selector = list.memory.name
129+
guard let signature = String(CString: list.memory.types, encoding: NSUTF8StringEncoding) else {
130+
log("Method signature not found, so it was excluded. (selector: \(selector))")
131+
continue
132+
}
133+
134+
// Ref: http://nshipster.com/type-encodings/
135+
// c: A char v: A void
136+
// C: An unsigned char B: A C++ bool or C99 _bool
137+
// i: An int @: An object (whether statically typed or typed id)
138+
// I: An unsigned int #: A class object
139+
// s: A short :: A method selector (SEL)
140+
// S: An unsigned short [array type]: An array
141+
// l: A long {name=type...}: A structure
142+
// L: An unsigned long (name=type...): A union
143+
// q: A long long bnum: A bit field of num bits
144+
// Q: An unsigned long long ^type: A pointer to type
145+
// f: A float ?: An unknown type (among other things, this code is used for function pointers)
146+
// d: A double
147+
if !signature.hasPrefix("v") {
148+
log("Can not receive native return in JavaScript, so it was excluded. (selector: \(selector))")
149+
continue
150+
}
151+
152+
if signature.rangeOfString("[cC#\\[\\{\\(b\\^\\?]", options: [.RegularExpressionSearch]) != nil {
153+
log("It has an unsupported reference type as arguments, so it was excluded. (selector: \(selector))")
154+
log("Allowed reference types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.")
155+
continue
156+
}
157+
158+
// Value types of Swift can't be used. because method call is based on ObjC.
159+
if signature.rangeOfString("[iIsSlLqQfdB]", options: [.RegularExpressionSearch]) != nil {
160+
log("It has an unsupported value type as arguments, so it was excluded. (selector: \(selector))")
161+
log("Allowed value types are JSBool, JSInt and JSFloat.")
162+
continue
163+
}
164+
165+
let bridge = MethodBridge(nativeSelector: selector)
166+
if bridge.argumentLength > limit {
167+
log("Argument length is longer than \(limit), so it was excluded. (selector: \(bridge.nativeSelector))")
168+
continue
169+
}
170+
171+
// Using ObjC style naming if have a method with the same name.
172+
let list = bridgeList.filter({ $0.jsSelector == bridge.jsSelector })
173+
if !list.isEmpty {
174+
bridge.extendJsSelector = true
113175
}
114-
list = list.successor()
176+
for bridge in list {
177+
bridge.extendJsSelector = true
178+
}
179+
180+
bridgeList.append(bridge)
181+
log("Read \(bridge.nativeSelector) -> \(bridge.jsSelector)")
115182
}
116183
free(methodList)
117184
}
@@ -142,6 +209,10 @@ public class WKJavaScriptController: NSObject {
142209
return WKUserScript(source: source, injectionTime: .AtDocumentStart, forMainFrameOnly: forMainFrameOnly)
143210
}
144211

212+
private func log(message: String) {
213+
NSLog("[WKJavaScriptController] \(message)")
214+
}
215+
145216
public func addUserScript(userScript: WKUserScript) {
146217
userScripts.append(userScript)
147218
needsInject = true
@@ -179,14 +250,14 @@ extension WKJavaScriptController: WKScriptMessageHandler {
179250
return
180251
}
181252

182-
if args.count != bridge.numberOfArguments {
183-
NSLog("[WKJavaScriptController:Error] Argument length is invalid. (received: \(args.count), required: \(bridge.numberOfArguments))")
253+
if args.count != bridge.argumentLength {
254+
log("Argument length is different. (received: \(args.count), required: \(bridge.argumentLength))")
184255
return
185256
}
186257

187258
let method = class_getInstanceMethod(target.classForCoder, bridge.nativeSelector)
188259
if method == nil {
189-
NSLog("[WKJavaScriptController:Warning] You are calling an unimplementation method.")
260+
log("An unimplemented method has been called. (selector: \(bridge.nativeSelector))")
190261
return
191262
}
192263

@@ -195,40 +266,58 @@ extension WKJavaScriptController: WKScriptMessageHandler {
195266
return
196267
}
197268

198-
switch bridge.numberOfArguments {
269+
func cast(arg: Arg) -> Arg {
270+
if let number = arg as? NSNumber,
271+
let type = String(CString: number.objCType, encoding: NSUTF8StringEncoding) {
272+
switch type {
273+
case "c", "C", "B":
274+
return JSBool(value: number)
275+
default:
276+
if number.stringValue.rangeOfString(".") != nil {
277+
return JSFloat(value: number)
278+
} else if number.stringValue == "nan" {
279+
return JSInt(value: NSNumber(integer: 0))
280+
}
281+
return JSInt(value: number)
282+
}
283+
}
284+
return arg
285+
}
286+
287+
switch bridge.argumentLength {
199288
case 0:
200289
let method = unsafeBitCast(imp, Invocation0.self)
201290
method(target, bridge.nativeSelector)
202291
case 1:
203292
let method = unsafeBitCast(imp, Invocation1.self)
204-
method(target, bridge.nativeSelector, args[0])
293+
method(target, bridge.nativeSelector, cast(args[0]))
205294
case 2:
206295
let method = unsafeBitCast(imp, Invocation2.self)
207-
method(target, bridge.nativeSelector, args[0], args[1])
296+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]))
208297
case 3:
209298
let method = unsafeBitCast(imp, Invocation3.self)
210-
method(target, bridge.nativeSelector, args[0], args[1], args[2])
299+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]))
211300
case 4:
212301
let method = unsafeBitCast(imp, Invocation4.self)
213-
method(target, bridge.nativeSelector, args[0], args[1], args[2], args[3])
302+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]))
214303
case 5:
215304
let method = unsafeBitCast(imp, Invocation5.self)
216-
method(target, bridge.nativeSelector, args[0], args[1], args[2], args[3], args[4])
305+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]))
217306
case 6:
218307
let method = unsafeBitCast(imp, Invocation6.self)
219-
method(target, bridge.nativeSelector, args[0], args[1], args[2], args[3], args[4], args[5])
308+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]))
220309
case 7:
221310
let method = unsafeBitCast(imp, Invocation7.self)
222-
method(target, bridge.nativeSelector, args[0], args[1], args[2], args[3], args[4], args[5], args[6])
311+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]))
223312
case 8:
224313
let method = unsafeBitCast(imp, Invocation8.self)
225-
method(target, bridge.nativeSelector, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])
314+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]))
226315
case 9:
227316
let method = unsafeBitCast(imp, Invocation9.self)
228-
method(target, bridge.nativeSelector, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])
317+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8]))
229318
case argumentLengthLimit:
230319
let method = unsafeBitCast(imp, Invocation10.self)
231-
method(target, bridge.nativeSelector, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9])
320+
method(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8]), cast(args[9]))
232321
default:
233322
// Not called.
234323
break

0 commit comments

Comments
 (0)