Compare commits
40 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
b40f0ac185 | |
|
|
dbcad92ff0 | |
|
|
605e1aeeef | |
|
|
6e712bf664 | |
|
|
0b860372e9 | |
|
|
c8f4fb213e | |
|
|
7ba0aff437 | |
|
|
b3994f932e | |
|
|
1d178f7154 | |
|
|
784187e911 | |
|
|
31a64e5967 | |
|
|
ce8acad6a2 | |
|
|
0e47c7264f | |
|
|
c9afbc22c1 | |
|
|
050ce19425 | |
|
|
dc4010cc6e | |
|
|
d0af0f686b | |
|
|
59c5223580 | |
|
|
484ed94a9b | |
|
|
7dc2722bb5 | |
|
|
9f3ac2efa7 | |
|
|
ee878521a3 | |
|
|
184be54b90 | |
|
|
a88fcb9850 | |
|
|
50046bff25 | |
|
|
0389c5afa0 | |
|
|
a9117cbb7d | |
|
|
11074817f1 | |
|
|
e540042554 | |
|
|
438bb57c38 | |
|
|
3821c300b7 | |
|
|
87355c1b13 | |
|
|
3c5cb4fe59 | |
|
|
66b70da874 | |
|
|
1c3af347e4 | |
|
|
0e3028956a | |
|
|
0642f8d832 | |
|
|
9d061aaaff | |
|
|
5989547f40 | |
|
|
667b78c2f9 |
|
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
---
|
||||||
|
<!--
|
||||||
|
## Is it really a bug?
|
||||||
|
Before opening an issue, check the following:
|
||||||
|
1. You are using the **SITE** key
|
||||||
|
2. The correct domain, with protocol, is setup.
|
||||||
|
3. You are using an **Invisible** reCAPTCHA v2 key.
|
||||||
|
4. If the widget doesn't appear, that is expected since the library will try to resolve the challenge _invisibly_.
|
||||||
|
|
||||||
|
https://www.google.com/recaptcha/admin#site
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Bug description
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
## To Reproduce
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '...'
|
||||||
|
3. ...
|
||||||
|
4. Profit (jk See error)
|
||||||
|
|
||||||
|
## Expected behavior
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
## Logs
|
||||||
|
Please add as many logs as you feel necessary. Be mindful of your application and remove any sensitive data.
|
||||||
|
|
||||||
|
## Additional context
|
||||||
|
Add any other context about the problem here.
|
||||||
|
|
@ -51,3 +51,6 @@ fastlane/Preview.html
|
||||||
fastlane/screenshots
|
fastlane/screenshots
|
||||||
fastlane/README.md
|
fastlane/README.md
|
||||||
fastlane/test_output
|
fastlane/test_output
|
||||||
|
|
||||||
|
## VS Code
|
||||||
|
.vscode
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
author: Flávio Caetano (@fjcaetano)
|
||||||
|
author_url: https://github.com/fjcaetano
|
||||||
|
github_url: https://github.com/fjcaetano/ReCaptcha
|
||||||
|
|
||||||
|
readme: README.md
|
||||||
|
|
||||||
|
module: ReCaptcha
|
||||||
|
xcodebuild_arguments:
|
||||||
|
- -workspace
|
||||||
|
- Example/ReCaptcha.xcworkspace
|
||||||
|
- -scheme
|
||||||
|
- ReCaptcha-Example
|
||||||
|
- -configuration
|
||||||
|
- Release
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
4.0
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
osx_image: xcode9
|
osx_image: xcode10
|
||||||
language: objective-c
|
language: objective-c
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
|
|
|
||||||
25
CHANGELOG.md
25
CHANGELOG.md
|
|
@ -1,3 +1,28 @@
|
||||||
|
# 1.4.2
|
||||||
|
|
||||||
|
- Fix: Webview's resource loading detection (#56 #60)
|
||||||
|
|
||||||
|
# 1.4.1
|
||||||
|
|
||||||
|
- Fix RxSwift dependency version (#58)
|
||||||
|
|
||||||
|
# 1.4
|
||||||
|
|
||||||
|
- Feature: Support Swift 4.2
|
||||||
|
- Feature: enable validation to be skipped for testing
|
||||||
|
|
||||||
|
# 1.3.1
|
||||||
|
|
||||||
|
- Fix: Removing leftover print
|
||||||
|
- Fix: Removing Result dependency from Carthage
|
||||||
|
|
||||||
|
# 1.3
|
||||||
|
|
||||||
|
- Feature: Locale support (#39)
|
||||||
|
|
||||||
|
- Fix: Reset not flagging ReCaptha as ready-to-execute (#36)
|
||||||
|
- Fix: Multiple configure calls after app being idle (#40)
|
||||||
|
|
||||||
# 1.2
|
# 1.2
|
||||||
|
|
||||||
- Feature: Resettable ReCaptchas. (#23)
|
- Feature: Resettable ReCaptchas. (#23)
|
||||||
|
|
|
||||||
3
Cartfile
3
Cartfile
|
|
@ -1,2 +1 @@
|
||||||
github "antitypical/Result" ~> 3.0
|
binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/RxSwift/RxSwift.json"
|
||||||
github "ReactiveX/RxSwift" ~> 4.0
|
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
github "ReactiveX/RxSwift" "4.1.2"
|
binary "https://raw.github.com/TouchInstinct/CarthageBinaries/master/RxSwift/RxSwift.json" "4.5.0"
|
||||||
github "antitypical/Result" "3.2.4"
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ inhibit_all_warnings!
|
||||||
|
|
||||||
target 'ReCaptcha_Example' do
|
target 'ReCaptcha_Example' do
|
||||||
pod 'ReCaptcha/RxSwift', :path => '../'
|
pod 'ReCaptcha/RxSwift', :path => '../'
|
||||||
pod 'RxCocoa', '~> 4.0'
|
pod 'RxCocoa', '~> 4.3'
|
||||||
pod 'SwiftLint', '~> 0.24'
|
pod 'SwiftLint', '~> 0.27'
|
||||||
|
|
||||||
target 'ReCaptcha_Tests' do
|
target 'ReCaptcha_Tests' do
|
||||||
inherit! :search_paths
|
inherit! :search_paths
|
||||||
|
|
||||||
pod 'AppSwizzle', :git => 'https://github.com/fjcaetano/AppSwizzle.git', :branch => 'swift4'
|
pod 'AppSwizzle', '~> 1.3'
|
||||||
pod 'RxBlocking', '~> 4.0'
|
pod 'RxBlocking', '~> 4.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,48 @@
|
||||||
PODS:
|
PODS:
|
||||||
- AppSwizzle (1.2)
|
- AppSwizzle (1.3.1)
|
||||||
- ReCaptcha/Core (1.1)
|
- ReCaptcha/Core (1.4.1)
|
||||||
- ReCaptcha/RxSwift (1.1):
|
- ReCaptcha/RxSwift (1.4.1):
|
||||||
- ReCaptcha/Core
|
- ReCaptcha/Core
|
||||||
|
- RxSwift (~> 4.3)
|
||||||
|
- RxAtomic (4.4.0)
|
||||||
|
- RxBlocking (4.4.0):
|
||||||
|
- RxAtomic (~> 4.4)
|
||||||
- RxSwift (~> 4.0)
|
- RxSwift (~> 4.0)
|
||||||
- RxBlocking (4.1.1):
|
- RxCocoa (4.4.0):
|
||||||
- RxSwift (~> 4.0)
|
- RxSwift (~> 4.0)
|
||||||
- RxCocoa (4.1.1):
|
- RxSwift (4.4.0):
|
||||||
- RxSwift (~> 4.0)
|
- RxAtomic (~> 4.4)
|
||||||
- RxSwift (4.1.1)
|
- SwiftLint (0.27.0)
|
||||||
- SwiftLint (0.24.1)
|
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- AppSwizzle (from `https://github.com/fjcaetano/AppSwizzle.git`, branch `swift4`)
|
- AppSwizzle (~> 1.3)
|
||||||
- ReCaptcha/RxSwift (from `../`)
|
- ReCaptcha/RxSwift (from `../`)
|
||||||
- RxBlocking (~> 4.0)
|
- RxBlocking (~> 4.0)
|
||||||
- RxCocoa (~> 4.0)
|
- RxCocoa (~> 4.3)
|
||||||
- SwiftLint (~> 0.24)
|
- SwiftLint (~> 0.27)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
https://github.com/cocoapods/specs.git:
|
||||||
|
- AppSwizzle
|
||||||
|
- RxAtomic
|
||||||
|
- RxBlocking
|
||||||
|
- RxCocoa
|
||||||
|
- RxSwift
|
||||||
|
- SwiftLint
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
AppSwizzle:
|
|
||||||
:branch: swift4
|
|
||||||
:git: https://github.com/fjcaetano/AppSwizzle.git
|
|
||||||
ReCaptcha:
|
ReCaptcha:
|
||||||
:path: ../
|
:path: "../"
|
||||||
|
|
||||||
CHECKOUT OPTIONS:
|
|
||||||
AppSwizzle:
|
|
||||||
:commit: c432a73e43779d20ef8e8a589aabd9622b7b6a5d
|
|
||||||
:git: https://github.com/fjcaetano/AppSwizzle.git
|
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
AppSwizzle: bbd3782652fc426ce59c045a92ec61d36f261984
|
AppSwizzle: db36e436f56110d93e5ae0147683435df593cabc
|
||||||
ReCaptcha: b94a673f3827e9b9cf7e77db0395694549241247
|
ReCaptcha: 520a707a38dfbb1e5de812aa3c041df60bd31827
|
||||||
RxBlocking: 22e7d7b86a1c0c42164a30d64f56f97128b4ffab
|
RxAtomic: eacf60db868c96bfd63320e28619fe29c179656f
|
||||||
RxCocoa: fd0862fd2df95fa55562ad28ffd2522c25eb4a85
|
RxBlocking: 138ad53217434444d6eeeb4fb406a45431d92e31
|
||||||
RxSwift: c6e3b1c7b325c7d121cd4327e9d98b7ed746b570
|
RxCocoa: df63ebf7b9a70d6b4eeea407ed5dd4efc8979749
|
||||||
SwiftLint: 2e4b89feed5909c42c3735bbd6745f4345c4b772
|
RxSwift: 5976ecd04fc2fefd648827c23de5e11157faa973
|
||||||
|
SwiftLint: 3207c1faa2240bf8973b191820a116113cd11073
|
||||||
|
|
||||||
PODFILE CHECKSUM: 3ae211ad7c1b9c86749b013587aa4b63661f345e
|
PODFILE CHECKSUM: 63f50401dee6a885ae0eea927fb966553c6d0f33
|
||||||
|
|
||||||
COCOAPODS: 1.4.0
|
COCOAPODS: 1.5.3
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
BD850CB2DF4C9C94FC51226C /* Pods_ReCaptcha_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62BEEA62161F672468CCFD64 /* Pods_ReCaptcha_Example.framework */; };
|
BD850CB2DF4C9C94FC51226C /* Pods_ReCaptcha_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62BEEA62161F672468CCFD64 /* Pods_ReCaptcha_Example.framework */; };
|
||||||
D091B6E053FD250B4757E34C /* Pods_ReCaptcha_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A28DC340FF0BC1627B3F /* Pods_ReCaptcha_Tests.framework */; };
|
D091B6E053FD250B4757E34C /* Pods_ReCaptcha_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A28DC340FF0BC1627B3F /* Pods_ReCaptcha_Tests.framework */; };
|
||||||
F206BAD51F8D3FEB00A25807 /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = F206BAD41F8D3FEB00A25807 /* Cartfile */; };
|
F206BAD51F8D3FEB00A25807 /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = F206BAD41F8D3FEB00A25807 /* Cartfile */; };
|
||||||
|
F211C22220F7E0B100709B26 /* ReCaptcha_Endpoint__Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F211C22120F7E0B100709B26 /* ReCaptcha_Endpoint__Tests.swift */; };
|
||||||
F231B3971FEC325A00F82943 /* DispatchQueue__Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F231B3961FEC325A00F82943 /* DispatchQueue__Tests.swift */; };
|
F231B3971FEC325A00F82943 /* DispatchQueue__Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F231B3961FEC325A00F82943 /* DispatchQueue__Tests.swift */; };
|
||||||
F231B39F1FED4A8C00F82943 /* ReCaptchaDecoder+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F231B39E1FED4A8C00F82943 /* ReCaptchaDecoder+Helper.swift */; };
|
F231B39F1FED4A8C00F82943 /* ReCaptchaDecoder+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F231B39E1FED4A8C00F82943 /* ReCaptchaDecoder+Helper.swift */; };
|
||||||
F288E9451F9537760018688D /* ReCaptchaError+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288E9441F9537760018688D /* ReCaptchaError+Equatable.swift */; };
|
F288E9451F9537760018688D /* ReCaptchaError+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F288E9441F9537760018688D /* ReCaptchaError+Equatable.swift */; };
|
||||||
|
|
@ -68,6 +69,7 @@
|
||||||
C2A0BDD35B5E219129E9BC65 /* Pods-ReCaptcha_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReCaptcha_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-ReCaptcha_Example/Pods-ReCaptcha_Example.release.xcconfig"; sourceTree = "<group>"; };
|
C2A0BDD35B5E219129E9BC65 /* Pods-ReCaptcha_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReCaptcha_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-ReCaptcha_Example/Pods-ReCaptcha_Example.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
C8537003ECC47117AF54DCA9 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
|
C8537003ECC47117AF54DCA9 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
|
||||||
F206BAD41F8D3FEB00A25807 /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Cartfile; path = ../Cartfile; sourceTree = "<group>"; };
|
F206BAD41F8D3FEB00A25807 /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Cartfile; path = ../Cartfile; sourceTree = "<group>"; };
|
||||||
|
F211C22120F7E0B100709B26 /* ReCaptcha_Endpoint__Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReCaptcha_Endpoint__Tests.swift; sourceTree = "<group>"; };
|
||||||
F21901D91F98D62F00D8E2C9 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = "<group>"; };
|
F21901D91F98D62F00D8E2C9 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = "<group>"; };
|
||||||
F231B3961FEC325A00F82943 /* DispatchQueue__Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DispatchQueue__Tests.swift; sourceTree = "<group>"; };
|
F231B3961FEC325A00F82943 /* DispatchQueue__Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DispatchQueue__Tests.swift; sourceTree = "<group>"; };
|
||||||
F231B39E1FED4A8C00F82943 /* ReCaptchaDecoder+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReCaptchaDecoder+Helper.swift"; sourceTree = "<group>"; };
|
F231B39E1FED4A8C00F82943 /* ReCaptchaDecoder+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReCaptchaDecoder+Helper.swift"; sourceTree = "<group>"; };
|
||||||
|
|
@ -214,6 +216,7 @@
|
||||||
F2E2685D1F7AEE3400CD876D /* ReCaptcha__Tests.swift */,
|
F2E2685D1F7AEE3400CD876D /* ReCaptcha__Tests.swift */,
|
||||||
F231B3961FEC325A00F82943 /* DispatchQueue__Tests.swift */,
|
F231B3961FEC325A00F82943 /* DispatchQueue__Tests.swift */,
|
||||||
F2AE8611204F3430002E28D7 /* ReCaptchaResult__Tests.swift */,
|
F2AE8611204F3430002E28D7 /* ReCaptchaResult__Tests.swift */,
|
||||||
|
F211C22120F7E0B100709B26 /* ReCaptcha_Endpoint__Tests.swift */,
|
||||||
);
|
);
|
||||||
path = Core;
|
path = Core;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -259,7 +262,6 @@
|
||||||
607FACCD1AFB9204008FA782 /* Frameworks */,
|
607FACCD1AFB9204008FA782 /* Frameworks */,
|
||||||
607FACCE1AFB9204008FA782 /* Resources */,
|
607FACCE1AFB9204008FA782 /* Resources */,
|
||||||
8F03FFB3F5C55E873C23C682 /* [CP] Embed Pods Frameworks */,
|
8F03FFB3F5C55E873C23C682 /* [CP] Embed Pods Frameworks */,
|
||||||
ED1C0E07490C9C4B4A401061 /* [CP] Copy Pods Resources */,
|
|
||||||
F231B3981FEC3B7F00F82943 /* SwiftLint */,
|
F231B3981FEC3B7F00F82943 /* SwiftLint */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
|
|
@ -279,8 +281,6 @@
|
||||||
F28FAC98200E425600E14987 /* Sources */,
|
F28FAC98200E425600E14987 /* Sources */,
|
||||||
F28FAC99200E425600E14987 /* Frameworks */,
|
F28FAC99200E425600E14987 /* Frameworks */,
|
||||||
F28FAC9A200E425600E14987 /* Resources */,
|
F28FAC9A200E425600E14987 /* Resources */,
|
||||||
BF2909E4520B29BFFBC94AD6 /* [CP] Embed Pods Frameworks */,
|
|
||||||
DCB1D99E77E589CD76CD3186 /* [CP] Copy Pods Resources */,
|
|
||||||
F28FACA6200E447600E14987 /* ShellScript */,
|
F28FACA6200E447600E14987 /* ShellScript */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
|
|
@ -302,7 +302,6 @@
|
||||||
F2ECCF731E9FC47B0097B199 /* Frameworks */,
|
F2ECCF731E9FC47B0097B199 /* Frameworks */,
|
||||||
F2ECCF741E9FC47B0097B199 /* Resources */,
|
F2ECCF741E9FC47B0097B199 /* Resources */,
|
||||||
77003100630E7783A936C451 /* [CP] Embed Pods Frameworks */,
|
77003100630E7783A936C451 /* [CP] Embed Pods Frameworks */,
|
||||||
44209D7CDDDE3A11075B8104 /* [CP] Copy Pods Resources */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
|
@ -391,21 +390,6 @@
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
44209D7CDDDE3A11075B8104 /* [CP] Copy Pods Resources */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ReCaptcha_Tests/Pods-ReCaptcha_Tests-resources.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
51299F67A8756E2B3EAE411A /* [CP] Check Pods Manifest.lock */ = {
|
51299F67A8756E2B3EAE411A /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
|
@ -431,12 +415,14 @@
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
"${SRCROOT}/Pods/Target Support Files/Pods-ReCaptcha_Tests/Pods-ReCaptcha_Tests-frameworks.sh",
|
"${SRCROOT}/Pods/Target Support Files/Pods-ReCaptcha_Tests/Pods-ReCaptcha_Tests-frameworks.sh",
|
||||||
|
"${BUILT_PRODUCTS_DIR}/RxAtomic/RxAtomic.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
|
"${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/AppSwizzle/AppSwizzle.framework",
|
"${BUILT_PRODUCTS_DIR}/AppSwizzle/AppSwizzle.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/RxBlocking/RxBlocking.framework",
|
"${BUILT_PRODUCTS_DIR}/RxBlocking/RxBlocking.framework",
|
||||||
);
|
);
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxAtomic.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppSwizzle.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppSwizzle.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxBlocking.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxBlocking.framework",
|
||||||
|
|
@ -454,12 +440,14 @@
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
"${SRCROOT}/Pods/Target Support Files/Pods-ReCaptcha_Example/Pods-ReCaptcha_Example-frameworks.sh",
|
"${SRCROOT}/Pods/Target Support Files/Pods-ReCaptcha_Example/Pods-ReCaptcha_Example-frameworks.sh",
|
||||||
"${BUILT_PRODUCTS_DIR}/ReCaptcha/ReCaptcha.framework",
|
"${BUILT_PRODUCTS_DIR}/ReCaptcha/ReCaptcha.framework",
|
||||||
|
"${BUILT_PRODUCTS_DIR}/RxAtomic/RxAtomic.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework",
|
"${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework",
|
||||||
"${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
|
"${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
|
||||||
);
|
);
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReCaptcha.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ReCaptcha.framework",
|
||||||
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxAtomic.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework",
|
||||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
|
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
|
||||||
);
|
);
|
||||||
|
|
@ -486,36 +474,6 @@
|
||||||
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";
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
BF2909E4520B29BFFBC94AD6 /* [CP] Embed Pods Frameworks */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ReCaptcha_UITests/Pods-ReCaptcha_UITests-frameworks.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
DCB1D99E77E589CD76CD3186 /* [CP] Copy Pods Resources */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ReCaptcha_UITests/Pods-ReCaptcha_UITests-resources.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
DDB47454887253730AB35230 /* [CP] Check Pods Manifest.lock */ = {
|
DDB47454887253730AB35230 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
|
@ -534,21 +492,6 @@
|
||||||
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";
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
ED1C0E07490C9C4B4A401061 /* [CP] Copy Pods Resources */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ReCaptcha_Example/Pods-ReCaptcha_Example-resources.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
F231B3981FEC3B7F00F82943 /* SwiftLint */ = {
|
F231B3981FEC3B7F00F82943 /* SwiftLint */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
|
@ -561,7 +504,7 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\" --path \"${PROJECT_DIR}/..\"";
|
shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\" --path \"${PROJECT_DIR}/..\"\n";
|
||||||
};
|
};
|
||||||
F28FACA6200E447600E14987 /* ShellScript */ = {
|
F28FACA6200E447600E14987 /* ShellScript */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
|
@ -606,6 +549,7 @@
|
||||||
F2ECCF981EA011370097B199 /* Result+Helpers.swift in Sources */,
|
F2ECCF981EA011370097B199 /* Result+Helpers.swift in Sources */,
|
||||||
F231B3971FEC325A00F82943 /* DispatchQueue__Tests.swift in Sources */,
|
F231B3971FEC325A00F82943 /* DispatchQueue__Tests.swift in Sources */,
|
||||||
F231B39F1FED4A8C00F82943 /* ReCaptchaDecoder+Helper.swift in Sources */,
|
F231B39F1FED4A8C00F82943 /* ReCaptchaDecoder+Helper.swift in Sources */,
|
||||||
|
F211C22220F7E0B100709B26 /* ReCaptcha_Endpoint__Tests.swift in Sources */,
|
||||||
F2E2685E1F7AEE3400CD876D /* ReCaptcha__Tests.swift in Sources */,
|
F2E2685E1F7AEE3400CD876D /* ReCaptcha__Tests.swift in Sources */,
|
||||||
F2ECCF931EA009360097B199 /* ReCaptcha+Rx__Tests.swift in Sources */,
|
F2ECCF931EA009360097B199 /* ReCaptcha+Rx__Tests.swift in Sources */,
|
||||||
F288E9451F9537760018688D /* ReCaptchaError+Equatable.swift in Sources */,
|
F288E9451F9537760018688D /* ReCaptchaError+Equatable.swift in Sources */,
|
||||||
|
|
@ -759,7 +703,7 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-Example";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-Example";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||||
SWIFT_VERSION = 4.0;
|
SWIFT_VERSION = 4.2;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
|
@ -775,7 +719,7 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-Example";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-Example";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||||
SWIFT_VERSION = 4.0;
|
SWIFT_VERSION = 4.2;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
|
@ -798,7 +742,7 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "ReCaptcha.ReCaptcha-UITests";
|
PRODUCT_BUNDLE_IDENTIFIER = "ReCaptcha.ReCaptcha-UITests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_VERSION = 4.0;
|
SWIFT_VERSION = 4.2;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_TARGET_NAME = ReCaptcha_Example;
|
TEST_TARGET_NAME = ReCaptcha_Example;
|
||||||
};
|
};
|
||||||
|
|
@ -821,7 +765,7 @@
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "ReCaptcha.ReCaptcha-UITests";
|
PRODUCT_BUNDLE_IDENTIFIER = "ReCaptcha.ReCaptcha-UITests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 4.0;
|
SWIFT_VERSION = 4.2;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
TEST_TARGET_NAME = ReCaptcha_Example;
|
TEST_TARGET_NAME = ReCaptcha_Example;
|
||||||
};
|
};
|
||||||
|
|
@ -844,7 +788,7 @@
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG UNIT_TESTS";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG UNIT_TESTS";
|
||||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||||
SWIFT_VERSION = 4.0;
|
SWIFT_VERSION = 4.2;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReCaptcha_Example.app/ReCaptcha_Example";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReCaptcha_Example.app/ReCaptcha_Example";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
|
@ -864,7 +808,7 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-Tests";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-Tests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
|
||||||
SWIFT_VERSION = 4.0;
|
SWIFT_VERSION = 4.2;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReCaptcha_Example.app/ReCaptcha_Example";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ReCaptcha_Example.app/ReCaptcha_Example";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,8 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
codeCoverageEnabled = "YES"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
codeCoverageEnabled = "YES">
|
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "NO">
|
||||||
|
|
@ -78,10 +77,9 @@
|
||||||
</AdditionalOptions>
|
</AdditionalOptions>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
@ -16,7 +16,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
func application(
|
func application(
|
||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
// Override point for customization after application launch.
|
// Override point for customization after application launch.
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vXZ-lx-hvc">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vXZ-lx-hvc">
|
||||||
<device id="retina4_7" orientation="portrait">
|
<device id="retina4_7" orientation="portrait">
|
||||||
<adaptation id="fullscreen"/>
|
<adaptation id="fullscreen"/>
|
||||||
</device>
|
</device>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RDW-bD-rSo">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RDW-bD-rSo">
|
||||||
<rect key="frame" x="50" y="498" width="275" height="50"/>
|
<rect key="frame" x="50" y="450" width="275" height="50"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="50" id="Bt8-Ou-ht2"/>
|
<constraint firstAttribute="height" constant="50" id="Bt8-Ou-ht2"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
|
|
@ -35,20 +35,20 @@
|
||||||
<rect key="frame" x="177" y="323" width="20" height="20"/>
|
<rect key="frame" x="177" y="323" width="20" height="20"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="249" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="o6f-cL-1PF">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="249" text="" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="o6f-cL-1PF">
|
||||||
<rect key="frame" x="20" y="199" width="335" height="269"/>
|
<rect key="frame" x="20" y="247" width="335" height="173"/>
|
||||||
<accessibility key="accessibilityConfiguration" identifier="resultLabel"/>
|
<accessibility key="accessibilityConfiguration" identifier="resultLabel"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<segmentedControl clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="X8E-G9-9IV">
|
<segmentedControl clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="X8E-G9-9IV">
|
||||||
<rect key="frame" x="117" y="568" width="141" height="29"/>
|
<rect key="frame" x="71" y="568" width="233" height="29"/>
|
||||||
<segments>
|
<segments>
|
||||||
<segment title="Default"/>
|
<segment title="Default Endpoint"/>
|
||||||
<segment title="Alternate"/>
|
<segment title="Alternate"/>
|
||||||
</segments>
|
</segments>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="didPressSegmentedControl:" destination="vXZ-lx-hvc" eventType="valueChanged" id="e5Z-W5-Q1d"/>
|
<action selector="didPressEndpointSegmentedControl:" destination="vXZ-lx-hvc" eventType="valueChanged" id="Sdm-dO-doL"/>
|
||||||
</connections>
|
</connections>
|
||||||
</segmentedControl>
|
</segmentedControl>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Uyt-0M-CR7">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Uyt-0M-CR7">
|
||||||
|
|
@ -76,27 +76,40 @@
|
||||||
<constraint firstItem="mGh-Ox-cFf" firstAttribute="leading" secondItem="awK-8H-OCQ" secondAttribute="trailing" constant="10" id="wzo-g0-VEj"/>
|
<constraint firstItem="mGh-Ox-cFf" firstAttribute="leading" secondItem="awK-8H-OCQ" secondAttribute="trailing" constant="10" id="wzo-g0-VEj"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
|
<segmentedControl clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="kq2-ci-l1u">
|
||||||
|
<rect key="frame" x="114" y="520" width="147" height="29"/>
|
||||||
|
<segments>
|
||||||
|
<segment title="Nil Locale"/>
|
||||||
|
<segment title="Chinese"/>
|
||||||
|
</segments>
|
||||||
|
<connections>
|
||||||
|
<action selector="didPressLocaleSegmentedControl:" destination="vXZ-lx-hvc" eventType="valueChanged" id="nSA-f3-8eS"/>
|
||||||
|
</connections>
|
||||||
|
</segmentedControl>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="o6f-cL-1PF" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="20" id="0dV-t0-W0Y"/>
|
<constraint firstItem="o6f-cL-1PF" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="20" id="0dV-t0-W0Y"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="RDW-bD-rSo" secondAttribute="trailing" constant="50" id="1Lj-Ox-Kl4"/>
|
||||||
|
<constraint firstItem="X8E-G9-9IV" firstAttribute="top" secondItem="kq2-ci-l1u" secondAttribute="bottom" constant="20" id="8ye-XO-bpK"/>
|
||||||
<constraint firstItem="X8E-G9-9IV" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="92w-cw-lR2"/>
|
<constraint firstItem="X8E-G9-9IV" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="92w-cw-lR2"/>
|
||||||
<constraint firstItem="2fi-mo-0CV" firstAttribute="top" secondItem="Uyt-0M-CR7" secondAttribute="bottom" constant="20" id="Bfs-p5-IBM"/>
|
<constraint firstItem="2fi-mo-0CV" firstAttribute="top" secondItem="Uyt-0M-CR7" secondAttribute="bottom" constant="20" id="Bfs-p5-IBM"/>
|
||||||
<constraint firstItem="o6f-cL-1PF" firstAttribute="centerY" secondItem="kh9-bI-dsS" secondAttribute="centerY" priority="750" id="NMD-ir-PXe"/>
|
<constraint firstItem="o6f-cL-1PF" firstAttribute="centerY" secondItem="kh9-bI-dsS" secondAttribute="centerY" priority="750" id="NMD-ir-PXe"/>
|
||||||
<constraint firstItem="X8E-G9-9IV" firstAttribute="top" secondItem="RDW-bD-rSo" secondAttribute="bottom" constant="20" id="Qbw-Jp-xGV"/>
|
|
||||||
<constraint firstItem="RDW-bD-rSo" firstAttribute="top" secondItem="o6f-cL-1PF" secondAttribute="bottom" constant="30" id="TZe-z9-MZS"/>
|
<constraint firstItem="RDW-bD-rSo" firstAttribute="top" secondItem="o6f-cL-1PF" secondAttribute="bottom" constant="30" id="TZe-z9-MZS"/>
|
||||||
<constraint firstItem="jHc-GP-v1Z" firstAttribute="centerY" secondItem="kh9-bI-dsS" secondAttribute="centerY" id="VOe-WJ-FKo"/>
|
<constraint firstItem="jHc-GP-v1Z" firstAttribute="centerY" secondItem="kh9-bI-dsS" secondAttribute="centerY" id="VOe-WJ-FKo"/>
|
||||||
<constraint firstItem="jHc-GP-v1Z" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="XkT-zr-eUO"/>
|
<constraint firstItem="jHc-GP-v1Z" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="XkT-zr-eUO"/>
|
||||||
|
<constraint firstItem="kq2-ci-l1u" firstAttribute="top" secondItem="RDW-bD-rSo" secondAttribute="bottom" constant="20" id="Y4H-6k-gDx"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="o6f-cL-1PF" secondAttribute="trailing" constant="20" id="c74-nm-rgi"/>
|
<constraint firstAttribute="trailing" secondItem="o6f-cL-1PF" secondAttribute="trailing" constant="20" id="c74-nm-rgi"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="RDW-bD-rSo" secondAttribute="trailing" constant="50" id="c7q-Rw-n0F"/>
|
|
||||||
<constraint firstItem="RDW-bD-rSo" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="50" id="iXO-hP-XZ7"/>
|
<constraint firstItem="RDW-bD-rSo" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" constant="50" id="iXO-hP-XZ7"/>
|
||||||
|
<constraint firstItem="kq2-ci-l1u" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="ice-pp-mVe"/>
|
||||||
<constraint firstItem="Uyt-0M-CR7" firstAttribute="top" secondItem="X8E-G9-9IV" secondAttribute="bottom" constant="20" id="nIE-QT-RpQ"/>
|
<constraint firstItem="Uyt-0M-CR7" firstAttribute="top" secondItem="X8E-G9-9IV" secondAttribute="bottom" constant="20" id="nIE-QT-RpQ"/>
|
||||||
<constraint firstItem="Uyt-0M-CR7" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="qfG-68-ySO"/>
|
<constraint firstItem="Uyt-0M-CR7" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="qfG-68-ySO"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
|
<outlet property="endpointSegmentedControl" destination="X8E-G9-9IV" id="jAb-fU-Yu5"/>
|
||||||
<outlet property="label" destination="o6f-cL-1PF" id="KQV-3X-RKr"/>
|
<outlet property="label" destination="o6f-cL-1PF" id="KQV-3X-RKr"/>
|
||||||
<outlet property="segmentedControl" destination="X8E-G9-9IV" id="sS0-3k-Alu"/>
|
<outlet property="localeSegmentedControl" destination="kq2-ci-l1u" id="gL7-du-K6l"/>
|
||||||
<outlet property="spinner" destination="jHc-GP-v1Z" id="gRn-JW-FIK"/>
|
<outlet property="spinner" destination="jHc-GP-v1Z" id="gRn-JW-FIK"/>
|
||||||
<outlet property="visibleChallengeSwitch" destination="mGh-Ox-cFf" id="R13-nD-EXL"/>
|
<outlet property="visibleChallengeSwitch" destination="mGh-Ox-cFf" id="R13-nD-EXL"/>
|
||||||
</connections>
|
</connections>
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"size" : "60x60",
|
"size" : "60x60",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ios-marketing",
|
||||||
|
"size" : "1024x1024",
|
||||||
|
"scale" : "1x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|
|
||||||
|
|
@ -21,23 +21,40 @@ class ViewController: UIViewController {
|
||||||
private var recaptcha: ReCaptcha!
|
private var recaptcha: ReCaptcha!
|
||||||
private var disposeBag = DisposeBag()
|
private var disposeBag = DisposeBag()
|
||||||
|
|
||||||
|
private var locale: Locale?
|
||||||
|
private var endpoint = ReCaptcha.Endpoint.default
|
||||||
|
|
||||||
@IBOutlet private weak var label: UILabel!
|
@IBOutlet private weak var label: UILabel!
|
||||||
@IBOutlet private weak var spinner: UIActivityIndicatorView!
|
@IBOutlet private weak var spinner: UIActivityIndicatorView!
|
||||||
@IBOutlet private weak var segmentedControl: UISegmentedControl!
|
@IBOutlet private weak var localeSegmentedControl: UISegmentedControl!
|
||||||
|
@IBOutlet private weak var endpointSegmentedControl: UISegmentedControl!
|
||||||
@IBOutlet private weak var visibleChallengeSwitch: UISwitch!
|
@IBOutlet private weak var visibleChallengeSwitch: UISwitch!
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
setupReCaptcha(endpoint: .default)
|
setupReCaptcha()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func didPressSegmentedControl(_ sender: UISegmentedControl) {
|
@IBAction func didPressEndpointSegmentedControl(_ sender: UISegmentedControl) {
|
||||||
label.text = ""
|
label.text = ""
|
||||||
switch sender.selectedSegmentIndex {
|
switch sender.selectedSegmentIndex {
|
||||||
case 0: setupReCaptcha(endpoint: .default)
|
case 0: endpoint = .default
|
||||||
case 1: setupReCaptcha(endpoint: .alternate)
|
case 1: endpoint = .alternate
|
||||||
default: assertionFailure("invalid index")
|
default: assertionFailure("invalid index")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupReCaptcha()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func didPressLocaleSegmentedControl(_ sender: UISegmentedControl) {
|
||||||
|
label.text = ""
|
||||||
|
switch sender.selectedSegmentIndex {
|
||||||
|
case 0: locale = nil
|
||||||
|
case 1: locale = Locale(identifier: "zh-CN")
|
||||||
|
default: assertionFailure("invalid index")
|
||||||
|
}
|
||||||
|
|
||||||
|
setupReCaptcha()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func didPressButton(button: UIButton) {
|
@IBAction private func didPressButton(button: UIButton) {
|
||||||
|
|
@ -66,7 +83,7 @@ class ViewController: UIViewController {
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
isEnabled
|
isEnabled
|
||||||
.bind(to: segmentedControl.rx.isEnabled)
|
.bind(to: endpointSegmentedControl.rx.isEnabled)
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
|
|
||||||
validate
|
validate
|
||||||
|
|
@ -89,9 +106,9 @@ class ViewController: UIViewController {
|
||||||
.disposed(by: disposeBag)
|
.disposed(by: disposeBag)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupReCaptcha(endpoint: ReCaptcha.Endpoint) {
|
private func setupReCaptcha() {
|
||||||
// swiftlint:disable:next force_try
|
// swiftlint:disable:next force_try
|
||||||
recaptcha = try! ReCaptcha(endpoint: endpoint)
|
recaptcha = try! ReCaptcha(endpoint: endpoint, locale: locale)
|
||||||
|
|
||||||
recaptcha.configureWebView { [weak self] webview in
|
recaptcha.configureWebView { [weak self] webview in
|
||||||
webview.frame = self?.view.bounds ?? CGRect.zero
|
webview.frame = self?.view.bounds ?? CGRect.zero
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,6 @@ disabled_rules:
|
||||||
- force_unwrapping
|
- force_unwrapping
|
||||||
- explicit_top_level_acl
|
- explicit_top_level_acl
|
||||||
- function_body_length
|
- function_body_length
|
||||||
|
- identifier_name
|
||||||
|
- file_length
|
||||||
|
- type_body_length
|
||||||
|
|
|
||||||
|
|
@ -151,4 +151,47 @@ class DispatchQueue__Tests: XCTestCase {
|
||||||
|
|
||||||
waitForExpectations(timeout: 5)
|
waitForExpectations(timeout: 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Once
|
||||||
|
|
||||||
|
func test__Once__Single_Dispatch() {
|
||||||
|
let token = 3
|
||||||
|
var dispatchCount = 0
|
||||||
|
|
||||||
|
// Does dispatch the given action
|
||||||
|
DispatchQueue.once(token: token) {
|
||||||
|
dispatchCount = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(dispatchCount, 1)
|
||||||
|
|
||||||
|
// Does not dispatch again for the same token
|
||||||
|
DispatchQueue.once(token: token) {
|
||||||
|
dispatchCount = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(dispatchCount, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test__Once__Multiple_Dispatches() {
|
||||||
|
let token1 = 4
|
||||||
|
var didDispatch1 = false
|
||||||
|
|
||||||
|
// Does dispatch the given action
|
||||||
|
DispatchQueue.once(token: token1) {
|
||||||
|
didDispatch1 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(didDispatch1)
|
||||||
|
|
||||||
|
// Dispatch for a different token
|
||||||
|
let token2 = 6
|
||||||
|
var didDispatch2 = false
|
||||||
|
|
||||||
|
DispatchQueue.once(token: token2) {
|
||||||
|
didDispatch2 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(didDispatch2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,34 @@ class ReCaptchaWebViewManager__Tests: XCTestCase {
|
||||||
XCTAssertEqual(count, 1)
|
XCTAssertEqual(count, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test__Configure_Web_View__Called_Again_With_Reset() {
|
||||||
|
let exp0 = expectation(description: "configure webview 0")
|
||||||
|
|
||||||
|
let manager = ReCaptchaWebViewManager(messageBody: "{action: \"showReCaptcha\"}")
|
||||||
|
manager.validate(on: presenterView) { _ in
|
||||||
|
XCTFail("should not call completion")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure Webview
|
||||||
|
manager.configureWebView { _ in
|
||||||
|
manager.webView.evaluateJavaScript("execute();") { XCTAssertNil($1) }
|
||||||
|
exp0.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
|
||||||
|
// Reset and ensure it calls again
|
||||||
|
let exp1 = expectation(description: "configure webview 1")
|
||||||
|
|
||||||
|
manager.configureWebView { _ in
|
||||||
|
manager.webView.evaluateJavaScript("execute();") { XCTAssertNil($1) }
|
||||||
|
exp1.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.reset()
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Stop
|
// MARK: Stop
|
||||||
|
|
||||||
func test__Stop() {
|
func test__Stop() {
|
||||||
|
|
@ -259,7 +287,7 @@ class ReCaptchaWebViewManager__Tests: XCTestCase {
|
||||||
|
|
||||||
func test__Endpoint_Setup() {
|
func test__Endpoint_Setup() {
|
||||||
let exp = expectation(description: "setup endpoint")
|
let exp = expectation(description: "setup endpoint")
|
||||||
let endpoint = ReCaptcha.Endpoint.alternate.url
|
let endpoint = ReCaptcha.Endpoint.alternate.getURL(locale: nil)
|
||||||
var result: ReCaptchaResult?
|
var result: ReCaptchaResult?
|
||||||
|
|
||||||
let manager = ReCaptchaWebViewManager(messageBody: "{token: endpoint}", endpoint: endpoint)
|
let manager = ReCaptchaWebViewManager(messageBody: "{token: endpoint}", endpoint: endpoint)
|
||||||
|
|
@ -338,6 +366,22 @@ class ReCaptchaWebViewManager__Tests: XCTestCase {
|
||||||
XCTAssertEqual(result?.token, apiKey)
|
XCTAssertEqual(result?.token, apiKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test__Validate__Should_Skip_For_Tests() {
|
||||||
|
let exp = expectation(description: "did skip validation")
|
||||||
|
|
||||||
|
let manager = ReCaptchaWebViewManager()
|
||||||
|
manager.shouldSkipForTests = true
|
||||||
|
|
||||||
|
manager.completion = { result in
|
||||||
|
XCTAssertEqual(result.token, "")
|
||||||
|
exp.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.validate(on: presenterView)
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 1)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Force Challenge Visible
|
// MARK: Force Challenge Visible
|
||||||
|
|
||||||
func test__Force_Visible_Challenge() {
|
func test__Force_Visible_Challenge() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// ReCaptcha_Endpoint__.swift
|
||||||
|
// ReCaptcha
|
||||||
|
//
|
||||||
|
// Created by Flávio Caetano on 12/07/18.
|
||||||
|
// Copyright © 2018 ReCaptcha. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
@testable import ReCaptcha
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class ReCaptcha_Endpoint__Tests: XCTestCase {
|
||||||
|
|
||||||
|
private let endpoint = ReCaptcha.Endpoint.default
|
||||||
|
private let endpointURL = "https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
|
||||||
|
|
||||||
|
// MARK: - Locale
|
||||||
|
|
||||||
|
func test__Locale__Nil() {
|
||||||
|
XCTAssertEqual(endpoint.getURL(locale: nil), endpointURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test__Locale__Valid() {
|
||||||
|
let locale = Locale(identifier: "pt-BR")
|
||||||
|
XCTAssertEqual(endpoint.getURL(locale: locale), "\(endpointURL)&hl=pt-BR")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -107,7 +107,7 @@ class ReCaptcha_Rx__Tests: XCTestCase {
|
||||||
let exp = expectation(description: "stop loading")
|
let exp = expectation(description: "stop loading")
|
||||||
|
|
||||||
// Stop
|
// Stop
|
||||||
let recaptcha = ReCaptcha(manager: ReCaptchaWebViewManager(messageBody: "{action: \"showReCaptcha\"}"))
|
let recaptcha = ReCaptcha(manager: ReCaptchaWebViewManager(messageBody: "{log: \"foo\"}"))
|
||||||
recaptcha.configureWebView { _ in
|
recaptcha.configureWebView { _ in
|
||||||
XCTFail("should not ask to configure the webview")
|
XCTFail("should not ask to configure the webview")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,6 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
post({action: "didLoad"});
|
|
||||||
};
|
|
||||||
|
|
||||||
var reset = function() {
|
var reset = function() {
|
||||||
shouldFail = false;
|
shouldFail = false;
|
||||||
post({action: "didLoad"});
|
post({action: "didLoad"});
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ class ReCaptcha_UITests: XCTestCase {
|
||||||
|
|
||||||
func test__Validate__Default_Endpoint() {
|
func test__Validate__Default_Endpoint() {
|
||||||
let app = XCUIApplication()
|
let app = XCUIApplication()
|
||||||
app.segmentedControls.buttons["Default"].tap()
|
app.segmentedControls.buttons["Default Endpoint"].tap()
|
||||||
app.switches["Switch"].tap()
|
app.switches["Switch"].tap()
|
||||||
app.buttons["Validate"].tap()
|
app.buttons["Validate"].tap()
|
||||||
|
|
||||||
|
|
|
||||||
4
Gemfile
4
Gemfile
|
|
@ -1,4 +1,4 @@
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem 'fastlane', '~> 2.75'
|
gem 'fastlane', '~> 2.105.2'
|
||||||
gem 'cocoapods', '~> 1.4'
|
gem 'cocoapods', '~> 1.5.3'
|
||||||
|
|
|
||||||
127
Gemfile.lock
127
Gemfile.lock
|
|
@ -1,7 +1,7 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (2.3.6)
|
CFPropertyList (3.0.0)
|
||||||
activesupport (4.2.10)
|
activesupport (4.2.10)
|
||||||
i18n (~> 0.7)
|
i18n (~> 0.7)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
|
|
@ -9,14 +9,15 @@ GEM
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.5.2)
|
addressable (2.5.2)
|
||||||
public_suffix (>= 2.0.2, < 4.0)
|
public_suffix (>= 2.0.2, < 4.0)
|
||||||
|
atomos (0.1.3)
|
||||||
babosa (1.0.2)
|
babosa (1.0.2)
|
||||||
claide (1.0.2)
|
claide (1.0.2)
|
||||||
cocoapods (1.4.0)
|
cocoapods (1.5.3)
|
||||||
activesupport (>= 4.0.2, < 5)
|
activesupport (>= 4.0.2, < 5)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
cocoapods-core (= 1.4.0)
|
cocoapods-core (= 1.5.3)
|
||||||
cocoapods-deintegrate (>= 1.0.2, < 2.0)
|
cocoapods-deintegrate (>= 1.0.2, < 2.0)
|
||||||
cocoapods-downloader (>= 1.1.3, < 2.0)
|
cocoapods-downloader (>= 1.2.0, < 2.0)
|
||||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||||
cocoapods-search (>= 1.0.0, < 2.0)
|
cocoapods-search (>= 1.0.0, < 2.0)
|
||||||
cocoapods-stats (>= 1.0.0, < 2.0)
|
cocoapods-stats (>= 1.0.0, < 2.0)
|
||||||
|
|
@ -26,59 +27,61 @@ GEM
|
||||||
escape (~> 0.0.4)
|
escape (~> 0.0.4)
|
||||||
fourflusher (~> 2.0.1)
|
fourflusher (~> 2.0.1)
|
||||||
gh_inspector (~> 1.0)
|
gh_inspector (~> 1.0)
|
||||||
molinillo (~> 0.6.4)
|
molinillo (~> 0.6.5)
|
||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
ruby-macho (~> 1.1)
|
ruby-macho (~> 1.1)
|
||||||
xcodeproj (>= 1.5.4, < 2.0)
|
xcodeproj (>= 1.5.7, < 2.0)
|
||||||
cocoapods-core (1.4.0)
|
cocoapods-core (1.5.3)
|
||||||
activesupport (>= 4.0.2, < 6)
|
activesupport (>= 4.0.2, < 6)
|
||||||
fuzzy_match (~> 2.0.4)
|
fuzzy_match (~> 2.0.4)
|
||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
cocoapods-deintegrate (1.0.2)
|
cocoapods-deintegrate (1.0.2)
|
||||||
cocoapods-downloader (1.1.3)
|
cocoapods-downloader (1.2.1)
|
||||||
cocoapods-plugins (1.0.0)
|
cocoapods-plugins (1.0.0)
|
||||||
nap
|
nap
|
||||||
cocoapods-search (1.0.0)
|
cocoapods-search (1.0.0)
|
||||||
cocoapods-stats (1.0.0)
|
cocoapods-stats (1.0.0)
|
||||||
cocoapods-trunk (1.3.0)
|
cocoapods-trunk (1.3.1)
|
||||||
nap (>= 0.8, < 2.0)
|
nap (>= 0.8, < 2.0)
|
||||||
netrc (~> 0.11)
|
netrc (~> 0.11)
|
||||||
cocoapods-try (1.1.0)
|
cocoapods-try (1.1.0)
|
||||||
colored (1.2)
|
colored (1.2)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
commander-fastlane (4.4.5)
|
commander-fastlane (4.4.6)
|
||||||
highline (~> 1.7.2)
|
highline (~> 1.7.2)
|
||||||
concurrent-ruby (1.0.5)
|
concurrent-ruby (1.0.5)
|
||||||
declarative (0.0.10)
|
declarative (0.0.10)
|
||||||
declarative-option (0.1.0)
|
declarative-option (0.1.0)
|
||||||
domain_name (0.5.20170404)
|
domain_name (0.5.20180417)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
dotenv (2.2.1)
|
dotenv (2.5.0)
|
||||||
|
emoji_regex (0.1.1)
|
||||||
escape (0.0.4)
|
escape (0.0.4)
|
||||||
excon (0.60.0)
|
excon (0.62.0)
|
||||||
faraday (0.13.1)
|
faraday (0.15.3)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
faraday-cookie_jar (0.0.6)
|
faraday-cookie_jar (0.0.6)
|
||||||
faraday (>= 0.7.4)
|
faraday (>= 0.7.4)
|
||||||
http-cookie (~> 1.0.0)
|
http-cookie (~> 1.0.0)
|
||||||
faraday_middleware (0.12.2)
|
faraday_middleware (0.12.2)
|
||||||
faraday (>= 0.7.4, < 1.0)
|
faraday (>= 0.7.4, < 1.0)
|
||||||
fastimage (2.1.1)
|
fastimage (2.1.4)
|
||||||
fastlane (2.75.1)
|
fastlane (2.105.2)
|
||||||
CFPropertyList (>= 2.3, < 3.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.3, < 3.0.0)
|
addressable (>= 2.3, < 3.0.0)
|
||||||
babosa (>= 1.0.2, < 2.0.0)
|
babosa (>= 1.0.2, < 2.0.0)
|
||||||
bundler (>= 1.12.0, < 2.0.0)
|
bundler (>= 1.12.0, < 2.0.0)
|
||||||
colored
|
colored
|
||||||
commander-fastlane (>= 4.4.5, < 5.0.0)
|
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||||
dotenv (>= 2.1.1, < 3.0.0)
|
dotenv (>= 2.1.1, < 3.0.0)
|
||||||
|
emoji_regex (~> 0.1)
|
||||||
excon (>= 0.45.0, < 1.0.0)
|
excon (>= 0.45.0, < 1.0.0)
|
||||||
faraday (~> 0.9)
|
faraday (~> 0.9)
|
||||||
faraday-cookie_jar (~> 0.0.6)
|
faraday-cookie_jar (~> 0.0.6)
|
||||||
faraday_middleware (~> 0.9)
|
faraday_middleware (~> 0.9)
|
||||||
fastimage (>= 2.1.0, < 3.0.0)
|
fastimage (>= 2.1.0, < 3.0.0)
|
||||||
gh_inspector (>= 1.0.1, < 2.0.0)
|
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||||
google-api-client (>= 0.13.1, < 0.14.0)
|
google-api-client (>= 0.21.2, < 0.24.0)
|
||||||
highline (>= 1.7.2, < 2.0.0)
|
highline (>= 1.7.2, < 2.0.0)
|
||||||
json (< 3.0.0)
|
json (< 3.0.0)
|
||||||
mini_magick (~> 4.5.1)
|
mini_magick (~> 4.5.1)
|
||||||
|
|
@ -87,100 +90,102 @@ GEM
|
||||||
multipart-post (~> 2.0.0)
|
multipart-post (~> 2.0.0)
|
||||||
plist (>= 3.1.0, < 4.0.0)
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
public_suffix (~> 2.0.0)
|
public_suffix (~> 2.0.0)
|
||||||
rubyzip (>= 1.1.0, < 2.0.0)
|
rubyzip (>= 1.2.2, < 2.0.0)
|
||||||
security (= 0.1.3)
|
security (= 0.1.3)
|
||||||
slack-notifier (>= 1.3, < 2.0.0)
|
simctl (~> 1.6.3)
|
||||||
|
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||||
terminal-notifier (>= 1.6.2, < 2.0.0)
|
terminal-notifier (>= 1.6.2, < 2.0.0)
|
||||||
terminal-table (>= 1.4.5, < 2.0.0)
|
terminal-table (>= 1.4.5, < 2.0.0)
|
||||||
tty-screen (>= 0.6.3, < 1.0.0)
|
tty-screen (>= 0.6.3, < 1.0.0)
|
||||||
tty-spinner (>= 0.7.0, < 1.0.0)
|
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||||
word_wrap (~> 1.0.0)
|
word_wrap (~> 1.0.0)
|
||||||
xcodeproj (>= 1.5.2, < 2.0.0)
|
xcodeproj (>= 1.6.0, < 2.0.0)
|
||||||
xcpretty (>= 0.2.4, < 1.0.0)
|
xcpretty (~> 0.3.0)
|
||||||
xcpretty-travis-formatter (>= 0.0.3)
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
fourflusher (2.0.1)
|
fourflusher (2.0.1)
|
||||||
fuzzy_match (2.0.4)
|
fuzzy_match (2.0.4)
|
||||||
gh_inspector (1.0.3)
|
gh_inspector (1.1.3)
|
||||||
google-api-client (0.13.6)
|
google-api-client (0.23.9)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
googleauth (~> 0.5)
|
googleauth (>= 0.5, < 0.7.0)
|
||||||
httpclient (>= 2.8.1, < 3.0)
|
httpclient (>= 2.8.1, < 3.0)
|
||||||
mime-types (~> 3.0)
|
mime-types (~> 3.0)
|
||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.0)
|
retriable (>= 2.0, < 4.0)
|
||||||
googleauth (0.6.2)
|
signet (~> 0.9)
|
||||||
|
googleauth (0.6.6)
|
||||||
faraday (~> 0.12)
|
faraday (~> 0.12)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
logging (~> 2.0)
|
|
||||||
memoist (~> 0.12)
|
memoist (~> 0.12)
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
os (~> 0.9)
|
os (>= 0.9, < 2.0)
|
||||||
signet (~> 0.7)
|
signet (~> 0.7)
|
||||||
highline (1.7.10)
|
highline (1.7.10)
|
||||||
http-cookie (1.0.3)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (0.9.1)
|
i18n (0.9.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
json (2.1.0)
|
json (2.1.0)
|
||||||
jwt (2.1.0)
|
jwt (2.1.0)
|
||||||
little-plugger (1.1.4)
|
|
||||||
logging (2.2.2)
|
|
||||||
little-plugger (~> 1.1)
|
|
||||||
multi_json (~> 1.10)
|
|
||||||
memoist (0.16.0)
|
memoist (0.16.0)
|
||||||
mime-types (3.1)
|
mime-types (3.2.2)
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2016.0521)
|
mime-types-data (3.2018.0812)
|
||||||
mini_magick (4.5.1)
|
mini_magick (4.5.1)
|
||||||
minitest (5.11.1)
|
minitest (5.11.3)
|
||||||
molinillo (0.6.4)
|
molinillo (0.6.6)
|
||||||
multi_json (1.13.0)
|
multi_json (1.13.1)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
multipart-post (2.0.0)
|
multipart-post (2.0.0)
|
||||||
nanaimo (0.2.3)
|
nanaimo (0.2.6)
|
||||||
nap (1.1.0)
|
nap (1.1.0)
|
||||||
|
naturally (2.2.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
os (0.9.6)
|
os (1.0.0)
|
||||||
plist (3.4.0)
|
plist (3.4.0)
|
||||||
public_suffix (2.0.5)
|
public_suffix (2.0.5)
|
||||||
representable (3.0.4)
|
representable (3.0.4)
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
declarative-option (< 0.2.0)
|
declarative-option (< 0.2.0)
|
||||||
uber (< 0.2.0)
|
uber (< 0.2.0)
|
||||||
retriable (3.1.1)
|
retriable (3.1.2)
|
||||||
rouge (2.0.7)
|
rouge (2.0.7)
|
||||||
ruby-macho (1.1.0)
|
ruby-macho (1.2.0)
|
||||||
rubyzip (1.2.1)
|
rubyzip (1.2.2)
|
||||||
security (0.1.3)
|
security (0.1.3)
|
||||||
signet (0.8.1)
|
signet (0.10.0)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
faraday (~> 0.9)
|
faraday (~> 0.9)
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 3.0)
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
slack-notifier (1.5.1)
|
simctl (1.6.5)
|
||||||
|
CFPropertyList
|
||||||
|
naturally
|
||||||
|
slack-notifier (2.3.2)
|
||||||
terminal-notifier (1.8.0)
|
terminal-notifier (1.8.0)
|
||||||
terminal-table (1.8.0)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tty-cursor (0.5.0)
|
tty-cursor (0.6.0)
|
||||||
tty-screen (0.6.4)
|
tty-screen (0.6.5)
|
||||||
tty-spinner (0.7.0)
|
tty-spinner (0.8.0)
|
||||||
tty-cursor (>= 0.5.0)
|
tty-cursor (>= 0.5.0)
|
||||||
tzinfo (1.2.4)
|
tzinfo (1.2.5)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.7.4)
|
unf_ext (0.0.7.5)
|
||||||
unicode-display_width (1.3.0)
|
unicode-display_width (1.4.0)
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
xcodeproj (1.5.4)
|
xcodeproj (1.6.0)
|
||||||
CFPropertyList (~> 2.3.3)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
|
atomos (~> 0.1.3)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
nanaimo (~> 0.2.3)
|
nanaimo (~> 0.2.6)
|
||||||
xcpretty (0.2.8)
|
xcpretty (0.3.0)
|
||||||
rouge (~> 2.0.7)
|
rouge (~> 2.0.7)
|
||||||
xcpretty-travis-formatter (1.0.0)
|
xcpretty-travis-formatter (1.0.0)
|
||||||
xcpretty (~> 0.2, >= 0.0.7)
|
xcpretty (~> 0.2, >= 0.0.7)
|
||||||
|
|
@ -189,8 +194,8 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
cocoapods (~> 1.4)
|
cocoapods (~> 1.5.3)
|
||||||
fastlane (~> 2.75)
|
fastlane (~> 2.105.2)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.16.0
|
1.16.1
|
||||||
|
|
|
||||||
22
README.md
22
README.md
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
-----
|
-----
|
||||||
|
|
||||||
Add Google's [Invisible ReCaptcha](https://developers.google.com/recaptcha/docs/invisible) to your project. This library
|
Add Google's [Invisible ReCaptcha v2](https://developers.google.com/recaptcha/docs/invisible) to your project. This library
|
||||||
automatically handles ReCaptcha's events and retrieves the validation token or notifies you to present the challenge if
|
automatically handles ReCaptcha's events and retrieves the validation token or notifies you to present the challenge if
|
||||||
invisibility is not possible.
|
invisibility is not possible.
|
||||||
|
|
||||||
|
|
@ -17,8 +17,15 @@ invisibility is not possible.
|
||||||
|
|
||||||
#### _Warning_ ⚠️
|
#### _Warning_ ⚠️
|
||||||
|
|
||||||
Beware that this library only works for Invisible ReCaptcha keys! Make sure to check the Invisible reCAPTCHA option
|
Beware that this library only works for ReCaptcha v2 Invisible keys! Make sure to check the reCAPTCHA
|
||||||
when creating your [API Key](https://www.google.com/recaptcha/admin).
|
v2 Invisible badge option when creating your [API Key](https://www.google.com/recaptcha/admin/create).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
You won't be able to use a ReCaptcha v3 key because it requires server-side validation. On v3, all
|
||||||
|
challenges succeed into a token which is then validated in the server for a score. For this reason,
|
||||||
|
a frontend app can't know on its own wether or not a user is valid since the challenge will always
|
||||||
|
result in a valid token.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -42,7 +49,7 @@ extension for the ReCaptcha framework.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Simply add `ReCaptchaKey` and `ReCaptchaDomain` (with a protocol) to your Info.plist and run:
|
Simply add `ReCaptchaKey` and `ReCaptchaDomain` (with a protocol ex. http:// or https://) to your Info.plist and run:
|
||||||
|
|
||||||
``` swift
|
``` swift
|
||||||
let recaptcha = try? ReCaptcha()
|
let recaptcha = try? ReCaptcha()
|
||||||
|
|
@ -85,6 +92,13 @@ public enum Endpoint {
|
||||||
let recaptcha = try? ReCaptcha(endpoint: .alternate) // Defaults to `default` when unset
|
let recaptcha = try? ReCaptcha(endpoint: .alternate) // Defaults to `default` when unset
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Help Wanted
|
||||||
|
|
||||||
|
Do you love ReCaptcha and work actively on apps that use it? We'd love if you could help us keep improving it!
|
||||||
|
Feel free to message us or to start contributing right away!
|
||||||
|
|
||||||
|
## [Full Documentation](http://fjcaetano.github.io/ReCaptcha)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
ReCaptcha is available under the MIT license. See the LICENSE file for more info.
|
ReCaptcha is available under the MIT license. See the LICENSE file for more info.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
F206BAB51F8D3DE900A25807 /* ReCaptcha-Carthage.h in Headers */ = {isa = PBXBuildFile; fileRef = F206BAB31F8D3DE900A25807 /* ReCaptcha-Carthage.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
F206BAB51F8D3DE900A25807 /* ReCaptcha-Carthage.h in Headers */ = {isa = PBXBuildFile; fileRef = F206BAB31F8D3DE900A25807 /* ReCaptcha-Carthage.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
F206BAD31F8D3E7600A25807 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F206BAD21F8D3E7600A25807 /* Result.framework */; };
|
|
||||||
F206BB1D1F8D4DBC00A25807 /* ReCaptcha_RxSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = F206BB1B1F8D4DBC00A25807 /* ReCaptcha_RxSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
F206BB1D1F8D4DBC00A25807 /* ReCaptcha_RxSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = F206BB1B1F8D4DBC00A25807 /* ReCaptcha_RxSwift.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
F206BB221F8D4DDF00A25807 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F206BB121F8D4D1400A25807 /* RxSwift.framework */; };
|
F206BB221F8D4DDF00A25807 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F206BB121F8D4D1400A25807 /* RxSwift.framework */; };
|
||||||
F231B39A1FEC51C800F82943 /* DispatchQueue+Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F231B3991FEC51C800F82943 /* DispatchQueue+Throttle.swift */; };
|
F231B39A1FEC51C800F82943 /* DispatchQueue+Throttle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F231B3991FEC51C800F82943 /* DispatchQueue+Throttle.swift */; };
|
||||||
|
|
@ -19,7 +18,6 @@
|
||||||
F24EA1E51F968403001DEC17 /* ReCaptchaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24EA1DB1F9683F5001DEC17 /* ReCaptchaError.swift */; };
|
F24EA1E51F968403001DEC17 /* ReCaptchaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24EA1DB1F9683F5001DEC17 /* ReCaptchaError.swift */; };
|
||||||
F24EA1E61F968403001DEC17 /* ReCaptcha.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24EA1DE1F9683F5001DEC17 /* ReCaptcha.swift */; };
|
F24EA1E61F968403001DEC17 /* ReCaptcha.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24EA1DE1F9683F5001DEC17 /* ReCaptcha.swift */; };
|
||||||
F24EA1E71F968406001DEC17 /* recaptcha.html in Resources */ = {isa = PBXBuildFile; fileRef = F24EA1E01F9683F5001DEC17 /* recaptcha.html */; };
|
F24EA1E71F968406001DEC17 /* recaptcha.html in Resources */ = {isa = PBXBuildFile; fileRef = F24EA1E01F9683F5001DEC17 /* recaptcha.html */; };
|
||||||
F255FBEC1F8D5654002F5FA8 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F255FBEB1F8D5654002F5FA8 /* Result.framework */; };
|
|
||||||
F2AE8614204F3B42002E28D7 /* ReCaptchaResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AE8613204F3B41002E28D7 /* ReCaptchaResult.swift */; };
|
F2AE8614204F3B42002E28D7 /* ReCaptchaResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2AE8613204F3B41002E28D7 /* ReCaptchaResult.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
|
@ -38,7 +36,6 @@
|
||||||
F206BAB31F8D3DE900A25807 /* ReCaptcha-Carthage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReCaptcha-Carthage.h"; sourceTree = "<group>"; };
|
F206BAB31F8D3DE900A25807 /* ReCaptcha-Carthage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReCaptcha-Carthage.h"; sourceTree = "<group>"; };
|
||||||
F206BAB41F8D3DE900A25807 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
F206BAB41F8D3DE900A25807 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
F206BACF1F8D3E2800A25807 /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Cartfile; sourceTree = "<group>"; };
|
F206BACF1F8D3E2800A25807 /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Cartfile; sourceTree = "<group>"; };
|
||||||
F206BAD21F8D3E7600A25807 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/iOS/Result.framework; sourceTree = "<group>"; };
|
|
||||||
F206BB121F8D4D1400A25807 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = "<group>"; };
|
F206BB121F8D4D1400A25807 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = "<group>"; };
|
||||||
F206BB191F8D4DBC00A25807 /* ReCaptcha_RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReCaptcha_RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
F206BB191F8D4DBC00A25807 /* ReCaptcha_RxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReCaptcha_RxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
F206BB1B1F8D4DBC00A25807 /* ReCaptcha_RxSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReCaptcha_RxSwift.h; sourceTree = "<group>"; };
|
F206BB1B1F8D4DBC00A25807 /* ReCaptcha_RxSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReCaptcha_RxSwift.h; sourceTree = "<group>"; };
|
||||||
|
|
@ -51,7 +48,6 @@
|
||||||
F24EA1DD1F9683F5001DEC17 /* ReCaptcha+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReCaptcha+Rx.swift"; sourceTree = "<group>"; };
|
F24EA1DD1F9683F5001DEC17 /* ReCaptcha+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReCaptcha+Rx.swift"; sourceTree = "<group>"; };
|
||||||
F24EA1DE1F9683F5001DEC17 /* ReCaptcha.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReCaptcha.swift; sourceTree = "<group>"; };
|
F24EA1DE1F9683F5001DEC17 /* ReCaptcha.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReCaptcha.swift; sourceTree = "<group>"; };
|
||||||
F24EA1E01F9683F5001DEC17 /* recaptcha.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = recaptcha.html; sourceTree = "<group>"; };
|
F24EA1E01F9683F5001DEC17 /* recaptcha.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = recaptcha.html; sourceTree = "<group>"; };
|
||||||
F255FBEB1F8D5654002F5FA8 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = "../ReCaptcha Carthage Test/Carthage/Build/iOS/Result.framework"; sourceTree = "<group>"; };
|
|
||||||
F2AE8613204F3B41002E28D7 /* ReCaptchaResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReCaptchaResult.swift; sourceTree = "<group>"; };
|
F2AE8613204F3B41002E28D7 /* ReCaptchaResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReCaptchaResult.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
|
@ -60,7 +56,6 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
F206BAD31F8D3E7600A25807 /* Result.framework in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -69,7 +64,6 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
F206BB221F8D4DDF00A25807 /* RxSwift.framework in Frameworks */,
|
F206BB221F8D4DDF00A25807 /* RxSwift.framework in Frameworks */,
|
||||||
F255FBEC1F8D5654002F5FA8 /* Result.framework in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -110,9 +104,7 @@
|
||||||
F206BAD11F8D3E7600A25807 /* Frameworks */ = {
|
F206BAD11F8D3E7600A25807 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
F255FBEB1F8D5654002F5FA8 /* Result.framework */,
|
|
||||||
F206BB121F8D4D1400A25807 /* RxSwift.framework */,
|
F206BB121F8D4D1400A25807 /* RxSwift.framework */,
|
||||||
F206BAD21F8D3E7600A25807 /* Result.framework */,
|
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -375,6 +367,7 @@
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 4.2;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
VERSION_INFO_PREFIX = "";
|
VERSION_INFO_PREFIX = "";
|
||||||
|
|
@ -427,6 +420,7 @@
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
|
SWIFT_VERSION = 4.2;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
|
@ -452,7 +446,7 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.flaviocaetano.ReCaptcha;
|
PRODUCT_BUNDLE_IDENTIFIER = com.flaviocaetano.ReCaptcha;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_VERSION = 4.2;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
|
@ -474,7 +468,7 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.flaviocaetano.ReCaptcha;
|
PRODUCT_BUNDLE_IDENTIFIER = com.flaviocaetano.ReCaptcha;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_VERSION = 4.2;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
|
@ -498,7 +492,7 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-RxSwift";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-RxSwift";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_VERSION = 4.2;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
|
|
@ -522,7 +516,7 @@
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-RxSwift";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.ReCaptcha-RxSwift";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_VERSION = 4.2;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
|
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'ReCaptcha'
|
s.name = 'ReCaptcha'
|
||||||
s.version = '1.2'
|
s.version = '1.4.2'
|
||||||
s.summary = 'ReCaptcha for iOS'
|
s.summary = 'ReCaptcha for iOS'
|
||||||
|
s.swift_version = '4.2'
|
||||||
|
|
||||||
s.description = <<-DESC
|
s.description = <<-DESC
|
||||||
Add Google's [Invisible ReCaptcha](https://developers.google.com/recaptcha/docs/invisible) to your project. This library
|
Add Google's [Invisible ReCaptcha](https://developers.google.com/recaptcha/docs/invisible) to your project. This library
|
||||||
automatically handles ReCaptcha's events and retrieves the validation token or notifies you to present the challenge if
|
automatically handles ReCaptcha's events and retrieves the validation token or notifies you to present the challenge if
|
||||||
invisibility is not possible.
|
invisibility is not possible.
|
||||||
DESC
|
DESC
|
||||||
|
|
||||||
s.homepage = 'https://github.com/fjcaetano/ReCaptcha'
|
s.homepage = 'https://github.com/fjcaetano/ReCaptcha'
|
||||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||||
s.author = { 'Flávio Caetano' => 'flavio@vieiracaetano.com' }
|
s.author = { 'Flávio Caetano' => 'flavio@vieiracaetano.com' }
|
||||||
s.source = { :git => 'https://github.com/fjcaetano/ReCaptcha.git', :tag => s.version.to_s }
|
s.source = { :git => 'https://github.com/fjcaetano/ReCaptcha.git', :tag => s.version.to_s }
|
||||||
s.social_media_url = 'https://twitter.com/flavio_caetano'
|
s.social_media_url = 'https://twitter.com/flavio_caetano'
|
||||||
|
s.documentation_url = 'http://fjcaetano.github.io/ReCaptcha'
|
||||||
|
|
||||||
s.ios.deployment_target = '8.0'
|
s.ios.deployment_target = '8.0'
|
||||||
s.default_subspecs = 'Core'
|
s.default_subspecs = 'Core'
|
||||||
|
|
@ -31,6 +33,6 @@ invisibility is not possible.
|
||||||
s.subspec 'RxSwift' do |rx|
|
s.subspec 'RxSwift' do |rx|
|
||||||
rx.source_files = 'ReCaptcha/Classes/Rx/**/*'
|
rx.source_files = 'ReCaptcha/Classes/Rx/**/*'
|
||||||
rx.dependency 'ReCaptcha/Core'
|
rx.dependency 'ReCaptcha/Core'
|
||||||
rx.dependency 'RxSwift', '~> 4.0'
|
rx.dependency 'RxSwift', '~> 4.3'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ extension DispatchQueue {
|
||||||
/// Stores the last call times for a given context
|
/// Stores the last call times for a given context
|
||||||
private static var lastDebounceCallTimes = [AnyHashable: DispatchTime]()
|
private static var lastDebounceCallTimes = [AnyHashable: DispatchTime]()
|
||||||
|
|
||||||
|
/// Dispatched actions' token storage
|
||||||
|
private static var onceTokenStorage = Set<AnyHashable>()
|
||||||
|
|
||||||
/// An object representing a context if none is given
|
/// An object representing a context if none is given
|
||||||
private static let nilContext = UUID()
|
private static let nilContext = UUID()
|
||||||
|
|
||||||
|
|
@ -61,4 +64,21 @@ extension DispatchQueue {
|
||||||
DispatchQueue.lastDebounceCallTimes.removeValue(forKey: context)
|
DispatchQueue.lastDebounceCallTimes.removeValue(forKey: context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- parameters:
|
||||||
|
- token: The control token for each dispatched action
|
||||||
|
- action: The closure to be executed
|
||||||
|
|
||||||
|
Dispatch the action only once for each given token
|
||||||
|
*/
|
||||||
|
static func once(token: AnyHashable, action: () -> Void) {
|
||||||
|
guard !onceTokenStorage.contains(token) else { return }
|
||||||
|
|
||||||
|
defer { objc_sync_exit(self) }
|
||||||
|
objc_sync_enter(self)
|
||||||
|
|
||||||
|
onceTokenStorage.insert(token)
|
||||||
|
action()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import WebKit
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
public class ReCaptcha {
|
public class ReCaptcha {
|
||||||
|
public typealias BoolParameterClosure = (Bool) -> ()
|
||||||
|
|
||||||
fileprivate struct Constants {
|
fileprivate struct Constants {
|
||||||
struct InfoDictKeys {
|
struct InfoDictKeys {
|
||||||
static let APIKey = "ReCaptchaKey"
|
static let APIKey = "ReCaptchaKey"
|
||||||
|
|
@ -30,10 +32,14 @@ public class ReCaptcha {
|
||||||
/// Alternate endpoint. Points to https://www.recaptcha.net/recaptcha/api.js
|
/// Alternate endpoint. Points to https://www.recaptcha.net/recaptcha/api.js
|
||||||
case alternate
|
case alternate
|
||||||
|
|
||||||
internal var url: String {
|
internal func getURL(locale: Locale?) -> String {
|
||||||
|
let localeAppendix = locale.map { "&hl=\($0.identifier)" } ?? ""
|
||||||
switch self {
|
switch self {
|
||||||
case .default: return "https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
|
case .default:
|
||||||
case .alternate: return "https://www.recaptcha.net/recaptcha/api.js?onload=onloadCallback&render=explicit"
|
return "https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" + localeAppendix
|
||||||
|
case .alternate:
|
||||||
|
return "https://www.recaptcha.net/recaptcha/api.js?onload=onloadCallback&render=explicit"
|
||||||
|
+ localeAppendix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +103,12 @@ public class ReCaptcha {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Callback for WebView loading state changing
|
||||||
|
public var onLoadingChanged: BoolParameterClosure? {
|
||||||
|
get { return manager.onLoadingChanged }
|
||||||
|
set { manager.onLoadingChanged = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
/// The worker that handles webview events and communication
|
/// The worker that handles webview events and communication
|
||||||
let manager: ReCaptchaWebViewManager
|
let manager: ReCaptchaWebViewManager
|
||||||
|
|
||||||
|
|
@ -106,6 +118,7 @@ public class ReCaptcha {
|
||||||
- infoPlistKey: The API key retrived from the application's Info.plist
|
- infoPlistKey: The API key retrived from the application's Info.plist
|
||||||
- baseURL: The base URL sent to the ReCaptcha init
|
- baseURL: The base URL sent to the ReCaptcha init
|
||||||
- infoPlistURL: The base URL retrieved from the application's Info.plist
|
- infoPlistURL: The base URL retrieved from the application's Info.plist
|
||||||
|
- locale: A locale value to translate ReCaptcha into a different language
|
||||||
|
|
||||||
Initializes a ReCaptcha object
|
Initializes a ReCaptcha object
|
||||||
|
|
||||||
|
|
@ -121,7 +134,12 @@ public class ReCaptcha {
|
||||||
Info.plist.
|
Info.plist.
|
||||||
- Throws: Rethrows any exceptions thrown by `String(contentsOfFile:)`
|
- Throws: Rethrows any exceptions thrown by `String(contentsOfFile:)`
|
||||||
*/
|
*/
|
||||||
public convenience init(apiKey: String? = nil, baseURL: URL? = nil, endpoint: Endpoint = .default) throws {
|
public convenience init(
|
||||||
|
apiKey: String? = nil,
|
||||||
|
baseURL: URL? = nil,
|
||||||
|
endpoint: Endpoint = .default,
|
||||||
|
locale: Locale? = nil
|
||||||
|
) throws {
|
||||||
let infoDict = Bundle.main.infoDictionary
|
let infoDict = Bundle.main.infoDictionary
|
||||||
|
|
||||||
let plistApiKey = infoDict?[Constants.InfoDictKeys.APIKey] as? String
|
let plistApiKey = infoDict?[Constants.InfoDictKeys.APIKey] as? String
|
||||||
|
|
@ -133,7 +151,7 @@ public class ReCaptcha {
|
||||||
html: config.html,
|
html: config.html,
|
||||||
apiKey: config.apiKey,
|
apiKey: config.apiKey,
|
||||||
baseURL: config.baseURL,
|
baseURL: config.baseURL,
|
||||||
endpoint: endpoint.url
|
endpoint: endpoint.getURL(locale: locale)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,12 +207,26 @@ public class ReCaptcha {
|
||||||
manager.reset()
|
manager.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Development
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
/// Forces the challenge to be explicitly displayed.
|
/// Forces the challenge widget to be explicitly displayed.
|
||||||
public var forceVisibleChallenge: Bool {
|
public var forceVisibleChallenge: Bool {
|
||||||
get { return manager.forceVisibleChallenge }
|
get { return manager.forceVisibleChallenge }
|
||||||
set { manager.forceVisibleChallenge = newValue }
|
set { manager.forceVisibleChallenge = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Allows validation stubbing for testing
|
||||||
|
|
||||||
|
When this property is set to `true`, every call to `validate()` will immediately be resolved with `.token("")`.
|
||||||
|
|
||||||
|
Use only when testing your application.
|
||||||
|
*/
|
||||||
|
public var shouldSkipForTests: Bool {
|
||||||
|
get { return manager.shouldSkipForTests }
|
||||||
|
set { manager.shouldSkipForTests = newValue }
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,88 +13,18 @@ import WebKit
|
||||||
/** Handles comunications with the webview containing the ReCaptcha challenge.
|
/** Handles comunications with the webview containing the ReCaptcha challenge.
|
||||||
*/
|
*/
|
||||||
internal class ReCaptchaWebViewManager {
|
internal class ReCaptchaWebViewManager {
|
||||||
/** The `webView` delegate object that performs execution uppon script loading
|
|
||||||
*/
|
|
||||||
fileprivate class WebViewDelegate: NSObject, WKNavigationDelegate {
|
|
||||||
struct Constants {
|
|
||||||
/// The host that loaded requests should have
|
|
||||||
static let apiURLHost = "www.google.com"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The parent manager
|
|
||||||
private weak var manager: ReCaptchaWebViewManager?
|
|
||||||
|
|
||||||
/// The active requests' urls
|
|
||||||
private var activeRequests = Set<String>(minimumCapacity: 0)
|
|
||||||
|
|
||||||
/// - parameter manager: The parent manager
|
|
||||||
init(manager: ReCaptchaWebViewManager) {
|
|
||||||
self.manager = manager
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
- parameters:
|
|
||||||
- webView: The web view invoking the delegate method.
|
|
||||||
- navigationAction: Descriptive information about the action triggering the navigation request.
|
|
||||||
- decisionHandler: The decision handler to call to allow or cancel the navigation. The argument is one of
|
|
||||||
the constants of the enumerated type WKNavigationActionPolicy.
|
|
||||||
|
|
||||||
Decides whether to allow or cancel a navigation.
|
|
||||||
*/
|
|
||||||
func webView(
|
|
||||||
_ webView: WKWebView,
|
|
||||||
decidePolicyFor navigationAction: WKNavigationAction,
|
|
||||||
decisionHandler: @escaping (WKNavigationActionPolicy
|
|
||||||
) -> Void) {
|
|
||||||
defer { decisionHandler(.allow) }
|
|
||||||
|
|
||||||
if let url = navigationAction.request.url, url.host == Constants.apiURLHost {
|
|
||||||
activeRequests.insert(url.absoluteString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
- parameters:
|
|
||||||
- webView: The web view invoking the delegate method.
|
|
||||||
- navigationResponse: Descriptive information about the navigation response.
|
|
||||||
- decisionHandler: A block to be called when your app has decided whether to allow or cancel the navigation
|
|
||||||
|
|
||||||
Decides whether to allow or cancel a navigation after its response is known.
|
|
||||||
*/
|
|
||||||
func webView(
|
|
||||||
_ webView: WKWebView,
|
|
||||||
decidePolicyFor navigationResponse: WKNavigationResponse,
|
|
||||||
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void
|
|
||||||
) {
|
|
||||||
defer { decisionHandler(.allow) }
|
|
||||||
guard let url = navigationResponse.response.url?.absoluteString,
|
|
||||||
activeRequests.remove(url) != nil, activeRequests.isEmpty else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
execute()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flag the requests as finished and call ReCaptcha execution if necessary
|
|
||||||
func execute() {
|
|
||||||
guard manager?.didFinishLoading != true else { return }
|
|
||||||
|
|
||||||
DispatchQueue.main.throttle(deadline: .now() + 1, context: self) { [weak self] in
|
|
||||||
// Did finish loading the ReCaptcha JS source
|
|
||||||
self?.manager?.didFinishLoading = true
|
|
||||||
|
|
||||||
if self?.manager?.completion != nil {
|
|
||||||
// User has requested for validation
|
|
||||||
self?.manager?.execute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate struct Constants {
|
fileprivate struct Constants {
|
||||||
static let ExecuteJSCommand = "execute();"
|
static let ExecuteJSCommand = "execute();"
|
||||||
static let ResetCommand = "reset();"
|
static let ResetCommand = "reset();"
|
||||||
static let BotUserAgent = "Googlebot/2.1"
|
static let BotUserAgent = "Googlebot/2.1"
|
||||||
|
static let NumberOfDivsCommand = "document.getElementsByTagName(\"div\").length"
|
||||||
|
|
||||||
|
static let MinNumberOfDivs = 5
|
||||||
|
static let NumberOfDivsFinishedLoadingAttempts = 10
|
||||||
|
|
||||||
|
// A page doesn't have enough time to load on old devices
|
||||||
|
static let NumberOfDivsLoadingDelay = 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
@ -109,14 +39,23 @@ internal class ReCaptchaWebViewManager {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows validation stubbing for testing
|
||||||
|
public var shouldSkipForTests = false
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Callback for loading state changing
|
||||||
|
var onLoadingChanged: ReCaptcha.BoolParameterClosure?
|
||||||
|
|
||||||
/// Sends the result message
|
/// Sends the result message
|
||||||
var completion: ((ReCaptchaResult) -> Void)?
|
var completion: ((ReCaptchaResult) -> Void)?
|
||||||
|
|
||||||
/// Configures the webview for display when required
|
/// Configures the webview for display when required
|
||||||
var configureWebView: ((WKWebView) -> Void)?
|
var configureWebView: ((WKWebView) -> Void)?
|
||||||
|
|
||||||
|
/// The dispatch token used to ensure `configureWebView` is only called once.
|
||||||
|
var configureWebViewDispatchToken = UUID()
|
||||||
|
|
||||||
/// If the ReCaptcha should be reset when it errors
|
/// If the ReCaptcha should be reset when it errors
|
||||||
var shouldResetOnError = true
|
var shouldResetOnError = true
|
||||||
|
|
||||||
|
|
@ -124,29 +63,39 @@ internal class ReCaptchaWebViewManager {
|
||||||
fileprivate var decoder: ReCaptchaDecoder!
|
fileprivate var decoder: ReCaptchaDecoder!
|
||||||
|
|
||||||
/// Indicates if the script has already been loaded by the `webView`
|
/// Indicates if the script has already been loaded by the `webView`
|
||||||
fileprivate var didFinishLoading = false // webView.isLoading does not work in this case
|
fileprivate var didFinishLoading = false { // webView.isLoading does not work for WKWebview.loadHTMLString
|
||||||
|
didSet {
|
||||||
|
if didFinishLoading && completion != nil {
|
||||||
|
// User has requested for validation
|
||||||
|
// A small delay is necessary to allow JS to wrap its operations and avoid errors.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
|
||||||
|
self?.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The observer for `.UIWindowDidBecomeVisible`
|
/// The observer for `.UIWindowDidBecomeVisible`
|
||||||
fileprivate var observer: NSObjectProtocol?
|
fileprivate var observer: NSObjectProtocol?
|
||||||
|
|
||||||
|
/// The observer for `\WKWebView.estimatedProgress`
|
||||||
|
fileprivate var loadingObservation: NSKeyValueObservation?
|
||||||
|
|
||||||
/// The endpoint url being used
|
/// The endpoint url being used
|
||||||
fileprivate var endpoint: String
|
fileprivate var endpoint: String
|
||||||
|
|
||||||
/// The `webView` delegate implementation
|
|
||||||
fileprivate lazy var webviewDelegate: WebViewDelegate = {
|
|
||||||
WebViewDelegate(manager: self)
|
|
||||||
}()
|
|
||||||
|
|
||||||
/// The webview that executes JS code
|
/// The webview that executes JS code
|
||||||
lazy var webView: WKWebView = {
|
lazy var webView: WKWebView = {
|
||||||
let webview = WKWebView(
|
let webview = WKWebView(
|
||||||
frame: CGRect(x: 0, y: 0, width: 1, height: 1),
|
frame: CGRect(x: 0, y: 0, width: 1, height: 1),
|
||||||
configuration: self.buildConfiguration()
|
configuration: self.buildConfiguration()
|
||||||
)
|
)
|
||||||
webview.navigationDelegate = self.webviewDelegate
|
|
||||||
webview.accessibilityIdentifier = "webview"
|
webview.accessibilityIdentifier = "webview"
|
||||||
webview.accessibilityTraits = UIAccessibilityTraitLink
|
webview.accessibilityTraits = UIAccessibilityTraits.link
|
||||||
webview.isHidden = true
|
webview.isHidden = true
|
||||||
|
self.loadingObservation = webview.observe(\.estimatedProgress, options: [.new]) { [weak self] _, change in
|
||||||
|
self?.didFinishLoading = change.newValue == 1
|
||||||
|
}
|
||||||
|
|
||||||
return webview
|
return webview
|
||||||
}()
|
}()
|
||||||
|
|
@ -160,6 +109,7 @@ internal class ReCaptchaWebViewManager {
|
||||||
*/
|
*/
|
||||||
init(html: String, apiKey: String, baseURL: URL, endpoint: String) {
|
init(html: String, apiKey: String, baseURL: URL, endpoint: String) {
|
||||||
self.endpoint = endpoint
|
self.endpoint = endpoint
|
||||||
|
|
||||||
self.decoder = ReCaptchaDecoder { [weak self] result in
|
self.decoder = ReCaptchaDecoder { [weak self] result in
|
||||||
self?.handle(result: result)
|
self?.handle(result: result)
|
||||||
}
|
}
|
||||||
|
|
@ -171,7 +121,7 @@ internal class ReCaptchaWebViewManager {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
observer = NotificationCenter.default.addObserver(
|
observer = NotificationCenter.default.addObserver(
|
||||||
forName: .UIWindowDidBecomeVisible,
|
forName: UIWindow.didBecomeVisibleNotification,
|
||||||
object: nil,
|
object: nil,
|
||||||
queue: nil
|
queue: nil
|
||||||
) { [weak self] notification in
|
) { [weak self] notification in
|
||||||
|
|
@ -187,6 +137,13 @@ internal class ReCaptchaWebViewManager {
|
||||||
Starts the challenge validation
|
Starts the challenge validation
|
||||||
*/
|
*/
|
||||||
func validate(on view: UIView) {
|
func validate(on view: UIView) {
|
||||||
|
onLoadingChanged?(true)
|
||||||
|
#if DEBUG
|
||||||
|
guard !shouldSkipForTests else {
|
||||||
|
completion?(.token(""))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
#endif
|
||||||
webView.isHidden = false
|
webView.isHidden = false
|
||||||
view.addSubview(webView)
|
view.addSubview(webView)
|
||||||
|
|
||||||
|
|
@ -206,6 +163,7 @@ internal class ReCaptchaWebViewManager {
|
||||||
*/
|
*/
|
||||||
func reset() {
|
func reset() {
|
||||||
didFinishLoading = false
|
didFinishLoading = false
|
||||||
|
configureWebViewDispatchToken = UUID()
|
||||||
|
|
||||||
webView.evaluateJavaScript(Constants.ResetCommand) { [weak self] _, error in
|
webView.evaluateJavaScript(Constants.ResetCommand) { [weak self] _, error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
|
|
@ -220,7 +178,7 @@ internal class ReCaptchaWebViewManager {
|
||||||
/** Private methods for ReCaptchaWebViewManager
|
/** Private methods for ReCaptchaWebViewManager
|
||||||
*/
|
*/
|
||||||
fileprivate extension ReCaptchaWebViewManager {
|
fileprivate extension ReCaptchaWebViewManager {
|
||||||
/** Executes the JS command that loads the ReCaptcha challenge.
|
/** Executes the JS command that loads the ReCaptcha challenge after a page finished loading.
|
||||||
This method has no effect if the webview hasn't finished loading.
|
This method has no effect if the webview hasn't finished loading.
|
||||||
*/
|
*/
|
||||||
func execute() {
|
func execute() {
|
||||||
|
|
@ -229,10 +187,65 @@ fileprivate extension ReCaptchaWebViewManager {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evaluateExecuteWhenLoadingFinished(count: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- parameter count: Number of checks of number of divs
|
||||||
|
|
||||||
|
Executes the JS command that returns number of divs.
|
||||||
|
*/
|
||||||
|
func evaluateExecuteWhenLoadingFinished(count: Int) {
|
||||||
|
webView.evaluateJavaScript(Constants.NumberOfDivsCommand) { [weak self] (result, error) -> Void in
|
||||||
|
if let error = error {
|
||||||
|
self?.decoder.send(error: .unexpected(error))
|
||||||
|
self?.onLoadingChanged?(false)
|
||||||
|
} else {
|
||||||
|
self?.handleNumberOfDivs(result: result, count: count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- parameters:
|
||||||
|
- result: Result of number of divs command evaluation
|
||||||
|
- count: Number of checks of divs count
|
||||||
|
|
||||||
|
Handles number of divs command result.
|
||||||
|
*/
|
||||||
|
func handleNumberOfDivs(result: Any?, count: Int) {
|
||||||
|
if let result = result as? Int, result >= Constants.MinNumberOfDivs {
|
||||||
|
evaluateExecute()
|
||||||
|
} else {
|
||||||
|
handleInvalidNumberOfDivsResult(count: count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- parameter count: Number of checks of number of divs
|
||||||
|
|
||||||
|
Handles invalid number of divs.
|
||||||
|
*/
|
||||||
|
func handleInvalidNumberOfDivsResult(count: Int) {
|
||||||
|
if count < Constants.NumberOfDivsFinishedLoadingAttempts {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.NumberOfDivsLoadingDelay) { [weak self] in
|
||||||
|
self?.evaluateExecuteWhenLoadingFinished(count: count + 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decoder.send(error: .htmlLoadError)
|
||||||
|
onLoadingChanged?(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Executes the JS command that loads the ReCaptcha challenge.
|
||||||
|
*/
|
||||||
|
func evaluateExecute() {
|
||||||
webView.evaluateJavaScript(Constants.ExecuteJSCommand) { [weak self] _, error in
|
webView.evaluateJavaScript(Constants.ExecuteJSCommand) { [weak self] _, error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
self?.decoder.send(error: .unexpected(error))
|
self?.decoder.send(error: .unexpected(error))
|
||||||
}
|
}
|
||||||
|
self?.onLoadingChanged?(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,15 +284,14 @@ fileprivate extension ReCaptchaWebViewManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
case .showReCaptcha:
|
case .showReCaptcha:
|
||||||
// Ensures `configureWebView` won't get called multiple times in a short period
|
DispatchQueue.once(token: configureWebViewDispatchToken) { [weak self] in
|
||||||
DispatchQueue.main.debounce(interval: 1) { [weak self] in
|
|
||||||
guard let `self` = self else { return }
|
guard let `self` = self else { return }
|
||||||
self.configureWebView?(self.webView)
|
self.configureWebView?(self.webView)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .didLoad:
|
case .didLoad:
|
||||||
// For testing purposes
|
// For testing purposes
|
||||||
webviewDelegate.execute()
|
didFinishLoading = true
|
||||||
|
|
||||||
case .log(let message):
|
case .log(let message):
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
public enum ReCaptchaRxError: Error {
|
||||||
|
case baseWasReleased
|
||||||
|
}
|
||||||
|
|
||||||
/// Makes ReCaptcha compatible with RxSwift extensions
|
/// Makes ReCaptcha compatible with RxSwift extensions
|
||||||
extension ReCaptcha: ReactiveCompatible {}
|
extension ReCaptcha: ReactiveCompatible {}
|
||||||
|
|
||||||
|
|
@ -64,4 +68,20 @@ public extension Reactive where Base: ReCaptcha {
|
||||||
base?.reset()
|
base?.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Observable of loading state
|
||||||
|
(will not work if someone changes onLoadingChanged variable; current onLodinglChanged will not work after subscription)
|
||||||
|
*/
|
||||||
|
var loadingObservable: Observable<Bool> {
|
||||||
|
return .create { [weak base] observer -> Disposable in
|
||||||
|
guard let base = base else {
|
||||||
|
observer.onError(ReCaptchaRxError.baseWasReleased)
|
||||||
|
return Disposables.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
base.onLoadingChanged = { observer.onNext($0) }
|
||||||
|
return Disposables.create { [weak base] in base?.onLoadingChanged = nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
|
|
@ -10,9 +10,10 @@ default_platform :ios
|
||||||
platform :ios do
|
platform :ios do
|
||||||
skip_docs
|
skip_docs
|
||||||
|
|
||||||
devices = ["iPhone X (~> 11)"]
|
devices = ["iPhone XR (~> 12)"]
|
||||||
|
devices << "iPhone X (~> 11)" if !Helper.is_ci?
|
||||||
devices << "iPhone 7 (~> 10)" if !Helper.is_ci?
|
devices << "iPhone 7 (~> 10)" if !Helper.is_ci?
|
||||||
devices << "iPhone 5s (~> 9)" if !Helper.is_ci?
|
devices << "iPhone 6s (~> 9)" if !Helper.is_ci?
|
||||||
|
|
||||||
desc "Runs the following lanes:\n- test\n- pod_lint\n- carthage_lint"
|
desc "Runs the following lanes:\n- test\n- pod_lint\n- carthage_lint"
|
||||||
lane :ci do
|
lane :ci do
|
||||||
|
|
@ -31,6 +32,7 @@ platform :ios do
|
||||||
swiftlint(
|
swiftlint(
|
||||||
executable: "Example/Pods/Swiftlint/swiftlint",
|
executable: "Example/Pods/Swiftlint/swiftlint",
|
||||||
strict: true,
|
strict: true,
|
||||||
|
reporter: "emoji",
|
||||||
)
|
)
|
||||||
|
|
||||||
# The problem lies in the fact (or rather: serious bug in xcodebuild) that
|
# The problem lies in the fact (or rather: serious bug in xcodebuild) that
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue