Skip to content

Commit 5faa0bc

Browse files
committed
fix(expo): link iOS SDK through podspec SPM dependencies
1 parent d4c4695 commit 5faa0bc

13 files changed

Lines changed: 37 additions & 469 deletions

.changeset/tidy-expo-ios-import.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"@clerk/expo": patch
33
---
44

5-
Fixes iOS development builds across Expo SDK versions by moving shared native bridge APIs into a separate iOS module.
5+
Fixes iOS development builds across Expo SDK versions by linking the Clerk iOS SDK through React Native's Swift Package Manager podspec support.

packages/expo/app.plugin.js

Lines changed: 5 additions & 379 deletions
Large diffs are not rendered by default.

packages/expo/ios/ClerkAuthNativeView.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React
22
import UIKit
3-
import ClerkExpoBridge
43

54
public class ClerkAuthNativeView: ClerkNativeViewHost {
65
private var currentMode: String = "signInOrUp"

packages/expo/ios/ClerkExpo.podspec

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ else
1717
}
1818
end
1919

20+
clerk_ios_repo = 'https://github.com/clerk/clerk-ios.git'
21+
clerk_ios_version = '1.2.4'
22+
2023
Pod::Spec.new do |s|
2124
s.name = 'ClerkExpo'
2225
s.version = package['version']
@@ -35,12 +38,20 @@ Pod::Spec.new do |s|
3538
'SWIFT_COMPILATION_MODE' => 'wholemodule'
3639
}
3740

38-
s.dependency 'ClerkExpoBridge'
41+
if defined?(spm_dependency)
42+
spm_dependency(
43+
s,
44+
url: clerk_ios_repo,
45+
requirement: { :kind => 'exactVersion', :version => clerk_ios_version },
46+
products: ['ClerkKit', 'ClerkKitUI']
47+
)
48+
else
49+
raise 'ClerkExpo requires React Native 0.75 or newer for iOS Swift Package Manager dependencies.'
50+
end
3951

40-
# Only include the module files in the pod (both Swift and ObjC bridges).
41-
# ClerkNativeBridge.swift is injected into the app target by the config plugin
42-
# because it uses `import ClerkKit` which is only available via SPM in the app target.
43-
s.source_files = "ClerkExpoModule.swift", "ClerkExpoModule.m",
52+
s.source_files = "ClerkExpoBridge.swift",
53+
"ClerkNativeBridge.swift",
54+
"ClerkExpoModule.swift", "ClerkExpoModule.m",
4455
"ClerkNativeViewHost.swift",
4556
"ClerkAuthNativeView.swift",
4657
"ClerkAuthViewManager.m",

packages/expo/ios/ClerkExpoBridge.podspec

Lines changed: 0 additions & 39 deletions
This file was deleted.

packages/expo/ios/ClerkExpoBridge.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public enum ClerkNativeViewEvent: String {
77
case dismissed
88
}
99

10-
// Protocol that the app target implements to provide Clerk SDK operations and SwiftUI views.
10+
// Protocol used by the native module and view wrappers to reach Clerk SDK operations and SwiftUI views.
1111
public protocol ClerkNativeBridgeProtocol {
1212
// Inline rendering - returns UIViewController to preserve SwiftUI lifecycle
1313
func makeAuthViewController(mode: String, dismissible: Bool, onEvent: @escaping (ClerkNativeViewEvent, [String: Any]) -> Void) -> UIViewController?
@@ -20,8 +20,8 @@ public protocol ClerkNativeBridgeProtocol {
2020
func syncFromJsClientToken(_ clientToken: String?, sourceId: String?, shouldRefreshClient: Bool) async throws
2121
}
2222

23-
// Global registry for the app-target native bridge (set by the app target at startup).
24-
public var clerkNativeBridge: ClerkNativeBridgeProtocol? {
23+
// Global native bridge used by the ClerkExpo pod.
24+
public var clerkNativeBridge: ClerkNativeBridgeProtocol? = ClerkNativeBridge.shared {
2525
didSet {
2626
if clerkNativeBridge != nil {
2727
emitClerkNativeBridgeReady()

packages/expo/ios/ClerkExpoModule.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
// ClerkExpoModule - Native module for Clerk integration
22
// This module provides the configure function, client sync, and native view bridges.
3-
// SwiftUI Clerk views are created by the app target through ClerkNativeBridge because
4-
// the Clerk SDK (SPM) isn't accessible from the CocoaPods-backed React Native pod.
3+
// SwiftUI Clerk views are created by ClerkNativeBridge through the Clerk iOS SPM dependency.
54

65
import UIKit
76
import React
8-
import ClerkExpoBridge
97

108
// MARK: - Module
119

@@ -69,7 +67,7 @@ class ClerkExpoModule: RCTEventEmitter {
6967
resolve: @escaping RCTPromiseResolveBlock,
7068
reject: @escaping RCTPromiseRejectBlock) {
7169
guard let bridge = clerkNativeBridge else {
72-
reject("E_NOT_INITIALIZED", "Clerk not initialized. Make sure ClerkNativeBridge is registered.", nil)
70+
reject("E_NOT_INITIALIZED", "Clerk native bridge is unavailable.", nil)
7371
return
7472
}
7573

packages/expo/ios/ClerkNativeBridge.swift

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
// ClerkNativeBridge - Provides app-target Clerk SDK operations and SwiftUI view controllers to ClerkExpo.
2-
// This file is injected into the app target by the config plugin.
3-
// It uses the ClerkKit Swift package, which is only accessible from the app target.
1+
// ClerkNativeBridge - Provides Clerk SDK operations and SwiftUI view controllers to ClerkExpo.
42

53
import UIKit
64
import SwiftUI
75
import Observation
86
@_spi(FrameworkIntegration) import ClerkKit
97
import ClerkKitUI
10-
import ClerkExpo // Import the pod to access ClerkNativeBridgeProtocol
118

129
private struct ClerkExpoHeaderMiddleware: ClerkRequestMiddleware {
13-
// Replaced by the config plugin when this bridge is copied into the app target.
14-
private static let hostSdkVersion = "__CLERK_EXPO_VERSION__"
10+
private static var hostSdkVersion: String? {
11+
Bundle.main.object(forInfoDictionaryKey: "ClerkExpoVersion") as? String
12+
}
1513

1614
func prepare(_ request: inout URLRequest) async throws {
1715
request.addValue("expo", forHTTPHeaderField: "x-clerk-host-sdk")
18-
request.addValue(Self.hostSdkVersion, forHTTPHeaderField: "x-clerk-host-sdk-version")
16+
if let hostSdkVersion = Self.hostSdkVersion, !hostSdkVersion.isEmpty {
17+
request.addValue(hostSdkVersion, forHTTPHeaderField: "x-clerk-host-sdk-version")
18+
}
1919
}
2020
}
2121

@@ -52,14 +52,10 @@ final class ClerkNativeBridge: ClerkNativeBridgeProtocol {
5252
return Bundle.main.bundleIdentifier
5353
}
5454

55-
// Register this app-target bridge with the ClerkExpo module.
56-
@MainActor static func register() {
57-
shared.loadThemes()
58-
clerkNativeBridge = shared
59-
}
60-
6155
@MainActor
6256
func configure(publishableKey: String, bearerToken: String? = nil) async throws {
57+
loadThemes()
58+
6359
if Self.shouldReconfigure(for: publishableKey) {
6460
try await Clerk.reconfigure(publishableKey: publishableKey, options: Self.makeClerkOptions())
6561
Self.clerkConfigured = true

packages/expo/ios/ClerkNativeViewHost.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import UIKit
2-
import ClerkExpoBridge
32

43
public class ClerkNativeViewHost: UIView, ClerkNativeBridgeReadyObserver {
54
private lazy var hostingCoordinator = ClerkNativeHostingCoordinator(containerView: self)

packages/expo/ios/ClerkUserButtonNativeView.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React
22
import UIKit
3-
import ClerkExpoBridge
43

54
public class ClerkUserButtonNativeView: ClerkNativeViewHost {
65
override func makeHostedController() -> UIViewController? {

0 commit comments

Comments
 (0)