Detect If Your iOS Device Is Running a Beta Version
Learn how to detect if iOS is running a beta, which type of beta (developer, public, RC), and get the full build information.
Detect If Your iOS Device Is Running a Beta Version
Why Detect iOS Beta Versions?
When your app runs on iOS beta versions, you might want to:
- Enable additional logging for debugging
- Report beta-specific crashes separately
- Disable features that break on pre-release iOS
- Show beta testers which build they’re testing
The Problem: No Official Beta API
Apple doesn’t provide an isBeta property. The UIDevice.systemVersion returns “18.0” for both beta and release versions. However, we can detect beta builds using ProcessInfo.
Solution: Complete Beta Detection System
Here’s a comprehensive solution that detects if iOS is running a beta and identifies the type:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import Foundation
import UIKit
enum iOSBetaType: String {
case developerBeta = "Developer Beta"
case publicBeta = "Public Beta"
case releaseCandidate = "Release Candidate"
case seed = "Seed Build"
case stable = "Stable Release"
}
struct iOSBetaDetector {
// Get the full version string with build info
static var fullVersionString: String {
ProcessInfo.processInfo.operatingSystemVersionString
}
// Get just the iOS version number
static var versionNumber: String {
UIDevice.current.systemVersion
}
// Check if device is running any beta version
static var isRunningBeta: Bool {
detectBetaType() != .stable
}
// Detect which type of beta (if any)
static func detectBetaType() -> iOSBetaType {
let versionString = fullVersionString.lowercased()
// Check for explicit beta markers
if versionString.contains("release candidate") ||
versionString.contains(" rc ") {
return .releaseCandidate
}
if versionString.contains("developer beta") {
return .developerBeta
}
if versionString.contains("public beta") ||
versionString.contains("beta") {
return .publicBeta
}
if versionString.contains("seed") {
return .seed
}
// Check build number pattern
if hasBetaBuildSuffix() {
return .developerBeta // Builds with letter suffix are typically dev betas
}
return .stable
}
// Get build number from version string
static var buildNumber: String? {
let pattern = "Build\\s+([A-Z0-9a-z]+)"
guard let regex = try? NSRegularExpression(pattern: pattern) else { return nil }
let range = NSRange(location: 0, length: fullVersionString.utf16.count)
guard let match = regex.firstMatch(in: fullVersionString, range: range),
let buildRange = Range(match.range(at: 1), in: fullVersionString) else {
return nil
}
return String(fullVersionString[buildRange])
}
// Beta builds have letter suffix: 22A5282m (beta) vs 22A5282 (release)
private static func hasBetaBuildSuffix() -> Bool {
guard let build = buildNumber else { return false }
// Check if last character is a lowercase letter
if let lastChar = build.last, lastChar.isLowercase {
return true
}
return false
}
// Get human-readable beta information
static func getBetaInfo() -> String {
let betaType = detectBetaType()
let version = versionNumber
let build = buildNumber ?? "Unknown"
switch betaType {
case .stable:
return "iOS \(version) (\(build)) - Stable Release"
default:
return "iOS \(version) (\(build)) - \(betaType.rawValue)"
}
}
}
Detecting TestFlight App Betas
If you also need to detect if your app is running via TestFlight:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
enum AppConfiguration {
case debug
case testFlight
case appStore
}
struct AppEnvironment {
private static let isTestFlight = Bundle.main.appStoreReceiptURL?
.lastPathComponent == "sandboxReceipt"
static var isDebug: Bool {
#if DEBUG
return true
#else
return false
#endif
}
static var configuration: AppConfiguration {
if isDebug {
return .debug
} else if isTestFlight {
return .testFlight
} else {
return .appStore
}
}
}
Practical Usage Examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Check if running on beta iOS
if iOSBetaDetector.isRunningBeta {
print("Device is running a beta iOS version")
// Get specific beta type
let betaType = iOSBetaDetector.detectBetaType()
switch betaType {
case .developerBeta:
print("Running Developer Beta")
enableDeveloperFeatures()
case .publicBeta:
print("Running Public Beta")
enablePublicBetaFeatures()
case .releaseCandidate:
print("Running Release Candidate")
// Almost stable, minimal changes
case .seed:
print("Running Seed Build")
enableInternalTestingFeatures()
case .stable:
print("Running Stable iOS")
}
}
// Display version info to users (useful for beta testers)
let betaInfo = iOSBetaDetector.getBetaInfo()
print(betaInfo)
// Output: "iOS 18.0 (22A5282m) - Developer Beta"
// Get specific build number
if let build = iOSBetaDetector.buildNumber {
analytics.log(event: "app_launched", parameters: ["ios_build": build])
}
// Check app distribution method
switch AppEnvironment.configuration {
case .debug:
print("Running from Xcode")
case .testFlight:
print("Distributed via TestFlight")
case .appStore:
print("Downloaded from App Store")
}
How Beta Detection Works
- Version String Analysis: The
ProcessInfo.operatingSystemVersionStringcontains detailed build information:- Developer Beta:
"Version 18.0 (Build 22A5282m)"- Note the ‘m’ suffix - Public Beta:
"Version 18.0 (Build 22A5282b)"- ‘b’ suffix - Release Candidate:
"Version 18.0 (Build 22A5282) RC" - Stable Release:
"Version 18.0 (Build 22A5282)"- No suffix
- Developer Beta:
- Build Number Pattern: Beta builds append a lowercase letter to the build number. The letter often indicates:
a,b,c: Early betasm,n,o: Mid-cycle betas- No letter: Stable release
Important Notes
- Cache Results: Store beta detection results at app launch to avoid repeated regex processing
- Not 100% Reliable: This relies on Apple’s naming conventions which could change
- Test on Real Betas: Always verify during each iOS beta cycle
- Use for Diagnostics: Best used for logging and debugging, not critical app logic
☕ Support My Work
If you found this post helpful and want to support more content like this, you can buy me a coffee!
Your support helps me continue creating useful articles and tips for fellow developers. Thank you! 🙏
This post is licensed under CC BY 4.0 by the author.