add screenshot and so on

This commit is contained in:
Yaqing Wang
2015-10-16 15:05:58 +08:00
parent 6cc290ac81
commit 9c9432b216
18 changed files with 627 additions and 442 deletions

View File

@@ -15,10 +15,12 @@
180DDFA71AD4C6B100EFAC49 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 180DDFA51AD4C6B100EFAC49 /* LaunchScreen.xib */; };
180DDFB31AD4C6B100EFAC49 /* ObjCAddJSInterfaceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 180DDFB21AD4C6B100EFAC49 /* ObjCAddJSInterfaceTests.m */; };
180DDFBE1AD4C6E100EFAC49 /* UIWebView+AddJavaScriptInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 180DDFBD1AD4C6E100EFAC49 /* UIWebView+AddJavaScriptInterface.m */; };
180DDFC21AD4C7D500EFAC49 /* YQWebViewProxyDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 180DDFC11AD4C7D500EFAC49 /* YQWebViewProxyDelegate.m */; };
180DDFC21AD4C7D500EFAC49 /* YQWebViewProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 180DDFC11AD4C7D500EFAC49 /* YQWebViewProxy.m */; };
180DDFC71AD5328000EFAC49 /* YQAddJSInterface.js in Resources */ = {isa = PBXBuildFile; fileRef = 180DDFC61AD5328000EFAC49 /* YQAddJSInterface.js */; };
18C876E11AD617D80080F561 /* test.html in Resources */ = {isa = PBXBuildFile; fileRef = 18C876E01AD617D80080F561 /* test.html */; };
18DF31A31AD7A4340000872F /* UINavigationItem+Addition.m in Sources */ = {isa = PBXBuildFile; fileRef = 18DF31A21AD7A4340000872F /* UINavigationItem+Addition.m */; };
C884A3D51BCF45E300F701BD /* YQWebViewProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 180DDFC11AD4C7D500EFAC49 /* YQWebViewProxy.m */; };
C884A3D61BCF45E600F701BD /* UIWebView+AddJavaScriptInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 180DDFBD1AD4C6E100EFAC49 /* UIWebView+AddJavaScriptInterface.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -47,8 +49,8 @@
180DDFB21AD4C6B100EFAC49 /* ObjCAddJSInterfaceTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjCAddJSInterfaceTests.m; sourceTree = "<group>"; };
180DDFBC1AD4C6E100EFAC49 /* UIWebView+AddJavaScriptInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIWebView+AddJavaScriptInterface.h"; path = "ObjCAddJSInterface/UIWebView+AddJavaScriptInterface.h"; sourceTree = "<group>"; };
180DDFBD1AD4C6E100EFAC49 /* UIWebView+AddJavaScriptInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIWebView+AddJavaScriptInterface.m"; path = "ObjCAddJSInterface/UIWebView+AddJavaScriptInterface.m"; sourceTree = "<group>"; };
180DDFC01AD4C7D500EFAC49 /* YQWebViewProxyDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YQWebViewProxyDelegate.h; path = ObjCAddJSInterface/YQWebViewProxyDelegate.h; sourceTree = "<group>"; };
180DDFC11AD4C7D500EFAC49 /* YQWebViewProxyDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = YQWebViewProxyDelegate.m; path = ObjCAddJSInterface/YQWebViewProxyDelegate.m; sourceTree = "<group>"; };
180DDFC01AD4C7D500EFAC49 /* YQWebViewProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YQWebViewProxy.h; path = ObjCAddJSInterface/YQWebViewProxy.h; sourceTree = "<group>"; };
180DDFC11AD4C7D500EFAC49 /* YQWebViewProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = YQWebViewProxy.m; path = ObjCAddJSInterface/YQWebViewProxy.m; sourceTree = "<group>"; };
180DDFC61AD5328000EFAC49 /* YQAddJSInterface.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = YQAddJSInterface.js; path = ObjCAddJSInterface/YQAddJSInterface.js; sourceTree = "<group>"; };
18C876E01AD617D80080F561 /* test.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = test.html; sourceTree = "<group>"; };
18DF31A11AD7A4340000872F /* UINavigationItem+Addition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UINavigationItem+Addition.h"; sourceTree = "<group>"; };
@@ -142,8 +144,8 @@
180DDFC61AD5328000EFAC49 /* YQAddJSInterface.js */,
180DDFBC1AD4C6E100EFAC49 /* UIWebView+AddJavaScriptInterface.h */,
180DDFBD1AD4C6E100EFAC49 /* UIWebView+AddJavaScriptInterface.m */,
180DDFC01AD4C7D500EFAC49 /* YQWebViewProxyDelegate.h */,
180DDFC11AD4C7D500EFAC49 /* YQWebViewProxyDelegate.m */,
180DDFC01AD4C7D500EFAC49 /* YQWebViewProxy.h */,
180DDFC11AD4C7D500EFAC49 /* YQWebViewProxy.m */,
);
name = AddJavaScriptInterface;
sourceTree = "<group>";
@@ -192,7 +194,7 @@
180DDF8B1AD4C6B100EFAC49 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0620;
LastUpgradeCheck = 0710;
ORGANIZATIONNAME = billwang1990.github.io;
TargetAttributes = {
180DDF921AD4C6B100EFAC49 = {
@@ -255,7 +257,7 @@
18DF31A31AD7A4340000872F /* UINavigationItem+Addition.m in Sources */,
180DDF9C1AD4C6B100EFAC49 /* AppDelegate.m in Sources */,
180DDF991AD4C6B100EFAC49 /* main.m in Sources */,
180DDFC21AD4C7D500EFAC49 /* YQWebViewProxyDelegate.m in Sources */,
180DDFC21AD4C7D500EFAC49 /* YQWebViewProxy.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -264,6 +266,8 @@
buildActionMask = 2147483647;
files = (
180DDFB31AD4C6B100EFAC49 /* ObjCAddJSInterfaceTests.m in Sources */,
C884A3D51BCF45E300F701BD /* YQWebViewProxy.m in Sources */,
C884A3D61BCF45E600F701BD /* UIWebView+AddJavaScriptInterface.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -317,6 +321,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -379,6 +384,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = ObjCAddJSInterface/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "billwang.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
@@ -389,6 +395,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = ObjCAddJSInterface/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "billwang.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
@@ -407,6 +414,7 @@
);
INFOPLIST_FILE = ObjCAddJSInterfaceTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "billwang.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ObjCAddJSInterface.app/ObjCAddJSInterface";
};
@@ -422,6 +430,7 @@
);
INFOPLIST_FILE = ObjCAddJSInterfaceTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "billwang.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ObjCAddJSInterface.app/ObjCAddJSInterface";
};

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
<BreakpointContent
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
scope = "0"
stopOnStyle = "0">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "180DDF921AD4C6B100EFAC49"
BuildableName = "ObjCAddJSInterface.app"
BlueprintName = "ObjCAddJSInterface"
ReferencedContainer = "container:ObjCAddJSInterface.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "180DDFAB1AD4C6B100EFAC49"
BuildableName = "ObjCAddJSInterfaceTests.xctest"
BlueprintName = "ObjCAddJSInterfaceTests"
ReferencedContainer = "container:ObjCAddJSInterface.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "180DDF921AD4C6B100EFAC49"
BuildableName = "ObjCAddJSInterface.app"
BlueprintName = "ObjCAddJSInterface"
ReferencedContainer = "container:ObjCAddJSInterface.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "180DDF921AD4C6B100EFAC49"
BuildableName = "ObjCAddJSInterface.app"
BlueprintName = "ObjCAddJSInterface"
ReferencedContainer = "container:ObjCAddJSInterface.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "180DDF921AD4C6B100EFAC49"
BuildableName = "ObjCAddJSInterface.app"
BlueprintName = "ObjCAddJSInterface"
ReferencedContainer = "container:ObjCAddJSInterface.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>ObjCAddJSInterface.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>180DDF921AD4C6B100EFAC49</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>180DDFAB1AD4C6B100EFAC49</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>billwang.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@@ -8,8 +8,6 @@
#import <UIKit/UIKit.h>
@class YQWebViewProxyDelegate;
@interface UIWebView (AddJavaScriptInterface)
- (void) addJavascriptInterfaces:(NSObject*) interface WithName:(NSString*) name;

View File

@@ -7,7 +7,7 @@
//
#import "UIWebView+AddJavaScriptInterface.h"
#import "YQWebViewProxyDelegate.h"
#import "YQWebViewProxy.h"
#import <objc/runtime.h>
@implementation UIWebView (AddJavaScriptInterface)
@@ -37,7 +37,7 @@
NSCAssert([del conformsToProtocol:@protocol(UIWebViewDelegate)], @"must conform UIWebViewDelegate");
YQWebViewProxyDelegate *proxyDelegate = [self webViewProxyDelegate];
YQWebViewProxy *proxyDelegate = [self webViewProxyDelegate];
if (![del isEqual:proxyDelegate]) {
[proxyDelegate setDelegate:del];
}
@@ -50,14 +50,14 @@
[[self webViewProxyDelegate] addJavascriptInterfaces:interface WithName:name];
}
- (YQWebViewProxyDelegate*)webViewProxyDelegate
- (YQWebViewProxy*)webViewProxyDelegate
{
YQWebViewProxyDelegate *retDelegate = nil;
YQWebViewProxy *retDelegate = nil;
retDelegate = objc_getAssociatedObject(self, @selector(webViewProxyDelegate));
if (!retDelegate) {
retDelegate = [[YQWebViewProxyDelegate alloc]init];
retDelegate = [[YQWebViewProxy alloc]init];
objc_setAssociatedObject(self, @selector(webViewProxyDelegate), retDelegate, OBJC_ASSOCIATION_RETAIN);
self.delegate = retDelegate;
}

View File

@@ -1,122 +1,65 @@
;
(function() {
// body...
"use strict";
if (window.HybridWeb) {
return
};
window.smeHybridPlatform = "ios";
return;
}
window.HybridWeb = {};
var _callBack = {};
var _callBackCount = 1;
var _cacheObjCount = 1;
var _cacheObj = {};
function _resultForCallback(callbackId, retStr) {
function _resultForCallback(callbackId, ret) {
try {
var callback = _callBack[callbackId];
if (!callback) return;
callback.apply(null, [retStr]);
callback.apply(null, [ret]);
} catch (e) {}
}
function _ajaxCallBack(originObjKey, ret) {
var orinalObj = HybridWeb.cachObj[originObjKey];
var data = {};
if (ret && ret != "") {
var decodeStr = decodeURIComponent(ret == null ? "" : ret);
data = JSON.parse(decodeStr);
} else {
data.code = "-1"
}
if (data.code != null && data.code != "-1") {
orinalObj.success(data);
} else {
orinalObj.error(data);
}
}
function _call(obj, functionName, args) {
var formattedArgs = [];
for (var i = 0, l = args.length; i < l; i++) {
var argType = typeof args[i];
var thisArg = args[i];
if (JSTypeIs.String(thisArg)) {
formattedArgs.push("s");
formattedArgs.push(thisArg);
} else if (JSTypeIs.Number(thisArg)) {
formattedArgs.push("d");
formattedArgs.push((args[i]).toString());
} else if (JSTypeIs.Boolean(thisArg)) {
formattedArgs.push("b");
formattedArgs.push((args[i]).toString());
} else if (JSTypeIs.Undefined(thisArg) || JSTypeIs.Null(thisArg)) {
formattedArgs.push("n");
formattedArgs.push("NSNull");
} else if (JSTypeIs.Array(thisArg)) {
formattedArgs.push("a");
formattedArgs.push(encodeURIComponent(JSON.stringify(thisArg)));
} else if (JSTypeIs.Function(thisArg)) {
var key = HybridWeb.callBackCount++;
if (JSTypeIs.Function(thisArg) && (i == args.length -1)) {
var key = window.HybridWeb.callBackCount++;
_callBack[key.toString()] = thisArg;
formattedArgs.push("f");
formattedArgs.push(encodeURIComponent(key.toString()));
} else {
//only for inject http service
formattedArgs.push("o");
var key = HybridWeb.cacheObjCount++;
_cacheObj[key.toString()] = thisArg;
var arr = [];
arr.push(key.toString());
var tmpStr = JSON.stringify(thisArg);
var cloneObj = JSON.parse(tmpStr);
for (var o in thisArg) {
//{key:value}, value不支持嵌套有function的任何对象即 value为 {key: function(){}}这种形式“
//只对第一层嵌套有function做处理
if (JSTypeIs.Function(thisArg[o])) {
cloneObj[o] = o;
}
}
arr.push(cloneObj);
formattedArgs.push(encodeURIComponent(JSON.stringify(arr)));
formattedArgs.push(key.toString());
}else{
formattedArgs.push(thisArg);
}
}
var argStr = (formattedArgs.length > 0 ? ":" + encodeURIComponent(formattedArgs.join("/@@/")) : "");
var argStr = (formattedArgs.length > 0 ? "?params=" + encodeURIComponent(JSON.stringify(formattedArgs)) : "");
var iframe = document.createElement("IFRAME");
iframe.style.display = 'none'
iframe.setAttribute("src", "ika-js-scheme:" + obj + ":" + encodeURIComponent(functionName) + argStr);
iframe.style.display = "none";
iframe.setAttribute("src", "hybrid-js-scheme://" + obj + "/" + encodeURIComponent(functionName) + argStr);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
var ret = HybridWeb.retValue;
HybridWeb.retValue = undefined;
var ret = window.HybridWeb.retValue;
window.HybridWeb.retValue = undefined;
if (ret) {
return decodeURIComponent(ret == null ? "" : ret);
return decodeURIComponent(ret === null ? "" : ret);
}
}
function _inject(obj, methods) {
window[obj] = {};
var jsObj = window[obj];
for (var i = 0, l = methods.length; i < l; i++) {
(function() {
var method = methods[i];
var jsMethod = method.replace(new RegExp(":", "g"), "");
jsObj[jsMethod] = function() {
return HybridWeb.call(obj, method, Array.prototype.slice.call(arguments));
jsObj[method] = function() {
return window.HybridWeb.call(obj, method, Array.prototype.slice.call(arguments));
};
})();
}
@@ -131,20 +74,16 @@
JSTypeIs[typeName] = (function(type) {
return function(obj) {
return Object.prototype.toString.call(obj) == "[object " + type + "]";
}
};
})(typeName);
}
window.HybridWeb = {
call: _call,
inject: _inject,
invokeCallBack: _resultForCallback,
callBack: _callBack,
callBackCount: _callBackCount,
cacheObjCount: _cacheObjCount,
cachObj: _cacheObj,
ajaxCallBack: _ajaxCallBack
}
};
})();

View File

@@ -9,11 +9,11 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface YQWebViewProxyDelegate : NSObject<UIWebViewDelegate>
@interface YQWebViewProxy : NSObject<UIWebViewDelegate>
@property (nonatomic, strong) NSMapTable *registInterface;
- (void) addJavascriptInterfaces:(NSObject*) interface WithName:(NSString*) name;
- (void) setDelegate:(id<UIWebViewDelegate>)delegate;
- (void) setDelegate: (id<UIWebViewDelegate>)delegate;
@end

View File

@@ -0,0 +1,357 @@
//
// YQWebViewProxyDelegate.m
// ObjCAddJSInterface
//
// Created by wangyaqing on 15/4/8.
// Copyright (c) 2015 billwang1990.github.io. All rights reserved.
//
#import "YQWebViewProxy.h"
#import <objc/runtime.h>
#define kCustomProtocolScheme @"hybrid-js-scheme"
#pragma mark NSDictionary & NSArray Category -JSONString
@interface NSDictionary(JSONString)
- (NSString*)JSONString;
@end
@implementation NSDictionary (JSONString)
- (NSString *)JSONString
{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error];
if (!jsonData) {
NSLog(@"jsonStringWithPrettyPrint: error: %@", error.localizedDescription);
return @"{}";
} else {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}
@end
@interface NSArray(JSONString)
- (NSString*)JSONString;
@end
@implementation NSArray (JSONString)
- (NSString *)JSONString
{
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error];
if (!jsonData) {
NSLog(@"jsonStringWithPrettyPrint: error: %@", error.localizedDescription);
return @"[]";
} else {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}
@end
#pragma mark YQWebViewProxyDelegate
@implementation YQWebViewProxy
{
__weak NSObject<UIWebViewDelegate> *_realDelegate;
}
- (void)addJavascriptInterfaces:(NSObject *)interface WithName:(NSString *)name
{
[self.registInterface setObject:interface forKey:name];
}
- (NSMapTable *)registInterface
{
if (!_registInterface) {
_registInterface = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];
}
return _registInterface;
}
- (void)setDelegate:(id<UIWebViewDelegate>)delegate
{
_realDelegate = delegate;
}
#pragma mark UIWebView Delegate
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if ([_realDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[_realDelegate webViewDidFinishLoad:webView];
}
[self injectObjectForWebView:webView];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if ([_realDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
[_realDelegate webView:webView didFailLoadWithError:error];
}
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
if ([_realDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[_realDelegate webViewDidStartLoad:webView];
};
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
[self injectObjectForWebView:webView];
NSURL *url = [request URL];
if ([[url scheme] isEqualToString:kCustomProtocolScheme]) {
NSString *obj = url.host;
NSString *method = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
NSArray *params = [self objectWithQueryString:url.query];
if (url.query.length > 0) {
method = [method stringByAppendingString:@"_"];
method = [method stringByReplacingOccurrencesOfString:@"_" withString:@":"];
}
NSObject* interface = [self.registInterface objectForKey:obj];
SEL selector = NSSelectorFromString(method);
NSMethodSignature* sig = [[interface class] instanceMethodSignatureForSelector:selector];
if (!sig) {
selector = NSSelectorFromString([method substringToIndex:method.length -1]);
sig = [[interface class]instanceMethodSignatureForSelector:selector];
if (!sig) {
return NO;
}
}
NSInvocation* invoker = [NSInvocation invocationWithMethodSignature:sig];
invoker.selector = selector;
invoker.target = interface;
NSString *callBackID = nil;
if (params.count > 0){
for (NSInteger i = 0; i < params.count; i++) {
id arg = params[i];
NSUInteger allCount = [sig numberOfArguments];
NSUInteger parameterCount = allCount - 2;
if (i < parameterCount) {
[invoker setArgument:&arg atIndex:i+2];
}else{
if ([arg isKindOfClass:[NSString class]]) {
callBackID = [arg stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
}
}
[invoker retainArguments];
}
if (callBackID) {
//exist callback function
__weak typeof(self) wSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[wSelf invoke:invoker methodSignature:sig onWebView:webView callBackID:callBackID];
});
}
else
{
[self invoke:invoker methodSignature:sig onWebView:webView callBackID:nil];
}
return NO;
}
if (!_realDelegate || ![_realDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]){
return YES;
}
return [_realDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
}
- (void)injectObjectForWebView:(UIWebView*)webView
{
if (![[webView stringByEvaluatingJavaScriptFromString:@"typeof HybridWeb == 'object'"] isEqualToString:@"true"]) {
NSBundle *bundle = [NSBundle mainBundle];
NSString *filePath = [bundle pathForResource:@"YQAddJSInterface" ofType:@"js"];
NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:js];
__block NSMutableString* injection = [[NSMutableString alloc] init];
[[self.registInterface dictionaryRepresentation] enumerateKeysAndObjectsUsingBlock:^(id objectKey, NSObject *obj, BOOL *stop) {
// HybridWeb.inject("objname", ["mehod1", "method2"]);
[injection appendString:@"HybridWeb.inject(\""];
[injection appendString:objectKey];
[injection appendString:@"\", ["];
NSArray *methods = DumpObjMethods(object_getClass(obj));
[methods enumerateObjectsUsingBlock:^(NSString *method, NSUInteger idx, BOOL * _Nonnull stop) {
[injection appendString:@"\""];
[injection appendString:method];
[injection appendString:@"\""];
if (idx == methods.count - 1) {
[injection appendString:@"]);"];
}else{
[injection appendString:@", "];
}
}];
[webView stringByEvaluatingJavaScriptFromString:injection];
}];
}
}
#pragma mark Dump Class
NSArray* DumpObjMethods(Class clz){
Class thisClass = clz;
NSMutableArray *ret = [[NSMutableArray alloc]init];
do {
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(thisClass, &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
NSString *name = @(sel_getName(method_getName(method)));
const char *encoding = method_getTypeEncoding(method);
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
NSUInteger allCount = [signature numberOfArguments]; // The parameter count including the self and the _cmd ones
NSUInteger parameterCount = allCount - 2;
if (parameterCount < 2) {
name = [name stringByReplacingOccurrencesOfString:@":" withString:@""];
}else{
name = [name substringToIndex:name.length-1];
name = [name stringByReplacingOccurrencesOfString:@":" withString:@"_"];
}
[ret addObject:name];
// printf("\t'%s' has method named '%s' of encoding '%s'\n",
// class_getName(clz),
// sel_getName(method_getName(method)),
// method_getTypeEncoding(method));
}
free(methods);
thisClass = class_getSuperclass(thisClass);
} while (thisClass);
return ret;
}
#pragma mark Parse params from url query
- (id)objectWithQueryString:(NSString *)queryString
{
if (queryString == nil) {
return nil;
}
NSMutableDictionary *resultDictionary = [[NSMutableDictionary alloc] init];
NSArray *components = [queryString componentsSeparatedByString:@"&"];
[components enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSString class]]) {
NSArray *param = [obj componentsSeparatedByString:@"="];
if ([param count] == 2) {
NSString *key = param[0];
NSString *encodedValue = param[1];
NSString *decodedValue = (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)encodedValue, CFSTR(""), kCFStringEncodingUTF8);
resultDictionary[key] = decodedValue;
}
}
}];
NSError *error = nil;
id param = [NSJSONSerialization JSONObjectWithData:[resultDictionary[@"params"] dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error];
return [param copy]?:nil;
}
#pragma mark send result back to js
- (void)sendResultFrom:(UIWebView*)web value:(NSString*)val asJSString:(BOOL)asStr callBack:(NSString*)callback{
NSString *jsVal = asStr ? [NSString stringWithFormat:@"\"%@\"",val] : [NSString stringWithFormat:@"%@",val];
if (!val) {
jsVal = @"undefined";
}
if (callback) {
dispatch_sync(dispatch_get_main_queue(), ^{
[web stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.HybridWeb.invokeCallBack(%@,%@);",callback, jsVal]];
});
}else{
jsVal = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,(CFStringRef)jsVal, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8));
[web stringByEvaluatingJavaScriptFromString:[@"" stringByAppendingFormat:@"window.HybridWeb.retValue=\"%@\";", jsVal]];
}
}
- (void)invoke:(NSInvocation*)invocation methodSignature:(NSMethodSignature*)sig onWebView:(UIWebView*)webView callBackID:(NSString*)callBack
{
[invocation invoke];
const char *returnType = [sig methodReturnType];
if (!strcmp(returnType, @encode(void))) {
//return void
}else{
NSUInteger bufferLen = [sig methodReturnLength];
void *buffer = (void*)malloc(bufferLen);
if (buffer == NULL) {
printf("malloc failed!");
return;
}
[invocation getReturnValue:&buffer];
NSString *ret = nil;
__weak typeof(self) wSelf = self;
void (^sendResult)(NSString*, BOOL) = ^(NSString *strValue, BOOL asJSString){
__strong typeof(wSelf) sSelf = wSelf;
[sSelf sendResultFrom:webView value:strValue asJSString:asJSString callBack:callBack];
};
if(!strcmp(returnType, @encode(id))){
id obj = (__bridge id)buffer;
if ([obj isKindOfClass:[NSString class]]) {
ret = (NSString*)obj;
sendResult(ret,YES);
}else if([obj isKindOfClass:[NSArray class]]){
ret = [(NSArray*)obj JSONString]?:@"";
sendResult(ret,NO);
}else if ([obj isKindOfClass:[NSDictionary class]]){
ret = [(NSDictionary*)obj JSONString]?:@"";
sendResult(ret,NO);
}else{
if ([obj respondsToSelector:@selector(description)]) {
ret = [obj description];
}else{
ret = nil;
}
sendResult(ret, YES);
}
}else{
//unsupported type
}
}
}
@end

View File

@@ -1,273 +0,0 @@
//
// YQWebViewProxyDelegate.m
// ObjCAddJSInterface
//
// Created by wangyaqing on 15/4/8.
// Copyright (c) 2015 billwang1990.github.io. All rights reserved.
//
#import "YQWebViewProxyDelegate.h"
#import <objc/runtime.h>
#define kCustomProtocolScheme @"hybrid-js-scheme"
@implementation YQWebViewProxyDelegate
{
__weak NSObject<UIWebViewDelegate> *_realDelegate;
__strong NSMutableArray *holdOnParams;
}
- (void)addJavascriptInterfaces:(NSObject *)interface WithName:(NSString *)name
{
[self.registInterface setObject:interface forKey:name];
}
- (NSMapTable *)registInterface
{
if (!_registInterface) {
_registInterface = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];
}
return _registInterface;
}
- (void)setDelegate:(id<UIWebViewDelegate>)delegate
{
_realDelegate = delegate;
}
#pragma mark UIWebViewDelegate
//webview
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if ([_realDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[_realDelegate webViewDidFinishLoad:webView];
}
[self injectObjectForWebView:webView];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if ([_realDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
[_realDelegate webView:webView didFailLoadWithError:error];
}
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
if ([_realDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[_realDelegate webViewDidStartLoad:webView];
};
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
[self injectObjectForWebView:webView];
NSURL *url = [request URL];
if ([[url scheme] hasPrefix:kCustomProtocolScheme]) {
NSArray *components = [[url absoluteString] componentsSeparatedByString:@":"];
NSString* obj = (NSString*)[components objectAtIndex:1];
NSString* method = [(NSString*)[components objectAtIndex:2]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSObject* interface = [self.registInterface objectForKey:obj];
SEL selector = NSSelectorFromString(method);
NSMethodSignature* sig = [[interface class] instanceMethodSignatureForSelector:selector];
NSInvocation* invoker = [NSInvocation invocationWithMethodSignature:sig];
invoker.selector = selector;
invoker.target = interface;
//An NSInvocation by default does not retain or copy given arguments for efficiency, so each object passed as argument must still live when the invocation is invoked.
if (!holdOnParams) {
holdOnParams = [[NSMutableArray alloc]init];
}
BOOL callAsync = NO;
NSString *callBackID = nil;
if ([components count] > 3){
NSString *argsAsString = [(NSString*)[components objectAtIndex:3]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSArray* formattedArgs = [argsAsString componentsSeparatedByString:@"/@@/"];
for (NSInteger i = 0, j = 0, l = [formattedArgs count]; i < l; i+=2, j++){
NSString* type = ((NSString*) [formattedArgs objectAtIndex:i]);
NSString* argStr = ((NSString*) [formattedArgs objectAtIndex:i + 1]);
if ([@"s" isEqualToString:type]){
[holdOnParams addObject:argStr];
[invoker setArgument:&argStr atIndex:(j + 2)];
}
else if([@"d" isEqualToString:type])
{
NSString* numStr = [argStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
CGFloat arg = [numStr floatValue];
[invoker setArgument:&arg atIndex:(j+2)];
}
else if ([@"b" isEqualToString:type])
{
NSString* boolStr = [argStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
BOOL arg = [boolStr boolValue];
[invoker setArgument:&arg atIndex:(j+2)];
}
else if ([@"a" isEqualToString:type])
{
NSString *arrStr = [argStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *data = [arrStr dataUsingEncoding:NSUTF8StringEncoding];
NSArray *array = [[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]copy];
[holdOnParams addObject:array];
[invoker setArgument:&array atIndex:(j+2)];
}
else if ([@"f" isEqualToString:type])
{
callAsync = YES;
callBackID = [argStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}
else if ([@"o" isEqualToString:type])
{
//ONLY for inject ajax
NSString* objStr = [argStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSData *data = [objStr dataUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *arr = [[NSJSONSerialization JSONObjectWithData:data
options:0
error:nil]mutableCopy];
// NSDictionary *dic = [arr objectAtIndex:1];
//arr[0]: jsobjkey, arr[1]:, arr[2]:webview
[arr addObject:webView];
[holdOnParams addObject:arr];
[invoker setArgument:&arr atIndex:(j+2)];
callAsync = YES;
}
}
printf("\n");
}
if (callAsync) {
__weak typeof(self) wSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// [invoker retainArguments];
[invoker invoke];
//return the value by using javascript, only support string, now!!!
if (callBackID && [sig methodReturnLength] > 0){
NSString* retValue = nil;
void *resultVal;
[invoker getReturnValue:&resultVal];
retValue = (__bridge NSString*)resultVal;
[wSelf returnResultFrom:webView callBkId:callBackID args:retValue];
}
});
}
else
{
[invoker retainArguments];
[invoker invoke];
if ([sig methodReturnLength] > 0){
NSString* retValue = nil;
void *resultVal;
[invoker getReturnValue:&resultVal];
retValue = (__bridge NSString*)resultVal;
if (retValue == NULL || retValue == nil){
[webView stringByEvaluatingJavaScriptFromString:@"window.HybridWeb.retValue=null;"];
}else{
retValue = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,(CFStringRef) retValue, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8));
[webView stringByEvaluatingJavaScriptFromString:[@"" stringByAppendingFormat:@"window.HybridWeb.retValue=\"%@\";", retValue]];
}
}
}
return NO;
}
if (!_realDelegate || ![_realDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]){
return YES;
}
return [_realDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
}
- (void)injectObjectForWebView:(UIWebView*)webView
{
if (![[webView stringByEvaluatingJavaScriptFromString:@"typeof HybridWeb == 'object'"] isEqualToString:@"true"]) {
NSBundle *bundle = [NSBundle mainBundle];
NSString *filePath = [bundle pathForResource:@"YQAddJSInterface" ofType:@"js"];
NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:js];
__block NSMutableString* injection = [[NSMutableString alloc] init];
[[self.registInterface dictionaryRepresentation] enumerateKeysAndObjectsUsingBlock:^(id objectKey, NSObject *obj, BOOL *stop) {
/*
HybridWeb.inject("objname", ["mehod1", "method2"]);
*/
//object name
[injection appendString:@"HybridWeb.inject(\""];
[injection appendString:objectKey];
[injection appendString:@"\", ["];
//get object methods
NSArray *methods = DumpObjMethods(object_getClass(obj));
for (NSString *method in methods) {
[injection appendString:@"\""];
[injection appendString:method];
[injection appendString:@"\""];
if ([method isEqual:methods.lastObject]) {
[injection appendString:@"]);"];
}else{
[injection appendString:@", "];
}
}
}];
[webView stringByEvaluatingJavaScriptFromString:injection];
}
}
- (void)returnResultFrom:(UIWebView*)web callBkId:(NSString*)callbackId args:(NSString*)arg
{
dispatch_sync(dispatch_get_main_queue(), ^{
[web stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.HybridWeb.invokeCallBack(%@,'%@');",callbackId,arg]];
});
}
#pragma mark Dump Class
NSArray* DumpObjMethods(Class clz){
Class thisClass = clz;
NSMutableArray *ret = [[NSMutableArray alloc]init];
do {
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(clz, &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
[ret addObject:@(sel_getName(method_getName(method)))];
printf("\t'%s' has method named '%s' of encoding '%s'\n",
class_getName(clz),
sel_getName(method_getName(method)),
method_getTypeEncoding(method));
}
free(methods);
thisClass = class_getSuperclass(thisClass);
} while (thisClass);
return ret;
}
@end

View File

@@ -21,14 +21,12 @@
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
typeof(self) wSelf = self;
[self.webView addJavascriptInterfaces:wSelf WithName:@"ViewController"];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"]isDirectory:NO]]];
[self.navigationItem setNavigationBarItemWithTitle:@"add" andTarget:self action:@selector(tap) isLeftItem:NO];
[self.navigationItem setNavigationBarItemWithTitle:@"+" andTarget:self action:@selector(createNewVC) isLeftItem:NO];
}
- (void)didReceiveMemoryWarning {
@@ -36,52 +34,38 @@
// Dispose of any resources that can be recreated.
}
- (void)tap
- (void)createNewVC
{
[self.navigationController pushViewController:[ViewController new] animated:YES];
}
- (void)testMethod:(id)param
{
if ([param isKindOfClass:[NSArray class]]) {
[param enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@"%@ is instance of %@ \n", obj, [obj class]);
}];
}
if ([param isKindOfClass:[NSDictionary class]]) {
NSDictionary *dic = param;
[dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
NSLog(@"%@ is instance of %@ \n", obj, [obj class]);
}];
}
NSLog(@"call testMethod from js %@", [param description]);
- (void)passArrayFromJS:(NSArray*)arr{
NSLog(@"%s: %@",__FUNCTION__, [arr description]);
}
- (void)testBool:(BOOL)val
{
if (val) {
NSLog(@"bool val");
}
- (void)passObjFromJS:(NSDictionary*)o{
NSLog(@"%s: %@",__FUNCTION__, [o description]);
}
- (NSString*)testFloat:(CGFloat)f
{
NSLog(@"%f",f);
__block NSString *ret = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
NSLog(@"fire");
ret = @"012343356";
});
while (!ret) {
}
return ret;
- (void)passStringFromJS:(NSString*)str{
NSLog(@"%s: %@",__FUNCTION__, str);
}
- (NSArray*)callArray{
return @[@1,@2,@"3"];
}
- (NSDictionary*)callObject{
return @{@"name":@"nativeObj", @"type":@"object"};
}
- (NSString*)callString{
return @"string value";
}
- (NSString*)asyncCall{
sleep(5);
return @"bill wang";
}
@end

View File

@@ -1,15 +1,22 @@
<html>
<head>
<script type="text/javascript">
ViewController.testFloat(1, function(ret){});
<head>
<script type="text/javascript">
ViewController.passArrayFromJS(["22","2","3"]);
ViewController.passObjFromJS({"name":"obj1","type":"object"});
ViewController.passStringFromJS("string value");
<!-- ViewController.callString();-->
<!-- ViewController.callArray();-->
<!-- ViewController.callObject();-->
</script>
</head>
<body>
</head>
<body>
<br>
<h1>少年带朵花</h1>
<h1>Hybrid Bridge!</h1>
<p>Support string, array, dictionary and callback</p>
</body>
</html>

View File

@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>billwang.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@@ -8,9 +8,12 @@
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "UIWebView+AddJavaScriptInterface.h"
@interface ObjCAddJSInterfaceTests : XCTestCase
@property (nonatomic, strong) UIWebView *web;
@end
@implementation ObjCAddJSInterfaceTests
@@ -18,6 +21,12 @@
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
self.web = [[UIWebView alloc]init];
__weak typeof(self) wSelf = self;
[self.web addJavascriptInterfaces:wSelf WithName:@"this"];
}
- (void)tearDown {
@@ -37,4 +46,5 @@
}];
}
@end

View File

@@ -1,30 +1,39 @@
# ObjCAddJSInterface
Inject native object to javascript
# HybridWebView
最近在开发过程中看安卓同事可以调用一个叫addJavaScriptInterface的API很方便的把Native的对象注册到js中从而可以在js中调用Native的代码。然而OC中本身是不支持这样的特性的。因此我做了一层封装在UIWebView上增加了一个category从而可以像安卓一样很方便的将Native的代码注入js。
Native object inject to javascript environment.
You can call native method from UIWebView, and send the result back. It also support async callback handler.
###Usage
First of all, you should import `UIWebView+AddJavaScriptInterface.h`
Native
Injection:
[self.webView addJavascriptInterfaces:wSelf WithName:@"ViewController"];
Native code:
@interface ViewController : UIViewController
- (void)testMethod:(id)param;
- (void)passArrayFromJS:(NSArray*)arr;
- (NSArray*)callArray;
@end
JS调用
Javascript call:
ViewController.passArrayFromJS([1, 2, "2"]);
ViewController.callArray();
ViewController.testMehtod(param);
这里js一旦调用了之后Native会执行对应的方法并返回返回值如果有的话。如果你希望通过闭包的方式比如Native是一个耗时的操作获得返回值可以像下面这样写在最后传入一个闭包
Async :
You can add a callback at last if you will get the result later.
ViewController.callArray( function(ret){console.log(ret)} );
ViewController.testMethod(param, function (ret){
//do something
});
现在的代码有一定的局限性,不过对于我个人目前的项目来说足够了,会在后续进行改进
###Problem
Only support pass string, array or dictionary.

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB