feat(project):添加 IOS 端 OC 编写项目 JSBridgeIOSOC

master
beichen 5 years ago
parent 5ce9f4cc15
commit ce40468a9a
  1. 97
      JSBridgeIOSOC/.gitignore
  2. 375
      JSBridgeIOSOC/JSBridgeIOSOC.xcodeproj/project.pbxproj
  3. 17
      JSBridgeIOSOC/JSBridgeIOSOC/AppDelegate.h
  4. 57
      JSBridgeIOSOC/JSBridgeIOSOC/AppDelegate.m
  5. 98
      JSBridgeIOSOC/JSBridgeIOSOC/Assets.xcassets/AppIcon.appiconset/Contents.json
  6. 6
      JSBridgeIOSOC/JSBridgeIOSOC/Assets.xcassets/Contents.json
  7. 25
      JSBridgeIOSOC/JSBridgeIOSOC/Base.lproj/LaunchScreen.storyboard
  8. 125
      JSBridgeIOSOC/JSBridgeIOSOC/Base.lproj/Main.storyboard
  9. 45
      JSBridgeIOSOC/JSBridgeIOSOC/Info.plist
  10. 36
      JSBridgeIOSOC/JSBridgeIOSOC/ViewController.h
  11. 234
      JSBridgeIOSOC/JSBridgeIOSOC/ViewController.m
  12. 15
      JSBridgeIOSOC/JSBridgeIOSOC/main.m
  13. 34
      JSBridgeIOSOC/WebViewJavascriptBridge/WKWebViewJavascriptBridge.h
  14. 198
      JSBridgeIOSOC/WebViewJavascriptBridge/WKWebViewJavascriptBridge.m
  15. 50
      JSBridgeIOSOC/WebViewJavascriptBridge/WebViewJavascriptBridge.h
  16. 211
      JSBridgeIOSOC/WebViewJavascriptBridge/WebViewJavascriptBridge.m
  17. 46
      JSBridgeIOSOC/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.h
  18. 221
      JSBridgeIOSOC/WebViewJavascriptBridge/WebViewJavascriptBridgeBase.m
  19. 3
      JSBridgeIOSOC/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.h
  20. 139
      JSBridgeIOSOC/WebViewJavascriptBridge/WebViewJavascriptBridge_JS.m

@ -0,0 +1,97 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Xcode Patch
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/1212123qw
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/

@ -0,0 +1,375 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
939CAD7922CF432B002D1A0C /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 939CAD7822CF432B002D1A0C /* .gitignore */; };
93B37D6F22CC525700FDA085 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 93B37D6E22CC525700FDA085 /* AppDelegate.m */; };
93B37D7222CC525700FDA085 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93B37D7122CC525700FDA085 /* ViewController.m */; };
93B37D7522CC525700FDA085 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 93B37D7322CC525700FDA085 /* Main.storyboard */; };
93B37D7722CC525700FDA085 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93B37D7622CC525700FDA085 /* Assets.xcassets */; };
93B37D7A22CC525700FDA085 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 93B37D7822CC525700FDA085 /* LaunchScreen.storyboard */; };
93B37D7D22CC525700FDA085 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 93B37D7C22CC525700FDA085 /* main.m */; };
93C8574822CDEE4C00091DA6 /* WebViewJavascriptBridgeBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 93C8574022CDEE4C00091DA6 /* WebViewJavascriptBridgeBase.m */; };
93C8574922CDEE4C00091DA6 /* WKWebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 93C8574122CDEE4C00091DA6 /* WKWebViewJavascriptBridge.m */; };
93C8574A22CDEE4C00091DA6 /* WebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 93C8574222CDEE4C00091DA6 /* WebViewJavascriptBridge.m */; };
93C8574B22CDEE4C00091DA6 /* WebViewJavascriptBridge_JS.m in Sources */ = {isa = PBXBuildFile; fileRef = 93C8574522CDEE4C00091DA6 /* WebViewJavascriptBridge_JS.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
939CAD7822CF432B002D1A0C /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
93B37D6A22CC525700FDA085 /* JSBridgeIOSOC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JSBridgeIOSOC.app; sourceTree = BUILT_PRODUCTS_DIR; };
93B37D6D22CC525700FDA085 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
93B37D6E22CC525700FDA085 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
93B37D7022CC525700FDA085 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
93B37D7122CC525700FDA085 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
93B37D7422CC525700FDA085 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
93B37D7622CC525700FDA085 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
93B37D7922CC525700FDA085 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
93B37D7B22CC525700FDA085 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
93B37D7C22CC525700FDA085 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
93C8574022CDEE4C00091DA6 /* WebViewJavascriptBridgeBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridgeBase.m; sourceTree = "<group>"; };
93C8574122CDEE4C00091DA6 /* WKWebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WKWebViewJavascriptBridge.m; sourceTree = "<group>"; };
93C8574222CDEE4C00091DA6 /* WebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge.m; sourceTree = "<group>"; };
93C8574322CDEE4C00091DA6 /* WebViewJavascriptBridge_JS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge_JS.h; sourceTree = "<group>"; };
93C8574422CDEE4C00091DA6 /* WebViewJavascriptBridgeBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridgeBase.h; sourceTree = "<group>"; };
93C8574522CDEE4C00091DA6 /* WebViewJavascriptBridge_JS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge_JS.m; sourceTree = "<group>"; };
93C8574622CDEE4C00091DA6 /* WebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge.h; sourceTree = "<group>"; };
93C8574722CDEE4C00091DA6 /* WKWebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKWebViewJavascriptBridge.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
93B37D6722CC525700FDA085 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
93B37D6122CC525700FDA085 = {
isa = PBXGroup;
children = (
939CAD7822CF432B002D1A0C /* .gitignore */,
93B37D6C22CC525700FDA085 /* JSBridgeIOSOC */,
93B37D6B22CC525700FDA085 /* Products */,
93C8573F22CDEE4C00091DA6 /* WebViewJavascriptBridge */,
);
sourceTree = "<group>";
};
93B37D6B22CC525700FDA085 /* Products */ = {
isa = PBXGroup;
children = (
93B37D6A22CC525700FDA085 /* JSBridgeIOSOC.app */,
);
name = Products;
sourceTree = "<group>";
};
93B37D6C22CC525700FDA085 /* JSBridgeIOSOC */ = {
isa = PBXGroup;
children = (
93B37D6D22CC525700FDA085 /* AppDelegate.h */,
93B37D6E22CC525700FDA085 /* AppDelegate.m */,
93B37D7022CC525700FDA085 /* ViewController.h */,
93B37D7122CC525700FDA085 /* ViewController.m */,
93B37D7322CC525700FDA085 /* Main.storyboard */,
93B37D7622CC525700FDA085 /* Assets.xcassets */,
93B37D7822CC525700FDA085 /* LaunchScreen.storyboard */,
93B37D7B22CC525700FDA085 /* Info.plist */,
93B37D7C22CC525700FDA085 /* main.m */,
);
path = JSBridgeIOSOC;
sourceTree = "<group>";
};
93C8573F22CDEE4C00091DA6 /* WebViewJavascriptBridge */ = {
isa = PBXGroup;
children = (
93C8574022CDEE4C00091DA6 /* WebViewJavascriptBridgeBase.m */,
93C8574122CDEE4C00091DA6 /* WKWebViewJavascriptBridge.m */,
93C8574222CDEE4C00091DA6 /* WebViewJavascriptBridge.m */,
93C8574322CDEE4C00091DA6 /* WebViewJavascriptBridge_JS.h */,
93C8574422CDEE4C00091DA6 /* WebViewJavascriptBridgeBase.h */,
93C8574522CDEE4C00091DA6 /* WebViewJavascriptBridge_JS.m */,
93C8574622CDEE4C00091DA6 /* WebViewJavascriptBridge.h */,
93C8574722CDEE4C00091DA6 /* WKWebViewJavascriptBridge.h */,
);
path = WebViewJavascriptBridge;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
93B37D6922CC525700FDA085 /* JSBridgeIOSOC */ = {
isa = PBXNativeTarget;
buildConfigurationList = 93B37D8022CC525700FDA085 /* Build configuration list for PBXNativeTarget "JSBridgeIOSOC" */;
buildPhases = (
93B37D6622CC525700FDA085 /* Sources */,
93B37D6722CC525700FDA085 /* Frameworks */,
93B37D6822CC525700FDA085 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = JSBridgeIOSOC;
productName = JSBridgeIOSOC;
productReference = 93B37D6A22CC525700FDA085 /* JSBridgeIOSOC.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
93B37D6222CC525700FDA085 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
TargetAttributes = {
93B37D6922CC525700FDA085 = {
CreatedOnToolsVersion = 10.2.1;
};
};
};
buildConfigurationList = 93B37D6522CC525700FDA085 /* Build configuration list for PBXProject "JSBridgeIOSOC" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 93B37D6122CC525700FDA085;
productRefGroup = 93B37D6B22CC525700FDA085 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
93B37D6922CC525700FDA085 /* JSBridgeIOSOC */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
93B37D6822CC525700FDA085 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
939CAD7922CF432B002D1A0C /* .gitignore in Resources */,
93B37D7A22CC525700FDA085 /* LaunchScreen.storyboard in Resources */,
93B37D7722CC525700FDA085 /* Assets.xcassets in Resources */,
93B37D7522CC525700FDA085 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
93B37D6622CC525700FDA085 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
93C8574822CDEE4C00091DA6 /* WebViewJavascriptBridgeBase.m in Sources */,
93C8574A22CDEE4C00091DA6 /* WebViewJavascriptBridge.m in Sources */,
93C8574922CDEE4C00091DA6 /* WKWebViewJavascriptBridge.m in Sources */,
93C8574B22CDEE4C00091DA6 /* WebViewJavascriptBridge_JS.m in Sources */,
93B37D7222CC525700FDA085 /* ViewController.m in Sources */,
93B37D7D22CC525700FDA085 /* main.m in Sources */,
93B37D6F22CC525700FDA085 /* AppDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
93B37D7322CC525700FDA085 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
93B37D7422CC525700FDA085 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
93B37D7822CC525700FDA085 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
93B37D7922CC525700FDA085 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
93B37D7E22CC525700FDA085 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
93B37D7F22CC525700FDA085 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
93B37D8122CC525700FDA085 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = JSBridgeIOSOC/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.beichen.JSBridgeIOSOC;
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
93B37D8222CC525700FDA085 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = JSBridgeIOSOC/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.beichen.JSBridgeIOSOC;
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
93B37D6522CC525700FDA085 /* Build configuration list for PBXProject "JSBridgeIOSOC" */ = {
isa = XCConfigurationList;
buildConfigurations = (
93B37D7E22CC525700FDA085 /* Debug */,
93B37D7F22CC525700FDA085 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
93B37D8022CC525700FDA085 /* Build configuration list for PBXNativeTarget "JSBridgeIOSOC" */ = {
isa = XCConfigurationList;
buildConfigurations = (
93B37D8122CC525700FDA085 /* Debug */,
93B37D8222CC525700FDA085 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 93B37D6222CC525700FDA085 /* Project object */;
}

@ -0,0 +1,17 @@
//
// AppDelegate.h
// JSBridgeIOSOC
//
// Created by BeiChen.
//
#import <UIKit/UIKit.h>
#import "ViewController.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

@ -0,0 +1,57 @@
//
// AppDelegate.m
// JSBridgeIOSOC
//
// Created by BeiChen.
//
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
// 当应用重新被前台调用时会走到这个生命周期方法,所以在这里进行 webView 的刷新
if ([self.window.rootViewController isKindOfClass:[ViewController class]]) {
ViewController *controller = (ViewController *)(self.window.rootViewController);
if (controller.webView) {
[controller.webView reload];
}
}
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<containerView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fOM-0y-V3i">
<rect key="frame" x="0.0" y="440" width="414" height="456"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<connections>
<segue destination="hhl-X1-HLq" kind="embed" id="ej7-M0-bJ9"/>
</connections>
</containerView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="原生 IOS 界面:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="h6K-lK-jdd">
<rect key="frame" x="20" y="454" width="123" height="42"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="User 值:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eES-ci-BSB">
<rect key="frame" x="28" y="533" width="76" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1u7-u1-fNB">
<rect key="frame" x="138" y="533" width="200" height="32"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1gR-9P-tJE">
<rect key="frame" x="20" y="597" width="151" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="修改 H5 界面 name 值"/>
<connections>
<action selector="onChangeNameBtnClick:forEvent:" destination="BYZ-38-t0r" eventType="touchDown" id="xBE-rL-lRt"/>
</connections>
</button>
<textField opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="请输入新的 name 值" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="94e-5K-FHs">
<rect key="frame" x="218" y="597" width="176" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<textField opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="请输入新的 token 值" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="pRP-xr-mQw">
<rect key="frame" x="218" y="654" width="176" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<attributedString key="userComments">
<fragment content="请输入新的 token 值"/>
</attributedString>
</textField>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wCt-J5-5Cb">
<rect key="frame" x="8" y="654" width="175" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="设置 Cookie token 值"/>
<connections>
<action selector="onCookieBtnClick:forEvent:" destination="BYZ-38-t0r" eventType="touchDown" id="G0c-EA-mPV"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="cookieText" destination="pRP-xr-mQw" id="nQb-59-zoQ"/>
<outlet property="nameText" destination="94e-5K-FHs" id="OEM-KH-xdC"/>
<outlet property="userLabel" destination="1u7-u1-fNB" id="sSn-L6-tNx"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="34.782608695652179" y="18.75"/>
</scene>
<!--View Controller-->
<scene sceneID="KbO-Gw-EOo">
<objects>
<viewController id="UIb-mE-txe" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="yef-Bt-fvp">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="KQp-Jg-fhP"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="0de-K8-zoW" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
<!--View Controller-->
<scene sceneID="Pr8-V0-dIy">
<objects>
<viewController id="hhl-X1-HLq" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="O4z-r0-0qf">
<rect key="frame" x="0.0" y="0.0" width="414" height="456"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="S8o-sO-0c4"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ANe-kx-hCP" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

@ -0,0 +1,45 @@
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

@ -0,0 +1,36 @@
//
// ViewController.h
// JSBridgeIOSOC
//
// Created by BeiChen.
//
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
#import "WebViewJavascriptBridge.h"
@interface ViewController : UIViewController
@property WKWebView *webView;
@property NSUserDefaults *defaults;
@property WebViewJavascriptBridge *bridge;
@property WKWebViewConfiguration *webConfig;
@property (strong, nonatomic) IBOutlet UITextField *nameText;
@property (strong, nonatomic) IBOutlet UITextField *cookieText;
@property (strong, nonatomic) IBOutlet UILabel *userLabel;
- (void)setWebConfig;
- (void)initWebView;
- (void)registerHandlers;
- (IBAction)onChangeNameBtnClick:(UIButton *)sender forEvent:(UIEvent *)event;
- (IBAction)onCookieBtnClick:(UIButton *)sender forEvent:(UIEvent *)event;
- (void)syncCookie:(NSString *)cookie;
+ (BOOL)judgeIsEmptyWithString:(NSString *)string;
+ (void)addToastWithString:(NSString *)string inView:(UIView *)view;
+ (void) removeToastWithView:(NSTimer *)timer;
@end

@ -0,0 +1,234 @@
//
// ViewController.m
// JSBridgeIOSOC
//
// Created by BeiChen.
//
#import "ViewController.h"
//尺寸设置
#define aiScreenWidth [UIScreen mainScreen].bounds.size.width
#define aiScreenHeight [UIScreen mainScreen].bounds.size.height
#define STATUS_BAR_HEIGHT [[UIApplication sharedApplication] statusBarFrame].size.height
//#define NAVIGATION_BAR_HEIGHT self.navigationController.navigationBar.frame.size.height
//#define TAB_BAR_HEIGHT self.tabBarController.tabBar.frame.size.height
@interface ViewController ()<WKNavigationDelegate, WKUIDelegate>
@end
@implementation ViewController
/**
* 注意事项:
* 一、Webview 初始化时设置 cookie 方式:
* 1.WebViewJavascriptBridge 必须是一个全局对象,不然的话界面不显示了
* 2.app中设置的cookie同步到js的话,使用下面这段代码
* WKUserContentController* userContentController = WKUserContentController.new;
* NSString *cookie = [self.defaults objectForKey:@"cookie"];
* if (![ViewController judgeIsEmptyWithString:cookie]) {
* NSString *cookieValue = [NSString stringWithFormat:@"document.cookie = '%@%@", cookie, @"';"];
* WKUserScript * cookieScript = [[WKUserScript alloc]
* initWithSource: cookieValue
* injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
* [userContentController addUserScript:cookieScript];
* }
* webConfig.userContentController = userContentController;
*
* 二、Webview 已经存在时设置 cookie 的方式
* NSString *cookieValue = [NSString stringWithFormat:@"document.cookie = '%@%@", cookie, @"';"];
* [_webView evaluateJavaScript:cookieValue completionHandler:^(id _Nullable result, NSError * _Nullable error) {
* NSLog(@"cookie-------%@", result);
* }];
*
*
*/
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化 WKWebViewConfiguration 对象
self.webConfig = [[WKWebViewConfiguration alloc] init];
// 设置 webConfig 属性
[self setWebConfig];
// 初始化 WKWebView
[self initWebView];
// 注册与 H5 交互的事件函数
[self registerHandlers];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.automaticallyAdjustsScrollViewInsets = NO;
}
- (BOOL)prefersStatusBarHidden {
return NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/**
* 设置 webConfig 属性
*/
- (void)setWebConfig {
// 设置偏好设置
_webConfig.preferences = [[WKPreferences alloc] init];
// 默认为0
_webConfig.preferences.minimumFontSize = 10;
// 默认认为YES
_webConfig.preferences.javaScriptEnabled = YES;
//iOS上默认为NO,表示不能自动通过窗口打开
_webConfig.preferences.javaScriptCanOpenWindowsAutomatically = NO;
}
/**
* 初始化 WKWebView
*/
- (void)initWebView {
// URL 网络请求地址
// TODO: 请替换成页面的 url 地址
NSString *URLSTR = @"http://xxx.xxx.xxx.xx:xxxx";
self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 360) configuration:_webConfig];
// 设置 UserAgent 后缀,这里设置为 ios 用于在 H5 端进行区分 IOS 平台还是 Android 平台
_webView.customUserAgent = [NSString stringWithFormat:self.webView.customUserAgent, @"ios"];
_webView.UIDelegate = self;
_webView.navigationDelegate = self;
NSURL *url = [NSURL URLWithString:URLSTR];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[_webView loadRequest:urlRequest];
[self.view addSubview:_webView];
}
/**
* 使用 WebViewJavascriptBridge 注册与 H5 交互的事件函数
*/
- (void)registerHandlers {
// 启用 WebViewJavascriptBridge
[WebViewJavascriptBridge enableLogging];
self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
[_bridge setWebViewDelegate:self];
// 注册刷新页面的 reloadUrl 函数
[self.bridge registerHandler:@"reloadUrl" handler:^(id data, WVJBResponseCallback responseCallback) {
[self.webView reload];
[ViewController addToastWithString:@"刷新成功~" inView:self.view];
if (responseCallback) {
responseCallback(@"");
}
}];
// 注册修改 User 名称的 changeUser 函数
[self.bridge registerHandler:@"changeUser" handler:^(id data, WVJBResponseCallback responseCallback) {
[self.userLabel setText:data];
if (responseCallback) {
responseCallback(@"");
}
}];
}
/**
* 修改 name 按钮被点击时触发
*/
- (IBAction)onChangeNameBtnClick:(UIButton *)sender forEvent:(UIEvent *)event {
NSString *name = [self.nameText text];
// 调用 H5 界面的 changeName 事件函数
[self.bridge callHandler:@"changeName" data:name responseCallback:^(id responseData) {
[ViewController addToastWithString:@"name 修改成功" inView:self.view];
[self.nameText setText:@""];
}];
}
/**
* 设置 Cookie 按钮被点击时触发
*/
- (IBAction)onCookieBtnClick:(UIButton *)sender forEvent:(UIEvent *)event {
NSString *cookie = [NSString stringWithFormat:@"token=%@", [self.cookieText text]];
[self syncCookie:cookie];
// 调用 H5 界面的 syncCookie 事件函数
[self.bridge callHandler:@"syncCookie" data:@"" responseCallback:^(id responseData) {
[ViewController addToastWithString:@"Cookie 同步成功" inView:self.view];
[self.cookieText setText:@""];
}];
}
/**
* 用来t设置并同步 Cookie 的工具函数
*/
- (void) syncCookie: (NSString *)cookie {
// 使用 WKUserScript 携带 cookie 参数传递到 js 页面
NSString *cookieValue = [NSString stringWithFormat:@"document.cookie = '%@%@", cookie, @"';"];
[_webView evaluateJavaScript:cookieValue completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"cookie-------%@", result);
}];
}
/**
* 判断字符串是否为空或者 null 的方法
*/
+ (BOOL)judgeIsEmptyWithString:(NSString *)string {
if (string.length == 0 || [string isEqualToString:@""] || string == nil || string == NULL || [string isEqual:[NSNull null]])
{
return YES;
}
return NO;
}
/**
* 模拟 Toast效果 的工具方法
*/
+ (void) addToastWithString:(NSString *)string inView:(UIView *)view {
CGRect initRect = CGRectMake(0, STATUS_BAR_HEIGHT, aiScreenWidth, 0);
CGRect rect = CGRectMake(0, STATUS_BAR_HEIGHT, aiScreenWidth, 22);
UILabel* label = [[UILabel alloc] initWithFrame:initRect];
label.text = string;
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
label.font = [UIFont systemFontOfSize:14];
label.backgroundColor = [UIColor colorWithRed:0 green:0.6 blue:0.9 alpha:0.6];
[view addSubview:label];
//弹出label
[UIView animateWithDuration:0.5 animations:^{
label.frame = rect;
} completion:^ (BOOL finished){
//弹出后持续1s
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(removeToastWithView:) userInfo:label repeats:NO];
}];
}
/**
* 移除 Toast
*/
+ (void) removeToastWithView:(NSTimer *)timer {
UILabel* label = [timer userInfo];
CGRect initRect = CGRectMake(0, STATUS_BAR_HEIGHT, aiScreenWidth, 0);
// label消失
[UIView animateWithDuration:0.5 animations:^{
label.frame = initRect;
} completion:^(BOOL finished){
[label removeFromSuperview];
}];
}
@end

@ -0,0 +1,15 @@
//
// main.m
// JSBridgeIOSOC
//
// Created by BeiChen.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

@ -0,0 +1,34 @@
//
// WKWebViewJavascriptBridge.h
//
// Created by @LokiMeyburg on 10/15/14.
// Copyright (c) 2014 @LokiMeyburg. All rights reserved.
//
#if (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_1)
#define supportsWKWebView
#endif
#if defined supportsWKWebView
#import <Foundation/Foundation.h>
#import "WebViewJavascriptBridgeBase.h"
#import <WebKit/WebKit.h>
@interface WKWebViewJavascriptBridge : NSObject<WKNavigationDelegate, WebViewJavascriptBridgeBaseDelegate>
+ (instancetype)bridgeForWebView:(WKWebView*)webView;
+ (void)enableLogging;
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
- (void)removeHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
- (void)reset;
- (void)setWebViewDelegate:(id)webViewDelegate;
- (void)disableJavscriptAlertBoxSafetyTimeout;
@end
#endif

@ -0,0 +1,198 @@
//
// WKWebViewJavascriptBridge.m
//
// Created by @LokiMeyburg on 10/15/14.
// Copyright (c) 2014 @LokiMeyburg. All rights reserved.
//
#import "WKWebViewJavascriptBridge.h"
#if defined supportsWKWebView
@implementation WKWebViewJavascriptBridge {
__weak WKWebView* _webView;
__weak id<WKNavigationDelegate> _webViewDelegate;
long _uniqueId;
WebViewJavascriptBridgeBase *_base;
}
/* API
*****/
+ (void)enableLogging { [WebViewJavascriptBridgeBase enableLogging]; }
+ (instancetype)bridgeForWebView:(WKWebView*)webView {
WKWebViewJavascriptBridge* bridge = [[self alloc] init];
[bridge _setupInstance:webView];
[bridge reset];
return bridge;
}
- (void)send:(id)data {
[self send:data responseCallback:nil];
}
- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:nil];
}
- (void)callHandler:(NSString *)handlerName {
[self callHandler:handlerName data:nil responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data {
[self callHandler:handlerName data:data responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_base.messageHandlers[handlerName] = [handler copy];
}
- (void)removeHandler:(NSString *)handlerName {
[_base.messageHandlers removeObjectForKey:handlerName];
}
- (void)reset {
[_base reset];
}
- (void)setWebViewDelegate:(id<WKNavigationDelegate>)webViewDelegate {
_webViewDelegate = webViewDelegate;
}
- (void)disableJavscriptAlertBoxSafetyTimeout {
[_base disableJavscriptAlertBoxSafetyTimeout];
}
/* Internals
***********/
- (void)dealloc {
_base = nil;
_webView = nil;
_webViewDelegate = nil;
_webView.navigationDelegate = nil;
}
/* WKWebView Specific Internals
******************************/
- (void) _setupInstance:(WKWebView*)webView {
_webView = webView;
_webView.navigationDelegate = self;
_base = [[WebViewJavascriptBridgeBase alloc] init];
_base.delegate = self;
}
- (void)WKFlushMessageQueue {
[_webView evaluateJavaScript:[_base webViewJavascriptFetchQueyCommand] completionHandler:^(NSString* result, NSError* error) {
if (error != nil) {
NSLog(@"WebViewJavascriptBridge: WARNING: Error when trying to fetch data from WKWebView: %@", error);
}
[_base flushMessageQueue:result];
}];
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFinishNavigation:)]) {
[strongDelegate webView:webView didFinishNavigation:navigation];
}
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationResponse:decisionHandler:)]) {
[strongDelegate webView:webView decidePolicyForNavigationResponse:navigationResponse decisionHandler:decisionHandler];
}
else {
decisionHandler(WKNavigationResponsePolicyAllow);
}
}
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didReceiveAuthenticationChallenge:completionHandler:)]) {
[strongDelegate webView:webView didReceiveAuthenticationChallenge:challenge completionHandler:completionHandler];
} else {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (webView != _webView) { return; }
NSURL *url = navigationAction.request.URL;
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if ([_base isWebViewJavascriptBridgeURL:url]) {
if ([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
} else if ([_base isQueueMessageURL:url]) {
[self WKFlushMessageQueue];
} else {
[_base logUnkownMessage:url];
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {
[_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)]) {
[strongDelegate webView:webView didStartProvisionalNavigation:navigation];
}
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailNavigation:withError:)]) {
[strongDelegate webView:webView didFailNavigation:navigation withError:error];
}
}
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
if (webView != _webView) { return; }
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailProvisionalNavigation:withError:)]) {
[strongDelegate webView:webView didFailProvisionalNavigation:navigation withError:error];
}
}
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
[_webView evaluateJavaScript:javascriptCommand completionHandler:nil];
return NULL;
}
@end
#endif

@ -0,0 +1,50 @@
//
// WebViewJavascriptBridge.h
// ExampleApp-iOS
//
// Created by Marcus Westin on 6/14/13.
// Copyright (c) 2013 Marcus Westin. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "WebViewJavascriptBridgeBase.h"
#if (__MAC_OS_X_VERSION_MAX_ALLOWED > __MAC_10_9 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_1)
#define supportsWKWebView
#endif
#if defined supportsWKWebView
#import <WebKit/WebKit.h>
#endif
#if defined __MAC_OS_X_VERSION_MAX_ALLOWED
#define WVJB_PLATFORM_OSX
#define WVJB_WEBVIEW_TYPE WebView
#define WVJB_WEBVIEW_DELEGATE_TYPE NSObject<WebViewJavascriptBridgeBaseDelegate>
#define WVJB_WEBVIEW_DELEGATE_INTERFACE NSObject<WebViewJavascriptBridgeBaseDelegate, WebPolicyDelegate>
#elif defined __IPHONE_OS_VERSION_MAX_ALLOWED
#import <UIKit/UIWebView.h>
#define WVJB_PLATFORM_IOS
#define WVJB_WEBVIEW_TYPE UIWebView
#define WVJB_WEBVIEW_DELEGATE_TYPE NSObject<UIWebViewDelegate>
#define WVJB_WEBVIEW_DELEGATE_INTERFACE NSObject<UIWebViewDelegate, WebViewJavascriptBridgeBaseDelegate>
#endif
@interface WebViewJavascriptBridge : WVJB_WEBVIEW_DELEGATE_INTERFACE
+ (instancetype)bridgeForWebView:(id)webView;
+ (instancetype)bridge:(id)webView;
+ (void)enableLogging;
+ (void)setLogMaxLength:(int)length;
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
- (void)removeHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
- (void)setWebViewDelegate:(id)webViewDelegate;
- (void)disableJavscriptAlertBoxSafetyTimeout;
@end

@ -0,0 +1,211 @@
//
// WebViewJavascriptBridge.m
// ExampleApp-iOS
//
// Created by Marcus Westin on 6/14/13.
// Copyright (c) 2013 Marcus Westin. All rights reserved.
//
#import "WebViewJavascriptBridge.h"
#if defined(supportsWKWebView)
#import "WKWebViewJavascriptBridge.h"
#endif
#if __has_feature(objc_arc_weak)
#define WVJB_WEAK __weak
#else
#define WVJB_WEAK __unsafe_unretained
#endif
@implementation WebViewJavascriptBridge {
WVJB_WEAK WVJB_WEBVIEW_TYPE* _webView;
WVJB_WEAK id _webViewDelegate;
long _uniqueId;
WebViewJavascriptBridgeBase *_base;
}
/* API
*****/
+ (void)enableLogging {
[WebViewJavascriptBridgeBase enableLogging];
}
+ (void)setLogMaxLength:(int)length {
[WebViewJavascriptBridgeBase setLogMaxLength:length];
}
+ (instancetype)bridgeForWebView:(id)webView {
return [self bridge:webView];
}
+ (instancetype)bridge:(id)webView {
#if defined supportsWKWebView
if ([webView isKindOfClass:[WKWebView class]]) {
return (WebViewJavascriptBridge*) [WKWebViewJavascriptBridge bridgeForWebView:webView];
}
#endif
if ([webView isKindOfClass:[WVJB_WEBVIEW_TYPE class]]) {
WebViewJavascriptBridge* bridge = [[self alloc] init];
[bridge _platformSpecificSetup:webView];
return bridge;
}
[NSException raise:@"BadWebViewType" format:@"Unknown web view type."];
return nil;
}
- (void)setWebViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate {
_webViewDelegate = webViewDelegate;
}
- (void)send:(id)data {
[self send:data responseCallback:nil];
}
- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:nil];
}
- (void)callHandler:(NSString *)handlerName {
[self callHandler:handlerName data:nil responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data {
[self callHandler:handlerName data:data responseCallback:nil];
}
- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
[_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}
- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
_base.messageHandlers[handlerName] = [handler copy];
}
- (void)removeHandler:(NSString *)handlerName {
[_base.messageHandlers removeObjectForKey:handlerName];
}
- (void)disableJavscriptAlertBoxSafetyTimeout {
[_base disableJavscriptAlertBoxSafetyTimeout];
}
/* Platform agnostic internals
*****************************/
- (void)dealloc {
[self _platformSpecificDealloc];
_base = nil;
_webView = nil;
_webViewDelegate = nil;
}
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand {
return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];
}
#if defined WVJB_PLATFORM_OSX
/* Platform specific internals: OSX
**********************************/
- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView {
_webView = webView;
_webView.policyDelegate = self;
_base = [[WebViewJavascriptBridgeBase alloc] init];
_base.delegate = self;
}
- (void) _platformSpecificDealloc {
_webView.policyDelegate = nil;
}
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
if (webView != _webView) { return; }
NSURL *url = [request URL];
if ([_base isWebViewJavascriptBridgeURL:url]) {
if ([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
} else if ([_base isQueueMessageURL:url]) {
NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
} else {
[_base logUnkownMessage:url];
}
[listener ignore];
} else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) {
[_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener];
} else {
[listener use];
}
}
#elif defined WVJB_PLATFORM_IOS
/* Platform specific internals: iOS
**********************************/
- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView {
_webView = webView;
_webView.delegate = self;
_base = [[WebViewJavascriptBridgeBase alloc] init];
_base.delegate = self;
}
- (void) _platformSpecificDealloc {
_webView.delegate = nil;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (webView != _webView) { return; }
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[strongDelegate webViewDidFinishLoad:webView];
}
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
if (webView != _webView) { return; }
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
[strongDelegate webView:webView didFailLoadWithError:error];
}
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if (webView != _webView) { return YES; }
NSURL *url = [request URL];
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if ([_base isWebViewJavascriptBridgeURL:url]) {
if ([_base isBridgeLoadedURL:url]) {
[_base injectJavascriptFile];
} else if ([_base isQueueMessageURL:url]) {
NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
[_base flushMessageQueue:messageQueueString];
} else {
[_base logUnkownMessage:url];
}
return NO;
} else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
} else {
return YES;
}
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
if (webView != _webView) { return; }
__strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[strongDelegate webViewDidStartLoad:webView];
}
}
#endif
@end

@ -0,0 +1,46 @@
//
// WebViewJavascriptBridgeBase.h
//
// Created by @LokiMeyburg on 10/15/14.
// Copyright (c) 2014 @LokiMeyburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#define kOldProtocolScheme @"wvjbscheme"
#define kNewProtocolScheme @"https"
#define kQueueHasMessage @"__wvjb_queue_message__"
#define kBridgeLoaded @"__bridge_loaded__"
typedef void (^WVJBResponseCallback)(id responseData);
typedef void (^WVJBHandler)(id data, WVJBResponseCallback responseCallback);
typedef NSDictionary WVJBMessage;
@protocol WebViewJavascriptBridgeBaseDelegate <NSObject>
- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand;
@end
@interface WebViewJavascriptBridgeBase : NSObject
@property (weak, nonatomic) id <WebViewJavascriptBridgeBaseDelegate> delegate;
@property (strong, nonatomic) NSMutableArray* startupMessageQueue;
@property (strong, nonatomic) NSMutableDictionary* responseCallbacks;
@property (strong, nonatomic) NSMutableDictionary* messageHandlers;
@property (strong, nonatomic) WVJBHandler messageHandler;
+ (void)enableLogging;
+ (void)setLogMaxLength:(int)length;
- (void)reset;
- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName;
- (void)flushMessageQueue:(NSString *)messageQueueString;
- (void)injectJavascriptFile;
- (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url;
- (BOOL)isQueueMessageURL:(NSURL*)urll;
- (BOOL)isBridgeLoadedURL:(NSURL*)urll;
- (void)logUnkownMessage:(NSURL*)url;
- (NSString *)webViewJavascriptCheckCommand;
- (NSString *)webViewJavascriptFetchQueyCommand;
- (void)disableJavscriptAlertBoxSafetyTimeout;
@end

@ -0,0 +1,221 @@
//
// WebViewJavascriptBridgeBase.m
//
// Created by @LokiMeyburg on 10/15/14.
// Copyright (c) 2014 @LokiMeyburg. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "WebViewJavascriptBridgeBase.h"
#import "WebViewJavascriptBridge_JS.h"
@implementation WebViewJavascriptBridgeBase {
__weak id _webViewDelegate;
long _uniqueId;
}
static bool logging = false;
static int logMaxLength = 500;
+ (void)enableLogging { logging = true; }
+ (void)setLogMaxLength:(int)length { logMaxLength = length;}
- (id)init {
if (self = [super init]) {
self.messageHandlers = [NSMutableDictionary dictionary];
self.startupMessageQueue = [NSMutableArray array];
self.responseCallbacks = [NSMutableDictionary dictionary];
_uniqueId = 0;
}
return self;
}
- (void)dealloc {
self.startupMessageQueue = nil;
self.responseCallbacks = nil;
self.messageHandlers = nil;
}
- (void)reset {
self.startupMessageQueue = [NSMutableArray array];
self.responseCallbacks = [NSMutableDictionary dictionary];
_uniqueId = 0;
}
- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
NSMutableDictionary* message = [NSMutableDictionary dictionary];
if (data) {
message[@"data"] = data;
}
if (responseCallback) {
NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
self.responseCallbacks[callbackId] = [responseCallback copy];
message[@"callbackId"] = callbackId;
}
if (handlerName) {
message[@"handlerName"] = handlerName;
}
[self _queueMessage:message];
}
- (void)flushMessageQueue:(NSString *)messageQueueString{
if (messageQueueString == nil || messageQueueString.length == 0) {
NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page.");
return;
}
id messages = [self _deserializeMessageJSON:messageQueueString];
for (WVJBMessage* message in messages) {
if (![message isKindOfClass:[WVJBMessage class]]) {
NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
continue;
}
[self _log:@"RCVD" json:message];
NSString* responseId = message[@"responseId"];
if (responseId) {
WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
responseCallback(message[@"responseData"]);
[self.responseCallbacks removeObjectForKey:responseId];
} else {
WVJBResponseCallback responseCallback = NULL;
NSString* callbackId = message[@"callbackId"];
if (callbackId) {
responseCallback = ^(id responseData) {
if (responseData == nil) {
responseData = [NSNull null];
}
WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
[self _queueMessage:msg];
};
} else {
responseCallback = ^(id ignoreResponseData) {
// Do nothing
};
}
WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];
if (!handler) {
NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
continue;
}
handler(message[@"data"], responseCallback);
}
}
}
- (void)injectJavascriptFile {
NSString *js = WebViewJavascriptBridge_js();
[self _evaluateJavascript:js];
if (self.startupMessageQueue) {
NSArray* queue = self.startupMessageQueue;
self.startupMessageQueue = nil;
for (id queuedMessage in queue) {
[self _dispatchMessage:queuedMessage];
}
}
}
- (BOOL)isWebViewJavascriptBridgeURL:(NSURL*)url {
if (![self isSchemeMatch:url]) {
return NO;
}
return [self isBridgeLoadedURL:url] || [self isQueueMessageURL:url];
}
- (BOOL)isSchemeMatch:(NSURL*)url {
NSString* scheme = url.scheme.lowercaseString;
return [scheme isEqualToString:kNewProtocolScheme] || [scheme isEqualToString:kOldProtocolScheme];
}
- (BOOL)isQueueMessageURL:(NSURL*)url {
NSString* host = url.host.lowercaseString;
return [self isSchemeMatch:url] && [host isEqualToString:kQueueHasMessage];
}
- (BOOL)isBridgeLoadedURL:(NSURL*)url {
NSString* host = url.host.lowercaseString;
return [self isSchemeMatch:url] && [host isEqualToString:kBridgeLoaded];
}
- (void)logUnkownMessage:(NSURL*)url {
NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@", [url absoluteString]);
}
- (NSString *)webViewJavascriptCheckCommand {
return @"typeof WebViewJavascriptBridge == \'object\';";
}
- (NSString *)webViewJavascriptFetchQueyCommand {
return @"WebViewJavascriptBridge._fetchQueue();";
}
- (void)disableJavscriptAlertBoxSafetyTimeout {
[self sendData:nil responseCallback:nil handlerName:@"_disableJavascriptAlertBoxSafetyTimeout"];
}
// Private
// -------------------------------------------
- (void) _evaluateJavascript:(NSString *)javascriptCommand {
[self.delegate _evaluateJavascript:javascriptCommand];
}
- (void)_queueMessage:(WVJBMessage*)message {
if (self.startupMessageQueue) {
[self.startupMessageQueue addObject:message];
} else {
[self _dispatchMessage:message];
}
}
- (void)_dispatchMessage:(WVJBMessage*)message {
NSString *messageJSON = [self _serializeMessage:message pretty:NO];
[self _log:@"SEND" json:messageJSON];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];
NSString* javascriptCommand = [NSString stringWithFormat:@"WebViewJavascriptBridge._handleMessageFromObjC('%@');", messageJSON];
if ([[NSThread currentThread] isMainThread]) {
[self _evaluateJavascript:javascriptCommand];
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
[self _evaluateJavascript:javascriptCommand];
});
}
}
- (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{
return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding];
}
- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON {
return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
}
- (void)_log:(NSString *)action json:(id)json {
if (!logging) { return; }
if (![json isKindOfClass:[NSString class]]) {
json = [self _serializeMessage:json pretty:YES];
}
if ([json length] > logMaxLength) {
NSLog(@"WVJB %@: %@ [...]", action, [json substringToIndex:logMaxLength]);
} else {
NSLog(@"WVJB %@: %@", action, json);
}
}
@end

@ -0,0 +1,3 @@
#import <Foundation/Foundation.h>
NSString * WebViewJavascriptBridge_js(void);

@ -0,0 +1,139 @@
// This file contains the source for the Javascript side of the
// WebViewJavascriptBridge. It is plaintext, but converted to an NSString
// via some preprocessor tricks.
//
// Previous implementations of WebViewJavascriptBridge loaded the javascript source
// from a resource. This worked fine for app developers, but library developers who
// included the bridge into their library, awkwardly had to ask consumers of their
// library to include the resource, violating their encapsulation. By including the
// Javascript as a string resource, the encapsulation of the library is maintained.
#import "WebViewJavascriptBridge_JS.h"
NSString * WebViewJavascriptBridge_js() {
#define __wvjb_js_func__(x) #x
// BEGIN preprocessorJSCode
static NSString * preprocessorJSCode = @__wvjb_js_func__(
;(function() {
if (window.WebViewJavascriptBridge) {
return;
}
if (!window.onerror) {
window.onerror = function(msg, url, line) {
console.log("WebViewJavascriptBridge: ERROR:" + msg + "@" + url + ":" + line);
}
}
window.WebViewJavascriptBridge = {
registerHandler: registerHandler,
callHandler: callHandler,
disableJavscriptAlertBoxSafetyTimeout: disableJavscriptAlertBoxSafetyTimeout,
_fetchQueue: _fetchQueue,
_handleMessageFromObjC: _handleMessageFromObjC
};
var messagingIframe;
var sendMessageQueue = [];
var messageHandlers = {};
var CUSTOM_PROTOCOL_SCHEME = 'https';
var QUEUE_HAS_MESSAGE = '__wvjb_queue_message__';
var responseCallbacks = {};
var uniqueId = 1;
var dispatchMessagesWithTimeoutSafety = true;
function registerHandler(handlerName, handler) {
messageHandlers[handlerName] = handler;
}
function callHandler(handlerName, data, responseCallback) {
if (arguments.length == 2 && typeof data == 'function') {
responseCallback = data;
data = null;
}
_doSend({ handlerName:handlerName, data:data }, responseCallback);
}
function disableJavscriptAlertBoxSafetyTimeout() {
dispatchMessagesWithTimeoutSafety = false;
}
function _doSend(message, responseCallback) {
if (responseCallback) {
var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime();
responseCallbacks[callbackId] = responseCallback;
message['callbackId'] = callbackId;
}
sendMessageQueue.push(message);
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
}
function _fetchQueue() {
var messageQueueString = JSON.stringify(sendMessageQueue);
sendMessageQueue = [];
return messageQueueString;
}
function _dispatchMessageFromObjC(messageJSON) {
if (dispatchMessagesWithTimeoutSafety) {
setTimeout(_doDispatchMessageFromObjC);
} else {
_doDispatchMessageFromObjC();
}
function _doDispatchMessageFromObjC() {
var message = JSON.parse(messageJSON);
var messageHandler;
var responseCallback;
if (message.responseId) {
responseCallback = responseCallbacks[message.responseId];
if (!responseCallback) {
return;
}
responseCallback(message.responseData);
delete responseCallbacks[message.responseId];
} else {
if (message.callbackId) {
var callbackResponseId = message.callbackId;
responseCallback = function(responseData) {
_doSend({ handlerName:message.handlerName, responseId:callbackResponseId, responseData:responseData });
};
}
var handler = messageHandlers[message.handlerName];
if (!handler) {
console.log("WebViewJavascriptBridge: WARNING: no handler for message from ObjC:", message);
} else {
handler(message.data, responseCallback);
}
}
}
}
function _handleMessageFromObjC(messageJSON) {
_dispatchMessageFromObjC(messageJSON);
}
messagingIframe = document.createElement('iframe');
messagingIframe.style.display = 'none';
messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
document.documentElement.appendChild(messagingIframe);
registerHandler("_disableJavascriptAlertBoxSafetyTimeout", disableJavscriptAlertBoxSafetyTimeout);
setTimeout(_callWVJBCallbacks, 0);
function _callWVJBCallbacks() {
var callbacks = window.WVJBCallbacks;
delete window.WVJBCallbacks;
for (var i=0; i<callbacks.length; i++) {
callbacks[i](WebViewJavascriptBridge);
}
}
})();
); // END preprocessorJSCode
#undef __wvjb_js_func__
return preprocessorJSCode;
};
Loading…
Cancel
Save