Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/calm-spoons-listen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/expo': patch
---

Keep post-authentication prompts open in the native `AuthView` when the Clerk iOS SDK configuration refreshes.
7 changes: 6 additions & 1 deletion packages/expo/ios/ClerkNativeViewHost.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ public class ClerkNativeViewHost: ExpoView {
object: nil,
queue: .main
) { [weak self] _ in
self?.setNeedsHostedViewUpdate()
guard let self, !hostingCoordinator.hasAttachedController else { return }
setNeedsHostedViewUpdate()
}
}

Expand Down Expand Up @@ -145,6 +146,10 @@ private final class ClerkNativeHostingCoordinator {
private weak var containerView: UIView?
private var hostingController: UIViewController?

var hasAttachedController: Bool {
hostingController != nil
}

init(containerView: UIView) {
self.containerView = containerView
}
Expand Down
66 changes: 66 additions & 0 deletions packages/expo/ios/Tests/ClerkNativeViewHostTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import UIKit
import XCTest
@testable import ClerkExpo

final class ClerkNativeViewHostTests: XCTestCase {
@MainActor
func testConfigureNotificationDoesNotReplaceAttachedController() {
let hostView = TestClerkNativeViewHost(appContext: nil)
let controller = UIViewController()
hostView.controller = controller
let window = mountInWindow(hostView)
let callsBeforeNotification = hostView.makeHostedControllerCallCount

NotificationCenter.default.post(name: .clerkNativeSDKDidConfigure, object: nil)

XCTAssertEqual(hostView.makeHostedControllerCallCount, callsBeforeNotification)
XCTAssertTrue(controller.view.superview === hostView)
unmount(hostView, from: window)
}

@MainActor
func testConfigureNotificationAttachesControllerWhenInitiallyUnavailable() {
let hostView = TestClerkNativeViewHost(appContext: nil)
let window = mountInWindow(hostView)
let callsBeforeConfiguration = hostView.makeHostedControllerCallCount
let controller = UIViewController()
hostView.controller = controller

NotificationCenter.default.post(name: .clerkNativeSDKDidConfigure, object: nil)

XCTAssertEqual(hostView.makeHostedControllerCallCount, callsBeforeConfiguration + 1)
XCTAssertTrue(controller.view.superview === hostView)

NotificationCenter.default.post(name: .clerkNativeSDKDidConfigure, object: nil)

XCTAssertEqual(hostView.makeHostedControllerCallCount, callsBeforeConfiguration + 1)
unmount(hostView, from: window)
}

@MainActor
private func mountInWindow(_ hostView: UIView) -> UIWindow {
let window = UIWindow(frame: UIScreen.main.bounds)
let viewController = UIViewController()
window.rootViewController = viewController
viewController.view.addSubview(hostView)
window.makeKeyAndVisible()
return window
}

@MainActor
private func unmount(_ hostView: UIView, from window: UIWindow) {
hostView.removeFromSuperview()
window.isHidden = true
}
}

@MainActor
private final class TestClerkNativeViewHost: ClerkNativeViewHost {
var controller: UIViewController?
private(set) var makeHostedControllerCallCount = 0

override func makeHostedController() -> UIViewController? {
makeHostedControllerCallCount += 1
return controller
}
}
Loading