jockeyjs
jockeyjs copied to clipboard
WKWebView Support
Are there any plans on supporting iOS8's WKWebView?
+1 here. The performance gains are pretty incredible, and apps for iOS8 forward are going to increasingly use it.
@tomerlevi444: I'm not using this in production as much as I was, so I haven't been adding new features. If you submit a PR, I'm happy to merge it.
@tcoulter, upon further research, WKWebKitView
has some code that's designed to accomplish Jockey's functionality, so it's probably best to tell users to just implement using Apple's built-in functionality rather than complicate the UIWebView
code that already exists and performs well.
This works fine:
In JockeyWK.h
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
@interface JockeyWK : NSObject
typedef void (^ JockeyWKHandler)(NSDictionary *payload);
typedef void (^ JockeyWKAsyncHandler)(WKWebView *webView, NSDictionary *payload, void (^complete)());
+ (void)on:(NSString*)type perform:(JockeyWKHandler)handler;
+ (void)on:(NSString*)type performAsync:(JockeyWKAsyncHandler)handler;
+ (void)send:(NSString*)type withPayload:(id)payload toWebView:(WKWebView*)webView;
+ (BOOL)webView:(WKWebView*)webView withUrl:(NSURL*)url;
@property (strong, atomic) NSNumber *messageCount;
@property (strong, nonatomic) NSMutableDictionary *listeners;
@property (strong, nonatomic) NSMutableDictionary *callbacks;
@end
In JockeyWK.m
#import "JockeyWK.h"
@implementation JockeyWK
+ (id)sharedInstance
{
static dispatch_once_t once;
static JockeyWK *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
sharedInstance.messageCount = @0;
sharedInstance.listeners = [[NSMutableDictionary alloc] init];
sharedInstance.callbacks = [[NSMutableDictionary alloc] init];
});
return sharedInstance;
}
+ (void)on:(NSString*)type perform:(JockeyWKHandler)handler {
void (^ extended)(WKWebView *webView, NSDictionary *payload, void (^ complete)()) = ^(WKWebView *webView, NSDictionary *payload, void(^ complete)()) {
handler(payload);
complete();
};
[self on:type performAsync:extended];
}
+ (void)on:(NSString *)type performAsync:(JockeyWKAsyncHandler)handler
{
JockeyWK *instance = [JockeyWK sharedInstance];
NSDictionary *listeners = [instance listeners];
NSMutableArray *listenerList = [listeners objectForKey:type];
if (listenerList == nil) {
listenerList = [[NSMutableArray alloc] init];
[instance.listeners setValue:listenerList forKey:type];
}
[listenerList addObject:handler];
}
+ (void)send:(NSString*)type withPayload:(id)payload toWebView:(WKWebView*)webView {
JockeyWK *jockey = [JockeyWK sharedInstance];
NSNumber *messageId = jockey.messageCount;
NSError *err;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:payload options:NSJSONWritingPrettyPrinted error:&err];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *javascript = [NSString stringWithFormat:@"Jockey.trigger(\"%@\", %i, %@);", type, (int)[messageId integerValue], jsonString];
[webView evaluateJavaScript:javascript completionHandler:nil];
jockey.messageCount = @([jockey.messageCount integerValue] + 1);
}
+ (BOOL)webView:(WKWebView*)webView withUrl:(NSURL*)url
{
if ( [[url scheme] isEqualToString:@"jockey"] )
{
NSString *eventType = [url host];
NSString *messageId = [[url path] substringFromIndex:1];
NSString *query = [url query];
NSString *jsonString = [query stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData: [jsonString dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];
if ([eventType isEqualToString:@"event"]) {
[[self sharedInstance] triggerEventFromWebView:webView withData:JSON];
} else if ([eventType isEqualToString:@"callback"]) {
[[self sharedInstance] triggerCallbackForMessage:@([messageId integerValue])];
}
return NO;
}
return YES;
}
# pragma mark - Private methods
- (void)triggerCallbackForMessage:(NSNumber *)messageId {
NSString *messageIdString = [messageId stringValue];
void (^ callback)() = [_callbacks objectForKey:messageIdString];
if (callback != nil) {
callback();
}
[_callbacks removeObjectForKey:messageIdString];
}
- (void)triggerCallbackOnWebView:(WKWebView*)webView forMessage:(NSString*)messageId
{
NSString *javascript = [NSString stringWithFormat:@"Jockey.triggerCallback(\"%@\");", messageId];
[webView evaluateJavaScript:javascript completionHandler:nil];
}
- (void)triggerEventFromWebView:(WKWebView*)webView withData:(NSDictionary*)envelope
{
NSDictionary *listeners = [[JockeyWK sharedInstance] listeners];
NSString *messageId = [envelope objectForKey:@"id"];
NSString *type = [envelope objectForKey:@"type"];
NSDictionary *payload = [envelope objectForKey:@"payload"];
NSArray *listenerList = (NSArray*)[listeners objectForKey: type];
__block NSInteger executedCount = 0;
void (^complete)() = ^() {
executedCount += 1;
if (executedCount >= [listenerList count]) {
[[JockeyWK sharedInstance] triggerCallbackOnWebView: webView forMessage: messageId];
}
};
for (JockeyWKAsyncHandler handler in listenerList) {
handler(webView, payload, complete);
}
}
@end
and just interact with it the same way you'd interact with Jockey usually.
I don't know the exact version of Jockey I'm interacting with tho. From 2013 at least.