Skip to content
Draft
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
60 changes: 60 additions & 0 deletions .github/workflows/scripts/prepare-ios-simulator.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env bash

set -euo pipefail

if [[ -z "${IOS_SIMULATOR_DEVICE:-}" ]]; then
echo "IOS_SIMULATOR_DEVICE must be set."
exit 1
fi

echo "--- Available simulators (diagnostic) ---"
xcrun simctl list

SIMULATOR_UDID="$(python3 - <<'PY'
import json
import os
import re
import subprocess
import sys

target_name = os.environ["IOS_SIMULATOR_DEVICE"]
raw = subprocess.check_output(
["xcrun", "simctl", "list", "devices", "available", "-j"],
text=True,
)
devices_by_runtime = json.loads(raw)["devices"]
pattern = re.compile(r"com\.apple\.CoreSimulator\.SimRuntime\.iOS-(\d+)(?:-(\d+))?")

candidates = []
for runtime_key, devices in devices_by_runtime.items():
match = pattern.fullmatch(runtime_key)
if not match:
continue

version = (int(match.group(1)), int(match.group(2) or 0))
for device in devices:
if device.get("isAvailable") and device.get("name") == target_name:
candidates.append((version, device["udid"]))

if not candidates:
sys.exit(f"No available simulator found for {target_name} in installed iOS runtimes.")

candidates.sort(reverse=True)
print(candidates[0][1])
PY
)"

if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
echo "udid=$SIMULATOR_UDID" >> "$GITHUB_OUTPUT"
else
echo "Resolved simulator UDID: $SIMULATOR_UDID"
fi

if ! boot_output="$(xcrun simctl boot "$SIMULATOR_UDID" 2>&1)"; then
if [[ "$boot_output" != *"Unable to boot device in current state: Booted"* ]]; then
echo "$boot_output"
exit 1
fi
fi

xcrun simctl bootstatus "$SIMULATOR_UDID" -b
55 changes: 33 additions & 22 deletions .github/workflows/swiftui-auth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ on:
permissions:
contents: read

env:
XCODE_VERSION: latest-stable
IOS_SIMULATOR_DEVICE: iPhone 17 Pro

jobs:
format-check:
name: Swift Format Check
Expand All @@ -37,7 +41,7 @@ jobs:
- name: Check Swift formatting
run: bash lint-swift.sh

# Package Unit Tests (standalone, no emulator needed)
# Package Unit Tests (simulator-backed)
unit-tests:
name: Package Unit Tests
runs-on: macos-26
Expand All @@ -48,15 +52,20 @@ jobs:
- name: Install xcpretty
run: gem install xcpretty

- name: Select Xcode version
run: sudo xcode-select -switch /Applications/Xcode_26.4.app/Contents/Developer
- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Prepare iOS Simulator
id: prepare-simulator
run: ./.github/workflows/scripts/prepare-ios-simulator.sh

- name: Run FirebaseSwiftUI Package Unit Tests
run: |
set -o pipefail
xcodebuild test \
-scheme FirebaseUI-Package \
-destination 'platform=iOS Simulator,OS=26.4,name=iPhone 17' \
-destination "id=${{ steps.prepare-simulator.outputs.udid }}" \
-enableCodeCoverage YES \
-resultBundlePath FirebaseSwiftUIPackageTests.xcresult | tee FirebaseSwiftUIPackageTests.log | xcpretty --test --color --simple

Expand Down Expand Up @@ -104,25 +113,22 @@ jobs:
- name: Install xcpretty
run: gem install xcpretty

- name: Select Xcode version
run: sudo xcode-select -switch /Applications/Xcode_26.4.app/Contents/Developer

- name: Build for Integration Tests
run: |
cd ./e2eTest/FirebaseSwiftUIExample
set -o pipefail
xcodebuild build-for-testing \
-scheme FirebaseSwiftUIExampleTests \
-destination 'platform=iOS Simulator,OS=26.4,name=iPhone 17' \
-enableCodeCoverage YES | xcpretty --color --simple

- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Prepare iOS Simulator
id: prepare-simulator
run: ./.github/workflows/scripts/prepare-ios-simulator.sh

- name: Run Integration Tests
run: |
cd ./e2eTest/FirebaseSwiftUIExample
set -o pipefail
xcodebuild test-without-building \
xcodebuild test \
-scheme FirebaseSwiftUIExampleTests \
-destination 'platform=iOS Simulator,OS=26.4,name=iPhone 17' \
-destination "id=${{ steps.prepare-simulator.outputs.udid }}" \
-parallel-testing-enabled NO \
-enableCodeCoverage YES \
-resultBundlePath FirebaseSwiftUIExampleTests.xcresult | tee FirebaseSwiftUIExampleTests.log | xcpretty --test --color --simple

Expand Down Expand Up @@ -170,16 +176,21 @@ jobs:
- name: Install xcpretty
run: gem install xcpretty

- name: Select Xcode version
run: sudo xcode-select -switch /Applications/Xcode_26.4.app/Contents/Developer
- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Prepare iOS Simulator
id: prepare-simulator
run: ./.github/workflows/scripts/prepare-ios-simulator.sh

- name: Build for UI Tests
run: |
cd ./e2eTest/FirebaseSwiftUIExample
set -o pipefail
xcodebuild build-for-testing \
-scheme FirebaseSwiftUIExampleUITests \
-destination 'platform=iOS Simulator,OS=26.4,name=iPhone 17' \
-destination "id=${{ steps.prepare-simulator.outputs.udid }}" \
-enableCodeCoverage YES | xcpretty --color --simple

- name: Run UI Tests
Expand All @@ -188,7 +199,7 @@ jobs:
set -o pipefail
xcodebuild test-without-building \
-scheme FirebaseSwiftUIExampleUITests \
-destination 'platform=iOS Simulator,OS=26.4,name=iPhone 17' \
-destination "id=${{ steps.prepare-simulator.outputs.udid }}" \
-parallel-testing-enabled NO \
-enableCodeCoverage YES \
-resultBundlePath FirebaseSwiftUIExampleUITests.xcresult | tee FirebaseSwiftUIExampleUITests.log | xcpretty --test --color --simple
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ Pods/
Podfile.lock
*.xcworkspace/
samples/**/GoogleService-Info.plist
e2eTest/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests.xcresult/
e2eTest/FirebaseSwiftUIExample/*.xcresult/
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,13 @@ extension SignInWithAppleButton: View {
return
}
} catch {
reportError?(error)

if case let AuthServiceError.accountConflict(ctx) = error,
let onConflict = accountConflictHandler {
onConflict(ctx)
return
}

reportError?(error)
throw error
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public enum AuthServiceError: LocalizedError {
case invalidCredentials(String)
case signInFailed(underlying: Error)
case accountConflict(AccountConflictContext)
case legacySignInRecoveryPresented
case providerNotFound(String)
case multiFactorAuth(String)
case rootViewControllerNotFound(String)
Expand Down Expand Up @@ -200,6 +201,8 @@ public enum AuthServiceError: LocalizedError {
return description
case let .accountConflict(context):
return context.errorDescription
case .legacySignInRecoveryPresented:
return nil
case let .providerNotFound(description):
return description
case let .multiFactorAuth(description):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public struct AuthConfiguration {
public let shouldHideCancelButton: Bool
public let interactiveDismissEnabled: Bool
public let shouldAutoUpgradeAnonymousUsers: Bool
public let legacyFetchSignInWithEmail: Bool
public let customStringsBundle: Bundle?
public let tosUrl: URL?
public let privacyPolicyUrl: URL?
Expand All @@ -39,6 +40,7 @@ public struct AuthConfiguration {
shouldHideCancelButton: Bool = false,
interactiveDismissEnabled: Bool = true,
shouldAutoUpgradeAnonymousUsers: Bool = false,
legacyFetchSignInWithEmail: Bool = false,
customStringsBundle: Bundle? = nil,
tosUrl: URL? = nil,
privacyPolicyUrl: URL? = nil,
Expand All @@ -51,6 +53,7 @@ public struct AuthConfiguration {
self.shouldHideCancelButton = shouldHideCancelButton
self.interactiveDismissEnabled = interactiveDismissEnabled
self.shouldAutoUpgradeAnonymousUsers = shouldAutoUpgradeAnonymousUsers
self.legacyFetchSignInWithEmail = legacyFetchSignInWithEmail
self.customStringsBundle = customStringsBundle
self.languageCode = languageCode
self.tosUrl = tosUrl
Expand Down
Loading
Loading