diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 1e52599..1491e4c 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 0A878E652CD4A4E4008FAE00 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; B4C6AE8F2AF546B200A68766 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; B4C6AE922AF546B200A68766 /* ExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleApp.swift; sourceTree = ""; }; B4C6AE942AF546B200A68766 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -57,6 +58,7 @@ B4C6AE912AF546B200A68766 /* Example */ = { isa = PBXGroup; children = ( + 0A878E652CD4A4E4008FAE00 /* Info.plist */, B4C6AE922AF546B200A68766 /* ExampleApp.swift */, B4C6AE942AF546B200A68766 /* ContentView.swift */, B4C6AE962AF546B300A68766 /* Assets.xcassets */, @@ -293,6 +295,7 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Example/Info.plist; INFOPLIST_KEY_NSCameraUsageDescription = "To capture photos of your property"; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; @@ -332,6 +335,7 @@ ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Example/Info.plist; INFOPLIST_KEY_NSCameraUsageDescription = "To capture photos of your property"; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; @@ -386,8 +390,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/hoverinc/hover-capture-ios.git"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.1.1; + branch = release/2.0.0; + kind = branch; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9fc3731..33876c6 100644 --- a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,14 +1,15 @@ { + "originHash" : "c8f3ebc6a23f3c1e8a73f3e85b85901f9db883bb57585cf587f6108a424a2640", "pins" : [ { "identity" : "hover-capture-ios", "kind" : "remoteSourceControl", "location" : "https://github.com/hoverinc/hover-capture-ios.git", "state" : { - "revision" : "a2fd119de617dc69d93af4f4ddcff95d74763583", - "version" : "0.1.1" + "branch" : "release/2.0.0", + "revision" : "4b0a5942180f6f9e50a409001f5352fff5985055" } } ], - "version" : 2 + "version" : 3 } diff --git a/Example/Example/ContentView.swift b/Example/Example/ContentView.swift index 67758ff..0680cfc 100644 --- a/Example/Example/ContentView.swift +++ b/Example/Example/ContentView.swift @@ -13,21 +13,28 @@ struct ContentView: View { VStack { Button("Start Flow") { Task { - try await HVCameraExterior.sharedInstance.startCaptureSession( - settings: HVCameraSettings(), - info: CaptureJobInformation( - firstTimeUser: true, - jobID: 12345, - clientIdentifier: "DEADBEEF_DEAD_BEEF_DEAD_BEEFDEADBEEF", - uploadSecret: "DEADBEEF_DEAD_BEEF_DEAD_BEEFDEADBEEF" - ) - ) + let jobInfo = CaptureJobInformation( + firstTimeUser: true, + identifier: JobIdentifier( + jobID: 123 + ), + uploadSecret: "DEADBEEF_DEAD_BEEF_DEAD_BEEFDEADBEEF") + let sessionSettings = HVCameraSettings() + + do { + _ = try await HVPartnerSDK.sharedInstance.startCaptureSession( + settings: sessionSettings, + info: jobInfo) + try await HVPartnerSDK.sharedInstance.startCaptureFlow() + } catch let error as HVSessionError { + print("Known capture flow error: \(error.localizedDescription)") + } catch { + print("Unknown Capture Flow Error: \(error.localizedDescription)") + + } } } - } - .padding() - .onAppear { - HVCameraExterior.sharedInstance.initialize() + .padding() } } } diff --git a/Example/Example/ExampleApp.swift b/Example/Example/ExampleApp.swift index 16774ca..1b0fb09 100644 --- a/Example/Example/ExampleApp.swift +++ b/Example/Example/ExampleApp.swift @@ -6,12 +6,60 @@ // import SwiftUI +import HVCaptureSDK +import BackgroundTasks @main struct ExampleApp: App { + + let uploadTaskIdentifier = "to.hover.uploads" + + init() { + // NOTE: to allow the SDK to handle its own background uploads, uncomment this line + // and comment out the `registerBackgroundTask` line. + //HVPartnerSDK.sharedInstance.registerForBackgroundJobs() + HVPartnerSDK.sharedInstance.initialize() + + registerBackgroundTask() + } + var body: some Scene { WindowGroup { ContentView() } } } + +extension ExampleApp { + /// Example of how we can manually trigger the SDK to perform background uploads + func registerBackgroundTask() { + // set up background tasks for scheduling HVCaptureSDK uploads, if applicable + BGTaskScheduler.shared.register(forTaskWithIdentifier: uploadTaskIdentifier, using: nil) { task in + self.uploadCaptureDataInBackground(task: task as! BGProcessingTask) + } + + // schedule the background task to be executed + let request = BGProcessingTaskRequest(identifier: uploadTaskIdentifier) + request.requiresNetworkConnectivity = true + request.requiresExternalPower = false + try? BGTaskScheduler.shared.submit(request) + } + + func uploadCaptureDataInBackground(task: BGProcessingTask) { + let processingTask = Task { + do { + // set up HVPartnerSDK for background uploading (on wifi only) + // NOTE: can configure whether to upload on wifi only (`true`) vs. wifi + cellular (`false`) + try await HVPartnerSDK.sharedInstance.initializeForBackground(parameters: .init( + uploadOnWiFiOnly: true)) + task.setTaskCompleted(success: true) + } catch { + task.setTaskCompleted(success: false) + } + } + + task.expirationHandler = { + processingTask.cancel() + } + } +} diff --git a/Package.swift b/Package.swift index f372240..6c1607d 100644 --- a/Package.swift +++ b/Package.swift @@ -19,8 +19,8 @@ let package = Package( name: "HVCaptureSDK", dependencies: [ "ObjcExceptionBridging", + "_HoverSDK", "_HVAVCamera", - "_HVCameraExterior", "_HVCore", "_HVCVPixelBufferHelper", "_XCGLogger", @@ -32,33 +32,33 @@ let package = Package( .binaryTarget( name: "ObjcExceptionBridging", - url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/179081155.zip", - checksum: "9a7b6d95fb1c5a2fcbe812dfec2dacdac564cc5eb035fed849994b7ae7798ebd" + url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/203311933.zip", + checksum: "5fa8986aa63f860c681eefece027e919ccdf02e80f4da4a66e701a561cad4e73" ), .binaryTarget( - name: "_HVAVCamera", - url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/179081156.zip", - checksum: "fcbcd6a0359bb95ce70ebd6ed151c7b9fef86bc115f68770d061c570d6ec26f9" + name: "_HoverSDK", + url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/203311994.zip", + checksum: "c4ae23a5ebb048c40b346f41d5757ba24b50980f017b8a1ff27f35902ef4ef40" ), .binaryTarget( - name: "_HVCameraExterior", - url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/179081164.zip", - checksum: "391966af56f124238dd8aaab383f2ce286a3821ef264636cf37c9545c3d0c586" + name: "_HVAVCamera", + url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/203311934.zip", + checksum: "2355881d8b1b50a0cec6d7480f3a10db5994abaef5bca0a4c6a52bc56b586660" ), .binaryTarget( name: "_HVCore", - url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/179081167.zip", - checksum: "096e8fdb1a1ecf01c6fd0a2133a7bc5516043dc670bdc8a81af66e9e7a0855c3" + url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/203311990.zip", + checksum: "44152baf504ab9872a357f298ef3e38f0c482de19d11a0effcfcf9f11ca96093" ), .binaryTarget( name: "_HVCVPixelBufferHelper", - url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/179081161.zip", - checksum: "59d089565ef612213ac325fd2ae7e0dad2a3a010be7613bba24a7a86ad435e7b" + url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/203311987.zip", + checksum: "838cfb39bbd2bdc66e5298e1dfa6b624fb7275ce1d7c5fc94bb717cf35efab1b" ), .binaryTarget( name: "_XCGLogger", - url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/179081168.zip", - checksum: "95110df7e425f8a770cf5b7192605917b1ed352dee0c5166ba3df7f3406f9d72" + url: "https://api.github.com/repos/hoverinc/hover-capture-ios/releases/assets/203311996.zip", + checksum: "2b774d6d83aa1ee5205fd169a22d5fd6e5340b050e06c52227a503b3298e1a5f" ), ] ) \ No newline at end of file diff --git a/README.md b/README.md index 274bf22..848e827 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,9 @@ The HOVER Capture iOS SDK provides a user flow for capturing and submitting 3D s The HOVER Capture iOS SDK is distributed using Swift Package Manager. You may integrate it using Xcode or a `Package.swift` file. -> [!TIP] -> Swift Package Manager requires a properly configured `.netrc` file to download binary assets from the private repository. Please see [Authenticating with .netrc](#authenticating-with-netrc) below for instructions on how to properly configure this. -
Xcode - + To integrate the SDK using Xcode, perform the following steps: 1. In the Xcode Project Navigator pane (on the left side), select your project. @@ -30,7 +27,6 @@ To integrate the SDK using Xcode, perform the following steps: 3. Select the Package Dependencies tab on the top 4. Click the + button under the Packages list. 5. In the "Search or Enter Package URL" search bar, enter the repository url (`https://github.com/hoverinc/hover-capture-ios.git`) - - We recommend using https to mitigate some bumps with github authentication. 6. Click "Add Package" 7. Select a target to add the `HVCaptureSDK` library. @@ -38,19 +34,18 @@ To integrate the SDK using Xcode, perform the following steps:
Package.swift - + To integrate the SDK into a Swift package, add the following line to your `dependencies` array in your `Package.swift` manifest: ```swift -.package(url: "https://github.com/hoverinc/hover-capture-ios.git", from: "0.1.1") +.package(url: "https://github.com/hoverinc/hover-capture-ios.git", from: "2.0.0") ```
## Usage -> [!CAUTION] -> APIs are non-final and considered unstable. +The `Example` app in the repository provides a minimal example of how to integrate the SDK into an application. Additionally, the [Getting Stated](https://hoverinc.github.io/hover-capture-ios/documentation/hvcapturesdk/gettingstarted) guide and [Tutorials](https://hoverinc.github.io/hover-capture-ios/tutorials/tutorials/) have more comprehensive and up-to-date documentation regartding SDK usage, customization and integration. ### Initializing the SDK @@ -58,13 +53,13 @@ The SDK should be initialized as early as possible in the app lifecycle. This is As such, the SDK should (ideally) be initialized in host application’s `applicationDidFinishLaunching` method, so that the SDK can continue uploading any files that remain to be uploaded. This helps expedite 3D model generation, as we need all the captured images and metadata to begin the 3D reconstruction process. ```swift -import HVCamera +import HVCaptureSDK class AppDelegate: UIResponder, UIApplicationDelegate { // ... @MainActor private func applicationDidFinishLaunching(_ notification: Notification) { - HVCameraExterior.sharedInstance.initialize() + HVPartnerSDK.sharedInstance.initialize() } } ``` @@ -74,7 +69,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { The host app can launch the SDK in any way it sees fit, as long as there is an active ViewController somewhere in the app. Here is one example using SwiftUI of launching the SDK capture flow on a button click: ```swift -import HVCamera +import HVCaptureSDK import SwiftUI struct FooView: View { @@ -87,7 +82,7 @@ struct FooView: View { Button("Start Capture") { let captureTask = Task { do { - try await HVCameraExterior.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) + try await HVPartnerSDK.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) } catch let error as HVSessionError { // maybe handle our known errors here switch error.kind { @@ -127,7 +122,7 @@ Since we execute asynchronously, within a Swift ``Task``, we also honor its canc ```swift let captureTask = Task { do { - try await HVCameraExterior.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) + try await HVPartnerSDK.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) } catch let error as HVSessionError { switch error.kind { case .UserCancelled: @@ -141,39 +136,3 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: { captureTask.cancel() }) ``` - -## Troubleshooting - -### Authenticating with `.netrc` - -The SDK wraps a number of sub-frameworks into one tidy package to simplify distribution and integration. However, to avoid checking these binaries into the repository, we host them as artifacts within the associated Github Release. To meet Swift Package Manager's security requirements, we must use Github's API to download these binary assets, and thus authenticate with the Github API using `netrc` to authorize the download. - -Your `netrc` file should look something like this: - -``` -machine api.github.com - login schrismartin - password ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -``` - -- `machine` – This will be `api.github.com`, as SPM will attempt to download the binary frameworks via Github's API -- `login` – This will be your Github username, omitting any `@domain.com` suffix. -- `password` – This will be a [Github Personal Access Token](#github-personal-access-token) - -After making any modifications to this file, you will want to restart Xcode for the changes to take effect. You may need to reset package caches if you continue to see errors. - -> [!NOTE] -> An incorrect configuration of this file will result in an `badResponseStatusCode(404)` error when attempting to download a framework binary. While a 404 typically indicates that a resources doesn't exist, in this case, it means it doesn't exist _for you_. If your `.netrc` file is correctly configured and you're still seeing this issue, please review the scopes for your configured Github PAT. - -### Github Personal Access Tokens: - -Both Xcode and the `.netrc` file will need access to Github in order to seamlessly download the requirements to build & run the SDK. The associated Github Personal Access Token (PAT) will need to be configured as below. - -1. Make sure you’re signed into Github for the account that you need to authenticate -2. Navigate to Settings → Developer Settings → Personal Access Tokens -3. Click “Generate new token” → “Generate new token (classic)” -4. Generate a new token that contains at least the following scopes - - `admin:public_key` - - `write:discussion` - - `repo` - - `user` diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/CaptureSDK.md b/Sources/HVCaptureSDK/HVCaptureSDK.docc/CaptureSDK.md index 7322a92..26ffa3b 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/CaptureSDK.md +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/CaptureSDK.md @@ -1,4 +1,4 @@ -# ``_HVCameraExterior`` +# ``_HoverSDK`` The HOVER capture experience diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/GettingStarted.md b/Sources/HVCaptureSDK/HVCaptureSDK.docc/GettingStarted.md index dbf39ff..e092966 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/GettingStarted.md +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/GettingStarted.md @@ -1,4 +1,4 @@ -# GettingStarted +# Getting Started Initialize the SDK, create a capture session and begin the capture flow UI. @@ -25,25 +25,27 @@ The SDK should be initialized as early as possible in the app lifecycle. This is As such, the SDK should (ideally) be initialized in host application’s `applicationDidFinishLaunching` method, so that the SDK can continue uploading any files that remain to be uploaded. This helps expedite 3D model generation, as we need all the captured images and metadata to begin the 3D reconstruction process. ```swift -import HVCamera +import HVPartnerSDK class AppDelegate: UIResponder, UIApplicationDelegate { // ... @MainActor private func applicationDidFinishLaunching(_ notification: Notification) { - HVCameraExterior.sharedInstance.initialize() + HVPartnerSDK.sharedInstance.initialize() } } ``` While running this in `applicationDidFinishLaunching` would be ideal, at a minimum it should be run at some point prior to starting a capture session. +NOTE: if background uploads are both enabled and wanted, the application should call `HVPartnerSDK.sharedInstance.registerForBackgroundJobs()` before the app completes launching. See the section on [Supporting Background Uploads](#supporting-background-uploads) for further details. + ### Creating a Capture Session The host app can launch the SDK in any way it sees fit, as long as there is an active `ViewController` running somewhere in the app. Here is a minimal example of launching the SDK capture flow on a button click using `SwiftUI`: ```swift -import HVCamera +import HVCaptureSDK import SwiftUI struct FooView: View { @@ -56,8 +58,8 @@ struct FooView: View { Button("Start Capture") { let captureTask = Task { do { - try await HVCameraExterior.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) - try await HVCameraExterior.sharedInstance.startCaptureFlow() + try await HVPartnerSDK.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) + try await HVPartnerSDK.sharedInstance.startCaptureFlow() } catch let error as HVSessionError { // TODO: handle the known errors here print("Known capture flow error: \(error.localizedDescription)") @@ -76,17 +78,17 @@ struct FooView: View { } ``` -Note that the SDK executes asynchronously, and the task that calls ``startCaptureSession`` will suspend until the capture flow completes. As such, there's a very linear flow to interacting with the SDK, and once the call's `await` returns, the host app knows the SDK's capture session is complete (as seen with the `captureSessionCompleted` function above). If the capture session encountered a fatal error, it will raise the error as an exception. +Note that the SDK executes asynchronously, and the task that calls ``HVPartnerSDK/startCaptureSession(settings:info:)`` will suspend until the capture flow completes. As such, there's a very linear flow to interacting with the SDK, and once the call's `await` returns, the host app knows the SDK's capture session is complete (as seen with the `captureSessionCompleted` function above). If the capture session encountered a fatal error, it will raise the error as an exception. #### Cancelling a Capture Session -Since we execute asynchronously within a Swift ``Task``, we also honor its cancellation functionality and stop the capture session and capture flow UI if the task is cancelled. +Since we execute asynchronously within a Swift `Task`, we also honor its cancellation functionality and stop the capture session and capture flow UI if the task is cancelled. ```swift let captureTask = Task { do { - try await HVCameraExterior.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) - try await HVCameraExterior.sharedInstance.startCaptureFlow() + try await HVPartnerSDK.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) + try await HVPartnerSDK.sharedInstance.startCaptureFlow() } catch let error as HVSessionError { switch error.kind { case .UserCancelled: @@ -105,14 +107,15 @@ DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: { Since the capture flow proceeds asynchronously, the host app may want to monitor the local job status as the capture proceeds. There are a few methods for obtaining Job status: -1. on-demand: The ``HVCameraExterior`` class exposes a public method ``_HVCameraExterior.getClientJobStatus(for:)``. This is an `async` method that will return what the requested `Job`'s current status as a ``JobStatus``. If the requested ``Job`` doesn't exist locally, then it will raise a ``HVJobError`` exception. -2. streaming: The ``HVCameraExterior`` class also exposes a public method ``getJobStateObservable`` that returns a `Combine` publisher for the requested `Job`. The publisher will emit ``JobStatus`` instances whenever there's a change in the `Job`'s status. Additionally, ``startCaptureSession`` will return what the current `Job`'s status is when called, so together with `getJobStateObservable` you can track the whole status history for the `Job` (n.b. the initial state won't be published for a `Job`, so to get the complete status history you need to use the initial state returned from ``_HVCameraExterior.startCaptureSession`` in conjunction with the publisher from `getJobStateObservable`). The initial Job state will generally be ``JobStatus.Created`` if newly created, or ``JobStatus.Draft`` if resuming an existing Job. +1. on-demand: The ``HVPartnerSDK`` class exposes a public method ``HVPartnerSDK/getClientJobStatus(for:)``. This is an `async` method that will return what the requested `Job`'s current status as a ``JobStatus``. If the requested ``Job`` doesn't exist locally, then it will raise a ``HVJobError`` exception. +2. streaming: The ``HVPartnerSDK`` class also exposes a public method ``HVPartnerSDK/getJobStateObservable(for:)`` that returns a `Combine` publisher for the requested `Job`. The publisher will emit ``JobStatus`` instances whenever there's a change in the `Job`'s status. Additionally, `startCaptureSession` will return what the current `Job`'s status is when called, so together with `getJobStateObservable` you can track the whole status history for the `Job` (n.b. the initial state won't be published for a `Job`, so to get the complete status history you need to use the initial state returned from ``HVPartnerSDK/startCaptureSession(settings:info:)`` in conjunction with the publisher from `getJobStateObservable`). The initial Job state will generally be ``JobStatus.Created`` if newly created, or ``JobStatus.Draft`` if resuming an existing Job. For example, adapting the previous example to monitor the `Job` status and build a complete `JobStatus` history for the capture session, you can do: ```swift -import HVCamera +import HVCaptureSDK import SwiftUI +import Combine struct FooView: View { let jobInfo: CaptureJobInformation @@ -128,12 +131,12 @@ struct FooView: View { Button("Start Capture") { let captureTask = Task { do { - let jobState = try await HVCameraExterior.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) + let jobState = try await HVPartnerSDK.sharedInstance.startCaptureSession(settings: sessionSettings, info: jobInfo) jobStatusHistory[jobInfo.identifier]?.append(jobStatus) // check if we have a listener for the job already, so we don't make duplicate listeners each time the view is created if jobCancellables[jobInfo.identifier] == nil { - let cancellable = HVCameraExterior.sharedInstance.getJobStateObservable(for: jobInfo.identifier).sink(receiveValue: { (jobState: JobStatus) in - // NOTE: you can t ake various actions here based on the status change + let cancellable = HVPartnerSDK.sharedInstance.getJobStateObservable(for: jobInfo.identifier).sink(receiveValue: { (jobState: JobStatus) in + // NOTE: you can take various actions here based on the status change if case let .UploadProgress(_, uploadStatus) = jobState { print("Job@State: \(jobState) --> File@State: \(String(describing: uploadStatus))") } else if case let .Error(_, error) = jobState { @@ -145,7 +148,7 @@ struct FooView: View { }) jobCancellables[jobInfo.identifier] = cancellable } - try await HVCameraExterior.sharedInstance.startCaptureFlow() + try await HVPartnerSDK.sharedInstance.startCaptureFlow() } catch let error as HVSessionError { // TODO: handle the known errors here print("Known capture flow error: \(error.localizedDescription)") @@ -172,7 +175,21 @@ To enable background uploads, follow these steps: - This can be done by selecting your project in the Project navigator, selecting your application target, going to Signing & Capabilities, and "+ Capability" 2. Check the "Background Processing" checkbox within the "Background Tasks" capability. -2. Add `to.hover.uploads` to the [`BGTaskSchedulerPermittedIdentifiers`](https://developer.apple.com/documentation/bundleresources/information_property_list/bgtaskschedulerpermittedidentifiers)) +3. Add `to.hover.uploads` to the [`BGTaskSchedulerPermittedIdentifiers`](https://developer.apple.com/documentation/bundleresources/information_property_list/bgtaskschedulerpermittedidentifiers)) key in your `Info.plist`. +To use background uploads for the given app session, the app needs to opt-in to using it. This can be done by calling `HVPartnerSDK.sharedInstance.registerForBackgroundJobs`, for example: +```swift +class AppDelegate: UIResponder, UIApplicationDelegate { + // ... + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + HVPartnerSDK.sharedInstance.registerForBackgroundJobs() + HVPartnerSDK.sharedInstance.initialize() + } +} +``` + +> Warning: Since we use `BGTaskScheduler` for our background processing, we need to call ``HVPartnerSDK/registerForBackgroundJobs()`` **before** the application finishes launching. If not, then the application will raise an `NSInternalInconsistencyException` exception with the reason: `'All launch handlers must be registered before application finishes launching'`. This is a constraint imposed by the [BGTaskScheduler framework itself](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler/register(fortaskwithidentifier:using:launchhandler:)#Discussion) and if ignored will likely crash the application. +While using `registerForBackgroundJobs` enables the SDK to schedule background tasks on its own as needed, it's also possible to disable automatic background task scheduling and have more manual control over background task scheduling. This can be useful for applications that want closer control over background tasks spawned from the SDK and which already have their own background task scheduling. This can be achieved by **not** calling ``HVPartnerSDK/registerForBackgroundJobs()``, and instead calling ``HVPartnerSDK/initializeForBackground(parameters:)`` from within a [BGProcessingTask](https://developer.apple.com/documentation/backgroundtasks/bgprocessingtask). Under the hood, this will check if there are pending uploads. If there are no pending uploads, then it will exit and do nothing. If there are, then it'll run asynchronously and attempt to complete the pending uploads, performing a single upload at a time and exiting once the pending upload queue has been completed. diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-01.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-01.swift index b881d2b..1377781 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-01.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-01.swift @@ -3,55 +3,43 @@ import SwiftUI struct HVTheme { // ... public init( - primary: Color? = nil, - onPrimary: Color? = nil, - primaryContainer: Color? = nil, - onPrimaryContainer: Color? = nil, - secondary: Color? = nil, - onSecondary: Color? = nil, - subtleElements: Color? = nil, - error: Color? = nil, - onError: Color? = nil, - surface: Color? = nil, - onSurface: Color? = nil, - textPrimary: Color? = nil, - textSecondary: Color? = nil, - textPrimaryDark: Color? = nil, - footerCTABackground: Color? = nil, - toolbarBack: Image? = nil, - toolbarClose: Image? = nil, - permissionCameraHeader: String? = nil, - permissionCameraBody: String? = nil, - permissionCameraConfirm: String? = nil, - permissionCameraDeniedButtonText: String? = nil, - permissionLocationHeader: String? = nil, - permissionLocationBody: String? = nil, - permissionLocationConfirm: String? = nil, - galleryDeleteConfirmHeader: String? = nil, - galleryDeleteConfirmBody: String? = nil, - successIcon: Image? = nil, - successHeader: String? = nil, - successMessage: String? = nil, - successNextSteps: String? = nil, - cameraCaptureBackground: Color? = nil, - toolbarBackground: Color? = nil, - toolbarHeader: Color? = nil, - toolbarIcon: Color? = nil, - textLink: Color? = nil, - illustrationMainStroke: Color? = nil, - illustrationSubStroke: Color? = nil, - gridItemBorder: Color? = nil, - gridItemBackground: Color? = nil, - gridItemBorderRadius: CGFloat? = nil, - primaryButtonBackground: HVColorStateList? = nil, - primaryButtonText: HVColorStateList? = nil, - primaryButtonShadowBackground: HVColorStateList? = nil, - buttonCornerRadius: CGFloat? = nil, - secondaryButtonBackground: HVColorStateList? = nil, - secondaryButtonText: HVColorStateList? = nil, - secondaryButtonShadowBackground: HVColorStateList? = nil, - secondaryButtonStrokeColor: HVColorStateList? = nil, - secondaryButtonStrokeWidth: CGFloat? = nil + hoverCaptureSecondaryButtonStrokeColor: HVColorStateList? = nil, + hoverCaptureSecondaryButtonStrokeWidth: CGFloat? = nil, + hoverCaptureButtonBorderRadius: CGFloat? = nil, + hoverCaptureToolbarIcon: Color? = nil, + hoverCaptureToolbarBack: Image? = nil, + hoverCaptureToolbarHeader: Color? = nil, + hoverCaptureToolbarBackground: Color? = nil, + hoverCaptureTextHeading: Color? = nil, + hoverCaptureTextLight: Color? = nil, + hoverCaptureIllustrationSubStroke: Color? = nil, + hoverCaptureGridItemBorder: Color? = nil, + hoverCaptureGridItemBackground: Color? = nil, + hoverCaptureGridItemBorderRadius: CGFloat? = nil, + hoverCaptureIllustrationMainStroke: Color? = nil, + hoverCaptureTextLink: Color? = nil, + hoverCaptureTutorialImageOverlayStroke: Color? = nil, + hoverCaptureGalleryDeleteConfirmHeader: String? = nil, + hoverCaptureGalleryDeleteConfirmBody: String? = nil, + hoverCaptureError: Color? = nil, + hoverCaptureCameraBackground: Color? = nil, + hoverCaptureBadgeBackground: Color? = nil, + hoverCaptureBadgeStroke: Color? = nil, + hoverCaptureBadgeText: Color? = nil, + hoverCaptureSuccessIcon: Image? = nil, + hoverCaptureSuccessNextSteps: String? = nil, + hoverCaptureSuccessHeader: String? = nil, + hoverCaptureSuccessMessage: String? = nil, + hoverCaptureSuccessConfirm: String? = nil, + hoverCaptureFooterCtaBackground: Color? = nil, + hoverCaptureDecorativeIcon: Color? = nil, + hoverCapturePermissionCameraHeader: String? = nil, + hoverCapturePermissionCameraBody: String? = nil, + hoverCapturePermissionCameraConfirm: String? = nil, + hoverCapturePermissionCameraDeniedButtonText: String? = nil, + hoverCapturePermissionLocationHeader: String? = nil, + hoverCapturePermissionLocationBody: String? = nil, + hoverCapturePermissionLocationConfirm: String? = nil ) { // ... } diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-02.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-02.swift index 59247fa..d67c9d3 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-02.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-02.swift @@ -10,55 +10,42 @@ public struct HVColorStateList { struct HVTheme { // ... public init( - primary: Color? = nil, - onPrimary: Color? = nil, - primaryContainer: Color? = nil, - onPrimaryContainer: Color? = nil, - secondary: Color? = nil, - onSecondary: Color? = nil, - subtleElements: Color? = nil, - error: Color? = nil, - onError: Color? = nil, - surface: Color? = nil, - onSurface: Color? = nil, - textPrimary: Color? = nil, - textSecondary: Color? = nil, - textPrimaryDark: Color? = nil, - footerCTABackground: Color? = nil, - toolbarBack: Image? = nil, - toolbarClose: Image? = nil, - permissionCameraHeader: String? = nil, - permissionCameraBody: String? = nil, - permissionCameraConfirm: String? = nil, - permissionCameraDeniedButtonText: String? = nil, - permissionLocationHeader: String? = nil, - permissionLocationBody: String? = nil, - permissionLocationConfirm: String? = nil, - galleryDeleteConfirmHeader: String? = nil, - galleryDeleteConfirmBody: String? = nil, - successIcon: Image? = nil, - successHeader: String? = nil, - successMessage: String? = nil, - successNextSteps: String? = nil, - cameraCaptureBackground: Color? = nil, - toolbarBackground: Color? = nil, - toolbarHeader: Color? = nil, - toolbarIcon: Color? = nil, - textLink: Color? = nil, - illustrationMainStroke: Color? = nil, - illustrationSubStroke: Color? = nil, - gridItemBorder: Color? = nil, - gridItemBackground: Color? = nil, - gridItemBorderRadius: CGFloat? = nil, - primaryButtonBackground: HVColorStateList? = nil, - primaryButtonText: HVColorStateList? = nil, - primaryButtonShadowBackground: HVColorStateList? = nil, - buttonCornerRadius: CGFloat? = nil, - secondaryButtonBackground: HVColorStateList? = nil, - secondaryButtonText: HVColorStateList? = nil, - secondaryButtonShadowBackground: HVColorStateList? = nil, - secondaryButtonStrokeColor: HVColorStateList? = nil, - secondaryButtonStrokeWidth: CGFloat? = nil + hoverCapturePrimaryButtonBackground: HVColorStateList? = nil, + hoverCapturePrimaryButtonText: HVColorStateList? = nil, + hoverCapturePrimaryButtonShadowBackground: HVColorStateList? = nil, + hoverCaptureSecondaryButtonBackground: HVColorStateList? = nil, + hoverCaptureSecondaryButtonText: HVColorStateList? = nil, + hoverCaptureSecondaryButtonShadowBackground: HVColorStateList? = nil, + hoverCaptureSecondaryButtonStrokeColor: HVColorStateList? = nil, + hoverCaptureSecondaryButtonStrokeWidth: CGFloat? = nil, + hoverCaptureButtonBorderRadius: CGFloat? = nil, + hoverCaptureToolbarIcon: Color? = nil, + hoverCaptureToolbarBack: Image? = nil, + hoverCaptureToolbarHeader: Color? = nil, + hoverCaptureToolbarBackground: Color? = nil, + hoverCaptureTextHeading: Color? = nil, + hoverCaptureTextLight: Color? = nil, + hoverCaptureIllustrationSubStroke: Color? = nil, + hoverCaptureGridItemBorder: Color? = nil, + hoverCaptureGridItemBackground: Color? = nil, + hoverCaptureGridItemBorderRadius: CGFloat? = nil, + hoverCaptureIllustrationMainStroke: Color? = nil, + hoverCaptureTextLink: Color? = nil, + hoverCaptureTutorialImageOverlayStroke: Color? = nil, + hoverCaptureGalleryDeleteConfirmHeader: String? = nil, + hoverCaptureGalleryDeleteConfirmBody: String? = nil, + hoverCaptureError: Color? = nil, + hoverCaptureCameraBackground: Color? = nil, + hoverCaptureBadgeBackground: Color? = nil, + hoverCaptureBadgeStroke: Color? = nil, + hoverCaptureBadgeText: Color? = nil, + hoverCaptureSuccessIcon: Image? = nil, + hoverCaptureSuccessNextSteps: String? = nil, + hoverCaptureSuccessHeader: String? = nil, + hoverCaptureSuccessMessage: String? = nil, + hoverCaptureSuccessConfirm: String? = nil, + hoverCaptureFooterCtaBackground: Color? = nil, + hoverCaptureDecorativeIcon: Color? = nil ) { // ... } diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-05.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-05.swift index 8803107..870594d 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-05.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-05.swift @@ -2,31 +2,74 @@ import HVCaptureSDK import SwiftUI extension HVTheme { - static let tutorialTheme = HVTheme( - primary: .green, - onPrimary: .black, - primaryContainer: .blue, - onPrimaryContainer: .black, - secondary: .mint, - onSecondary: .black, - subtleElements: .indigo, - error: .red, - onError: .pink, - surface: .pink, - onSurface: .red, - textPrimary: .black, - textSecondary: - Color(uiColor: .darkGray), - textPrimaryDark: .gray, - footerCTABackground: - Color(uiColor: .lightGray), - cameraCaptureBackground: .teal, - toolbarBackground: - .orange.opacity(0.3), - toolbarHeader: .purple, - toolbarIcon: .mint, - textLink: .yellow - ) +static let tutorialTheme = HVTheme( + hoverCapturePrimaryButtonBackground: + HVColorStateList( + active: .black, + pressed: .blue, + inactive: .brown), + hoverCapturePrimaryButtonText: + HVColorStateList( + active: .cyan, + pressed: .gray, + inactive: .green), + hoverCapturePrimaryButtonShadowBackground: + HVColorStateList( + active: .indigo, + pressed: .mint, + inactive: .orange), + hoverCaptureSecondaryButtonBackground: + HVColorStateList( + active: .pink, + pressed: .purple, + inactive: .red), + hoverCaptureSecondaryButtonText: + HVColorStateList( + active: .teal, + pressed: .white, + inactive: .yellow), + hoverCaptureSecondaryButtonShadowBackground: + HVColorStateList( + active: .black, + pressed: .cyan, + inactive: .indigo), + hoverCaptureSecondaryButtonStrokeColor: + HVColorStateList( + active: .pink, + pressed: .teal, + inactive: .black), + hoverCaptureToolbarIcon: .blue, + hoverCaptureToolbarHeader: .gray, + hoverCaptureToolbarBackground: .mint, + hoverCaptureTextHeading: .purple, + hoverCaptureTextLight: .black, + hoverCaptureIllustrationSubStroke: .cyan, + hoverCaptureGridItemBorder: .teal, + hoverCaptureGridItemBackground: .brown, + hoverCaptureIllustrationMainStroke: .green, + hoverCaptureTextLink: .orange, + hoverCaptureTutorialImageOverlayStroke: .red, + hoverCaptureError: .yellow, + hoverCaptureCameraBackground: .indigo, + hoverCaptureBadgeBackground: .black, + hoverCaptureBadgeStroke: + Color( + hue: 0.5, + saturation: 0.5, + brightness: 0.5, + opacity: 0.5), + hoverCaptureBadgeText: + Color(cgColor: + CGColor( + red: 1.0, + green: 0.5, + blue: 0.3, + alpha: 0.9) + ), + hoverCaptureFooterCtaBackground: + .green.opacity(0.5), + hoverCaptureDecorativeIcon: .gray +) } struct ContentView: View { @@ -47,10 +90,10 @@ struct ContentView: View { var settings = HVCameraSettings() settings.theme = .tutorialTheme do { - try await HVCameraExterior.sharedInstance + try await HVPartnerSDK.sharedInstance .startCaptureSession(settings: settings, info: jobInfo) - try await HVCameraExterior.sharedInstance + try await HVPartnerSDK.sharedInstance .startCaptureFlow() } catch { // ... diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-06.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-06.swift index 4bb013c..d5b0830 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-06.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-06.swift @@ -2,37 +2,76 @@ import HVCaptureSDK import SwiftUI extension HVTheme { - static let tutorialTheme = HVTheme( - primary: .green, - onPrimary: .black, - primaryContainer: .blue, - onPrimaryContainer: .black, - secondary: .mint, - onSecondary: .black, - subtleElements: .indigo, - error: .red, - onError: .pink, - surface: .pink, - onSurface: .red, - textPrimary: .black, - textSecondary: - Color(uiColor: .darkGray), - textPrimaryDark: .gray, - footerCTABackground: - Color(uiColor: .lightGray), - toolbarBack: - Image(systemName: "paperplane"), - toolbarClose: - Image(systemName: "heart.slash"), - successIcon: - Image(systemName: "checkmark.seal.fill"), - cameraCaptureBackground: .teal, - toolbarBackground: - .orange.opacity(0.3), - toolbarHeader: .purple, - toolbarIcon: .mint, - textLink: .yellow - ) +static let tutorialTheme = HVTheme( + hoverCapturePrimaryButtonBackground: + HVColorStateList( + active: .black, + pressed: .blue, + inactive: .brown), + hoverCapturePrimaryButtonText: + HVColorStateList( + active: .cyan, + pressed: .gray, + inactive: .green), + hoverCapturePrimaryButtonShadowBackground: + HVColorStateList( + active: .indigo, + pressed: .mint, + inactive: .orange), + hoverCaptureSecondaryButtonBackground: + HVColorStateList( + active: .pink, + pressed: .purple, + inactive: .red), + hoverCaptureSecondaryButtonText: + HVColorStateList( + active: .teal, + pressed: .white, + inactive: .yellow), + hoverCaptureSecondaryButtonShadowBackground: + HVColorStateList( + active: .black, + pressed: .cyan, + inactive: .indigo), + hoverCaptureSecondaryButtonStrokeColor: + HVColorStateList( + active: .pink, + pressed: .teal, + inactive: .black), + hoverCaptureToolbarIcon: .blue, + hoverCaptureToolbarBack: Image(systemName: "arrowshape.backward.circle"), + hoverCaptureToolbarHeader: .gray, + hoverCaptureToolbarBackground: .mint, + hoverCaptureTextHeading: .purple, + hoverCaptureTextLight: .black, + hoverCaptureIllustrationSubStroke: .cyan, + hoverCaptureGridItemBorder: .teal, + hoverCaptureGridItemBackground: .brown, + hoverCaptureIllustrationMainStroke: .green, + hoverCaptureTextLink: .orange, + hoverCaptureTutorialImageOverlayStroke: .red, + hoverCaptureError: .yellow, + hoverCaptureCameraBackground: .indigo, + hoverCaptureBadgeBackground: .black, + hoverCaptureBadgeStroke: + Color( + hue: 0.5, + saturation: 0.5, + brightness: 0.5, + opacity: 0.5), + hoverCaptureBadgeText: + Color(cgColor: + CGColor( + red: 1.0, + green: 0.5, + blue: 0.3, + alpha: 0.9) + ), + hoverCaptureSuccessIcon: Image(systemName: "paperplane"), + hoverCaptureFooterCtaBackground: + .green.opacity(0.5), + hoverCaptureDecorativeIcon: .gray +) } struct ContentView: View { @@ -53,10 +92,10 @@ struct ContentView: View { var settings = HVCameraSettings() settings.theme = .tutorialTheme do { - try await HVCameraExterior.sharedInstance + try await HVPartnerSDK.sharedInstance .startCaptureSession(settings: settings, info: jobInfo) - try await HVCameraExterior.sharedInstance + try await HVPartnerSDK.sharedInstance .startCaptureFlow() } catch { // ... diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-07.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-07.swift index fc8029f..abcd163 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-07.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-07.swift @@ -2,42 +2,77 @@ import HVCaptureSDK import SwiftUI extension HVTheme { - static let tutorialTheme = HVTheme( - primary: .green, - onPrimary: .black, - primaryContainer: .blue, - onPrimaryContainer: .black, - secondary: .mint, - onSecondary: .black, - subtleElements: .indigo, - error: .red, - onError: .pink, - surface: .pink, - onSurface: .red, - textPrimary: .black, - textSecondary: - Color(uiColor: .darkGray), - textPrimaryDark: .gray, - footerCTABackground: - Color(uiColor: .lightGray), - toolbarBack: - Image(systemName: "paperplane"), - toolbarClose: - Image(systemName: "heart.slash"), - successIcon: - Image(systemName: "checkmark.seal.fill"), - cameraCaptureBackground: .teal, - toolbarBackground: - .orange.opacity(0.3), - toolbarHeader: .purple, - toolbarIcon: .mint, - textLink: .yellow, - illustrationMainStroke: .white, - illustrationSubStroke: .black, - gridItemBorder: .purple, - gridItemBackground: .gray, - gridItemBorderRadius: 30 - ) +static let tutorialTheme = HVTheme( + hoverCapturePrimaryButtonBackground: + HVColorStateList( + active: .black, + pressed: .blue, + inactive: .brown), + hoverCapturePrimaryButtonText: + HVColorStateList( + active: .cyan, + pressed: .gray, + inactive: .green), + hoverCapturePrimaryButtonShadowBackground: + HVColorStateList( + active: .indigo, + pressed: .mint, + inactive: .orange), + hoverCaptureSecondaryButtonBackground: + HVColorStateList( + active: .pink, + pressed: .purple, + inactive: .red), + hoverCaptureSecondaryButtonText: + HVColorStateList( + active: .teal, + pressed: .white, + inactive: .yellow), + hoverCaptureSecondaryButtonShadowBackground: + HVColorStateList( + active: .black, + pressed: .cyan, + inactive: .indigo), + hoverCaptureSecondaryButtonStrokeColor: + HVColorStateList( + active: .pink, + pressed: .teal, + inactive: .black), + hoverCaptureToolbarIcon: .blue, + hoverCaptureToolbarBack: Image(systemName: "arrowshape.backward.circle"), + hoverCaptureToolbarHeader: .gray, + hoverCaptureToolbarBackground: .mint, + hoverCaptureTextHeading: .purple, + hoverCaptureTextLight: .black, + hoverCaptureIllustrationSubStroke: .yellow, + hoverCaptureGridItemBorder: .purple, + hoverCaptureGridItemBackground: .black, + hoverCaptureGridItemBorderRadius: .infinity, + hoverCaptureIllustrationMainStroke: .red, + hoverCaptureTextLink: .green, + hoverCaptureTutorialImageOverlayStroke: .indigo, + hoverCaptureError: .yellow, + hoverCaptureCameraBackground: .indigo, + hoverCaptureBadgeBackground: .black, + hoverCaptureBadgeStroke: + Color( + hue: 0.5, + saturation: 0.5, + brightness: 0.5, + opacity: 0.5), + hoverCaptureBadgeText: + Color(cgColor: + CGColor( + red: 1.0, + green: 0.5, + blue: 0.3, + alpha: 0.9) + ), + hoverCaptureSuccessIcon: Image(systemName: "paperplane"), + hoverCaptureFooterCtaBackground: + .green.opacity(0.5), + hoverCaptureDecorativeIcon: .gray +) } struct ContentView: View { @@ -58,10 +93,10 @@ struct ContentView: View { var settings = HVCameraSettings() settings.theme = .tutorialTheme do { - try await HVCameraExterior.sharedInstance + try await HVPartnerSDK.sharedInstance .startCaptureSession(settings: settings, info: jobInfo) - try await HVCameraExterior.sharedInstance + try await HVPartnerSDK.sharedInstance .startCaptureFlow() } catch { // ... diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-08.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-08.swift index b642953..fef7c78 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-08.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-08.swift @@ -2,62 +2,81 @@ import HVCaptureSDK import SwiftUI extension HVTheme { - static let tutorialTheme = HVTheme( - primary: .green, - onPrimary: .black, - primaryContainer: .blue, - onPrimaryContainer: .black, - secondary: .mint, - onSecondary: .black, - subtleElements: .indigo, - error: .red, - onError: .pink, - surface: .pink, - onSurface: .red, - textPrimary: .black, - textSecondary: - Color(uiColor: .darkGray), - textPrimaryDark: .gray, - footerCTABackground: - Color(uiColor: .lightGray), - toolbarBack: - Image(systemName: "paperplane"), - toolbarClose: - Image(systemName: "heart.slash"), - permissionCameraHeader: - "Camera permissions header is customizable", - permissionCameraBody: - "Camera permissions body is customizable", - permissionCameraConfirm: - "Customizable Camera button", - permissionCameraDeniedButtonText: - "Camera permissions denied button is customizable", - permissionLocationHeader: - "Location permissions header is customizable", - permissionLocationBody: - "Location permissions body is customizable", - permissionLocationConfirm: - "Customizable Location button", - galleryDeleteConfirmHeader: - "Photo gallery delete header is customizable", - galleryDeleteConfirmBody: - "Customizable delete button", - successIcon: Image(systemName: "checkmark.seal.fill"), - successHeader: "Success screen header is customizable", - successMessage: "Success screen message is customizable", - successNextSteps: "Success screen next steps are customizable", - successButtonText: "Continue", - cameraCaptureBackground: .teal, - toolbarBackground: .orange.opacity(0.3), - toolbarHeader: .purple, - toolbarIcon: .mint, - textLink: .yellow, - illustrationMainStroke: .white, - illustrationSubStroke: .black, - gridItemBorder: .purple, - gridItemBackground: .gray, - gridItemBorderRadius: 30 - ) +static let tutorialTheme = HVTheme( + hoverCapturePrimaryButtonBackground: + HVColorStateList( + active: .black, + pressed: .blue, + inactive: .brown), + hoverCapturePrimaryButtonText: + HVColorStateList( + active: .cyan, + pressed: .gray, + inactive: .green), + hoverCapturePrimaryButtonShadowBackground: + HVColorStateList( + active: .indigo, + pressed: .mint, + inactive: .orange), + hoverCaptureSecondaryButtonBackground: + HVColorStateList( + active: .pink, + pressed: .purple, + inactive: .red), + hoverCaptureSecondaryButtonText: + HVColorStateList( + active: .teal, + pressed: .white, + inactive: .yellow), + hoverCaptureSecondaryButtonShadowBackground: + HVColorStateList( + active: .black, + pressed: .cyan, + inactive: .indigo), + hoverCaptureSecondaryButtonStrokeColor: + HVColorStateList( + active: .pink, + pressed: .teal, + inactive: .black), + hoverCaptureToolbarIcon: .blue, + hoverCaptureToolbarBack: Image(systemName: "arrowshape.backward.circle"), + hoverCaptureToolbarHeader: .gray, + hoverCaptureToolbarBackground: .mint, + hoverCaptureTextHeading: .purple, + hoverCaptureTextLight: .black, + hoverCaptureIllustrationSubStroke: .yellow, + hoverCaptureGridItemBorder: .purple, + hoverCaptureGridItemBackground: .black, + hoverCaptureGridItemBorderRadius: .infinity, + hoverCaptureIllustrationMainStroke: .red, + hoverCaptureTextLink: .green, + hoverCaptureTutorialImageOverlayStroke: .indigo, + hoverCaptureError: .yellow, + hoverCaptureCameraBackground: .indigo, + hoverCaptureBadgeBackground: .black, + hoverCaptureBadgeStroke: + Color( + hue: 0.5, + saturation: 0.5, + brightness: 0.5, + opacity: 0.5), + hoverCaptureBadgeText: + Color(cgColor: + CGColor( + red: 1.0, + green: 0.5, + blue: 0.3, + alpha: 0.9) + ), + hoverCaptureSuccessIcon: Image(systemName: "paperplane"), + hoverCaptureSuccessNextSteps: "You did it!", + hoverCaptureSuccessHeader: "Great Job!", + hoverCaptureSuccessMessage: "Hooray!", + hoverCaptureSuccessConfirm: "Wooohoo!", + hoverCaptureFooterCtaBackground: + .green.opacity(0.5), + hoverCaptureDecorativeIcon: .gray +) } struct ContentView: View { @@ -78,10 +97,10 @@ struct ContentView: View { var settings = HVCameraSettings() settings.theme = .tutorialTheme do { - try await HVCameraExterior.sharedInstance + try await HoverSDK.sharedInstance .startCaptureSession(settings: settings, info: jobInfo) - try await HVCameraExterior.sharedInstance + try await HoverSDK.sharedInstance .startCaptureFlow() } catch { // ... diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-09.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-09.swift deleted file mode 100644 index e020e43..0000000 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-hvtheme-code-09.swift +++ /dev/null @@ -1,108 +0,0 @@ -import HVCaptureSDK -import SwiftUI - -extension HVTheme { - static let tutorialTheme = HVTheme( - primary: .green, - onPrimary: .black, - primaryContainer: .blue, - onPrimaryContainer: .black, - secondary: .mint, - onSecondary: .black, - subtleElements: .indigo, - error: .red, - onError: .pink, - surface: .pink, - onSurface: .red, - textPrimary: .black, - textSecondary: - Color(uiColor: .darkGray), - textPrimaryDark: .gray, - footerCTABackground: - Color(uiColor: .lightGray), - toolbarBack: - Image(systemName: "paperplane"), - toolbarClose: - Image(systemName: "heart.slash"), - permissionCameraHeader: - "Camera permissions header is customizable", - permissionCameraBody: - "Camera permissions body is customizable", - permissionCameraConfirm: - "Customizable Camera button", - permissionCameraDeniedButtonText: - "Camera permissions denied button is customizable", - permissionLocationHeader: - "Location permissions header is customizable", - permissionLocationBody: - "Location permissions body is customizable", - permissionLocationConfirm: - "Customizable Location button", - galleryDeleteConfirmHeader: - "Photo gallery delete header is customizable", - galleryDeleteConfirmBody: - "Customizable delete button", - successIcon: Image(systemName: "checkmark.seal.fill"), - successHeader: "Success screen header is customizable", - successMessage: "Success screen message is customizable", - successNextSteps: "Success screen next steps are customizable", - successButtonText: "Continue", - cameraCaptureBackground: .teal, - toolbarBackground: .orange.opacity(0.3), - toolbarHeader: .purple, - toolbarIcon: .mint, - textLink: .yellow, - illustrationMainStroke: .white, - illustrationSubStroke: .black, - gridItemBorder: .purple, - gridItemBackground: .gray, - gridItemBorderRadius: 30, - primaryButtonBackground: - HVColorStateList(active: .black, pressed: .gray, inactive: .init(uiColor: .darkGray)), - primaryButtonText: - HVColorStateList(active: .red, pressed: .green, inactive: .blue), - primaryButtonShadowBackground: - HVColorStateList(active: .mint, pressed: .green, inactive: .teal), - buttonCornerRadius: .infinity, - secondaryButtonBackground: - HVColorStateList(active: .red, pressed: .indigo, inactive: .pink), - secondaryButtonText: - HVColorStateList(active: .black, pressed: .gray, inactive: .white), - secondaryButtonShadowBackground: - HVColorStateList(active: .green, pressed: .mint, inactive: .teal), - secondaryButtonStrokeColor: - HVColorStateList(active: .blue, pressed: .teal, inactive: .brown), - secondaryButtonStrokeWidth: 10 - ) -} - -struct ContentView: View { - - // ... - - var body: some View { - NavigationView { - Button("Start Capture") { - - // ... - - // NOTE: pretend this is filled with valid values... - let jobInfo = CaptureJobInformation( - firstTimeUser: true, - identifier: JobIdentifier(jobID: 42, localID: UUID()), - uploadSecret: "String") - var settings = HVCameraSettings() - settings.theme = .tutorialTheme - do { - try await HVCameraExterior.sharedInstance - .startCaptureSession(settings: settings, - info: jobInfo) - try await HVCameraExterior.sharedInstance - .startCaptureFlow() - } catch { - // ... - } - } - } - } -} diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-02.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-02.swift index 7789d0c..f63a2e2 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-02.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-02.swift @@ -6,7 +6,7 @@ struct BinarySDKExampleApp: App { init() { let params = HVCameraExteriorParameters() - HVCameraExterior.sharedInstance.initialize(parameters: params) + HVPartnerSDK.sharedInstance.initialize(parameters: params) } var body: some Scene { diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-04.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-04.swift index f492f5d..01a5029 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-04.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-04.swift @@ -25,8 +25,8 @@ struct ContentView: View { Button("Start Capture") { Task { do { - try await HVCameraExterior.sharedInstance.startCaptureSession(settings: settings, info: jobInfo) - try await HVCameraExterior.sharedInstance.startCaptureFlow() + try await HVPartnerSDK.sharedInstance.startCaptureSession(settings: settings, info: jobInfo) + try await HVPartnerSDK.sharedInstance.startCaptureFlow() } catch let error as HVSessionError { // maybe handle our known errors here switch error.kind { diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-05.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-05.swift index c6a35c5..424fd10 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-05.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-05.swift @@ -27,8 +27,8 @@ struct ContentView: View { Button("Start Capture") { Task { do { - try await HVCameraExterior.sharedInstance.startCaptureSession(settings: settings, info: jobInfo) - try await HVCameraExterior.sharedInstance.startCaptureFlow() + try await HVPartnerSDK.sharedInstance.startCaptureSession(settings: settings, info: jobInfo) + try await HVPartnerSDK.sharedInstance.startCaptureFlow() } catch let error as HVSessionError { // maybe handle our known errors here switch error.kind { diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-06.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-06.swift index b505b17..1206d7f 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-06.swift +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-06.swift @@ -29,11 +29,11 @@ struct ContentView: View { Button("Start Capture") { Task { do { - let initialStatus = try await HVCameraExterior.sharedInstance.startCaptureSession(settings: settings, info: jobInfo) + let initialStatus = try await HVPartnerSDK.sharedInstance.startCaptureSession(settings: settings, info: jobInfo) jobStateHistory[jobInfo.identifier]?.append(initialStatus) // check if we have a listener for the job already, so we don't make duplicate listeners each time the view is created if jobCancellables[jobInfo.identifier] == nil { - let cancellable = HVCameraExterior.sharedInstance.getJobStateObservable(for: jobInfo.identifier).sink(receiveValue: { (jobState: JobStatus) in + let cancellable = HVPartnerSDK.sharedInstance.getJobStateObservable(for: jobInfo.identifier).sink(receiveValue: { (jobState: JobStatus) in if case let .UploadProgress(_, uploadStatus) = jobState { print("Job@State: \(jobState) --> File@State: \(String(describing: uploadStatus))") } else if case let .Error(_, error) = jobState { @@ -46,7 +46,7 @@ struct ContentView: View { jobCancellables[jobInfo.identifier] = cancellable } - try await HVCameraExterior.sharedInstance.startCaptureFlow() + try await HVPartnerSDK.sharedInstance.startCaptureFlow() } catch let error as HVSessionError { // maybe handle our known errors here switch error.kind { diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-07.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-07.swift new file mode 100644 index 0000000..a5e818f --- /dev/null +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-07.swift @@ -0,0 +1,14 @@ +import SwiftUI +import HVCaptureSDK + +class AppDelegate: UIResponder, UIApplicationDelegate { + // ... + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + HVPartnerSDK.sharedInstance.registerForBackgroundJobs() + // NOTE: can configure whether to upload on wifi only (`true`) vs. wifi + cellular (`false`) + let uploadOnlyOnWiFi = true + HVPartnerSDK.sharedInstance.initialize(parameters: .init( + uploadOnWiFiOnly: uploadOnlyOnWiFi)) + } +} diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-08.swift b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-08.swift new file mode 100644 index 0000000..354ac89 --- /dev/null +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/code-files/tutorial-sdk-code-08.swift @@ -0,0 +1,47 @@ +import SwiftUI +import HVCaptureSDK + +class AppDelegate: UIResponder, UIApplicationDelegate { + + // NOTE: this should match the identifier in Info.plist + let captureSDKBackgroundTaskIdentifier = "to.hover.uploads" + + // ... + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + // NOTE: can configure whether to upload on wifi only (`true`) vs. wifi + cellular (`false`) + let uploadOnlyOnWiFi = true + HVPartnerSDK.sharedInstance.initialize(parameters: .init( + uploadOnWiFiOnly: uploadOnlyOnWiFi)) + + // set up background tasks for scheduling HVCaptureSDK uploads, if applicable + BGTaskScheduler.shared.register(forTaskWithIdentifier: captureSDKBackgroundTaskIdentifier, using: nil) { task in + self.uploadCaptureDataInBackground(task: task as! BGProcessingTask) + } + + // schedule the background task to be executed + let request = BGProcessingTaskRequest(identifier: captureSDKBackgroundTaskIdentifier) + request.requiresNetworkConnectivity = true + request.requiresExternalPower = false + try? BGTaskScheduler.shared.submit(request) + } + + func uploadCaptureDataInBackground(task: BGProcessingTask) { + let processingTask = Task { + do { + // set up HVPartnerSDK for background uploading (on wifi only). This will check for any un-uploaded files, and if any exist, begin uploading them. + // NOTE: a more robust approach would re-enqueue the background task in case not all the files are uploaded in this background task session + try await HVPartnerSDK.sharedInstance.initializeForBackground(parameters: .init( + uploadOnWiFiOnly: true)) + task.setTaskCompleted(success: true) + } catch { + task.setTaskCompleted(success: false) + } + } + + task.expirationHandler = { + processingTask.cancel() + } + } +} diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s01-images/tutorial-s01-05.png b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s01-images/tutorial-s01-05.png new file mode 100644 index 0000000..5dd844d Binary files /dev/null and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s01-images/tutorial-s01-05.png differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s01-images/tutorial-s01-06.png b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s01-images/tutorial-s01-06.png new file mode 100644 index 0000000..b122c41 Binary files /dev/null and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s01-images/tutorial-s01-06.png differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s1-colors.gif b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s1-colors.gif index fb93b8d..7401c2e 100644 Binary files a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s1-colors.gif and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s1-colors.gif differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s2-tutorial.gif b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s2-tutorial.gif new file mode 100644 index 0000000..13f09ca Binary files /dev/null and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s2-tutorial.gif differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s2-tutorial.png b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s2-tutorial.png deleted file mode 100644 index 38248d5..0000000 Binary files a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s2-tutorial.png and /dev/null differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s3-icons.gif b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s3-icons.gif index 09e4df6..b4f105c 100644 Binary files a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s3-icons.gif and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s3-icons.gif differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s4-gridscreen.gif b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s4-gridscreen.gif new file mode 100644 index 0000000..e3d3cac Binary files /dev/null and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s4-gridscreen.gif differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s4-gridscreen.png b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s4-gridscreen.png deleted file mode 100644 index 5485e0b..0000000 Binary files a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s4-gridscreen.png and /dev/null differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s5-text.gif b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s5-text.gif deleted file mode 100644 index da89e45..0000000 Binary files a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s5-text.gif and /dev/null differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s5-text.jpg b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s5-text.jpg new file mode 100644 index 0000000..5afb920 Binary files /dev/null and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s5-text.jpg differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s6-buttons.gif b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s6-buttons.gif index e1d3b13..8430c9d 100644 Binary files a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s6-buttons.gif and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2-s6-buttons.gif differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2p1-button-types.jpg b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2p1-button-types.jpg new file mode 100644 index 0000000..658030e Binary files /dev/null and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2p1-button-types.jpg differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2p1-button-types.png b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2p1-button-types.png deleted file mode 100644 index a8751ec..0000000 Binary files a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/t2p1-button-types.png and /dev/null differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/tutorial-s02-default.jpg b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/tutorial-s02-default.jpg new file mode 100644 index 0000000..781c6e5 Binary files /dev/null and b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/tutorial-s02-default.jpg differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/tutorial-s02-default.png b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/tutorial-s02-default.png deleted file mode 100644 index 1b2f134..0000000 Binary files a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Resources/tutorial-s02-images/tutorial-s02-default.png and /dev/null differ diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Tutorials/BasicSDK.tutorial b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Tutorials/BasicSDK.tutorial index 6fe5e16..236fbea 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Tutorials/BasicSDK.tutorial +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Tutorials/BasicSDK.tutorial @@ -88,7 +88,41 @@ @Code(name: "ContentView.swift", file: tutorial-sdk-code-06) } + } + } + + @Section(title: "Background Uploads") { + @ContentAndMedia { + Enable support for background uploads in the HOVER Capture SDK + + @Image(source: hover-logo, alt: "HOVER logo") + } + + @Steps { + + @Step { + If the host application does not have support for background tasks enabled yet, the "Background Tasks" capability has to be added to the application. Select your project in the Project navigator, select your application target, navigate to `Signing & Capabilities`, and `"+ Capability"`. Check the "Background Processing" checkbox within the "Background Modes" capability. + + @Image(source: tutorial-s01-05, alt: "Screenshot showing adding background processing to the application") + } + + @Step { + Add `to.hover.uploads` to the `BGTaskSchedulerPermittedIdentifiers` key in your `Info.plist`. + + @Image(source: tutorial-s01-06, alt: "Screenshot showing application Info.plist with `to.hover.uploads` as an authorized background task key") + } + + @Step { + To enable the SDK to automatically enqueue background upload tasks, call the ``HVPartnerSDK/registerForBackgroundJobs()`` method. As long as the preceeding step has been done, this call enables the SDK to enqueue background tasks (`BGProcessingTask`) for uploading photos as needed. If there are no pending uploads, the SDK will not enqueue any background tasks to run. + + @Code(name: "ContentView.swift", file: tutorial-sdk-code-07) + } + @Step { + For applications that already have a background task infrastructure and want more direct control over the SDK's background task scheduling, it's possible to manually schedule the SDK to perform pending uploads in the background. Call ``HVPartnerSDK/initializeForBackground(parameters:)`` to set up the uploading infrastructure and perform uploads in the background. + + @Code(name: "ContentView.swift", file: tutorial-sdk-code-08) + } } } } diff --git a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Tutorials/HVThemeCustomization.tutorial b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Tutorials/HVThemeCustomization.tutorial index 33f8091..8e5d06c 100644 --- a/Sources/HVCaptureSDK/HVCaptureSDK.docc/Tutorials/HVThemeCustomization.tutorial +++ b/Sources/HVCaptureSDK/HVCaptureSDK.docc/Tutorials/HVThemeCustomization.tutorial @@ -1,6 +1,6 @@ @Tutorial(time: 15) { @Intro(title: "Customizing SDK Theming") { - Learn how to customize aspects of the HOVER Capture SDK UI appearance + Learn how to customize aspects of the Hover Capture SDK UI appearance. @Image(source: hover-graph-logo, alt: "HOVER logo as a graph") } @@ -13,8 +13,8 @@ @Steps { @Step { - The ``HVTheme`` constructor has a lot of configuration options, all of which are optional. Values that aren't specified fall back to the default theme values, which is the HOVER theme. - Certain parameters have interactions, so for example, if the ``HVTheme/primaryButtonBackground`` parameter isn't specified, then we'll build one out of a combination of the HVTheme `primary`, `secondary`, and `surface` parameters. + The ``HVTheme`` constructor has a long list of configuration options, all of which are optional. Values that aren't specified fall back to the default theme values, which is the Hover theme. + Certain parameters have interactions, so for example, if the ``HVTheme/hoverCapturePrimaryButtonBackground`` parameter isn't specified, then we'll build one out of a combination of the HVTheme internal `primary`, `secondary`, and `surface` parameters. @Code(name: "HVTheme.swift", file: tutorial-hvtheme-code-01) } @@ -44,12 +44,13 @@ @Image(source: tutorial-s02-default, alt: "Default Property Screen") } + @Steps { @Step { Create a custom `HVTheme` object and apply it in the capture session settings. We can specify different colors to use for different visual elements. - Take a minute to note how the different colors show up on the various screens. - We are specifying a subset of colors in this step, but no icons or direct button styles yet. + Take a minute to note how the different colors show up on the various screens, and that we can use any number of methods to construct our `Color` values. + We are specifying a subset of colors in this step, but no icons or direct button styles yet. @Code(name: "ContentView.swift", file: tutorial-hvtheme-code-05) { @Image(source: t2-s1-colors, alt: "Screens customized with colors") @@ -64,11 +65,8 @@ @Step { In this step, we specify some of the customizable icons. - The icon for the button to navigate to the previous screen is specified with ``HVTheme/toolbarBack``, and we have it set to `Image(systemName: "paperplane")` in this step. - The icon for the success screen is specified with ``HVTheme/successIcon``, set to `Image(systemName: "checkmark.seal.fill")` in this step. - The icon for the camera permission denied screen is ``HVTheme/toolbarClose``, set to `Image(systemName: "heart.slash")` here. - The icon for top toolbar banner is specified with ``HVTheme/toolbarLogo``, and is specified as a `View`. - Applications can use arbitrary custom images by supplying them in the app bundle, downloading them from the internet at runtime, etc. We simply used system images here for simplicity's sake. + The icon for the success screen is specified with ``HVTheme/hoverCaptureSuccessIcon``, set to `Image(systemName: "paperplane")` in this step. It's color is determined by the `hoverCaptureTutorialImageOverlayStroke` setting. + Applications can use arbitrary custom images by supplying them in the application bundle, downloading them from the internet at runtime, etc. We simply used system images here for simplicity's sake. @Code(name: "ContentView.swift", file: tutorial-hvtheme-code-06) { @Image(source: t2-s3-icons, alt: "Screens with configured icons") @@ -76,8 +74,7 @@ } @Step { - In this step, we configure parameters specific to the capture grid screen. - Note that we already have a degree of customization applied to the grid screen by way of e.g. ``HVTheme/primary`` and ``HVTheme/subtleElements``, but the parameters specified in this step give a more direct control of the specific grid elements. + In this step, we configure parameters specific to the capture grid screen. Note how the color and grid shape changes based on the modified parameters. @Code(name: "ContentView.swift", file: tutorial-hvtheme-code-07) { @Image(source: t2-s4-gridscreen, alt: "Capture grid screen customization") @@ -86,7 +83,7 @@ @Step { Additionally, certain screens can have their text customized based on their strings provided in ``HVTheme``. - Often the messages consist of 3 components: the header, the message body, and a message to display on a button that the user can press. + Often the messages consist of 3 components: the header, the message body, and a message to display on a button that the user can press, though in the case of the success screen there's an extra `hoverCaptureSuccessNextSteps` field. Also note that any field can be left `nil` (in which case the default message will be used, as specified in ``HVTheme/defaultValues``), or can be set to `""`, in which case no message will be shown. @Code(name: "ContentView.swift", file: tutorial-hvtheme-code-08) { @Image(source: t2-s5-text, alt: "Text message screen customization") @@ -96,11 +93,9 @@ @Step { It's possible to customize specifics of the primary and secondary button appearance. Note how the `Continue` button changes appearance as the capture proceeds -- at the start when no photos have been taken, it's disabled and clicking it changes it's appearance to match its `inactive` theme. - Once the minimum (4) number of photos have been taken, the button becomes `active` and its `pressed` appearance changes as well to match the given ``HVTheme``. + Once the minimum number of required photos have been taken, the button becomes `active` and its `pressed` appearance changes as well to match the colors in ``HVTheme``. - @Code(name: "ContentView.swift", file: tutorial-hvtheme-code-09) { - @Image(source: t2-s6-buttons, alt: "Button customization") - } + @Image(source: t2-s6-buttons, alt: "Button customization") } } } diff --git a/Sources/HVCaptureSDK/Umbrella.swift b/Sources/HVCaptureSDK/Umbrella.swift index b077bab..0dd78e9 100644 --- a/Sources/HVCaptureSDK/Umbrella.swift +++ b/Sources/HVCaptureSDK/Umbrella.swift @@ -3,4 +3,4 @@ @_exported import _HVAVCamera @_exported import _HVCore -@_exported import _HVCameraExterior +@_exported import _HoverSDK