From 5f7fc8e222f34a83205ede815afdec0f7c93f1bc Mon Sep 17 00:00:00 2001 From: Alexande B Date: Tue, 10 Dec 2024 13:32:05 +0100 Subject: [PATCH 1/3] docs: split documentation per Examples --- Example/HCaptcha.xcodeproj/project.pbxproj | 24 +- .../HCaptcha-Passive-Example.xcscheme | 12 +- Example/ObjC-Example/README.md | 17 ++ Example/Passive-Example/README.md | 13 + Example/RxSwift-Example/README.md | 63 +++++ Example/SwiftUI-Example/README.md | 13 + Example/UIKit-Example/README.md | 116 +++++++++ README.md | 233 ++++-------------- 8 files changed, 286 insertions(+), 205 deletions(-) create mode 100644 Example/ObjC-Example/README.md create mode 100644 Example/Passive-Example/README.md create mode 100644 Example/RxSwift-Example/README.md create mode 100644 Example/SwiftUI-Example/README.md create mode 100644 Example/UIKit-Example/README.md diff --git a/Example/HCaptcha.xcodeproj/project.pbxproj b/Example/HCaptcha.xcodeproj/project.pbxproj index 1a36320..213f1de 100644 --- a/Example/HCaptcha.xcodeproj/project.pbxproj +++ b/Example/HCaptcha.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ E651460F277A09C20079668A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E65146042779E5380079668A /* Main.storyboard */; }; E6514610277A09C60079668A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; E6586CCF2893DBCA0051EFE7 /* HCaptcha in Frameworks */ = {isa = PBXBuildFile; productRef = E6586CCE2893DBCA0051EFE7 /* HCaptcha */; }; + E68472802CFF91B500779F6A /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E684727E2CFF91B500779F6A /* Info.plist */; }; E6A23A0E2CEA1AC50071D531 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A23A0C2CEA1AC50071D531 /* ViewController.swift */; }; E6A23A192CEA22190071D531 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; E6A23A1A2CEA29F40071D531 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Assets.xcassets */; }; @@ -125,12 +126,12 @@ E65145EB2779AE830079668A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; E65145F02779AE830079668A /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; E65145F12779AE830079668A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; - E65145FB2779AE850079668A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E65145FC2779AE850079668A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; E65146042779E5380079668A /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + E684727E2CFF91B500779F6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E6A239E62CEA15E50071D531 /* HCaptcha_UIKit_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HCaptcha_UIKit_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; E6A23A0C2CEA1AC50071D531 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - E6A23A392CEA6D480071D531 /* HCaptacha_PassiveExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HCaptacha_PassiveExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E6A23A392CEA6D480071D531 /* HCaptcha_PassiveExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HCaptcha_PassiveExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; E6A23A4A2CEA6DA70071D531 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; E6A23A4B2CEA6DA70071D531 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; E6A23A542CEA78430071D531 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; @@ -251,7 +252,7 @@ E65145E82779AE830079668A /* HCaptcha_Objc_Example.app */, E626A7FB2890568A0069D449 /* HCaptcha_SwiftUI_Example.app */, E6A239E62CEA15E50071D531 /* HCaptcha_UIKit_Example.app */, - E6A23A392CEA6D480071D531 /* HCaptacha_PassiveExample.app */, + E6A23A392CEA6D480071D531 /* HCaptcha_PassiveExample.app */, ); name = Products; sourceTree = ""; @@ -323,12 +324,12 @@ E65145E92779AE830079668A /* ObjC-Example */ = { isa = PBXGroup; children = ( + E684727E2CFF91B500779F6A /* Info.plist */, E65146042779E5380079668A /* Main.storyboard */, E65145EA2779AE830079668A /* AppDelegate.h */, E65145EB2779AE830079668A /* AppDelegate.m */, E65145F02779AE830079668A /* ViewController.h */, E65145F12779AE830079668A /* ViewController.m */, - E65145FB2779AE850079668A /* Info.plist */, E65145FC2779AE850079668A /* main.m */, ); path = "ObjC-Example"; @@ -514,9 +515,9 @@ productReference = E6A239E62CEA15E50071D531 /* HCaptcha_UIKit_Example.app */; productType = "com.apple.product-type.application"; }; - E6A23A382CEA6D480071D531 /* HCaptacha_PassiveExample */ = { + E6A23A382CEA6D480071D531 /* HCaptcha_PassiveExample */ = { isa = PBXNativeTarget; - buildConfigurationList = E6A23A462CEA6D4C0071D531 /* Build configuration list for PBXNativeTarget "HCaptacha_PassiveExample" */; + buildConfigurationList = E6A23A462CEA6D4C0071D531 /* Build configuration list for PBXNativeTarget "HCaptcha_PassiveExample" */; buildPhases = ( E6A23A352CEA6D480071D531 /* Sources */, E6A23A362CEA6D480071D531 /* Frameworks */, @@ -526,12 +527,12 @@ ); dependencies = ( ); - name = HCaptacha_PassiveExample; + name = HCaptcha_PassiveExample; packageProductDependencies = ( E6A23A522CEA73790071D531 /* HCaptcha */, ); productName = HCaptacha_PassiveExample; - productReference = E6A23A392CEA6D480071D531 /* HCaptacha_PassiveExample.app */; + productReference = E6A23A392CEA6D480071D531 /* HCaptcha_PassiveExample.app */; productType = "com.apple.product-type.application"; }; F28FAC9B200E425600E14987 /* HCaptcha_UITests */ = { @@ -638,7 +639,7 @@ E626A7FA2890568A0069D449 /* HCaptcha_SwiftUI_Example */, 607FACCF1AFB9204008FA782 /* HCaptcha_RxSwift_Example */, E65145E72779AE830079668A /* HCaptcha_Objc_Example */, - E6A23A382CEA6D480071D531 /* HCaptacha_PassiveExample */, + E6A23A382CEA6D480071D531 /* HCaptcha_PassiveExample */, F2ECCF751E9FC47B0097B199 /* HCaptcha_Tests */, F28FAC9B200E425600E14987 /* HCaptcha_UITests */, ); @@ -669,6 +670,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + E68472802CFF91B500779F6A /* Info.plist in Resources */, E651460C2779E9D30079668A /* LaunchScreen.xib in Resources */, E6A23A2D2CEA48BA0071D531 /* Assets.xcassets in Resources */, E651460F277A09C20079668A /* Main.storyboard in Resources */, @@ -1473,6 +1475,7 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Passive-Example/Info.plist"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -1516,6 +1519,7 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Passive-Example/Info.plist"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -1689,7 +1693,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - E6A23A462CEA6D4C0071D531 /* Build configuration list for PBXNativeTarget "HCaptacha_PassiveExample" */ = { + E6A23A462CEA6D4C0071D531 /* Build configuration list for PBXNativeTarget "HCaptcha_PassiveExample" */ = { isa = XCConfigurationList; buildConfigurations = ( E6A23A442CEA6D4C0071D531 /* Debug */, diff --git a/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha-Passive-Example.xcscheme b/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha-Passive-Example.xcscheme index 6fffcc9..64f7018 100644 --- a/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha-Passive-Example.xcscheme +++ b/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha-Passive-Example.xcscheme @@ -16,8 +16,8 @@ @@ -45,8 +45,8 @@ @@ -62,8 +62,8 @@ diff --git a/Example/ObjC-Example/README.md b/Example/ObjC-Example/README.md new file mode 100644 index 0000000..62b10b8 --- /dev/null +++ b/Example/ObjC-Example/README.md @@ -0,0 +1,17 @@ +## Objective-C Example + +Make sure [installation is complete](../../README.md#installation) and [pre-requisites are met](../../README.md#installation) + +`HCaptcha` exposes its API to real native code, so it can be used from `Objective-C`. + +Because `HCaptcha` is a pure Swift library, it exposes its API through the `@objc` attribute. + +The `@objc` attribute makes Swift methods, properties, and classes available to Objective-C code. By marking a Swift method or class with `@objc`, it is automatically bridged to Objective-C, allowing for interoperability between Swift and Objective-C. + +Also because `Objective-C` doesn't support optional arguments, the most popular constructors of `HCaptcha` available in `Objective-C` + +The full code is in [ViewController.m](./ViewController.m). + +---- + +[Back to the main README](../../README.md) \ No newline at end of file diff --git a/Example/Passive-Example/README.md b/Example/Passive-Example/README.md new file mode 100644 index 0000000..7fb4bba --- /dev/null +++ b/Example/Passive-Example/README.md @@ -0,0 +1,13 @@ +## Passive ApiKey Example + +Make sure [installation is complete](../../README.md#installation) and [pre-requisites are met](../../README.md#installation) + +HCaptcha Enterprise supports verification with no interaction from the user: [Passive Site Keys](https://docs.hcaptcha.com/faq#what-are-the-difficulty-levels-for-the-challenges-and-how-are-they-selected). + +Using the `passiveApiKey` option with Passive sitekeys provides performance improvements in SDK token generation time, at the cost of less flexibility if you want to change the sitekey mode in the future without a code update. + +The full code is in [Example](./ContentView.swift). + +---- + +[Back to the main README](../../README.md) \ No newline at end of file diff --git a/Example/RxSwift-Example/README.md b/Example/RxSwift-Example/README.md new file mode 100644 index 0000000..0937005 --- /dev/null +++ b/Example/RxSwift-Example/README.md @@ -0,0 +1,63 @@ +## RxSwift Example + +Once [installation is complete](../../README.md#installation) and [pre-requisites are met](../../README.md#installation), we are ready to proceed with the actual implementation: + +``` swift +var hcaptcha: HCaptcha! + +// ... + +hcaptcha.rx.validate(on: view) + .subscribe(onNext: { (token: String) in + // Do something + }) +``` + +Note: caller code is responsible for hiding the `WebView` after challenge processing. This is illustrated in the Example app, and can be achieved with: + +```swift +... +hcaptcha?.configureWebView { [weak self] webview in + webview.tag = "hCaptchaViewTag" +} +... + +let disposeBag = DisposeBag() +let validate = hcaptcha.rx.validate(on: view) +... + +validate + .map { [weak self] _ in + self?.view.viewWithTag("hCaptchaViewTag") + } + .subscribe(onNext: { webview in + webview?.removeFromSuperview() + }) + .disposed(by: disposeBag) +``` + +The full code is in [`ViewController.swift`](./ViewController.swift) + +### SDK Events + +This SDK allows you to receive [interaction events](../../README.md#sdk-events). This is how it can be implemented: + +For `RxSwift`: + +```swift +let hcaptcha = try? HCaptcha(...) +... +hcaptcha.rx.events() + .subscribe { [weak self] rxevent in + let event = rxevent.element?.0 + + if event == .open { + ... + } + } + ... +``` + +---- + +[Back to the main README](../../README.md) \ No newline at end of file diff --git a/Example/SwiftUI-Example/README.md b/Example/SwiftUI-Example/README.md new file mode 100644 index 0000000..ce1bfd5 --- /dev/null +++ b/Example/SwiftUI-Example/README.md @@ -0,0 +1,13 @@ +### SwiftUI Example + +Once [installation is complete](../../README.md#installation) and [pre-requisites are met](../../README.md#installation), we are ready to proceed with the actual implementation: + +While `HCaptcha` was originally designed to be used with UIKit, but you can easily use it with `SwiftUI` as well. Check out the [SwiftUI Example](./ContentView.swift). + +To integrate `UIKit` components into a `SwiftUI` project, you can use `UIViewRepresentable`. This protocol allows you to wrap a `UIKit` view and make it compatible with `SwiftUI`. + +For example, to use a `HCaptcha` component (originally designed to show over `UIKit` views) in `SwiftUI`, you would create a `UIViewRepresentable` that provides a "placeholder" view for the `HCaptcha` to be used as host view to add `WKWebView`. + +---- + +[Back to the main README](../../README.md) \ No newline at end of file diff --git a/Example/UIKit-Example/README.md b/Example/UIKit-Example/README.md new file mode 100644 index 0000000..77b3dd8 --- /dev/null +++ b/Example/UIKit-Example/README.md @@ -0,0 +1,116 @@ +## UIKit Example + +Once [installation is complete](../../README.md#installation) and [pre-requisites are met](../../README.md#installation), we are ready to proceed with the actual implementation: + +``` swift +let hcaptcha = try? HCaptcha() + +override func viewDidLoad() { + super.viewDidLoad() + + hcaptcha?.configureWebView { [weak self] webview in + webview.frame = self?.view.bounds ?? CGRect.zero + } +} + +// ... + +func validate() { + hcaptcha?.validate(on: view) { [weak self] (result: HCaptchaResult) in + print(try? result.dematerialize()) + } +} +``` + +**Note**: caller code is responsible for hiding the `WebView` after challenge processing. This is illustrated in the Example app, and can be achieved with: + +```swift +... +var captchaWebView: WKWebView? +... + +hcaptcha?.configureWebView { [weak self] webview in + self?.captchaWebView = webview +} +... + +hcaptcha.validate(on: view) { result in + ... + + captchaWebView?.removeFromSuperview() +} + +``` + +The full code is in [`ViewContoller.swift`](./ViewContoller.swift) + +## Show `hCaptcha` above `UIVisualEffectView` + +In case you need to show hCaptcha above `UIVisualEffectView` make sure to pass `visualEffectView.contentView` instead `visualEffectView`. Per Apple's documentation: + +> After you add the visual effect view to the view hierarchy, add any subviews to the contentView property of the visual effect view. Do not add subviews directly to the visual effect view itself. + +More details [here](https://github.com/hCaptcha/HCaptcha-ios-sdk/issues/50). + +## Change hCaptcha frame + +If you are customizing display beyond the defaults and need to resize or change the hCaptcha layout, for example after a visual challenge appears, you can use the following approach to trigger a redraw of the view: + +``` swift +let hcaptcha = try? HCaptcha(...) +var visualChallengeShown = false +... +hcaptcha?.configureWebView { [weak self] webview in + webview.tag = "hCaptchaViewTag" + if visualChallengeShown { + let padding = 10 + webview.frame = CGRect( + x: padding, + y: padding, + width: view.frame.size.width - 2 * padding, + height: targetHeight - 2 * padding + ) + } else { + webview.frame = self?.view.bounds ?? CGRect.zero + } +} +... +hcaptcha.onEvent { (event, data) in + if event == .open { + visualChallengeShown = true + hcaptcha.redrawView() + } else if event == .error { + let error = data as? HCaptchaError + print("onEvent error: \(String(describing: error))") + ... + } +} +... +hcaptcha.validate(on: view, resetOnError: false) { result in + visualChallengeShown = false +} +``` + +## SDK Events + +This SDK allows you to receive [interaction events](../../README.md#sdk-events). This is how it can be implemented: + +``` swift +let hcaptcha = try? HCaptcha(...) + +// ... + +hcaptcha.onEvent { (event, data) in + if event == .open { + ... + } else if event == .error { + let error = data as? HCaptchaError + print("onEvent error: \(String(describing: error))") + ... + } +} +``` + +---- + +[Back to the main README](../../README.md) \ No newline at end of file diff --git a/README.md b/README.md index bd9d9e1..075e52c 100644 --- a/README.md +++ b/README.md @@ -10,22 +10,26 @@ ----- -- [HCaptcha](#hcaptcha) - * [Installation](#installation) - - [Cocoapods](#cocoapods) - - [SPM](#spm) - * [Requirements](#requirements) - * [Usage](#usage) - - [Change widget theme](#change-widget-theme) - - [More params for Enterprise (optional)](#more-params-for-enterprise-optional) - + [Enabling the visible checkbox flow](#enabling-the-visible-checkbox-flow) - + [Using landscape instead of portrait orientation](#using-landscape-instead-of-portrait-orientation) - + [SDK Events](#sdk-events) - + [Disable new token fetch on expiry](#disable-new-token-fetch-on-expiry) - + [Change hCaptcha frame](#change-hcaptcha-frame) - * [Known issues](#known-issues) - * [License](#license) - * [Troubleshooting](#troubleshooting) +- [Installation](#installation) + * [Cocoapods](#cocoapods) + * [Carthage](#carthage) + * [SPM](#spm) +- [Requirements](#requirements) +- [Pre-requisites](#pre-requisites) +- [Examples](#examples) +- [Use cases](#use-cases) + * [Setting the host override (optional)](#setting-the-host-override-optional) + * [Change widget theme](#change-widget-theme) + * [Alternate endpoint (optional)](#alternate-endpoint-optional) + * [More params for Enterprise (optional)](#more-params-for-enterprise-optional) + * [Enabling the visible checkbox flow](#enabling-the-visible-checkbox-flow) + * [Using landscape instead of portrait orientation](#using-landscape-instead-of-portrait-orientation) + * [SDK Events](#sdk-events) + * [Disable new token fetch on expiry](#disable-new-token-fetch-on-expiry) +- [Compiled size](#compiled-size-impact-on-including-in-your-app) +- [Known issues](#known-issues) +- [License](#license) +- [Troubleshooting](#troubleshooting) Add [hCaptcha](https://www.hcaptcha.com/) to your project. This library automatically handles hCaptcha's events and returns a validation token, presenting the challenge via a modal if needed. @@ -39,14 +43,14 @@ HCaptcha is available through [CocoaPods](http://cocoapods.org) and packaged for To install it, simply add the following line to your dependencies file: -#### Cocoapods +### Cocoapods ``` ruby pod "HCaptcha" # or pod "HCaptcha/RxSwift" ``` -#### Carthage +### Carthage ``` ruby github "hCaptcha/HCaptcha-ios-sdk" ``` @@ -58,7 +62,7 @@ Known issues: - Carthage has a `RxSwift` build issue, also avoidable via `--no-use-binaries` - https://github.com/Carthage/Carthage/issues/3243 -#### SPM +### SPM Standard SPM formula: uses [Package.swift](./Package.swift) ## Requirements @@ -68,42 +72,17 @@ Standard SPM formula: uses [Package.swift](./Package.swift) | iOS | :white_check_mark: >= 12.0 | | WatchOS | :heavy_multiplication_x: | -## Usage +## Pre-requisites -hCaptcha sitekeys can be specified as Info.plist keys or can be passed as parameters when instantiating `HCaptcha()`. +Once you have the hCaptcha `apiKey` (also referred to as `sitekey`, which can be obtained at [`https://dashboard.hcaptcha.com/sites`](https://dashboard.hcaptcha.com/sites)), + +the hCaptcha `apiKey` can be specified in `Info.plist` keys or can be passed as parameters when instantiating `HCaptcha()`. For the Info.plist configuration, add `HCaptchaKey` (sitekey) and `HCaptchaDomain` (with a protocol, i.e. https://) to your Info.plist. - `HCaptchaKey` is your hCaptcha sitekey. - `HCaptchaDomain` should be a string like `https://www.your.com` -With these values set, run: - -``` swift -let hcaptcha = try? HCaptcha() - -override func viewDidLoad() { - super.viewDidLoad() - - hcaptcha?.configureWebView { [weak self] webview in - webview.frame = self?.view.bounds ?? CGRect.zero - } -} - - -func validate() { - hcaptcha?.validate(on: view) { [weak self] (result: HCaptchaResult) in - print(try? result.dematerialize()) - } -} -``` - -**Note**: in case you need to show hCaptcha above `UIVisualEffectView` make sure to pass `visualEffectView.contentView` instead `visualEffectView`. Per Apple's documentation: - -> After you add the visual effect view to the view hierarchy, add any subviews to the contentView property of the visual effect view. Do not add subviews directly to the visual effect view itself. - -More details [here](https://github.com/hCaptcha/HCaptcha-ios-sdk/issues/50). - If you prefer to keep the information out of the Info.plist, you can instead use: ``` swift @@ -120,61 +99,19 @@ let hcaptcha = try? HCaptcha( - in most cases `baseURL` can be `http://localhost`. This value is mainly used for your convenience in analytics. - `baseURL` should match `HCaptchaDomain` if specified; it controls the URI used to initialize the hCaptcha session. Example: `https://www.your.com` +## Examples -You can also install the reactive subpod and use it with RxSwift: - -``` swift -hcaptcha.rx.validate(on: view) - .subscribe(onNext: { (token: String) in - // Do something - }) -``` - -Note: caller code is responsible for hiding the `WebView` after challenge processing. This is illustrated in the Example app, and can be achieved with: - -1. Regular Swift API: - ```swift - ... - var captchaWebView: WKWebView? - ... - - hcaptcha?.configureWebView { [weak self] webview in - self?.captchaWebView = webview - } - ... - - hcaptcha.validate(on: view) { result in - ... - - captchaWebView?.removeFromSuperview() - } - - ``` - -1. `RxSwift` API (check [the example](./Example/RxSwift-Example/ViewController.swift) for more details): - ```swift - ... - hcaptcha?.configureWebView { [weak self] webview in - webview.tag = "hCaptchaViewTag" - } - ... - - let disposeBag = DisposeBag() - let validate = hcaptcha.rx.validate(on: view) - ... +If you are looking for a complete example please check links below: - validate - .map { [weak self] _ in - self?.view.viewWithTag("hCaptchaViewTag") - } - .subscribe(onNext: { webview in - webview?.removeFromSuperview() - }) - .disposed(by: disposeBag) - ``` +- [UIKit Example](./Example/UIKit-Example/README.md) +- [SwiftUI Example](./Example/SwiftUI-Example/README.md) +- [RxSwift Example](./Example/RxSwift-Example/README.md) +- [SwiftUI Passive Example](./Example/Passive-Example/README.md) +- [Objective-C Example](./Example/ObjC-Example/README.md) +## Use cases -#### Setting the host override (optional) +### Setting the host override (optional) Since this SDK uses local resources, you may want to set a host override for better tracking and enforcement of siteverify parameters. @@ -192,7 +129,7 @@ let hcaptcha = try? HCaptcha( Note: this should be the **bare** host, i.e. not including a protocol prefix like https://. -#### Change widget theme +### Change widget theme The SDK supports three built-in themes: `light`, `dark`, and `contrast` @@ -208,7 +145,7 @@ let hcaptcha = try? HCaptcha( For Enterprise sitekeys we also support custom themes via the `customTheme` parameter, described below. -#### Alternate endpoint (optional) +### Alternate endpoint (optional) If you are an Enterprise user with first-party hosting access, you can use your own endpoint (i.e. verify.your.com). @@ -224,7 +161,7 @@ let hcaptcha = try? HCaptcha( ... ``` -#### More params for Enterprise (optional) +### More params for Enterprise (optional) Enterprise params like: @@ -274,37 +211,10 @@ This SDK allows you to receive interaction events, for your analytics via the `o - `close` fires when the user dismisses a challenge. - `error` fires when an internal error happens during challenge verification, for example a network error. Details about the error will be provided by the `data` param, as in the example below. Note: This event is not intended for error handling, but only for analytics purposes. For error handling please see the `validate` API call docs. -You can implement this with the code below: +You can check the implementation details in: -``` swift -let hcaptcha = try? HCaptcha(...) -... -hcaptcha.onEvent { (event, data) in - if event == .open { - ... - } else if event == .error { - let error = data as? HCaptchaError - print("onEvent error: \(String(describing: error))") - ... - } -} -``` - -For `RxSwift`: - -```swift -let hcaptcha = try? HCaptcha(...) -... -hcaptcha.rx.events() - .subscribe { [weak self] rxevent in - let event = rxevent.element?.0 - - if event == .open { - ... - } - } - ... -``` +- [UIKit Example](./Example/UIKit-Example/README.md) +- [SwiftUI Example](./Example/SwiftUI-Example/README.md) ### Disable new token fetch on expiry @@ -316,62 +226,7 @@ hcaptcha.validate(on: view, resetOnError: false) { result in } ``` -### Change hCaptcha frame - -If you are customizing display beyond the defaults and need to resize or change the hCaptcha layout, for example after a visual challenge appears, you can use the following approach to trigger a redraw of the view: - -``` swift -let hcaptcha = try? HCaptcha(...) -var visualChallengeShown = false -... -hcaptcha?.configureWebView { [weak self] webview in - webview.tag = "hCaptchaViewTag" - if visualChallengeShown { - let padding = 10 - webview.frame = CGRect( - x: padding, - y: padding, - width: view.frame.size.width - 2 * padding, - height: targetHeight - 2 * padding - ) - } else { - webview.frame = self?.view.bounds ?? CGRect.zero - } -} -... -hcaptcha.onEvent { (event, data) in - if event == .open { - visualChallengeShown = true - hcaptcha.redrawView() - } else if event == .error { - let error = data as? HCaptchaError - print("onEvent error: \(String(describing: error))") - ... - } -} -... -hcaptcha.validate(on: view, resetOnError: false) { result in - visualChallengeShown = false -} -``` - -### SwiftUI Example - -`HCaptcha` was originally designed to be used with UIKit. But you can easily use it with `SwiftUI` as well. Check out the [SwiftUI Example](./Example/SwiftUI-Example/ContentView.swift) - -### Objective-C Example - -`HCaptcha` can be used from Objective-C code. Check out the [Example Project](./Example/ObjC-Example/ViewController.m) - -### Passive Sitekey Example - -HCaptcha Enterprise supports verification with no interaction from the user: [Passive Site Keys](https://docs.hcaptcha.com/faq#what-are-the-difficulty-levels-for-the-challenges-and-how-are-they-selected). - -Using the `passiveApiKey` option with Passive sitekeys provides performance improvements in SDK token generation time, at the cost of less flexibility if you want to change the sitekey mode in the future without a code update. - -Check out the [Example](./Example/Passive-Example/ContentView.swift) for more details. - -### Compiled size: impact on including in your app +## Compiled size: impact on including in your app HCaptcha pod size: **140** KB as of Jan 2024. You can always see the latest number in the CI logs by searching for the "pod size" string. @@ -404,7 +259,7 @@ A: There are several ways this can happen: - You may have unintentionally added a transparent overlay over the SDK's view layer. This can be checked with [the view debugger](https://developer.apple.com/documentation/xcode/diagnosing-and-resolving-bugs-in-your-running-app#Inspect-and-resolve-appearance-and-layout-issues) -### Inspiration +## Inspiration Originally forked from fjcaetano's ReCaptcha IOS SDK, licensed under MIT. From 6496d01104be80d40096c1c24d182c75b2f7570b Mon Sep 17 00:00:00 2001 From: Alexande B Date: Sun, 15 Dec 2024 09:34:46 +0100 Subject: [PATCH 2/3] chore: apply Xcode update suggestions --- Example/HCaptcha.xcodeproj/project.pbxproj | 70 +++++++++---------- .../xcschemes/HCaptcha-Objc-Example.xcscheme | 2 +- .../HCaptcha-SwiftUI-Example.xcscheme | 2 +- .../xcschemes/HCaptcha_Tests.xcscheme | 3 +- .../xcschemes/HCaptcha_UITests.xcscheme | 2 +- Example/SwiftUI-Example/Info.plist | 10 +++ 6 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 Example/SwiftUI-Example/Info.plist diff --git a/Example/HCaptcha.xcodeproj/project.pbxproj b/Example/HCaptcha.xcodeproj/project.pbxproj index 213f1de..ca680bf 100644 --- a/Example/HCaptcha.xcodeproj/project.pbxproj +++ b/Example/HCaptcha.xcodeproj/project.pbxproj @@ -29,7 +29,6 @@ E651460F277A09C20079668A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E65146042779E5380079668A /* Main.storyboard */; }; E6514610277A09C60079668A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; E6586CCF2893DBCA0051EFE7 /* HCaptcha in Frameworks */ = {isa = PBXBuildFile; productRef = E6586CCE2893DBCA0051EFE7 /* HCaptcha */; }; - E68472802CFF91B500779F6A /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E684727E2CFF91B500779F6A /* Info.plist */; }; E6A23A0E2CEA1AC50071D531 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A23A0C2CEA1AC50071D531 /* ViewController.swift */; }; E6A23A192CEA22190071D531 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; E6A23A1A2CEA29F40071D531 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Assets.xcassets */; }; @@ -97,7 +96,6 @@ 1CF662E463D56A0421D8A84A /* Pods-HCaptcha_RxSwift_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HCaptcha_RxSwift_Example.debug.xcconfig"; path = "Target Support Files/Pods-HCaptcha_RxSwift_Example/Pods-HCaptcha_RxSwift_Example.debug.xcconfig"; sourceTree = ""; }; 4D10E8098B5018907486C310 /* Pods_HCaptcha_UIKit_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HCaptcha_UIKit_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD01AFB9204008FA782 /* HCaptcha_RxSwift_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HCaptcha_RxSwift_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -115,6 +113,8 @@ C4CC6777C1126E7650593DB0 /* Pods-HCaptcha_UIKit_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HCaptcha_UIKit_Example.debug.xcconfig"; path = "Target Support Files/Pods-HCaptcha_UIKit_Example/Pods-HCaptcha_UIKit_Example.debug.xcconfig"; sourceTree = ""; }; C8537003ECC47117AF54DCA9 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; DE831E3AC800884C050910DF /* Pods-HCaptcha_UIKit_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HCaptcha_UIKit_Example.release.xcconfig"; path = "Target Support Files/Pods-HCaptcha_UIKit_Example/Pods-HCaptcha_UIKit_Example.release.xcconfig"; sourceTree = ""; }; + E605B7682D0ECBB9008EA9C8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E605B76C2D0ED712008EA9C8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E626A7FB2890568A0069D449 /* HCaptcha_SwiftUI_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HCaptcha_SwiftUI_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; E626A7FF2890568A0069D449 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; E626A809289056E00069D449 /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; @@ -128,7 +128,6 @@ E65145F12779AE830079668A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; E65145FC2779AE850079668A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; E65146042779E5380079668A /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; - E684727E2CFF91B500779F6A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E6A239E62CEA15E50071D531 /* HCaptcha_UIKit_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HCaptcha_UIKit_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; E6A23A0C2CEA1AC50071D531 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; E6A23A392CEA6D480071D531 /* HCaptcha_PassiveExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HCaptcha_PassiveExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -265,14 +264,6 @@ path = "RxSwift-Example"; sourceTree = ""; }; - 607FACD31AFB9204008FA782 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 607FACD41AFB9204008FA782 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { isa = PBXGroup; children = ( @@ -307,6 +298,7 @@ E626A7FC2890568A0069D449 /* SwiftUI-Example */ = { isa = PBXGroup; children = ( + E605B76C2D0ED712008EA9C8 /* Info.plist */, E626A809289056E00069D449 /* App.swift */, E626A7FF2890568A0069D449 /* ContentView.swift */, ); @@ -324,7 +316,6 @@ E65145E92779AE830079668A /* ObjC-Example */ = { isa = PBXGroup; children = ( - E684727E2CFF91B500779F6A /* Info.plist */, E65146042779E5380079668A /* Main.storyboard */, E65145EA2779AE830079668A /* AppDelegate.h */, E65145EB2779AE830079668A /* AppDelegate.m */, @@ -346,12 +337,12 @@ E6A23A182CEA20130071D531 /* Shared */ = { isa = PBXGroup; children = ( - E6A23A542CEA78430071D531 /* BaseViewController.swift */, - 607FACD51AFB9204008FA782 /* AppDelegate.swift */, - 607FACD91AFB9204008FA782 /* Main.storyboard */, 607FACDC1AFB9204008FA782 /* Assets.xcassets */, + 607FACD51AFB9204008FA782 /* AppDelegate.swift */, + E6A23A542CEA78430071D531 /* BaseViewController.swift */, + E605B7682D0ECBB9008EA9C8 /* Info.plist */, 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, - 607FACD31AFB9204008FA782 /* Supporting Files */, + 607FACD91AFB9204008FA782 /* Main.storyboard */, ); path = Shared; sourceTree = ""; @@ -360,10 +351,10 @@ isa = PBXGroup; children = ( E6A23A0D2CEA1AC50071D531 /* UIKit-Example */, - E626A7FC2890568A0069D449 /* SwiftUI-Example */, - E65145E92779AE830079668A /* ObjC-Example */, 607FACD21AFB9204008FA782 /* RxSwift-Example */, + E626A7FC2890568A0069D449 /* SwiftUI-Example */, E6A23A4C2CEA6DA70071D531 /* Passive-Example */, + E65145E92779AE830079668A /* ObjC-Example */, E6A23A182CEA20130071D531 /* Shared */, ); name = Examples; @@ -583,8 +574,9 @@ 607FACC81AFB9204008FA782 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1600; - LastUpgradeCheck = 1150; + LastUpgradeCheck = 1600; ORGANIZATIONNAME = HCaptcha; TargetAttributes = { 607FACCF1AFB9204008FA782 = { @@ -623,10 +615,9 @@ }; buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "HCaptcha" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, Base, ); @@ -636,8 +627,8 @@ projectRoot = ""; targets = ( E6A239E52CEA15E50071D531 /* HCaptcha_UIKit_Example */, - E626A7FA2890568A0069D449 /* HCaptcha_SwiftUI_Example */, 607FACCF1AFB9204008FA782 /* HCaptcha_RxSwift_Example */, + E626A7FA2890568A0069D449 /* HCaptcha_SwiftUI_Example */, E65145E72779AE830079668A /* HCaptcha_Objc_Example */, E6A23A382CEA6D480071D531 /* HCaptcha_PassiveExample */, F2ECCF751E9FC47B0097B199 /* HCaptcha_Tests */, @@ -670,7 +661,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - E68472802CFF91B500779F6A /* Info.plist in Resources */, E651460C2779E9D30079668A /* LaunchScreen.xib in Resources */, E6A23A2D2CEA48BA0071D531 /* Assets.xcassets in Resources */, E651460F277A09C20079668A /* Main.storyboard in Resources */, @@ -1083,6 +1073,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1139,6 +1130,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1172,12 +1164,17 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 58EEZG76L8; + DONT_GENERATE_INFOPLIST_FILE = NO; + GENERATE_INFOPLIST_FILE = NO; + GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = Shared/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.HCaptcha-Example"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1192,12 +1189,17 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; + DONT_GENERATE_INFOPLIST_FILE = NO; + GENERATE_INFOPLIST_FILE = NO; + GENERATE_PKGINFO_FILE = YES; INFOPLIST_FILE = Shared/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = 1.0; MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.HCaptcha-Example"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1225,7 +1227,7 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Shared/Info.plist; + INFOPLIST_FILE = "SwiftUI-Example/Info.plist"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -1266,7 +1268,7 @@ ENABLE_PREVIEWS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = Shared/Info.plist; + INFOPLIST_FILE = "SwiftUI-Example/Info.plist"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -1304,7 +1306,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; + GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = Shared/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; @@ -1323,7 +1325,6 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.HCaptcha-Objc-Example.HCaptcha-Objc-Example"; PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_SWIFT_SYMBOLS = NO; SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -1345,7 +1346,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; + GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = Shared/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; @@ -1363,7 +1364,6 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.flaviocaetano.HCaptcha-Objc-Example.HCaptcha-Objc-Example"; PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_SWIFT_SYMBOLS = NO; SWIFT_EMIT_LOC_STRINGS = NO; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -1388,7 +1388,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; - GENERATE_INFOPLIST_FILE = YES; + GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = Shared/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; @@ -1431,7 +1431,7 @@ CURRENT_PROJECT_VERSION = 1; ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; - GENERATE_INFOPLIST_FILE = YES; + GENERATE_INFOPLIST_FILE = NO; INFOPLIST_FILE = Shared/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; @@ -1475,13 +1475,13 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "Passive-Example/Info.plist"; + INFOPLIST_FILE = ""; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1519,13 +1519,13 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "Passive-Example/Info.plist"; + INFOPLIST_FILE = ""; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha-Objc-Example.xcscheme b/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha-Objc-Example.xcscheme index 27b1080..3b5e2bb 100644 --- a/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha-Objc-Example.xcscheme +++ b/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha-Objc-Example.xcscheme @@ -1,6 +1,6 @@ + reference = "container:HCaptcha_Tests/HCaptcha_Tests.xctestplan" + default = "YES"> diff --git a/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha_UITests.xcscheme b/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha_UITests.xcscheme index bf9370b..05cdd7c 100644 --- a/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha_UITests.xcscheme +++ b/Example/HCaptcha.xcodeproj/xcshareddata/xcschemes/HCaptcha_UITests.xcscheme @@ -1,6 +1,6 @@ + + + + HCaptchaDomain + http://localhost + HCaptchaKey + a5f74b19-9e45-40e0-b45d-47ff91b7a6c2 + + From 7a8c7ed16c126ff686b80204642adfd4ea24da6e Mon Sep 17 00:00:00 2001 From: e271828- Date: Sun, 15 Dec 2024 20:11:18 -0500 Subject: [PATCH 3/3] Apply suggestions from code review --- Example/ObjC-Example/README.md | 2 +- Example/SwiftUI-Example/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/ObjC-Example/README.md b/Example/ObjC-Example/README.md index 62b10b8..c2609d7 100644 --- a/Example/ObjC-Example/README.md +++ b/Example/ObjC-Example/README.md @@ -8,7 +8,7 @@ Because `HCaptcha` is a pure Swift library, it exposes its API through the `@obj The `@objc` attribute makes Swift methods, properties, and classes available to Objective-C code. By marking a Swift method or class with `@objc`, it is automatically bridged to Objective-C, allowing for interoperability between Swift and Objective-C. -Also because `Objective-C` doesn't support optional arguments, the most popular constructors of `HCaptcha` available in `Objective-C` +Also because `Objective-C` doesn't support optional arguments, the most popular constructors of `HCaptcha` are available in `Objective-C` The full code is in [ViewController.m](./ViewController.m). diff --git a/Example/SwiftUI-Example/README.md b/Example/SwiftUI-Example/README.md index ce1bfd5..33e18f1 100644 --- a/Example/SwiftUI-Example/README.md +++ b/Example/SwiftUI-Example/README.md @@ -2,7 +2,7 @@ Once [installation is complete](../../README.md#installation) and [pre-requisites are met](../../README.md#installation), we are ready to proceed with the actual implementation: -While `HCaptcha` was originally designed to be used with UIKit, but you can easily use it with `SwiftUI` as well. Check out the [SwiftUI Example](./ContentView.swift). +While `HCaptcha` was originally designed for UIKit, you can easily use it with `SwiftUI` as well. Check out the [SwiftUI Example](./ContentView.swift). To integrate `UIKit` components into a `SwiftUI` project, you can use `UIViewRepresentable`. This protocol allows you to wrap a `UIKit` view and make it compatible with `SwiftUI`.