mirror of
https://github.com/zhigang1992/HybridWebView.git
synced 2026-04-29 04:25:15 +08:00
add screenshot and so on
This commit is contained in:
@@ -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";
|
||||
};
|
||||
|
||||
Binary file not shown.
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class YQWebViewProxyDelegate;
|
||||
|
||||
@interface UIWebView (AddJavaScriptInterface)
|
||||
|
||||
- (void) addJavascriptInterfaces:(NSObject*) interface WithName:(NSString*) name;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -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
|
||||
357
ObjCAddJSInterface/ObjCAddJSInterface/YQWebViewProxy.m
Normal file
357
ObjCAddJSInterface/ObjCAddJSInterface/YQWebViewProxy.m
Normal 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
|
||||
|
||||
@@ -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]: 在js中对应的obj的key, 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
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
37
README.md
37
README.md
@@ -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
BIN
screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 KiB |
Reference in New Issue
Block a user