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/.travis.yml b/.travis.yml index e74a261..45d159e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,14 +3,13 @@ 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) + - 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 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index de67b88..48083c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,121 +7,138 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -* None. +- None. + +## [2.2.1 (2023-01-03)] + +- Revert wrong modified for thread. + +## [2.2.0 (2022-12-23)] + +- Change `bridges` access modifier to open. + +## [2.1.0 (2021-08-28)] + +- Deprecation cocoapods support. +- Remove userScripts property. ## [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.1...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 [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 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..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. - return true + 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 a1af4e0..1977371 100644 --- a/Demo/WKJavaScriptController-Demo/ViewController.swift +++ b/Demo/WKJavaScriptController-Demo/ViewController.swift @@ -17,62 +17,79 @@ import WKJavaScriptController extension ViewController: JavaScriptInterface { func onSubmit(_ dictonary: [String: AnyObject]) { NSLog("onSubmit \(dictonary)") + _isSubmitted = true } - + func onSubmit(_ dictonary: [String: AnyObject], clear: JSBool) { NSLog("onSubmit \(dictonary)") 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") + _isSubmitted = false } - - 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) - + if webView == nil { // Create javaScriptController. 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) - - webView = WKWebView(frame: view.frame) + 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 + + // Create WKWebView instance. + webView = WKWebView(frame: view.frame, configuration: configuration) + 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) + 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) } } - - override var prefersStatusBarHidden: Bool { - return true +} + +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.'); +} 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..ebd58b8 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 @@ -89,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 jsCode = ... + let userScript = WKUserScript(source: jsCode, injectionTime: .AtDocumentEnd, forMainFrameOnly: true) + userContentController.addUserScript(userScript) + + configuration.userContentController = userContentController - let webView = WKWebView(...) - ... + // 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 ccf972b..2e7a093 100644 --- a/Sources/WKJavaScriptController/WKJavaScriptController.swift +++ b/Sources/WKJavaScriptController/WKJavaScriptController.swift @@ -9,10 +9,10 @@ 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 } } - + // You must call it before initializing WKWebView or loading page. // ex) override func loadHTMLString(string: String, baseURL: NSURL?) -> WKNavigation? { // prepareForJavaScriptController() @@ -29,41 +29,33 @@ public extension WKWebView { open class JSValueType: NSObject { fileprivate var _value: NSNumber - + fileprivate init(_ number: NSNumber) { _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) } } 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) } } 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) } @@ -74,54 +66,53 @@ 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. 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]() - + + open private(set) 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 { + + 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 + + open var jsSelector: String { let selector = NSStringFromSelector(nativeSelector) let components = selector.components(separatedBy: ":") if components.isEmpty { @@ -144,30 +135,36 @@ open class WKJavaScriptController: NSObject { return components.first! } } - - var argumentCount: Int { - return max(NSStringFromSelector(nativeSelector).components(separatedBy: ":").count - 1, 0) + + open var argumentCount: Int { + max(NSStringFromSelector(nativeSelector).components(separatedBy: ":").count - 1, 0) } - + init(nativeSelector selector: Selector) { nativeSelector = selector isExtendJsSelector = false isReturnRequired = false } } - - 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 + ] } } - + public init(name: String, target: AnyObject, bridgeProtocol: Protocol) { self.name = name self.target = target @@ -175,7 +172,7 @@ open class WKJavaScriptController: NSObject { super.init() parseBridgeProtocol() } - + private func protocolsAdoptedBy(protocol: Protocol) -> [Protocol] { var protocols = [`protocol`] let protocolList = protocol_copyProtocolList(`protocol`, nil) @@ -187,11 +184,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) @@ -200,14 +197,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 @@ -226,26 +223,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 { @@ -254,16 +251,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)") } @@ -272,48 +269,50 @@ 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() { + \(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 +322,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,33 +337,27 @@ 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]); }); }, }); """ } - return WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: forMainFrameOnly) + return WKUserScript( + source: "\(identifier)\n\(source)", + injectionTime: .atDocumentStart, + forMainFrameOnly: true + ) } - - fileprivate func log(_ message: String) { + + 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 - } } // MARK: - WKScriptMessageHandler @@ -405,7 +398,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)) @@ -424,7 +417,7 @@ extension WKJavaScriptController: WKScriptMessageHandler { } return arg } - + private func stringFrom(_ result: Result!) -> String { if result != nil { if let jsBool = result as? JSBool { @@ -448,27 +441,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, @@ -476,7 +469,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 { @@ -491,7 +484,7 @@ extension WKJavaScriptController: WKScriptMessageHandler { } } } - + let imp = method_getImplementation(method) var result: Result! switch bridge.argumentCount { @@ -565,10 +558,10 @@ extension WKJavaScriptController: WKScriptMessageHandler { // Not called. break } - + 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