Yes, a message before termination is needed. But end of the game, once user clicks OK (UIKit) will be app termination, with all our apologies for the inconvenience.
So here is what the code could look like in UIKit. Any comment welcomed.
import UIKit
import DeclaredAgeRange
import StoreKit // In case need to check appStoreAgeRating
class ViewController: UIViewController {
@IBOutlet weak var welcomeLabel: UILabel!
@IBOutlet weak var stopButton: UIButton!
var task: Task<Void, Never>? // To allow to cancel Task if needed
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// test for age to be done here instead of button action ?
}
@available(iOS 26.2, *)
// Called only on iOS 26.2+ in any case
func testAgeRange() async -> Bool {
do {
print("Calling isEligibleForAgeFeatures")
var isEligible = false
print("iOS 26.2 or later")
isEligible = try await AgeRangeService.shared.isEligibleForAgeFeatures
// try Task.checkCancellation() // How to allow user to cancel the check to avoid being blocked ?
print("isEligible", isEligible) // Does not work on simulator
if !isEligible {
print("Not in Texas")
return true // Not in Texas, so we can proceed
}
} catch { // AgeRangeService.Error.notAvailable {
// No age range provided.
return true // should we accept if no ageRange provided, in order not to cause problem out of Texas ?
}
do {
let response = try await AgeRangeService.shared.requestAgeRange(ageGates: /*13, 15,*/ 18, in: self) // To be safe, test for 18 in Texas
var lowerBound = 18
switch response {
case .declinedSharing:
print("User declined to share age.")
return false
case .sharing(let range):
lowerBound = range.lowerBound ?? 18
print("User age range: \(range.lowerBound ?? 0)-\(range.upperBound ?? 99)")
@unknown default:
print( "fatalError()")
return false
}
var ok = false
if lowerBound >= 18 {
// Allow access to 18+ features. We test only for Texas
ok = true
} else {
// Require parental consent ?
// Show age-appropriate content
ok = false // Allow access only to 18+ in Texas
}
return ok // Authorized for all 18+ in Texas
} catch { // AgeRangeService.Error.notAvailable {
// No age range provided.
return false
}
}
func executeStart() {
welcomeLabel.isHidden = false
}
// In case too long wait
@IBAction func stopTask(_ sender: UIButton) {
print("ask to stop task", task?.isCancelled)
task?.cancel()
print("cancelled?", task?.isCancelled)
stopButton.isHidden = true
}
@IBAction func start(_ sender: UIButton) {
stopButton.isHidden = false
task = Task {
// @MainActor in
print("Start")
if #available(iOS 26.2, *) {
let appStoreAgeRating = await AppStore.ageRatingCode ?? 18 // Not used yet
if await self.testAgeRange() { // self needed if detached Task
// Need to test for parental control here ?
if self.task?.isCancelled != nil {
print("Task has been cancelled")
}
} else {
print("No testAgeRange")
// Alert and exit the app when user acks alerts, with the following message
// "Access to this app is age-restricted due to local laws in your state or territory.")
// "Please verify your age with Apple and allow this app to access your age information.")
// "For further information, please refer to the following Apple support article: https://support.apple.com/en-us/122770")
}
} else {
print("Not 26.2")
// do nothing ? We can run the app.
}
self.executeStart()
}
print("We have completed the task")
}
}