From c49472d8aab3c2ddc9dda523717bfe7f8217692e Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Mon, 6 Apr 2020 00:31:35 +0900 Subject: [PATCH 01/16] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de67b88..2a43021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,7 +121,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * First release. -[Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.0.1...HEAD +[Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.0.2...HEAD +[2.0.2 (2020-02-06)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.1...2.0.2 [2.0.1 (2019-03-06)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.0...2.0.1 [2.0.0 (2019-02-28)]: https://github.com/ridi/WKJavaScriptController/compare/1.2.0...2.0.0 [1.2.0 (2019-02-26)]: https://github.com/ridi/WKJavaScriptController/compare/1.1.9...1.2.0 From 2281a24b3355a55c202d45902e71079a2d08b4ff Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Wed, 15 Jul 2020 12:02:40 +0900 Subject: [PATCH 02/16] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e74a261..b779c5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,5 @@ notifications: email: false slack: rooms: - secure: bTrvofwn1zmzN8r+2wayyYAwzLyKdqk9lFEL2ufAqduouAEW4f6aWyupnvHFqWpSJkuBdHpEM1tnUTyhUUdKodn9AkkYlPEEYXOfshRipsxwCnnqjqdvVlNqn3bjlc3gq7FEDkYqBBrT69C8xcf5MqgfrqjRq+E1QvZuxg/P9THSoXPsysHUmbzbBN5g+zMZL0RmxGGSKLGC7QUPZyVxbZMX+yxry7P1xx1xrHf2b+yKhTIousmYPvFnzyxx5M7D5mHmGbkh24ELVHoCDTJid9S14Y0y8KnzTwtlPG2JuHAMaUDeXjSgAE7Bb0uFliXyQc2Zo/i6+ybDqXBiLLcczmJluuWNYi5Q6CTzmKAYoqQ8EOuYGIGNRIj1CJ1PXNz1exeayrXnoN2ZEYZXt+Sw1FBbrKzQkDugESVDlx7A6HDa8oKT+0Teyu4CD42Vs00KcNvCypzwYsZrGfo+x4RRjbJohWImHzczXEoYmIniKaP8XhBNow7QEx/SGnvcIkeh4Ww0wvfIztRehIJFbCEVUxwCUmwjpOgg032O3xgNR89pLOT7EZ1R4T+4ZFuTbxOqA1Rn1zM7NZxN+oduD1sNgT1mpVfmyQZfdtvxGq+Lh94pkT61KWZURUl+UhBOz8JQz/EUs6U9+1c7lh0OjP5dhyWotLiKxBYN51pqsLkz7TM= + secure: UT7XfIcfY5xSszbOEQOxc32AFtFmSuXxBjqH8GYR7fDJ1PCVfkDnWECUCOYX43bslhppJUpU/v5lFp18IDWiiPkDJoQzub+KASrIn+4U+3wEMlnX48cXq9C21lNpjaCFLjHINE0KF+Yped69NHBn8wn0zXggJnk0ny1Gy0G4LNFLB3n5DISJBRn/lm33sPUEva/ntLQ+Szm8aA7DGaVE79A+jYuKZDraZiDUwBuw7Rg9AqVvt7xijmEhMioZ2ECExMrI+Fl3YyViRN5hxwVm+39d4vAD6nO6NPOatvJtjK9/us2WSEBzDB73rL5oh3mOWiIcy18gYc0DGcEgRpTpxO7GLMSNcv/U17/unzqIgmH6FzpzDCsUyL2A8Dz0jCaDhNqBd46pfPaigc9kxBDbjRgV2ckumOrDSlHQFz9S1mt02UfmB1sU+s4iFRvXXhdsmrDQsdkjuCjlrRr2FXHcJ6+wlFIcDrYl+WumSjs9sce34tNbvhNvvpfr2Q2K+1FsWPmooXXc2AUp+h6RgPzS4+PDnNxA41Ke8Y58Cq+OITINBGJhoWjrO4DfoSgoUyVwKTXg8FQpaWZVs9lfZKX6yH+eiC2Bw9Jk5BvN81Bu/5Jq/u4I/f47oC2jD+7ILUQ9g/9y0+jdAnTpLa6nSYOwux8guBhsDS4/trishOhm1Pc= on_success: change From 7685aed4a05640a5f6b832915b15dbfdbb81bea4 Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Fri, 14 Aug 2020 10:36:03 +0900 Subject: [PATCH 03/16] Project cleanup - Bump swift version to 5.2 - Remove cocoapods - Remove demo workspace - Code cleanup - Update swiftlint and swiftformat rules - Update README.md --- .swift-version | 2 +- .swiftformat | 7 +- .swiftlint.yml | 50 ++-- Demo/Podfile | 6 - .../project.pbxproj | 269 +++++++++++++----- .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../WKJavaScriptController-Demo.xcscheme | 10 +- .../contents.xcworkspacedata | 10 - .../AppDelegate.swift | 2 +- .../ViewController.swift | 15 +- Demo/WKJavaScriptController/Info.plist | 22 ++ .../WKJavaScriptController.h | 9 + Package.swift | 11 +- README.md | 40 +-- .../WKJavaScriptController.swift | 74 +++-- WKJavaScriptController.podspec | 12 - 16 files changed, 323 insertions(+), 216 deletions(-) delete mode 100644 Demo/Podfile rename Demo/{WKJavaScriptController-Demo.xcworkspace => WKJavaScriptController-Demo.xcodeproj/project.xcworkspace}/xcshareddata/IDEWorkspaceChecks.plist (100%) delete mode 100644 Demo/WKJavaScriptController-Demo.xcworkspace/contents.xcworkspacedata create mode 100644 Demo/WKJavaScriptController/Info.plist create mode 100644 Demo/WKJavaScriptController/WKJavaScriptController.h delete mode 100644 WKJavaScriptController.podspec diff --git a/.swift-version b/.swift-version index bf77d54..ef425ca 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.2 +5.2 diff --git a/.swiftformat b/.swiftformat index 74ee64d..5c6aedd 100644 --- a/.swiftformat +++ b/.swiftformat @@ -1,4 +1,4 @@ ---exclude Demo/Pods +--exclude Pods,Carthage,**/*.generated.swift --disable consecutiveSpaces,indent,isEmpty,redundantParens,strongOutlets,trailingClosures --allman false @@ -8,6 +8,7 @@ --comments indent --decimalgrouping ignore --elseposition same-line +--empty void --header ignore --hexgrouping ignore --hexliteralcase lowercase @@ -21,7 +22,11 @@ --patternlet hoist --ranges no-space --semicolons never +--self remove +--selfrequired true --stripunusedargs closure-only +--trailingclosures true --trimwhitespace nonblank-lines --wraparguments before-first --wrapcollections before-first +--closingparen balanced diff --git a/.swiftlint.yml b/.swiftlint.yml index b9d3f7d..80d8b78 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -10,7 +10,6 @@ excluded: - Demo/Pods disabled_rules: - - block_based_kvo # Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. - cyclomatic_complexity # Complexity of function bodies should be limited. - file_length # Files should not span too many lines. - for_where # where clauses are preferred over a single if inside a for. @@ -21,39 +20,51 @@ disabled_rules: - todo # TODOs and FIXMEs should be resolved. - trailing_whitespace # Lines should not have trailing whitespace. - unused_setter_value # Setter value is not used. + - multiple_closures_with_trailing_closure # Trailing closure syntax should not be used when passing more than one closure argument. opt_in_rules: - - anyobject_protocol # Prefer using AnyObject over class for class-only protocols. - - array_init # Prefer using Array(seq) over seq.map { $0 } to convert a sequence into an Array. + - anyobject_protocol # Prefer using `AnyObject` over class for class-only protocols. + - array_init # Prefer using `Array(seq)` over `seq.map { $0 }` to convert a sequence into an Array. - closure_end_indentation # Closure end should have the same indentation as the line that started it. - closure_spacing # Closure expressions should have a single space inside each brace. - collection_alignment # All elements in a collection literal should be vertically aligned - - conditional_returns_on_newline # Conditional statements should always return on the next line. + - contains_over_filter_count # Prefer `contains` over comparing `filter(where:).count` to `0`. + - contains_over_filter_is_empty # Prefer `contains` over using `filter(where:).isEmpty` + - contains_over_first_not_nil # Prefer `contains` over `first(where:) != nil` and `firstIndex(where:) != nil`. + - contains_over_range_nil_comparison # Prefer `contains` over `range(of:) != nil` and `range(of:) == nil`. - discouraged_object_literal # Prefer initializers over object literals. - - empty_count # Prefer checking isEmpty over comparing count to zero. - - empty_string # Prefer checking isEmpty over comparing string to an empty string literal. - - empty_xctest_method # Empty XCTest method should be avoided. - - explicit_init # Explicitly calling .init() should be avoided. - - extension_access_modifier # Prefer to use extension access modifiers. + - empty_collection_literal # Prefer checking `isEmpty` over comparing collection to an empty array or dictionary literal. + - empty_count # Prefer checking `isEmpty` over comparing count to zero. + - empty_string # Prefer checking `isEmpty` over comparing string to an empty string literal. + - empty_xctest_method # Empty `XCTest` method should be avoided. + - explicit_init # Explicitly calling `.init()` should be avoided. - fallthrough # Fallthrough should be avoided. - - fatal_error_message # A fatalError call should have a message. + - fatal_error_message # A `fatalError` call should have a message. - file_header # Header comments should be consistent with project patterns. - - first_where # Prefer using .first(where:) over .filter { }.first in collections. + - first_where # Prefer using `.first(where:)` over `.filter { }.first` in collections. + - flatmap_over_map_reduce # Prefer `flatMap` over `map` followed by `reduce([], +)`. - function_default_parameter_at_end # Prefer to locate parameters with defaults toward the end of the parameter list. + - legacy_multiple # Prefer using the `isMultiple(of:)` function instead of using the remainder operator (`%`). - joined_default_parameter # Discouraged explicit usage of the default. separator. - - last_where # Prefer using .last(where:) over .filter { }.last in collections. - - legacy_random # Prefer using type.random(in:) over legacy functions. + - last_where # Prefer using `.last(where:)` over `.filter { }.last` in collections. + - legacy_random # Prefer using `type.random(in:)` over legacy functions. - literal_expression_end_indentation # Array and dictionary literal end should have the same indentation as the line that started it. - lower_acl_than_parent # Ensure definitions have a lower access control level than their enclosing parent. - multiline_literal_brackets # Multiline literals should have their surrounding brackets in a new line. - prohibited_super_call # Some methods should not call super. - - sorted_imports # Imports should be sorted. - - toggle_bool # Prefer someBool.toggle() over someBool = !someBool. + - redundant_type_annotation # Variables should not have redundant type annotation + - toggle_bool # Prefer `someBool.toggle()` over `someBool = !someBool`. - unneeded_parentheses_in_closure_argument # Parentheses are not needed when declaring closure arguments. - unused_import # All imported modules should be required to make the file compile. - unused_private_declaration # Private declarations should be referenced in that file. - vertical_parameter_alignment_on_call # Function parameters should be aligned vertically if they're in multiple lines in a method call. +custom_rules: + trailing_logical_operator: + name: 'Trailing logical operator' + message: 'Leading logical operator should move to trailing.' + regex: '^[\s]*(\|\||&&)' + function_parameter_count: warning: 11 error: 11 @@ -76,11 +87,6 @@ nesting: warning: 2 error: 2 -# Type bodies should not span too many lines. -type_body_length: - warning: 1100 - error: 1100 - # Type name should only contain alphanumeric characters, start with an uppercase character and span between 3 and 50 characters in length. type_name: allowed_symbols: ['_'] @@ -91,3 +97,7 @@ type_name: # Limit vertical whitespace to a single empty line. vertical_whitespace: max_empty_lines: 2 + +# Forbid "Created by" line in file header +file_header: + forbidden_pattern: \b(Created by)\b diff --git a/Demo/Podfile b/Demo/Podfile deleted file mode 100644 index 30bfd33..0000000 --- a/Demo/Podfile +++ /dev/null @@ -1,6 +0,0 @@ -platform :ios, '8.0' -use_frameworks! - -target 'WKJavaScriptController-Demo' do - pod 'WKJavaScriptController', :path => '../' -end diff --git a/Demo/WKJavaScriptController-Demo.xcodeproj/project.pbxproj b/Demo/WKJavaScriptController-Demo.xcodeproj/project.pbxproj index 94d5487..ddede32 100644 --- a/Demo/WKJavaScriptController-Demo.xcodeproj/project.pbxproj +++ b/Demo/WKJavaScriptController-Demo.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 2A2FA98024E26E44005FE611 /* WKJavaScriptController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A2FA97E24E26E44005FE611 /* WKJavaScriptController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2A2FA98324E26E44005FE611 /* WKJavaScriptController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A2FA97C24E26E44005FE611 /* WKJavaScriptController.framework */; }; + 2A2FA98424E26E44005FE611 /* WKJavaScriptController.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2A2FA97C24E26E44005FE611 /* WKJavaScriptController.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 2A2FA98A24E26E57005FE611 /* WKJavaScriptController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2FA98924E26E57005FE611 /* WKJavaScriptController.swift */; }; 83D538401E2C960D00E42B6A /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 83D5383F1E2C960D00E42B6A /* index.html */; }; 83D538421E2C978000E42B6A /* index.js in Resources */ = {isa = PBXBuildFile; fileRef = 83D538411E2C978000E42B6A /* index.js */; }; 83FA358E1E2C5C6900A6B171 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FA358D1E2C5C6900A6B171 /* AppDelegate.swift */; }; @@ -14,14 +18,39 @@ 83FA35931E2C5C6900A6B171 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83FA35911E2C5C6900A6B171 /* Main.storyboard */; }; 83FA35951E2C5C6900A6B171 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 83FA35941E2C5C6900A6B171 /* Assets.xcassets */; }; 83FA35981E2C5C6900A6B171 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 83FA35961E2C5C6900A6B171 /* LaunchScreen.storyboard */; }; - 9C30B52454ECA3F3E3AEDF4E /* Pods_WKJavaScriptController_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 272FAEE1CFFC059EDAA9D22F /* Pods_WKJavaScriptController_Demo.framework */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 2A2FA98124E26E44005FE611 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83FA35821E2C5C6900A6B171 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2A2FA97B24E26E44005FE611; + remoteInfo = WKJavaScriptController; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 2A2FA98824E26E44005FE611 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 2A2FA98424E26E44005FE611 /* WKJavaScriptController.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ - 272FAEE1CFFC059EDAA9D22F /* Pods_WKJavaScriptController_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WKJavaScriptController_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2A2FA97C24E26E44005FE611 /* WKJavaScriptController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WKJavaScriptController.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2A2FA97E24E26E44005FE611 /* WKJavaScriptController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKJavaScriptController.h; sourceTree = ""; }; + 2A2FA97F24E26E44005FE611 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2A2FA98924E26E57005FE611 /* WKJavaScriptController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WKJavaScriptController.swift; path = ../../Sources/WKJavaScriptController/WKJavaScriptController.swift; sourceTree = ""; }; 2A34D7D622277CB800250021 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../README.md; sourceTree = ""; }; 2A34D7D722277CC200250021 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../../CHANGELOG.md; sourceTree = ""; }; - 2CC90156977464A015EF3DCC /* Pods-WKJavaScriptController-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WKJavaScriptController-Demo.debug.xcconfig"; path = "Target Support Files/Pods-WKJavaScriptController-Demo/Pods-WKJavaScriptController-Demo.debug.xcconfig"; sourceTree = ""; }; 83D5383F1E2C960D00E42B6A /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = ""; }; 83D538411E2C978000E42B6A /* index.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = index.js; sourceTree = ""; }; 83FA358A1E2C5C6900A6B171 /* WKJavaScriptController-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WKJavaScriptController-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -31,28 +60,43 @@ 83FA35941E2C5C6900A6B171 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 83FA35971E2C5C6900A6B171 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 83FA35991E2C5C6900A6B171 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - FB0AE82DFFE17201B9503726 /* Pods-WKJavaScriptController-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WKJavaScriptController-Demo.release.xcconfig"; path = "Target Support Files/Pods-WKJavaScriptController-Demo/Pods-WKJavaScriptController-Demo.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 2A2FA97924E26E44005FE611 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 83FA35871E2C5C6900A6B171 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9C30B52454ECA3F3E3AEDF4E /* Pods_WKJavaScriptController_Demo.framework in Frameworks */, + 2A2FA98324E26E44005FE611 /* WKJavaScriptController.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2A2FA97D24E26E44005FE611 /* WKJavaScriptController */ = { + isa = PBXGroup; + children = ( + 2A2FA98924E26E57005FE611 /* WKJavaScriptController.swift */, + 2A2FA97E24E26E44005FE611 /* WKJavaScriptController.h */, + 2A2FA97F24E26E44005FE611 /* Info.plist */, + ); + path = WKJavaScriptController; + sourceTree = ""; + }; 83FA35811E2C5C6900A6B171 = { isa = PBXGroup; children = ( 83FA358C1E2C5C6900A6B171 /* WKJavaScriptController-Demo */, + 2A2FA97D24E26E44005FE611 /* WKJavaScriptController */, 83FA358B1E2C5C6900A6B171 /* Products */, - C601BA64EDC03710DF43F3A0 /* Pods */, - C71904662FF5A905B66EB106 /* Frameworks */, ); sourceTree = ""; }; @@ -60,6 +104,7 @@ isa = PBXGroup; children = ( 83FA358A1E2C5C6900A6B171 /* WKJavaScriptController-Demo.app */, + 2A2FA97C24E26E44005FE611 /* WKJavaScriptController.framework */, ); name = Products; sourceTree = ""; @@ -81,41 +126,53 @@ path = "WKJavaScriptController-Demo"; sourceTree = ""; }; - C601BA64EDC03710DF43F3A0 /* Pods */ = { - isa = PBXGroup; - children = ( - 2CC90156977464A015EF3DCC /* Pods-WKJavaScriptController-Demo.debug.xcconfig */, - FB0AE82DFFE17201B9503726 /* Pods-WKJavaScriptController-Demo.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - C71904662FF5A905B66EB106 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 272FAEE1CFFC059EDAA9D22F /* Pods_WKJavaScriptController_Demo.framework */, +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2A2FA97724E26E44005FE611 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2A2FA98024E26E44005FE611 /* WKJavaScriptController.h in Headers */, ); - name = Frameworks; - sourceTree = ""; + runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXGroup section */ +/* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 2A2FA97B24E26E44005FE611 /* WKJavaScriptController */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2A2FA98524E26E44005FE611 /* Build configuration list for PBXNativeTarget "WKJavaScriptController" */; + buildPhases = ( + 2A2FA97724E26E44005FE611 /* Headers */, + 2A2FA97824E26E44005FE611 /* Sources */, + 2A2FA97924E26E44005FE611 /* Frameworks */, + 2A2FA97A24E26E44005FE611 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WKJavaScriptController; + productName = WKJavaScriptController; + productReference = 2A2FA97C24E26E44005FE611 /* WKJavaScriptController.framework */; + productType = "com.apple.product-type.framework"; + }; 83FA35891E2C5C6900A6B171 /* WKJavaScriptController-Demo */ = { isa = PBXNativeTarget; buildConfigurationList = 83FA359C1E2C5C6900A6B171 /* Build configuration list for PBXNativeTarget "WKJavaScriptController-Demo" */; buildPhases = ( - 16F30D6D14CA9CB2B5D05932 /* [CP] Check Pods Manifest.lock */, 2A34D7D522250B8B00250021 /* SwiftFormat Script */, 83054A791F0C76460001C4B0 /* SwiftLint Script */, 83FA35861E2C5C6900A6B171 /* Sources */, 83FA35871E2C5C6900A6B171 /* Frameworks */, 83FA35881E2C5C6900A6B171 /* Resources */, - F74DCF0DFEA075B1393A95B5 /* [CP] Embed Pods Frameworks */, + 2A2FA98824E26E44005FE611 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 2A2FA98224E26E44005FE611 /* PBXTargetDependency */, ); name = "WKJavaScriptController-Demo"; productName = "WKJavaScriptController-Demo"; @@ -129,19 +186,24 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0820; - LastUpgradeCheck = 0940; + LastUpgradeCheck = 1160; ORGANIZATIONNAME = "Davin Ahn"; TargetAttributes = { + 2A2FA97B24E26E44005FE611 = { + CreatedOnToolsVersion = 11.6; + LastSwiftMigration = 1160; + ProvisioningStyle = Automatic; + }; 83FA35891E2C5C6900A6B171 = { CreatedOnToolsVersion = 8.2.1; - LastSwiftMigration = 1010; + LastSwiftMigration = 1160; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 83FA35851E2C5C6900A6B171 /* Build configuration list for PBXProject "WKJavaScriptController-Demo" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -153,11 +215,19 @@ projectRoot = ""; targets = ( 83FA35891E2C5C6900A6B171 /* WKJavaScriptController-Demo */, + 2A2FA97B24E26E44005FE611 /* WKJavaScriptController */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 2A2FA97A24E26E44005FE611 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 83FA35881E2C5C6900A6B171 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -173,28 +243,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 16F30D6D14CA9CB2B5D05932 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-WKJavaScriptController-Demo-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 2A34D7D522250B8B00250021 /* SwiftFormat Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -227,31 +275,17 @@ shellPath = /bin/sh; shellScript = "function toVersion { echo -e \"$@\" | awk -F. '{ printf(\"%d%03d%03d%03d\\n\", $1,$2,$3,$4); }'; }\nif which swiftlint >/dev/null; then\n current=$(swiftlint version)\n require=\"0.30.1\"\n if [ $(toVersion \"$current\") -ge $(toVersion \"$require\") ]; then\n cd \"${PROJECT_DIR}/../\"\n swiftlint\n else\n echo \"SwiftLint requires a SwiftLint version of >= $require. You are on $current.\"\n echo \"Please update using 'brew upgrade swiftlint'.\"\n exit 1\n fi\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\n exit 127\nfi\n"; }; - F74DCF0DFEA075B1393A95B5 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2A2FA97824E26E44005FE611 /* Sources */ = { + isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-WKJavaScriptController-Demo/Pods-WKJavaScriptController-Demo-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/WKJavaScriptController/WKJavaScriptController.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - ); - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WKJavaScriptController.framework", + 2A2FA98A24E26E57005FE611 /* WKJavaScriptController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-WKJavaScriptController-Demo/Pods-WKJavaScriptController-Demo-frameworks.sh\"\n"; - showEnvVarsInLog = 0; }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ 83FA35861E2C5C6900A6B171 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -263,6 +297,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 2A2FA98224E26E44005FE611 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2A2FA97B24E26E44005FE611 /* WKJavaScriptController */; + targetProxy = 2A2FA98124E26E44005FE611 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 83FA35911E2C5C6900A6B171 /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -283,10 +325,75 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 2A2FA98624E26E44005FE611 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = WKJavaScriptController/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ridi.WKJavaScriptController; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2A2FA98724E26E44005FE611 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = WKJavaScriptController/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ridi.WKJavaScriptController; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_VERSION = 5.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; 83FA359A1E2C5C6900A6B171 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -331,7 +438,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -344,6 +451,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -382,7 +490,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -392,8 +500,8 @@ }; 83FA359D1E2C5C6900A6B171 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2CC90156977464A015EF3DCC /* Pods-WKJavaScriptController-Demo.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "WKJavaScriptController-Demo/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -401,15 +509,15 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.davin.WKJavaScriptController-Demo"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.2; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; 83FA359E1E2C5C6900A6B171 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FB0AE82DFFE17201B9503726 /* Pods-WKJavaScriptController-Demo.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "WKJavaScriptController-Demo/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; @@ -417,7 +525,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.davin.WKJavaScriptController-Demo"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.2; TARGETED_DEVICE_FAMILY = 1; }; name = Release; @@ -425,6 +533,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 2A2FA98524E26E44005FE611 /* Build configuration list for PBXNativeTarget "WKJavaScriptController" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2A2FA98624E26E44005FE611 /* Debug */, + 2A2FA98724E26E44005FE611 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 83FA35851E2C5C6900A6B171 /* Build configuration list for PBXProject "WKJavaScriptController-Demo" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Demo/WKJavaScriptController-Demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Demo/WKJavaScriptController-Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Demo/WKJavaScriptController-Demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Demo/WKJavaScriptController-Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Demo/WKJavaScriptController-Demo.xcodeproj/xcshareddata/xcschemes/WKJavaScriptController-Demo.xcscheme b/Demo/WKJavaScriptController-Demo.xcodeproj/xcshareddata/xcschemes/WKJavaScriptController-Demo.xcscheme index 5afa7fd..9a3f733 100644 --- a/Demo/WKJavaScriptController-Demo.xcodeproj/xcshareddata/xcschemes/WKJavaScriptController-Demo.xcscheme +++ b/Demo/WKJavaScriptController-Demo.xcodeproj/xcshareddata/xcschemes/WKJavaScriptController-Demo.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - - - - - - - diff --git a/Demo/WKJavaScriptController-Demo/AppDelegate.swift b/Demo/WKJavaScriptController-Demo/AppDelegate.swift index 27ea1f6..19b9fbd 100644 --- a/Demo/WKJavaScriptController-Demo/AppDelegate.swift +++ b/Demo/WKJavaScriptController-Demo/AppDelegate.swift @@ -6,7 +6,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. - return true + true } func applicationWillResignActive(_ application: UIApplication) { diff --git a/Demo/WKJavaScriptController-Demo/ViewController.swift b/Demo/WKJavaScriptController-Demo/ViewController.swift index a1af4e0..a02f738 100644 --- a/Demo/WKJavaScriptController-Demo/ViewController.swift +++ b/Demo/WKJavaScriptController-Demo/ViewController.swift @@ -17,6 +17,7 @@ import WKJavaScriptController extension ViewController: JavaScriptInterface { func onSubmit(_ dictonary: [String: AnyObject]) { NSLog("onSubmit \(dictonary)") + isSubmitted = true } func onSubmit(_ dictonary: [String: AnyObject], clear: JSBool) { @@ -24,27 +25,29 @@ extension ViewController: JavaScriptInterface { if clear.value { webView.evaluateJavaScript("clearAll()", completionHandler: nil) } + isSubmitted = true } func onSubmit(_ email: String, firstName: String, lastName: String, address1: String, address2: String, zipCode: JSInt, phoneNumber: String) { NSLog("onSubmit \(email), \(firstName), \(lastName), \(address1), \(address2), \(zipCode.value), \(phoneNumber)") + isSubmitted = true } func onCancel() { NSLog("onCancel") } - var isSubmitted: JSBool { - return JSBool(true) - } + var isSubmitted: JSBool { JSBool(isSubmitted) } func getErrorMessages(codes: [JSInt]) -> [String] { - return codes.map { "message\($0)" } + codes.map { "message\($0)" } } } class ViewController: UIViewController { fileprivate var webView: WKWebView! + + private var isSubmitted = false override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) @@ -71,8 +74,4 @@ class ViewController: UIViewController { webView.loadHTMLString(htmlString, baseURL: Bundle.main.bundleURL) } } - - override var prefersStatusBarHidden: Bool { - return true - } } diff --git a/Demo/WKJavaScriptController/Info.plist b/Demo/WKJavaScriptController/Info.plist new file mode 100644 index 0000000..9bcb244 --- /dev/null +++ b/Demo/WKJavaScriptController/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/Demo/WKJavaScriptController/WKJavaScriptController.h b/Demo/WKJavaScriptController/WKJavaScriptController.h new file mode 100644 index 0000000..c7e88b6 --- /dev/null +++ b/Demo/WKJavaScriptController/WKJavaScriptController.h @@ -0,0 +1,9 @@ +#import + +//! Project version number for WKJavaScriptController. +FOUNDATION_EXPORT double WKJavaScriptControllerVersionNumber; + +//! Project version string for WKJavaScriptController. +FOUNDATION_EXPORT const unsigned char WKJavaScriptControllerVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import diff --git a/Package.swift b/Package.swift index 5218e5d..625f41e 100644 --- a/Package.swift +++ b/Package.swift @@ -6,13 +6,14 @@ import PackageDescription let package = Package( name: "WKJavaScriptController", platforms: [ - .iOS(.v8), + .iOS(.v8) ], products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "WKJavaScriptController", - targets: ["WKJavaScriptController"]), + targets: ["WKJavaScriptController"] + ) ], dependencies: [ // Dependencies declare other packages that this package depends on. @@ -23,9 +24,7 @@ let package = Package( // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "WKJavaScriptController", - dependencies: []), - ], - swiftLanguageVersions: [ - .v4_2 + dependencies: [] + ) ] ) diff --git a/README.md b/README.md index 46cf1bc..c3271d3 100644 --- a/README.md +++ b/README.md @@ -3,44 +3,24 @@ Calling native code from Javascript in iOS likes JavascriptInterface in Android. [![Build Status](https://travis-ci.com/ridi/WKJavaScriptController.svg?branch=master)](https://travis-ci.com/ridi/WKJavaScriptController) -[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/WKJavaScriptController.svg?style=flat)](https://cocoadocs.org/docsets/WKJavaScriptController) -[![Platform](https://img.shields.io/cocoapods/p/WKJavaScriptController.svg?style=flat)](https://cocoadocs.org/docsets/WKJavaScriptController) -[![License](https://img.shields.io/cocoapods/l/WKJavaScriptController.svg?style=flat)](https://cocoadocs.org/docsets/WKJavaScriptController) ## Requirements -- Xcode 10.0+ -- Swift 4.2 +- Xcode 11.4+ +- Swift 5.2+ - iOS8+ -(based on WKJavaScriptController 1.2.0+) +(based on WKJavaScriptController 2.1.0+) -## Installation +## Installation with Swift Package Manager -This library is distributed by [CocoaPods](https://cocoapods.org). +Add the following to your `Package.swift`: - CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command: - -``` -$ gem install cocoapods -``` - -To integrate WKJavaScriptController into your Xcode project using CocoaPods, specify it in your Podfile: - -```ruby -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' -use_frameworks! - -target '' do - pod 'WKJavaScriptController' -end -``` - -Then, run the following command: - -``` -$ pod install +```swift +dependencies: [ + // ... + .package(url: "https://github.com/ridi/WKJavaScriptController.git"), +] ``` ## Usage diff --git a/Sources/WKJavaScriptController/WKJavaScriptController.swift b/Sources/WKJavaScriptController/WKJavaScriptController.swift index ccf972b..aed8e35 100644 --- a/Sources/WKJavaScriptController/WKJavaScriptController.swift +++ b/Sources/WKJavaScriptController/WKJavaScriptController.swift @@ -9,7 +9,7 @@ public extension WKWebView { objc_setAssociatedObject(self, &javaScriptControllerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } get { - return objc_getAssociatedObject(self, &javaScriptControllerKey) as? WKJavaScriptController + objc_getAssociatedObject(self, &javaScriptControllerKey) as? WKJavaScriptController } } @@ -34,15 +34,11 @@ open class JSValueType: NSObject { _value = number } - open override var description: String { - return _value.stringValue - } + override open var description: String { _value.stringValue } } open class JSBool: JSValueType { - open var value: Bool { - return _value.boolValue - } + open var value: Bool { _value.boolValue } public convenience init(_ value: Bool) { self.init(value as NSNumber) @@ -50,9 +46,7 @@ open class JSBool: JSValueType { } open class JSInt: JSValueType { - open var value: Int { - return _value.intValue - } + open var value: Int { _value.intValue } public convenience init(_ value: Int) { self.init(value as NSNumber) @@ -60,9 +54,7 @@ open class JSInt: JSValueType { } open class JSFloat: JSValueType { - open var value: Float { - return _value.floatValue - } + open var value: Float { _value.floatValue } public convenience init(_ value: Float) { self.init(value as NSNumber) @@ -146,7 +138,7 @@ open class WKJavaScriptController: NSObject { } var argumentCount: Int { - return max(NSStringFromSelector(nativeSelector).components(separatedBy: ":").count - 1, 0) + max(NSStringFromSelector(nativeSelector).components(separatedBy: ":").count - 1, 0) } init(nativeSelector selector: Selector) { @@ -156,15 +148,21 @@ open class WKJavaScriptController: NSObject { } } - fileprivate enum ReserveKeyword: String { - case _createUUID - case _callbackList - case _addCallback - case _cancel - case _cancelAll + private enum ReserveKeyword: String { + case createUUID = "_createUUID" + case callbackList = "_callbackList" + case addCallback = "_addCallback" + case cancel = "_cancel" + case cancelAll = "_cancelAll" static var all: [ReserveKeyword] { - return [._createUUID, ._callbackList, ._addCallback, ._cancel, ._cancelAll] + [ + .createUUID, + .callbackList, + .addCallback, + .cancel, + .cancelAll + ] } } @@ -291,29 +289,29 @@ open class WKJavaScriptController: NSObject { private func bridgeScript(_ forMainFrameOnly: Bool) -> WKUserScript { var source = """ window.\(name) = { - \(ReserveKeyword._createUUID): function() { + \(ReserveKeyword.createUUID): function() { const s4 = () => ((1 + Math.random()) * 0x10000 | 0).toString(16).substring(1); return s4() + s4() + s4() + s4() + s4() + s4() + s4() + s4(); }, - \(ReserveKeyword._cancel): function(id, resaon) { - const callback = \(name).\(ReserveKeyword._callbackList)[id]; + \(ReserveKeyword.cancel): function(id, resaon) { + const callback = \(name).\(ReserveKeyword.callbackList)[id]; resaon = resaon || new Error(`Callback cancelled. (id: ${id})`); callback.cancel = new Date(); callback.reject(resaon); clearTimeout(callback.timer); }, - \(ReserveKeyword._cancelAll): function() { - Object.getOwnPropertyNames(\(name).\(ReserveKeyword._callbackList)).forEach((key) => { - \(name).\(ReserveKeyword._cancel)(key); + \(ReserveKeyword.cancelAll): function() { + Object.getOwnPropertyNames(\(name).\(ReserveKeyword.callbackList)).forEach((key) => { + \(name).\(ReserveKeyword.cancel)(key); }); }, - \(ReserveKeyword._addCallback): function(id, name, resolve, reject) { + \(ReserveKeyword.addCallback): function(id, name, resolve, reject) { const timer = setTimeout(() => { - \(name).\(ReserveKeyword._cancel)(id, new Error(`Callback timeout. (id: ${id})`)); + \(name).\(ReserveKeyword.cancel)(id, new Error(`Callback timeout. (id: ${id})`)); }, \(callbackTimeout * 1000)); - \(name).\(ReserveKeyword._callbackList)[id] = { name, resolve, reject, timer, start: new Date() }; + \(name).\(ReserveKeyword.callbackList)[id] = { name, resolve, reject, timer, start: new Date() }; }, - \(ReserveKeyword._callbackList): {}, + \(ReserveKeyword.callbackList): {}, """ var readOnlyProperties = [MethodBridge]() for bridge in bridges { @@ -323,10 +321,10 @@ open class WKJavaScriptController: NSObject { } source += """ \(bridge.jsSelector): function() { - const id = \(name).\(ReserveKeyword._createUUID)(); + const id = \(name).\(ReserveKeyword.createUUID)(); const args = Array.from(arguments).concat(id); return new Promise((resolve, reject) => { - \(name).\(ReserveKeyword._addCallback)(id, '\(bridge.jsSelector)', resolve, reject); + \(name).\(ReserveKeyword.addCallback)(id, '\(bridge.jsSelector)', resolve, reject); webkit.messageHandlers.\((bridge.jsSelector)).postMessage(args); }); }, @@ -338,9 +336,9 @@ open class WKJavaScriptController: NSObject { Object.defineProperty(\(name), '\(bridge.jsSelector)', { key: '\(bridge.jsSelector)', get: function get() { - const id = \(name).\(ReserveKeyword._createUUID)(); + const id = \(name).\(ReserveKeyword.createUUID)(); return new Promise((resolve, reject) => { - \(name).\(ReserveKeyword._addCallback)(id, '\(bridge.jsSelector)', resolve, reject); + \(name).\(ReserveKeyword.addCallback)(id, '\(bridge.jsSelector)', resolve, reject); webkit.messageHandlers.\((bridge.jsSelector)).postMessage([id]); }); }, @@ -350,7 +348,7 @@ open class WKJavaScriptController: NSObject { return WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: forMainFrameOnly) } - fileprivate func log(_ message: String) { + private func log(_ message: String) { if logEnabled { NSLog("[WKJavaScriptController] \(message)") } @@ -405,7 +403,7 @@ extension WKJavaScriptController: WKScriptMessageHandler { case "c", "C", "B": return JSBool(number) default: - if number.stringValue.range(of: ".") != nil { + if number.stringValue.contains(".") { return JSFloat(number) } else if number.stringValue == "nan" { return JSInt(NSNumber(value: 0 as Int)) @@ -568,7 +566,7 @@ extension WKJavaScriptController: WKScriptMessageHandler { let script = """ (() => { - const callback = \(name).\(ReserveKeyword._callbackList)['\(callbackId)']; + const callback = \(name).\(ReserveKeyword.callbackList)['\(callbackId)']; callback.end = new Date(); callback.resolve(\(bridge.isReturnRequired ? stringFrom(result) : "")); clearTimeout(callback.timer); diff --git a/WKJavaScriptController.podspec b/WKJavaScriptController.podspec deleted file mode 100644 index d82b200..0000000 --- a/WKJavaScriptController.podspec +++ /dev/null @@ -1,12 +0,0 @@ -Pod::Spec.new do |s| - s.name = 'WKJavaScriptController' - s.version = '2.0.1' - s.summary = 'Calling native code from Javascript in the iOS application likes JavascriptInterface in the Android.' - s.homepage = 'https://github.com/ridi/WKJavaScriptController' - s.authors = { 'Ridibooks Viewer Team' => 'viewer.team@ridi.com' } - s.license = 'MIT' - s.ios.deployment_target = '8.0' - s.source = { :git => 'https://github.com/ridi/WKJavaScriptController.git', :tag => s.version } - s.source_files = 'Sources/WKJavaScriptController/WKJavaScriptController.swift' - s.frameworks = 'Foundation', 'WebKit' -end From dc578c769d44fced0d3bd6c7268467b278b68255 Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Fri, 14 Aug 2020 10:36:45 +0900 Subject: [PATCH 04/16] Update async spec example --- .../ViewController.swift | 22 ++++++++++++++----- Demo/WKJavaScriptController-Demo/index.html | 1 + Demo/WKJavaScriptController-Demo/index.js | 5 +++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Demo/WKJavaScriptController-Demo/ViewController.swift b/Demo/WKJavaScriptController-Demo/ViewController.swift index a02f738..65d4d05 100644 --- a/Demo/WKJavaScriptController-Demo/ViewController.swift +++ b/Demo/WKJavaScriptController-Demo/ViewController.swift @@ -17,7 +17,7 @@ import WKJavaScriptController extension ViewController: JavaScriptInterface { func onSubmit(_ dictonary: [String: AnyObject]) { NSLog("onSubmit \(dictonary)") - isSubmitted = true + _isSubmitted = true } func onSubmit(_ dictonary: [String: AnyObject], clear: JSBool) { @@ -25,19 +25,20 @@ extension ViewController: JavaScriptInterface { if clear.value { webView.evaluateJavaScript("clearAll()", completionHandler: nil) } - isSubmitted = true + _isSubmitted = true } func onSubmit(_ email: String, firstName: String, lastName: String, address1: String, address2: String, zipCode: JSInt, phoneNumber: String) { NSLog("onSubmit \(email), \(firstName), \(lastName), \(address1), \(address2), \(zipCode.value), \(phoneNumber)") - isSubmitted = true + _isSubmitted = true } func onCancel() { NSLog("onCancel") + _isSubmitted = false } - var isSubmitted: JSBool { JSBool(isSubmitted) } + var isSubmitted: JSBool { JSBool(_isSubmitted) } func getErrorMessages(codes: [JSInt]) -> [String] { codes.map { "message\($0)" } @@ -47,7 +48,7 @@ extension ViewController: JavaScriptInterface { class ViewController: UIViewController { fileprivate var webView: WKWebView! - private var isSubmitted = false + private var _isSubmitted = false override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) @@ -63,6 +64,7 @@ class ViewController: UIViewController { javaScriptController.addUserScript(userScript) webView = WKWebView(frame: view.frame) + webView.uiDelegate = self view.addSubview(webView) // Assign javaScriptController. @@ -75,3 +77,13 @@ class ViewController: UIViewController { } } } + +extension ViewController: WKUIDelegate { + func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { + let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in + completionHandler() + })) + present(alertController, animated: true, completion: nil) + } +} diff --git a/Demo/WKJavaScriptController-Demo/index.html b/Demo/WKJavaScriptController-Demo/index.html index d61ef6a..ba1c306 100644 --- a/Demo/WKJavaScriptController-Demo/index.html +++ b/Demo/WKJavaScriptController-Demo/index.html @@ -20,5 +20,6 @@
+ diff --git a/Demo/WKJavaScriptController-Demo/index.js b/Demo/WKJavaScriptController-Demo/index.js index 34f3661..44a1148 100644 --- a/Demo/WKJavaScriptController-Demo/index.js +++ b/Demo/WKJavaScriptController-Demo/index.js @@ -27,3 +27,8 @@ function clearAll() { function isChecked(id) { return document.getElementById(id).checked; }; + +async function checkSumbit() { + const result = await native.isSubmitted; + alert(result ? 'Submitted.' : 'Not submitted.'); +} From 87e0594afb5e9646cc4e8b9c6e87561b510e511f Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Mon, 24 Aug 2020 17:33:30 +0900 Subject: [PATCH 05/16] Reformat code --- .../AppDelegate.swift | 12 +-- .../ViewController.swift | 22 ++--- .../WKJavaScriptController.swift | 98 +++++++++---------- 3 files changed, 66 insertions(+), 66 deletions(-) diff --git a/Demo/WKJavaScriptController-Demo/AppDelegate.swift b/Demo/WKJavaScriptController-Demo/AppDelegate.swift index 19b9fbd..572ff61 100644 --- a/Demo/WKJavaScriptController-Demo/AppDelegate.swift +++ b/Demo/WKJavaScriptController-Demo/AppDelegate.swift @@ -3,30 +3,30 @@ import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. true } - + func applicationWillResignActive(_ application: UIApplication) { // 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. } - + func applicationDidEnterBackground(_ application: UIApplication) { // 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. } - + func applicationWillEnterForeground(_ application: UIApplication) { // 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. } - + func applicationDidBecomeActive(_ application: UIApplication) { // 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. } - + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/Demo/WKJavaScriptController-Demo/ViewController.swift b/Demo/WKJavaScriptController-Demo/ViewController.swift index 65d4d05..070789a 100644 --- a/Demo/WKJavaScriptController-Demo/ViewController.swift +++ b/Demo/WKJavaScriptController-Demo/ViewController.swift @@ -19,7 +19,7 @@ extension ViewController: JavaScriptInterface { NSLog("onSubmit \(dictonary)") _isSubmitted = true } - + func onSubmit(_ dictonary: [String: AnyObject], clear: JSBool) { NSLog("onSubmit \(dictonary)") if clear.value { @@ -27,19 +27,19 @@ extension ViewController: JavaScriptInterface { } _isSubmitted = true } - + func onSubmit(_ email: String, firstName: String, lastName: String, address1: String, address2: String, zipCode: JSInt, phoneNumber: String) { NSLog("onSubmit \(email), \(firstName), \(lastName), \(address1), \(address2), \(zipCode.value), \(phoneNumber)") _isSubmitted = true } - + func onCancel() { NSLog("onCancel") _isSubmitted = false } - + var isSubmitted: JSBool { JSBool(_isSubmitted) } - + func getErrorMessages(codes: [JSInt]) -> [String] { codes.map { "message\($0)" } } @@ -49,27 +49,27 @@ class ViewController: UIViewController { fileprivate var webView: WKWebView! private var _isSubmitted = false - + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - + if webView == nil { // Create javaScriptController. let javaScriptController = WKJavaScriptController(name: "native", target: self, bridgeProtocol: JavaScriptInterface.self) - + // [Optional] Add your javascript. let jsPath = Bundle.main.path(forResource: "index", ofType: "js")! let jsString = try! String(contentsOfFile: jsPath, encoding: String.Encoding.utf8) let userScript = WKUserScript(source: jsString, injectionTime: .atDocumentEnd, forMainFrameOnly: true) javaScriptController.addUserScript(userScript) - + webView = WKWebView(frame: view.frame) webView.uiDelegate = self view.addSubview(webView) - + // Assign javaScriptController. webView.javaScriptController = javaScriptController - + let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")! let htmlString = try! String(contentsOfFile: htmlPath, encoding: String.Encoding.utf8) webView.prepareForJavaScriptController() // Call prepareForJavaScriptController before initializing WKWebView or loading page. diff --git a/Sources/WKJavaScriptController/WKJavaScriptController.swift b/Sources/WKJavaScriptController/WKJavaScriptController.swift index aed8e35..1d9f455 100644 --- a/Sources/WKJavaScriptController/WKJavaScriptController.swift +++ b/Sources/WKJavaScriptController/WKJavaScriptController.swift @@ -12,7 +12,7 @@ public extension WKWebView { objc_getAssociatedObject(self, &javaScriptControllerKey) as? WKJavaScriptController } } - + // You must call it before initializing WKWebView or loading page. // ex) override func loadHTMLString(string: String, baseURL: NSURL?) -> WKNavigation? { // prepareForJavaScriptController() @@ -29,17 +29,17 @@ public extension WKWebView { open class JSValueType: NSObject { fileprivate var _value: NSNumber - + fileprivate init(_ number: NSNumber) { _value = number } - + override open var description: String { _value.stringValue } } open class JSBool: JSValueType { open var value: Bool { _value.boolValue } - + public convenience init(_ value: Bool) { self.init(value as NSNumber) } @@ -47,7 +47,7 @@ open class JSBool: JSValueType { open class JSInt: JSValueType { open var value: Int { _value.intValue } - + public convenience init(_ value: Int) { self.init(value as NSNumber) } @@ -55,7 +55,7 @@ open class JSInt: JSValueType { open class JSFloat: JSValueType { open var value: Float { _value.floatValue } - + public convenience init(_ value: Float) { self.init(value as NSNumber) } @@ -70,49 +70,49 @@ open class WKJavaScriptController: NSObject { // If true, do not allow NSNull(when `undefined` or `null` passed from JavaScript) for method arguments. // That is, if receive NSNull as an argument, method call ignored. open var ignoreMethodCallWhenReceivedNull = true - + @available(*, deprecated, renamed: "ignoreMethodCallWhenReceivedNull") open var shouldSafeMethodCall = true { willSet { ignoreMethodCallWhenReceivedNull = newValue } } - + open var convertsToDictionaryWhenReceivedJsonString = true - + @available(*, deprecated, renamed: "convertsToDictionaryWhenReceivedJsonString") open var shouldConvertJSONString = true { willSet { convertsToDictionaryWhenReceivedJsonString = newValue } } - + open var callbackTimeout: TimeInterval = 10 { didSet { isInjectRequired = true } } - + open var logEnabled = true - + private let bridgeProtocol: Protocol private let name: String private weak var target: AnyObject? - + // User script that will use the bridge. private var userScripts = [WKUserScript]() - + fileprivate weak var webView: WKWebView? - + fileprivate var bridges = [MethodBridge]() - + fileprivate var isInjectRequired = true - + fileprivate class MethodBridge { var nativeSelector: Selector var isExtendJsSelector: Bool // If true, use ObjC style naming. var isReturnRequired: Bool - + var jsSelector: String { let selector = NSStringFromSelector(nativeSelector) let components = selector.components(separatedBy: ":") @@ -136,25 +136,25 @@ open class WKJavaScriptController: NSObject { return components.first! } } - + var argumentCount: Int { max(NSStringFromSelector(nativeSelector).components(separatedBy: ":").count - 1, 0) } - + init(nativeSelector selector: Selector) { nativeSelector = selector isExtendJsSelector = false isReturnRequired = false } } - + private enum ReserveKeyword: String { case createUUID = "_createUUID" case callbackList = "_callbackList" case addCallback = "_addCallback" case cancel = "_cancel" case cancelAll = "_cancelAll" - + static var all: [ReserveKeyword] { [ .createUUID, @@ -165,7 +165,7 @@ open class WKJavaScriptController: NSObject { ] } } - + public init(name: String, target: AnyObject, bridgeProtocol: Protocol) { self.name = name self.target = target @@ -173,7 +173,7 @@ open class WKJavaScriptController: NSObject { super.init() parseBridgeProtocol() } - + private func protocolsAdoptedBy(protocol: Protocol) -> [Protocol] { var protocols = [`protocol`] let protocolList = protocol_copyProtocolList(`protocol`, nil) @@ -185,11 +185,11 @@ open class WKJavaScriptController: NSObject { } return protocols } - + private func parseBridgeProtocol() { for `protocol` in protocolsAdoptedBy(protocol: bridgeProtocol.self).reversed() { log("Protocol: \(String(format: "%s", protocol_getName(`protocol`)))") - + // Class methods are not supported. for (isRequired, isInstance) in [(true, true), (false, true)] { let methodList = protocol_copyMethodDescriptionList(`protocol`, isRequired, isInstance, nil) @@ -198,14 +198,14 @@ open class WKJavaScriptController: NSObject { let limit = argumentCountLimit while list?.pointee.name != nil { defer { list = list?.successor() } - + guard let selector = list?.pointee.name, let types = list?.pointee.types, let signature = String(cString: types, encoding: .utf8) else { log("Method signature not found, so it was excluded. (selector: \(list?.pointee.name ?? Selector(("nil"))))") continue } - + // Ref: http://nshipster.com/type-encodings/ // c: A char v: A void // C: An unsigned char B: A C++ bool or C99 _bool @@ -224,26 +224,26 @@ open class WKJavaScriptController: NSObject { log("Allowed return types are Void, String, Array, Dictionary, JSBool, JSInt, JSFloat and NSNull.") continue } - + if signature.range(of: "[cC#\\[\\{\\(b\\^\\?]", options: .regularExpression) != nil { log("It has an unsupported reference type as arguments, so it was excluded. (selector: \(selector))") log("Allowed reference types are String, Date, Array, Dictionary, NSNumber and NSNull.") continue } - + // Swift value types can't be used. because method call is based on ObjC. if signature.range(of: "[iIsSlLqQfdB]", options: .regularExpression) != nil { log("It has an unsupported value type as arguments, so it was excluded. (selector: \(selector))") log("Allowed value types are JSBool, JSInt and JSFloat.") continue } - + let bridge = MethodBridge(nativeSelector: selector) if bridge.argumentCount > limit { log("Argument length is longer than \(limit), so it was excluded. (selector: \(bridge.nativeSelector))") continue } - + // Using ObjC style naming if have a method with the same name. let list = bridges.filter { $0.jsSelector == bridge.jsSelector } if !list.isEmpty { @@ -252,16 +252,16 @@ open class WKJavaScriptController: NSObject { for bridge in list { bridge.isExtendJsSelector = true } - + if signature.hasPrefix("@") { bridge.isReturnRequired = true } - + if let keyword = ReserveKeyword.all.first(where: { bridge.jsSelector == $0.rawValue }) { log("Cannot use the keyword '\(keyword)' as a method name, so it was excluded. (selector: \(bridge.nativeSelector))") continue } - + bridges.append(bridge) log("Parsed: \(isRequired ? "" : "Optional ")\(bridge.nativeSelector) -> \(bridge.jsSelector)") } @@ -270,7 +270,7 @@ open class WKJavaScriptController: NSObject { } } } - + fileprivate func injectTo(_ userContentController: WKUserContentController) { userContentController.removeAllUserScripts() var forMainFrameOnly = true @@ -285,7 +285,7 @@ open class WKJavaScriptController: NSObject { } isInjectRequired = false } - + private func bridgeScript(_ forMainFrameOnly: Bool) -> WKUserScript { var source = """ window.\(name) = { @@ -347,18 +347,18 @@ open class WKJavaScriptController: NSObject { } return WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: forMainFrameOnly) } - + private func log(_ message: String) { if logEnabled { NSLog("[WKJavaScriptController] \(message)") } } - + open func addUserScript(_ userScript: WKUserScript) { userScripts.append(userScript) isInjectRequired = true } - + open func removeAllUserScripts() { userScripts.removeAll() isInjectRequired = true @@ -422,7 +422,7 @@ extension WKJavaScriptController: WKScriptMessageHandler { } return arg } - + private func stringFrom(_ result: Result!) -> String { if result != nil { if let jsBool = result as? JSBool { @@ -446,27 +446,27 @@ extension WKJavaScriptController: WKScriptMessageHandler { } return "undefined" } - + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard let target = target, var args = message.body as? [Arg], let bridge = bridges.first(where: { $0.jsSelector == message.name }) else { return } - + let callbackId = args.last as! String args = Array(args.dropLast()) - + if args.count != bridge.argumentCount { log("Argument length is different. (selector: \(bridge.jsSelector), received: \(args.count), required: \(bridge.argumentCount))") return } - + guard let method = class_getInstanceMethod(target.classForCoder, bridge.nativeSelector) else { log("An unimplemented method has been called. (selector: \(bridge.nativeSelector))") return } - + let notificationCenter = NotificationCenter.default let userInfo = [ "nativeSelector": bridge.nativeSelector, @@ -474,7 +474,7 @@ extension WKJavaScriptController: WKScriptMessageHandler { "args": args ] as [String: Any] notificationCenter.post(name: .WKJavaScriptControllerWillMethodInvocation, object: nil, userInfo: userInfo) - + if ignoreMethodCallWhenReceivedNull { for arg in args { if arg is NSNull { @@ -489,7 +489,7 @@ extension WKJavaScriptController: WKScriptMessageHandler { } } } - + let imp = method_getImplementation(method) var result: Result! switch bridge.argumentCount { @@ -563,7 +563,7 @@ extension WKJavaScriptController: WKScriptMessageHandler { // Not called. break } - + let script = """ (() => { const callback = \(name).\(ReserveKeyword.callbackList)['\(callbackId)']; From 6f36c9b0ffc1b686511143a6566027685648d344 Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Mon, 24 Aug 2020 18:19:34 +0900 Subject: [PATCH 06/16] Remove userScripts property --- .../ViewController.swift | 16 +++++--- README.md | 14 ++++--- .../WKJavaScriptController.swift | 37 ++++++++----------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/Demo/WKJavaScriptController-Demo/ViewController.swift b/Demo/WKJavaScriptController-Demo/ViewController.swift index 070789a..1977371 100644 --- a/Demo/WKJavaScriptController-Demo/ViewController.swift +++ b/Demo/WKJavaScriptController-Demo/ViewController.swift @@ -58,12 +58,18 @@ class ViewController: UIViewController { let javaScriptController = WKJavaScriptController(name: "native", target: self, bridgeProtocol: JavaScriptInterface.self) // [Optional] Add your javascript. + let configuration = WKWebViewConfiguration() + + let userContentController = WKUserContentController() let jsPath = Bundle.main.path(forResource: "index", ofType: "js")! - let jsString = try! String(contentsOfFile: jsPath, encoding: String.Encoding.utf8) - let userScript = WKUserScript(source: jsString, injectionTime: .atDocumentEnd, forMainFrameOnly: true) - javaScriptController.addUserScript(userScript) + let jsCode = try! String(contentsOfFile: jsPath, encoding: String.Encoding.utf8) + let userScript = WKUserScript(source: jsCode, injectionTime: .atDocumentEnd, forMainFrameOnly: true) + userContentController.addUserScript(userScript) + + configuration.userContentController = userContentController - webView = WKWebView(frame: view.frame) + // Create WKWebView instance. + webView = WKWebView(frame: view.frame, configuration: configuration) webView.uiDelegate = self view.addSubview(webView) @@ -71,7 +77,7 @@ class ViewController: UIViewController { webView.javaScriptController = javaScriptController let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")! - let htmlString = try! String(contentsOfFile: htmlPath, encoding: String.Encoding.utf8) + let htmlString = try! String(contentsOfFile: htmlPath, encoding: .utf8) webView.prepareForJavaScriptController() // Call prepareForJavaScriptController before initializing WKWebView or loading page. webView.loadHTMLString(htmlString, baseURL: Bundle.main.bundleURL) } diff --git a/README.md b/README.md index c3271d3..ebd58b8 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,16 @@ class ViewController: UIViewController { let javaScriptController = WKJavaScriptController(name: "native", target: self, bridgeProtocol: JavaScriptInterface.self) // [Optional] Add your javascript. - let jsString = ... - let userScript = WKUserScript(source: jsString, injectionTime: .AtDocumentEnd, forMainFrameOnly: true) - javaScriptController.addUserScript(userScript) + let configuration = WKWebViewConfiguration() - let webView = WKWebView(...) - ... + let jsCode = ... + let userScript = WKUserScript(source: jsCode, injectionTime: .AtDocumentEnd, forMainFrameOnly: true) + userContentController.addUserScript(userScript) + + configuration.userContentController = userContentController + + // Create WKWebView instance. + webView = WKWebView(frame: view.frame, configuration: configuration) // Assign javaScriptController. webView.javaScriptController = javaScriptController diff --git a/Sources/WKJavaScriptController/WKJavaScriptController.swift b/Sources/WKJavaScriptController/WKJavaScriptController.swift index 1d9f455..271c372 100644 --- a/Sources/WKJavaScriptController/WKJavaScriptController.swift +++ b/Sources/WKJavaScriptController/WKJavaScriptController.swift @@ -66,6 +66,8 @@ public extension Notification.Name { static let WKJavaScriptControllerWillMethodInvocation = Notification.Name("WKJavaScriptControllerWillMethodInvocationNotification") } +private let identifier = "/* WKJavaScriptController */" + open class WKJavaScriptController: NSObject { // If true, do not allow NSNull(when `undefined` or `null` passed from JavaScript) for method arguments. // That is, if receive NSNull as an argument, method call ignored. @@ -99,9 +101,6 @@ open class WKJavaScriptController: NSObject { private let name: String private weak var target: AnyObject? - // User script that will use the bridge. - private var userScripts = [WKUserScript]() - fileprivate weak var webView: WKWebView? fileprivate var bridges = [MethodBridge]() @@ -272,21 +271,23 @@ open class WKJavaScriptController: NSObject { } fileprivate func injectTo(_ userContentController: WKUserContentController) { - userContentController.removeAllUserScripts() - var forMainFrameOnly = true - for userScript in userScripts { - forMainFrameOnly = forMainFrameOnly && userScript.isForMainFrameOnly - userContentController.addUserScript(userScript) + let userScripts = userContentController.userScripts.filter { + !$0.source.hasPrefix(identifier) } - userContentController.addUserScript(bridgeScript(forMainFrameOnly)) + userContentController.removeAllUserScripts() + + userContentController.addUserScript(bridgeScript()) for bridge in bridges { userContentController.removeScriptMessageHandler(forName: bridge.jsSelector) userContentController.add(self, name: bridge.jsSelector) } + + userScripts.forEach { userContentController.addUserScript($0) } + isInjectRequired = false } - private func bridgeScript(_ forMainFrameOnly: Bool) -> WKUserScript { + private func bridgeScript() -> WKUserScript { var source = """ window.\(name) = { \(ReserveKeyword.createUUID): function() { @@ -345,7 +346,11 @@ open class WKJavaScriptController: NSObject { }); """ } - return WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: forMainFrameOnly) + return WKUserScript( + source: "\(identifier)\n\(source)", + injectionTime: .atDocumentStart, + forMainFrameOnly: true + ) } private func log(_ message: String) { @@ -353,16 +358,6 @@ open class WKJavaScriptController: NSObject { NSLog("[WKJavaScriptController] \(message)") } } - - open func addUserScript(_ userScript: WKUserScript) { - userScripts.append(userScript) - isInjectRequired = true - } - - open func removeAllUserScripts() { - userScripts.removeAll() - isInjectRequired = true - } } // MARK: - WKScriptMessageHandler From be573669c1099a25a8cc33dbddeaa41cb94da70c Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Mon, 24 Aug 2020 18:23:45 +0900 Subject: [PATCH 07/16] Update .travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b779c5a..0ca526f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ osx_image: xcode11.3 install: - brew install swiftlint swiftformat - - cd ./Demo; pod install script: - xcodebuild -workspace WKJavaScriptController-Demo.xcworkspace -scheme WKJavaScriptController-Demo -destination "platform=iOS Simulator,name=iPhone 7,OS=11.4" -configuration Debug ONLY_ACTIVE_ARCH=NO > >(xcpretty -c) From 7b6500acd21876e3ba99e8b7f975425ae6a5b4a4 Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Mon, 24 Aug 2020 18:28:00 +0900 Subject: [PATCH 08/16] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0ca526f..45d159e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ install: - brew install swiftlint swiftformat script: - - xcodebuild -workspace WKJavaScriptController-Demo.xcworkspace -scheme WKJavaScriptController-Demo -destination "platform=iOS Simulator,name=iPhone 7,OS=11.4" -configuration Debug ONLY_ACTIVE_ARCH=NO > >(xcpretty -c) + - xcodebuild -project ./Demo/WKJavaScriptController-Demo.xcodeproj -scheme WKJavaScriptController-Demo -destination "platform=iOS Simulator,name=iPhone 7,OS=11.4" -configuration Debug ONLY_ACTIVE_ARCH=NO > >(xcpretty -c) notifications: email: false From dc258ea386f63aacf05439b480f358194c588f29 Mon Sep 17 00:00:00 2001 From: "davin.ahn" Date: Sat, 28 Aug 2021 01:47:06 +0900 Subject: [PATCH 09/16] Update CHANGELOG.md --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a43021..bedee66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,119 +7,123 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -* None. +- None. + +## [2.1.0 (2021-08-28)] + +- Deprecation cocoapods support. ## [2.0.2 (2020-02-06)] ### Added -* Support Swift Package Manager. +- Support Swift Package Manager. ## [2.0.1 (2019-03-06)] ### Changed -* Change `JSValueType` description to string value. +- Change `JSValueType` description to string value. ## [2.0.0 (2019-02-28)] ### Added -* Support native return to JavaScript as [Promise](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise). -* Add `logEnabled` property. (default: `true`) -* Add `callbackTimeout` property. (default: `10`) +- Support native return to JavaScript as [Promise](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise). +- Add `logEnabled` property. (default: `true`) +- Add `callbackTimeout` property. (default: `10`) ### Changed -* Rename `shouldSafeMethodCall` to `ignoreMethodCallWhenReceivedNull`. -* Rename `shouldConvertJSONString` to `convertsToDictionaryWhenReceivedJsonString`. +- Rename `shouldSafeMethodCall` to `ignoreMethodCallWhenReceivedNull`. +- Rename `shouldConvertJSONString` to `convertsToDictionaryWhenReceivedJsonString`. ### Fixed -* Fix an issue where string composed of numbers were cast to `JSInt` by parsing top-level objects as `JSONSerialization` with `allowFragments` read option. +- Fix an issue where string composed of numbers were cast to `JSInt` by parsing top-level objects as `JSONSerialization` with `allowFragments` read option. ## [1.2.0 (2019-02-26)] ### Changed -* Migrate to Swift 4.2. +- Migrate to Swift 4.2. ## [1.1.9 (2018-07-31)] ### Fixed -* Fix an issue where compile error with Xcode 9 in Swift 4 mode. +- Fix an issue where compile error with Xcode 9 in Swift 4 mode. ## [1.1.8 (2017-11-29)] -* None. +- None. ## [1.1.7 (2017-09-26)] ### Fixed -* Fix build error with Xcode 9. +- Fix build error with Xcode 9. ## [1.1.6 (2017-07-05)] ### Fixed -* Fix build error with Xcode 9. +- Fix build error with Xcode 9. ## [1.1.5 (2017-02-08)] ### Changed -* Change method invocation notification send order. +- Change method invocation notification send order. ## [1.1.4 (2017-02-05)] ### Added -* Add `shouldConvertJSONString` option. +- Add `shouldConvertJSONString` option. ### Fixed -* Fix to read methods of higher protocols. +- Fix to read methods of higher protocols. ## [1.1.3 (2017-01-26)] ### Fixed -* Fix JSON parse error. +- Fix JSON parse error. ## [1.1.2 (2017-01-26)] ### Fixed -* Fix cast arguments. +- Fix cast arguments. ## [1.1.1 (2017-01-26)] ### Added -* Add method invocation notification. -* Add `shouldSafeMethodCall` property. +- Add method invocation notification. +- Add `shouldSafeMethodCall` property. ## [1.1.0 (2017-01-18)] ### Changed -* Migrate to Swift 3. +- Migrate to Swift 3. ## [1.0.2 (2017-01-18)] -* None. +- None. ## [1.0.1 (2017-01-18)] ### Fixed -* Fix an issue where swift value type was parsed incorrectly. +- Fix an issue where swift value type was parsed incorrectly. ## [1.0.0 (2017-01-17)] -* First release. +- First release. [Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.0.2...HEAD [2.0.2 (2020-02-06)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.1...2.0.2 From b690ccfb9c9ac286c40bc65c927c382c2ba591e0 Mon Sep 17 00:00:00 2001 From: "davin.ahn" Date: Sat, 28 Aug 2021 01:48:19 +0900 Subject: [PATCH 10/16] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bedee66..402cdb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [2.1.0 (2021-08-28)] - Deprecation cocoapods support. +- Remove userScripts property. ## [2.0.2 (2020-02-06)] @@ -125,7 +126,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - First release. -[Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.0.2...HEAD +[Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.1.0...HEAD +[2.1.0 (2021-08-28)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.2...2.1.0 [2.0.2 (2020-02-06)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.1...2.0.2 [2.0.1 (2019-03-06)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.0...2.0.1 [2.0.0 (2019-02-28)]: https://github.com/ridi/WKJavaScriptController/compare/1.2.0...2.0.0 From a9f4fa0fb1da9376f854caa6032f240c5e3f8c71 Mon Sep 17 00:00:00 2001 From: "davin.ahn" Date: Sat, 28 Aug 2021 02:14:21 +0900 Subject: [PATCH 11/16] Change JavaScriptInterface bridge to be called on a background thread --- .../WKJavaScriptController.swift | 162 +++++++++--------- 1 file changed, 83 insertions(+), 79 deletions(-) diff --git a/Sources/WKJavaScriptController/WKJavaScriptController.swift b/Sources/WKJavaScriptController/WKJavaScriptController.swift index 271c372..b88defc 100644 --- a/Sources/WKJavaScriptController/WKJavaScriptController.swift +++ b/Sources/WKJavaScriptController/WKJavaScriptController.swift @@ -486,87 +486,91 @@ extension WKJavaScriptController: WKScriptMessageHandler { } let imp = method_getImplementation(method) - var result: Result! - switch bridge.argumentCount { - case 0: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation0.self)(target, bridge.nativeSelector) - } else { - unsafeBitCast(imp, to: Invocation0.self)(target, bridge.nativeSelector) - } - case 1: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation1.self)(target, bridge.nativeSelector, cast(args[0])) - } else { - unsafeBitCast(imp, to: Invocation1.self)(target, bridge.nativeSelector, cast(args[0])) - } - case 2: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation2.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1])) - } else { - unsafeBitCast(imp, to: Invocation2.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1])) - } - case 3: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation3.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2])) - } else { - unsafeBitCast(imp, to: Invocation3.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2])) - } - case 4: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation4.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3])) - } else { - unsafeBitCast(imp, to: Invocation4.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3])) - } - case 5: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation5.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4])) - } else { - unsafeBitCast(imp, to: Invocation5.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4])) - } - case 6: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation6.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5])) - } else { - unsafeBitCast(imp, to: Invocation6.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5])) - } - case 7: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation7.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6])) - } else { - unsafeBitCast(imp, to: Invocation7.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6])) - } - case 8: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation8.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7])) - } else { - unsafeBitCast(imp, to: Invocation8.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7])) - } - case 9: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation9.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8])) - } else { - unsafeBitCast(imp, to: Invocation9.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8])) + DispatchQueue.global().async { + var result: Result! + switch bridge.argumentCount { + case 0: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation0.self)(target, bridge.nativeSelector) + } else { + unsafeBitCast(imp, to: Invocation0.self)(target, bridge.nativeSelector) + } + case 1: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation1.self)(target, bridge.nativeSelector, self.cast(args[0])) + } else { + unsafeBitCast(imp, to: Invocation1.self)(target, bridge.nativeSelector, self.cast(args[0])) + } + case 2: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation2.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1])) + } else { + unsafeBitCast(imp, to: Invocation2.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1])) + } + case 3: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation3.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2])) + } else { + unsafeBitCast(imp, to: Invocation3.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2])) + } + case 4: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation4.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3])) + } else { + unsafeBitCast(imp, to: Invocation4.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3])) + } + case 5: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation5.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4])) + } else { + unsafeBitCast(imp, to: Invocation5.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4])) + } + case 6: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation6.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5])) + } else { + unsafeBitCast(imp, to: Invocation6.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5])) + } + case 7: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation7.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6])) + } else { + unsafeBitCast(imp, to: Invocation7.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6])) + } + case 8: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation8.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7])) + } else { + unsafeBitCast(imp, to: Invocation8.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7])) + } + case 9: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation9.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7]), self.cast(args[8])) + } else { + unsafeBitCast(imp, to: Invocation9.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7]), self.cast(args[8])) + } + case argumentCountLimit: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation10.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7]), self.cast(args[8]), self.cast(args[9])) + } else { + unsafeBitCast(imp, to: Invocation10.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7]), self.cast(args[8]), self.cast(args[9])) + } + default: + // Not called. + break } - case argumentCountLimit: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation10.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8]), cast(args[9])) - } else { - unsafeBitCast(imp, to: Invocation10.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8]), cast(args[9])) + + DispatchQueue.main.async { + let script = """ + (() => { + const callback = \(self.name).\(ReserveKeyword.callbackList)['\(callbackId)']; + callback.end = new Date(); + callback.resolve(\(bridge.isReturnRequired ? self.stringFrom(result) : "")); + clearTimeout(callback.timer); + })(); + """ + self.webView?.evaluateJavaScript(script, completionHandler: nil) } - default: - // Not called. - break } - - let script = """ - (() => { - const callback = \(name).\(ReserveKeyword.callbackList)['\(callbackId)']; - callback.end = new Date(); - callback.resolve(\(bridge.isReturnRequired ? stringFrom(result) : "")); - clearTimeout(callback.timer); - })(); - """ - webView?.evaluateJavaScript(script, completionHandler: nil) } } From 4ea8160eae9209166733099d60dfffdf39d382ec Mon Sep 17 00:00:00 2001 From: DavinAhn Date: Fri, 23 Dec 2022 17:05:02 +0900 Subject: [PATCH 12/16] =?UTF-8?q?=EC=99=B8=EB=B6=80=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=B8=8C=EB=A6=BF=EC=A7=80=20=EC=A0=95=EB=B3=B4=EC=97=90=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=A0=91=EA=B7=BC=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WKJavaScriptController.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/WKJavaScriptController/WKJavaScriptController.swift b/Sources/WKJavaScriptController/WKJavaScriptController.swift index b88defc..ff03419 100644 --- a/Sources/WKJavaScriptController/WKJavaScriptController.swift +++ b/Sources/WKJavaScriptController/WKJavaScriptController.swift @@ -103,16 +103,16 @@ open class WKJavaScriptController: NSObject { fileprivate weak var webView: WKWebView? - fileprivate var bridges = [MethodBridge]() + open var bridges = [MethodBridge]() fileprivate var isInjectRequired = true - fileprivate class MethodBridge { - var nativeSelector: Selector - var isExtendJsSelector: Bool // If true, use ObjC style naming. - var isReturnRequired: Bool + open class MethodBridge { + open private(set) var nativeSelector: Selector + open fileprivate(set) var isExtendJsSelector: Bool // If true, use ObjC style naming. + open fileprivate(set) var isReturnRequired: Bool - var jsSelector: String { + open var jsSelector: String { let selector = NSStringFromSelector(nativeSelector) let components = selector.components(separatedBy: ":") if components.isEmpty { @@ -136,7 +136,7 @@ open class WKJavaScriptController: NSObject { } } - var argumentCount: Int { + open var argumentCount: Int { max(NSStringFromSelector(nativeSelector).components(separatedBy: ":").count - 1, 0) } From 19f5b4338c4c4c70d3c65c0856493512397fe0cc Mon Sep 17 00:00:00 2001 From: DavinAhn Date: Fri, 23 Dec 2022 17:20:29 +0900 Subject: [PATCH 13/16] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 402cdb0..25f6175 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - None. +## [2.2.0 (2022-12-23)] + +- Change `bridges` access modifier to open. + ## [2.1.0 (2021-08-28)] - Deprecation cocoapods support. @@ -126,7 +130,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - First release. -[Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.1.0...HEAD +[Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.2.0...HEAD +[2.2.0 (2022-12-23)]: https://github.com/ridi/WKJavaScriptController/compare/2.1.0...2.2.0 [2.1.0 (2021-08-28)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.2...2.1.0 [2.0.2 (2020-02-06)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.1...2.0.2 [2.0.1 (2019-03-06)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.0...2.0.1 From b4d88a44113698c0f55fb3b66cc599805be5d218 Mon Sep 17 00:00:00 2001 From: DavinAhn Date: Tue, 3 Jan 2023 01:27:09 +0900 Subject: [PATCH 14/16] =?UTF-8?q?#9=20=EC=97=90=EC=84=9C=20=EC=9E=98?= =?UTF-8?q?=EB=AA=BB=20=EC=98=A4=ED=94=88=ED=95=9C=20=EC=95=A1=EC=84=B8?= =?UTF-8?q?=EC=8A=A4=20=EC=A7=80=EC=8B=9C=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/WKJavaScriptController/WKJavaScriptController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WKJavaScriptController/WKJavaScriptController.swift b/Sources/WKJavaScriptController/WKJavaScriptController.swift index ff03419..8073a86 100644 --- a/Sources/WKJavaScriptController/WKJavaScriptController.swift +++ b/Sources/WKJavaScriptController/WKJavaScriptController.swift @@ -103,7 +103,7 @@ open class WKJavaScriptController: NSObject { fileprivate weak var webView: WKWebView? - open var bridges = [MethodBridge]() + open private(set) var bridges = [MethodBridge]() fileprivate var isInjectRequired = true From 90e1809737b1537809ba087b9d1c977f557f6807 Mon Sep 17 00:00:00 2001 From: DavinAhn Date: Tue, 3 Jan 2023 01:28:07 +0900 Subject: [PATCH 15/16] Revert "Change JavaScriptInterface bridge to be called on a background thread" This reverts commit a9f4fa0fb1da9376f854caa6032f240c5e3f8c71. --- .../WKJavaScriptController.swift | 162 +++++++++--------- 1 file changed, 79 insertions(+), 83 deletions(-) diff --git a/Sources/WKJavaScriptController/WKJavaScriptController.swift b/Sources/WKJavaScriptController/WKJavaScriptController.swift index ff03419..f5390cb 100644 --- a/Sources/WKJavaScriptController/WKJavaScriptController.swift +++ b/Sources/WKJavaScriptController/WKJavaScriptController.swift @@ -486,91 +486,87 @@ extension WKJavaScriptController: WKScriptMessageHandler { } let imp = method_getImplementation(method) - DispatchQueue.global().async { - var result: Result! - switch bridge.argumentCount { - case 0: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation0.self)(target, bridge.nativeSelector) - } else { - unsafeBitCast(imp, to: Invocation0.self)(target, bridge.nativeSelector) - } - case 1: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation1.self)(target, bridge.nativeSelector, self.cast(args[0])) - } else { - unsafeBitCast(imp, to: Invocation1.self)(target, bridge.nativeSelector, self.cast(args[0])) - } - case 2: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation2.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1])) - } else { - unsafeBitCast(imp, to: Invocation2.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1])) - } - case 3: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation3.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2])) - } else { - unsafeBitCast(imp, to: Invocation3.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2])) - } - case 4: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation4.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3])) - } else { - unsafeBitCast(imp, to: Invocation4.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3])) - } - case 5: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation5.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4])) - } else { - unsafeBitCast(imp, to: Invocation5.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4])) - } - case 6: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation6.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5])) - } else { - unsafeBitCast(imp, to: Invocation6.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5])) - } - case 7: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation7.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6])) - } else { - unsafeBitCast(imp, to: Invocation7.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6])) - } - case 8: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation8.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7])) - } else { - unsafeBitCast(imp, to: Invocation8.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7])) - } - case 9: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation9.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7]), self.cast(args[8])) - } else { - unsafeBitCast(imp, to: Invocation9.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7]), self.cast(args[8])) - } - case argumentCountLimit: - if bridge.isReturnRequired { - result = unsafeBitCast(imp, to: XInvocation10.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7]), self.cast(args[8]), self.cast(args[9])) - } else { - unsafeBitCast(imp, to: Invocation10.self)(target, bridge.nativeSelector, self.cast(args[0]), self.cast(args[1]), self.cast(args[2]), self.cast(args[3]), self.cast(args[4]), self.cast(args[5]), self.cast(args[6]), self.cast(args[7]), self.cast(args[8]), self.cast(args[9])) - } - default: - // Not called. - break + var result: Result! + switch bridge.argumentCount { + case 0: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation0.self)(target, bridge.nativeSelector) + } else { + unsafeBitCast(imp, to: Invocation0.self)(target, bridge.nativeSelector) } - - DispatchQueue.main.async { - let script = """ - (() => { - const callback = \(self.name).\(ReserveKeyword.callbackList)['\(callbackId)']; - callback.end = new Date(); - callback.resolve(\(bridge.isReturnRequired ? self.stringFrom(result) : "")); - clearTimeout(callback.timer); - })(); - """ - self.webView?.evaluateJavaScript(script, completionHandler: nil) + case 1: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation1.self)(target, bridge.nativeSelector, cast(args[0])) + } else { + unsafeBitCast(imp, to: Invocation1.self)(target, bridge.nativeSelector, cast(args[0])) + } + case 2: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation2.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1])) + } else { + unsafeBitCast(imp, to: Invocation2.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1])) + } + case 3: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation3.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2])) + } else { + unsafeBitCast(imp, to: Invocation3.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2])) + } + case 4: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation4.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3])) + } else { + unsafeBitCast(imp, to: Invocation4.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3])) + } + case 5: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation5.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4])) + } else { + unsafeBitCast(imp, to: Invocation5.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4])) + } + case 6: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation6.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5])) + } else { + unsafeBitCast(imp, to: Invocation6.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5])) + } + case 7: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation7.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6])) + } else { + unsafeBitCast(imp, to: Invocation7.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6])) } + case 8: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation8.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7])) + } else { + unsafeBitCast(imp, to: Invocation8.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7])) + } + case 9: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation9.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8])) + } else { + unsafeBitCast(imp, to: Invocation9.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8])) + } + case argumentCountLimit: + if bridge.isReturnRequired { + result = unsafeBitCast(imp, to: XInvocation10.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8]), cast(args[9])) + } else { + unsafeBitCast(imp, to: Invocation10.self)(target, bridge.nativeSelector, cast(args[0]), cast(args[1]), cast(args[2]), cast(args[3]), cast(args[4]), cast(args[5]), cast(args[6]), cast(args[7]), cast(args[8]), cast(args[9])) + } + default: + // Not called. + break } + + let script = """ + (() => { + const callback = \(name).\(ReserveKeyword.callbackList)['\(callbackId)']; + callback.end = new Date(); + callback.resolve(\(bridge.isReturnRequired ? stringFrom(result) : "")); + clearTimeout(callback.timer); + })(); + """ + webView?.evaluateJavaScript(script, completionHandler: nil) } } From c059b3e6e14d24e22d91eccb85a69943da48f26a Mon Sep 17 00:00:00 2001 From: Davin Ahn Date: Tue, 3 Jan 2023 11:35:34 +0900 Subject: [PATCH 16/16] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f6175..48083c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - None. +## [2.2.1 (2023-01-03)] + +- Revert wrong modified for thread. + ## [2.2.0 (2022-12-23)] - Change `bridges` access modifier to open. @@ -130,7 +134,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - First release. -[Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.2.0...HEAD +[Unreleased]: https://github.com/ridi/WKJavaScriptController/compare/2.2.1...HEAD +[2.2.1 (2023-01-03)]: https://github.com/ridi/WKJavaScriptController/compare/2.2.0...2.2.1 [2.2.0 (2022-12-23)]: https://github.com/ridi/WKJavaScriptController/compare/2.1.0...2.2.0 [2.1.0 (2021-08-28)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.2...2.1.0 [2.0.2 (2020-02-06)]: https://github.com/ridi/WKJavaScriptController/compare/2.0.1...2.0.2