Skip to content

Commit

Permalink
Add DescopeFlowHook for customizing how flow pages look and behave
Browse files Browse the repository at this point in the history
  • Loading branch information
shilgapira committed Dec 29, 2024
1 parent 028ab48 commit a162fc8
Show file tree
Hide file tree
Showing 13 changed files with 985 additions and 264 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,13 @@ guide to learn more.

When your application delegate is notified about a universal link being triggered, you'll
need to provide it to the flow so it can continue with the authentication. See the documentation
for `DescopeFlow.resume(with:)` for more details.
for `Descope.handleURL` for more details.

```swift
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return false }
DescopeFlow.current?.resume(with: url)
return true
let handled = Descope.handleURL(url)
return handled
}
```

Expand All @@ -181,8 +181,7 @@ hierarchy. See the documentation for both classes for more details.

```swift
func showLoginScreen() {
let url = URL(string: "https://example.com/myflow")!
let flow = DescopeFlow(url: url)
let flow = DescopeFlow(url: "https://example.com/myflow")

let flowViewController = DescopeFlowViewController()
flowViewController.delegate = self
Expand Down
52 changes: 52 additions & 0 deletions src/DescopeKit.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

import Foundation

/// Provides functions for working with the Descope API.
///
/// This singleton object is provided as a simple way to access the Descope SDK from anywhere
Expand Down Expand Up @@ -93,3 +95,53 @@ public extension Descope {
/// Provides functions for exchanging access keys for session tokens.
static var accessKey: DescopeAccessKey { sdk.accessKey }
}

/// Support for working with Universal Links.
public extension Descope {
/// Resumes an ongoing authentication that's waiting for Magic Link authentication.
///
/// When a flow performs authentication with Magic Link at some point it will wait for
/// the user to receive an email and tap on the authentication link provided inside.
/// The host application is expected to intercept this URL via Universal Links and
/// resume the running flow with it.
///
/// You can do this by calling this function and passing the URL from the Universal Link.
/// For example, in a SwiftUI application:
///
/// ```swift
/// @main
/// struct MyApp: App {
/// // ...
/// var body: some Scene {
/// WindowGroup {
/// ContentView().onOpenURL { url in
/// Descope.handleURL(url)
/// }
/// }
/// }
/// }
/// ```
///
/// You can pass the return value of this function directly to the `UIApplicationDelegate`
/// method for handling Universal Links. For example:
///
/// ```swift
/// @main
/// class AppDelegate: UIResponder, UIApplicationDelegate {
/// // ...
/// func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
/// guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return false }
/// return Descope.handleURL(url)
/// }
/// }
/// ```
///
/// - Parameter url: The `url` to use for resuming the authentication.
///
/// - Returns: `true` when an ongoing authentication handled the URL or `false` to
/// let the caller know that the function didn't handle it.
@discardableResult @MainActor
static func handleURL(_ url: URL) -> Bool {
return sdk.handleURL(url)
}
}
82 changes: 28 additions & 54 deletions src/flows/Flow.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@

#if os(iOS)

import Foundation

/// The state of the flow or presenting object.
Expand All @@ -23,24 +21,28 @@ public enum DescopeFlowState: String {

/// A helper object that encapsulates a single flow run for authenticating a user.
///
/// You can use Descope Flows as a visual no-code interface to build screens and authentication
/// flows for common user interactions with your application.
/// You can use Descope Flows as a visual no-code interface to build screens and
/// authentication flows for common user interactions with your application.
///
/// Flows are hosted on a webpage and are run by creating an instance of
/// ``DescopeFlowViewController``, ``DescopeFlowView``, or ``DescopeFlowCoordinator`` and
/// calling `start(flow:)`.
/// ``DescopeFlowViewController``, ``DescopeFlowView``, or ``DescopeFlowCoordinator``
/// and calling `start(flow:)`.
///
/// There are some preliminary setup steps you might need to do:
///
/// - As a prerequisite, the flow itself must be created and hosted somewhere on the web. You can
/// either host it on your own web server or use Descope's auth hosting. Read more [here](https://docs.descope.com/auth-hosting-app).
/// - As a prerequisite, the flow itself must be created and hosted somewhere on
/// the web. You can either host it on your own web server or use Descope's
/// auth hosting. Read more [here](https://docs.descope.com/auth-hosting-app).
///
/// - You should configure any required Descope authentication methods in the [Descope console](https://app.descope.com/settings/authentication)
/// before making use of them in a Descope Flow. Some of the default configurations might work
/// well enough to start with, but it is likely that some changes will be needed before release.
/// - You should configure any required Descope authentication methods in the
/// [Descope console](https://app.descope.com/settings/authentication) before
/// making use of them in a Descope Flow. Some of the default configurations
/// might work well enough to start with, but it is likely that some changes
/// will be needed before release.
///
/// - For flows that use `Magic Link` authentication you will need to set up [Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app)
/// in your app. See the documentation for ``resume(with:)`` for more details.
/// - For flows that use `Magic Link` authentication you will need to set up
/// [Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app)
/// in your app. See the documentation for ``Descope for more details.
///
/// - You can leverage the native `Sign in with Apple` automatically for flows that use `OAuth`
/// by setting the ``oauthProvider`` property and configuring native OAuth in your app. See the
Expand All @@ -49,12 +51,14 @@ public enum DescopeFlowState: String {
/// - SeeAlso: You can read more about Descope Flows on the [docs website](https://docs.descope.com/flows).
@MainActor
public class DescopeFlow {
/// Returns the ``DescopeFlow`` object for the current running flow or
/// `nil` if no flow is currently running.
public internal(set) static weak var current: DescopeFlow?

/// The URL where the flow is hosted.
public let url: URL
public let url: String

/// A list of hooks that customize how the flow webpage looks or behaves.
///
/// You can use the built-in hooks or create custom ones. See the documentation
/// for ``DescopeFlowHook`` for more details.
public var hooks: [DescopeFlowHook] = []

/// An optional instance of ``DescopeSDK`` to use for running the flow.
///
Expand All @@ -75,7 +79,7 @@ public class DescopeFlow {
/// You only need to set this if you explicitly want to override whichever URL is
/// configured in the flow or in the Descope project, perhaps because the app cannot
/// be configured for universal links using the same redirect URL as on the web.
public var magicLinkRedirect: URL?
public var magicLinkRedirect: String?

/// An optional timeout interval to set on the `URLRequest` object used for loading
/// the flow webpage. If this is not set the platform default value is be used.
Expand All @@ -84,44 +88,16 @@ public class DescopeFlow {
/// Creates a new ``DescopeFlow`` object that encapsulates a single flow run.
///
/// - Parameter url: The URL where the flow is hosted.
public init(url: URL) {
public init(url: String) {
self.url = url
}

/// Resumes a running flow that's waiting for Magic Link authentication.
///
/// When a flow performs authentication with Magic Link at some point it will wait
/// for the user to receive an email and tap on the authentication URL provided inside.
/// The host application is expected to intercept this URL via Universal Links and
/// resume the running flow with it.
///
/// You can do this by first getting a reference to the current running flow from
/// the ``DescopeFlow/current`` property and then calling the ``resume(with:)`` method
/// with the URL from the Universal Link.
///
/// @main
/// struct MyApp: App {
/// // ...
/// Creates a new ``DescopeFlow`` object that encapsulates a single flow run.
///
/// var body: some Scene {
/// WindowGroup {
/// ContentView().onOpenURL { url in
/// DescopeFlow.current?.resume(with: url)
/// }
/// }
/// }
/// }
public func resume(with url: URL) {
resume?(url)
/// - Parameter url: The URL where the flow is hosted.
public init(url: URL) {
self.url = url.absoluteString
}

// Internal

typealias ResumeClosure = @MainActor (URL) -> ()

/// While the flow is running this is set to a closure with a weak reference to
/// the ``DescopeFlowCoordinator`` to provide it with the resume URL.
var resume: ResumeClosure?
}

extension DescopeFlow: CustomStringConvertible {
Expand All @@ -132,5 +108,3 @@ extension DescopeFlow: CustomStringConvertible {
return "DescopeFlow(url: \"\(url)\")"
}
}

#endif
Loading

0 comments on commit a162fc8

Please sign in to comment.