@@ -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+
3561public 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