Post

Replies

Boosts

Views

Activity

Reply to Stuck in a loop between Guideline 2.1 and 2.2: Reviewer refuses 5 mins of gameplay
Why not just have a way to allow the user to buy the IAP without having to play for 5 minutes? If there's some sort of menu screen just add a way of buying the IAP from there. Some users might play your game for two minutes ten times and never see the screen that appears after five minutes, but they might like it enough to buy it. One of my apps lets you add three "things" in the free version, and unlimited "things" once you've bought the IAP, but you don't have to add exactly three "things" before the buy button appears; it's always there.
Jan ’26
Reply to Too large clock size on iOS 26 Lock Screen.
Whilst it's a consumer feature and not something us third-party devs should be concerned with, I'd say it should be raised as a bug in the usual way so it gets looked at and fixed. Posting it in the Apple Support Communities won't do anything. No one can there can recommend a fix other than not enabling large text... @yrcrt You can raise a bug at: https://feedbackassistant.apple.com/ then post the FB number here.
Topic: Design SubTopic: General Tags:
Jan ’26
Reply to What's Wrong with Apple Review?
@Tomato You could always take this as an opportunity to make the UI more obvious as to what needs to be done. Perhaps you could change the button to say "Select a message to send", then when the user has selected a message, change the button to say "Send", and enable it? You could also reduce the clutter by removing all those "Select" buttons, and making each message a button in its own right. Make them all a light blue with rounded corners, and make the selected one that blue colour so it's obvious (and then enable the "Send" button, as above). Just a thought.
Jan ’26
Reply to In app purchases
// StoreView.swift import StoreKit import SwiftUI struct StoreView: View { @ObservedObject private var storeController = StoreActor.shared.productController var hasPro: Bool { storeController.isEntitled } var body: some View { if let product = storeController.product, !storeController.isEntitled { // Pro features available to the user TO PURCHASE if(product.displayName == ProductNames.pro.humanReadablePurchaseLevel()) { // Show the product in a nice way, using the properties of the `product` variable ProductRow(product: product) } } if(hasPro) { // The user has already purchased the pro version, so show the pro features } } } struct ProductRow: View { @ObservedObject private var storeController = StoreActor.shared.productController var product: Product var body: some View { Text("Upgrade to \(product.displayName)") Text(product.description) // Purchase button Button { Task(priority: .userInitiated) { await storeController.purchase() } } label: { Text("\(product.displayPrice)") } } }
Jan ’26
Reply to In app purchases
These links will explain everything: Explainer: https://developer.apple.com/in-app-purchase/ IAPs with StoreKit: https://developer.apple.com/documentation/storekit/in-app-purchase Create a Sandbox Apple Account: https://developer.apple.com/help/app-store-connect/test-in-app-purchases/create-a-sandbox-apple-account/ Product information for an IAP: https://developer.apple.com/help/app-store-connect/reference/in-app-purchases-and-subscriptions/in-app-purchase-information Overview for configuring IAPs: https://developer.apple.com/help/app-store-connect/configure-in-app-purchase-settings/overview-for-configuring-in-app-purchases View and edit In-App Purchase information: https://developer.apple.com/help/app-store-connect/manage-in-app-purchases/view-and-edit-in-app-purchase-information Testing refund requests: https://developer.apple.com/documentation/storekit/testing-refund-requests Note: You must test this on a physical device. In other words, create your sandbox Apple Account and sign in on a physical device. Deploy your app to that device to test the IAPs. You cannot test them in the Simulator. This code might help you to start: // StoreProductController.swift import Combine import Foundation import StoreKit @MainActor public final class StoreProductController: ObservableObject { @Published public internal(set) var product: Product? @Published public private(set) var isEntitled: Bool = false @Published public private(set) var purchaseError: Error? private let productID: String internal nonisolated init(identifiedBy productID: String) { self.productID = productID Task(priority: .background) { await self.updateEntitlement() } } public func purchase() async { guard let product = product else { return } do { let result = try await product.purchase() switch result { case .success(let verificationResult): switch(verificationResult) { case .verified(let transaction): // Give the user access to purchased content self.isEntitled = true await transaction.finish() case .unverified(let transaction, let verificationError): // Handle unverified transactions based on your business model print("purchase(): Unverified transaction: \(transaction.debugDescription), error: \(verificationError.localizedDescription)") } case .pending: // The purchase requires action from the customer. If the transaction completes, it's available through Transaction.updates break case .userCancelled: // The user cancelled the purchase break @unknown default: // Unknown result break } } catch { purchaseError = error print("purchase(): Error: \(error.localizedDescription)") } } internal func set(isEntitled: Bool) { self.isEntitled = isEntitled } private func updateEntitlement() async { switch await StoreKit.Transaction.currentEntitlement(for: productID) { case .verified: isEntitled = true case .unverified(_, let error): logger.info("Unverified entitlement for \(self.productID): \(error)") fallthrough case .none: isEntitled = false } } } // These can go anywhere enum ProductNames: String, Codable { case free = "feature.free" case pro = "feature.pro" // This must match the IAP in App Store Connect } extension ProductNames { func humanReadablePurchaseLevel() -> String { switch self { case .free: return "Free" case .pro: return "Pro" } } } // StoreActor.swift import Foundation import StoreKit @globalActor public actor StoreActor { public static let featurePro = ProductNames.pro.rawValue static let allProductIDs: Set<String> = [featurePro] public static let shared = StoreActor() private var loadedProducts: [String: Product] = [:] private var lastLoadError: Error? private var productLoadingTask: Task<Void, Never>? private var transactionUpdatesTask: Task<Void, Never>? private var storefrontUpdatesTask: Task<Void, Never>? public nonisolated let productController: StoreProductController init() { self.productController = StoreProductController(identifiedBy: Self.featurePro) Task(priority: .background) { await self.setupListenerTasksIfNecessary() await self.loadProducts() } } public func product(identifiedBy productID: String) async -> Product? { await waitUntilProductsLoaded() return loadedProducts[productID] } private func setupListenerTasksIfNecessary() { if(transactionUpdatesTask == nil) { transactionUpdatesTask = Task(priority: .background) { for await update in StoreKit.Transaction.updates { await self.handle(transaction: update) } } } if(storefrontUpdatesTask == nil) { storefrontUpdatesTask = Task(priority: .background) { for await update in Storefront.updates { self.handle(storefrontUpdate: update) } } } } private func waitUntilProductsLoaded() async { if let task = productLoadingTask { await task.value } else if(loadedProducts.isEmpty) { // You load all the products at once, so you can skip this if the dictionary is empty let newTask = Task { await loadProducts() } productLoadingTask = newTask await newTask.value } } private func loadProducts() async { do { let products = try await Product.products(for: Self.allProductIDs) try Task.checkCancellation() loadedProducts = products.reduce(into: [:]) { $0[$1.id] = $1 } let premiumProduct = loadedProducts[Self.featurePro] Task(priority: .utility) { @MainActor in self.productController.product = premiumProduct } } catch { lastLoadError = error } productLoadingTask = nil } private func handle(transaction: VerificationResult<StoreKit.Transaction>) async { guard case .verified(let transaction) = transaction else { print("Received unverified transaction: \(transaction)") return } if(transaction.productType == .nonConsumable && transaction.productID == Self.featurePro) { await productController.set(isEntitled: !transaction.isRevoked) if(transaction.isRevoked) { // Refund has been issued; disable pro features } } await transaction.finish() } private func handle(storefrontUpdate newStorefront: Storefront) { // Cancel existing loading task if necessary if let task = productLoadingTask { task.cancel() } // Load products again productLoadingTask = Task(priority: .utility) { await self.loadProducts() } } } public extension StoreKit.Transaction { var isRevoked: Bool { // The revocation date is never in the future revocationDate != nil } }
Jan ’26
Reply to nable to Distribute iOS App to App Store – Archive Shows Only 'Distribute Content'
You didn't attach any screenshots here. Did you report this in the Feedback Assistant and just copy it to here? These are the Developer Forums, where developers of third-party apps for Apple's platforms ask each other for hints and tips on coding. You're basically speaking to other third-party developers like you. If you need to get in touch with Developer Support, go here: https://developer.apple.com/support/
Jan ’26
Reply to something that's probably easy to fix but it's driving me crazy
One of your targets is likely including the Info.plist file in the "Copy Bundle Resources" step. Select your project in the project viewer. It's the first item at the top, with the little blue icon. Select your main iOS target under the TARGETS section. In the big main pane, select the Build Settings tab. In the search field in the top-right, enter "info", then scroll down to the Packaging section. You likely have "Generate Info.plist file" set to Yes. This is fine, leave it as it is. For each target you have (you may have only one): Select the target from the TARGETS section. In the big main pane, select the "Build Phases" tab. Expand the Copy Bundle Resources item. If "Info.plist" is listed there, remove it. (Do not remove InfoPlist.strings!) Hope this helps.
Jan ’26
Reply to The Liquid Glass effect disappeared after being released online
Use the formatting toolbar! if #available(iOS 26.0, *) { let blurEffect = UIBlurEffect(style: .systemUltraThinMaterial) appearance.backgroundEffect = blurEffect tabBar.directionalLayoutMargins = .zero tabBar.preservesSuperviewLayoutMargins = false appearance.backgroundColor = .clear tabBar.layer.allowsEdgeAntialiasing = true tabBar.clipsToBounds = false tabBar.isTranslucent = true } tabBar.standardAppearance = appearance
Topic: UI Frameworks SubTopic: UIKit
Jan ’26
Reply to Is this normal while developing an app on the watch?
When you say you unpaired the Watch, do you mean you actually unpaired it from the iPhone or unpaired it in the Devices & Simulators window in Xcode? I recently had a bunch of those errors, and the fix for me was to do the following. Note, this took about 12 hours overnight for it to "self-heal," so you may also have to try again in the morning. Unpair the Watch in Xcode, in the Devices & Simulators window. Click the + button in the bottom-left of that window. Your Watch will not be listed. Close the windows and quit Xcode. Re-launch Xcode. Re-open the Devices & Simulators window. If your Watch is not automatically listed as a disconnected device, click the + button again. If your device is listed and available to set up, click Next to do so. Your Watch should now work normally with Xcode. If your device is not listed, quit Xcode, wait about 12 hours, and try again. I don't know why it took so long, but I was sitting there trying to add it again for at least an hour, quitting Xcode and retrying, rebooting the Mac and retrying, to no avail. But the next morning, it just worked.
Jan ’26
Reply to Big problem on account!
Not sure if this is how you do it, but in the "Detalles de la membresía" section, there should be a blue link that says (in English) "Update your information". It has a little + sign in a circle. When you click that a dialog box should appear with an option to convert to an individual account. If that option is there, click it and follow any on-screen instructions. If it's not there, then you may have to get in touch with Apple Developer Support via this page: https://developer.apple.com/contact/ and ask that your account be changed to an individual account. That's all I can do to help you, sorry. I've never had to do this myself.
Jan ’26
Reply to My app crashes on startup in iOS 26
As far as I understand Core Data - and who really understands it, really? - an entity will have an ObjC class generated for it, so you've just unfortunately used a name that Apple is now using for a class in one of their private frameworks on iOS 26. As you've seen, renaming your entity fixes the issue because your new entity's generated class no longer has the same name, and so the compiler isn't seeing two classes with the same name. As a general rule, avoid using names anywhere in your code that start with three capital letters, i.e. PSSegment. It's tempting to use your own name to name classes, and in your case perhaps you would name something HHSegment (because: HeezerHiker). Apple use this convention, so you should try and avoid it. For example, NSString == NS String == NextStep String. You could use something like HHEntitySegment, or the name of your app, i.e. MyAppSegment.
Jan ’26
Reply to Cyber Stalking from Criminal Organization
No evidence provided here, but what do you think a bunch of random third-party developers can do to help you? WE ARE NOT APPLE. WE CANNOT DO ANYTHING TO YOUR DEVICE. Take your suspicions to your local law enforcement and have them deal with it. These forums are not the right place for this.
Replies
Boosts
Views
Activity
Jan ’26
Reply to Stuck in a loop between Guideline 2.1 and 2.2: Reviewer refuses 5 mins of gameplay
Why not just have a way to allow the user to buy the IAP without having to play for 5 minutes? If there's some sort of menu screen just add a way of buying the IAP from there. Some users might play your game for two minutes ten times and never see the screen that appears after five minutes, but they might like it enough to buy it. One of my apps lets you add three "things" in the free version, and unlimited "things" once you've bought the IAP, but you don't have to add exactly three "things" before the buy button appears; it's always there.
Replies
Boosts
Views
Activity
Jan ’26
Reply to Too large clock size on iOS 26 Lock Screen.
Whilst it's a consumer feature and not something us third-party devs should be concerned with, I'd say it should be raised as a bug in the usual way so it gets looked at and fixed. Posting it in the Apple Support Communities won't do anything. No one can there can recommend a fix other than not enabling large text... @yrcrt You can raise a bug at: https://feedbackassistant.apple.com/ then post the FB number here.
Topic: Design SubTopic: General Tags:
Replies
Boosts
Views
Activity
Jan ’26
Reply to something that's probably easy to fix but it's driving me crazy
@GGdev26 My suggestion? If so, please accept it as the answer so that others know there's a solution to this problem. Thanks!
Replies
Boosts
Views
Activity
Jan ’26
Reply to What's Wrong with Apple Review?
@Tomato You could always take this as an opportunity to make the UI more obvious as to what needs to be done. Perhaps you could change the button to say "Select a message to send", then when the user has selected a message, change the button to say "Send", and enable it? You could also reduce the clutter by removing all those "Select" buttons, and making each message a button in its own right. Make them all a light blue with rounded corners, and make the selected one that blue colour so it's obvious (and then enable the "Send" button, as above). Just a thought.
Replies
Boosts
Views
Activity
Jan ’26
Reply to In app purchases
As you've discovered, ChatGPT didn't help you, and "vibe coding" was a bust. The real way to do these things is to ask a human. We're here to help. Always ask a human first.
Replies
Boosts
Views
Activity
Jan ’26
Reply to In app purchases
// StoreView.swift import StoreKit import SwiftUI struct StoreView: View { @ObservedObject private var storeController = StoreActor.shared.productController var hasPro: Bool { storeController.isEntitled } var body: some View { if let product = storeController.product, !storeController.isEntitled { // Pro features available to the user TO PURCHASE if(product.displayName == ProductNames.pro.humanReadablePurchaseLevel()) { // Show the product in a nice way, using the properties of the `product` variable ProductRow(product: product) } } if(hasPro) { // The user has already purchased the pro version, so show the pro features } } } struct ProductRow: View { @ObservedObject private var storeController = StoreActor.shared.productController var product: Product var body: some View { Text("Upgrade to \(product.displayName)") Text(product.description) // Purchase button Button { Task(priority: .userInitiated) { await storeController.purchase() } } label: { Text("\(product.displayPrice)") } } }
Replies
Boosts
Views
Activity
Jan ’26
Reply to In app purchases
These links will explain everything: Explainer: https://developer.apple.com/in-app-purchase/ IAPs with StoreKit: https://developer.apple.com/documentation/storekit/in-app-purchase Create a Sandbox Apple Account: https://developer.apple.com/help/app-store-connect/test-in-app-purchases/create-a-sandbox-apple-account/ Product information for an IAP: https://developer.apple.com/help/app-store-connect/reference/in-app-purchases-and-subscriptions/in-app-purchase-information Overview for configuring IAPs: https://developer.apple.com/help/app-store-connect/configure-in-app-purchase-settings/overview-for-configuring-in-app-purchases View and edit In-App Purchase information: https://developer.apple.com/help/app-store-connect/manage-in-app-purchases/view-and-edit-in-app-purchase-information Testing refund requests: https://developer.apple.com/documentation/storekit/testing-refund-requests Note: You must test this on a physical device. In other words, create your sandbox Apple Account and sign in on a physical device. Deploy your app to that device to test the IAPs. You cannot test them in the Simulator. This code might help you to start: // StoreProductController.swift import Combine import Foundation import StoreKit @MainActor public final class StoreProductController: ObservableObject { @Published public internal(set) var product: Product? @Published public private(set) var isEntitled: Bool = false @Published public private(set) var purchaseError: Error? private let productID: String internal nonisolated init(identifiedBy productID: String) { self.productID = productID Task(priority: .background) { await self.updateEntitlement() } } public func purchase() async { guard let product = product else { return } do { let result = try await product.purchase() switch result { case .success(let verificationResult): switch(verificationResult) { case .verified(let transaction): // Give the user access to purchased content self.isEntitled = true await transaction.finish() case .unverified(let transaction, let verificationError): // Handle unverified transactions based on your business model print("purchase(): Unverified transaction: \(transaction.debugDescription), error: \(verificationError.localizedDescription)") } case .pending: // The purchase requires action from the customer. If the transaction completes, it's available through Transaction.updates break case .userCancelled: // The user cancelled the purchase break @unknown default: // Unknown result break } } catch { purchaseError = error print("purchase(): Error: \(error.localizedDescription)") } } internal func set(isEntitled: Bool) { self.isEntitled = isEntitled } private func updateEntitlement() async { switch await StoreKit.Transaction.currentEntitlement(for: productID) { case .verified: isEntitled = true case .unverified(_, let error): logger.info("Unverified entitlement for \(self.productID): \(error)") fallthrough case .none: isEntitled = false } } } // These can go anywhere enum ProductNames: String, Codable { case free = "feature.free" case pro = "feature.pro" // This must match the IAP in App Store Connect } extension ProductNames { func humanReadablePurchaseLevel() -> String { switch self { case .free: return "Free" case .pro: return "Pro" } } } // StoreActor.swift import Foundation import StoreKit @globalActor public actor StoreActor { public static let featurePro = ProductNames.pro.rawValue static let allProductIDs: Set<String> = [featurePro] public static let shared = StoreActor() private var loadedProducts: [String: Product] = [:] private var lastLoadError: Error? private var productLoadingTask: Task<Void, Never>? private var transactionUpdatesTask: Task<Void, Never>? private var storefrontUpdatesTask: Task<Void, Never>? public nonisolated let productController: StoreProductController init() { self.productController = StoreProductController(identifiedBy: Self.featurePro) Task(priority: .background) { await self.setupListenerTasksIfNecessary() await self.loadProducts() } } public func product(identifiedBy productID: String) async -> Product? { await waitUntilProductsLoaded() return loadedProducts[productID] } private func setupListenerTasksIfNecessary() { if(transactionUpdatesTask == nil) { transactionUpdatesTask = Task(priority: .background) { for await update in StoreKit.Transaction.updates { await self.handle(transaction: update) } } } if(storefrontUpdatesTask == nil) { storefrontUpdatesTask = Task(priority: .background) { for await update in Storefront.updates { self.handle(storefrontUpdate: update) } } } } private func waitUntilProductsLoaded() async { if let task = productLoadingTask { await task.value } else if(loadedProducts.isEmpty) { // You load all the products at once, so you can skip this if the dictionary is empty let newTask = Task { await loadProducts() } productLoadingTask = newTask await newTask.value } } private func loadProducts() async { do { let products = try await Product.products(for: Self.allProductIDs) try Task.checkCancellation() loadedProducts = products.reduce(into: [:]) { $0[$1.id] = $1 } let premiumProduct = loadedProducts[Self.featurePro] Task(priority: .utility) { @MainActor in self.productController.product = premiumProduct } } catch { lastLoadError = error } productLoadingTask = nil } private func handle(transaction: VerificationResult<StoreKit.Transaction>) async { guard case .verified(let transaction) = transaction else { print("Received unverified transaction: \(transaction)") return } if(transaction.productType == .nonConsumable && transaction.productID == Self.featurePro) { await productController.set(isEntitled: !transaction.isRevoked) if(transaction.isRevoked) { // Refund has been issued; disable pro features } } await transaction.finish() } private func handle(storefrontUpdate newStorefront: Storefront) { // Cancel existing loading task if necessary if let task = productLoadingTask { task.cancel() } // Load products again productLoadingTask = Task(priority: .utility) { await self.loadProducts() } } } public extension StoreKit.Transaction { var isRevoked: Bool { // The revocation date is never in the future revocationDate != nil } }
Replies
Boosts
Views
Activity
Jan ’26
Reply to nable to Distribute iOS App to App Store – Archive Shows Only 'Distribute Content'
You didn't attach any screenshots here. Did you report this in the Feedback Assistant and just copy it to here? These are the Developer Forums, where developers of third-party apps for Apple's platforms ask each other for hints and tips on coding. You're basically speaking to other third-party developers like you. If you need to get in touch with Developer Support, go here: https://developer.apple.com/support/
Replies
Boosts
Views
Activity
Jan ’26
Reply to something that's probably easy to fix but it's driving me crazy
One of your targets is likely including the Info.plist file in the "Copy Bundle Resources" step. Select your project in the project viewer. It's the first item at the top, with the little blue icon. Select your main iOS target under the TARGETS section. In the big main pane, select the Build Settings tab. In the search field in the top-right, enter "info", then scroll down to the Packaging section. You likely have "Generate Info.plist file" set to Yes. This is fine, leave it as it is. For each target you have (you may have only one): Select the target from the TARGETS section. In the big main pane, select the "Build Phases" tab. Expand the Copy Bundle Resources item. If "Info.plist" is listed there, remove it. (Do not remove InfoPlist.strings!) Hope this helps.
Replies
Boosts
Views
Activity
Jan ’26
Reply to The Liquid Glass effect disappeared after being released online
Use the formatting toolbar! if #available(iOS 26.0, *) { let blurEffect = UIBlurEffect(style: .systemUltraThinMaterial) appearance.backgroundEffect = blurEffect tabBar.directionalLayoutMargins = .zero tabBar.preservesSuperviewLayoutMargins = false appearance.backgroundColor = .clear tabBar.layer.allowsEdgeAntialiasing = true tabBar.clipsToBounds = false tabBar.isTranslucent = true } tabBar.standardAppearance = appearance
Topic: UI Frameworks SubTopic: UIKit
Replies
Boosts
Views
Activity
Jan ’26
Reply to Big problem on account!
Don't use comments to respond; they don't mark the thread as being updated. Anyway, you'll definitely have to get in touch with Developer Support if the options aren't there for you.
Replies
Boosts
Views
Activity
Jan ’26
Reply to Is this normal while developing an app on the watch?
When you say you unpaired the Watch, do you mean you actually unpaired it from the iPhone or unpaired it in the Devices & Simulators window in Xcode? I recently had a bunch of those errors, and the fix for me was to do the following. Note, this took about 12 hours overnight for it to "self-heal," so you may also have to try again in the morning. Unpair the Watch in Xcode, in the Devices & Simulators window. Click the + button in the bottom-left of that window. Your Watch will not be listed. Close the windows and quit Xcode. Re-launch Xcode. Re-open the Devices & Simulators window. If your Watch is not automatically listed as a disconnected device, click the + button again. If your device is listed and available to set up, click Next to do so. Your Watch should now work normally with Xcode. If your device is not listed, quit Xcode, wait about 12 hours, and try again. I don't know why it took so long, but I was sitting there trying to add it again for at least an hour, quitting Xcode and retrying, rebooting the Mac and retrying, to no avail. But the next morning, it just worked.
Replies
Boosts
Views
Activity
Jan ’26
Reply to Big problem on account!
Not sure if this is how you do it, but in the "Detalles de la membresía" section, there should be a blue link that says (in English) "Update your information". It has a little + sign in a circle. When you click that a dialog box should appear with an option to convert to an individual account. If that option is there, click it and follow any on-screen instructions. If it's not there, then you may have to get in touch with Apple Developer Support via this page: https://developer.apple.com/contact/ and ask that your account be changed to an individual account. That's all I can do to help you, sorry. I've never had to do this myself.
Replies
Boosts
Views
Activity
Jan ’26
Reply to My app crashes on startup in iOS 26
As far as I understand Core Data - and who really understands it, really? - an entity will have an ObjC class generated for it, so you've just unfortunately used a name that Apple is now using for a class in one of their private frameworks on iOS 26. As you've seen, renaming your entity fixes the issue because your new entity's generated class no longer has the same name, and so the compiler isn't seeing two classes with the same name. As a general rule, avoid using names anywhere in your code that start with three capital letters, i.e. PSSegment. It's tempting to use your own name to name classes, and in your case perhaps you would name something HHSegment (because: HeezerHiker). Apple use this convention, so you should try and avoid it. For example, NSString == NS String == NextStep String. You could use something like HHEntitySegment, or the name of your app, i.e. MyAppSegment.
Replies
Boosts
Views
Activity
Jan ’26