Post

Replies

Boosts

Views

Activity

Reply to Question: How to support landscape-only on iPad app after 'Support for all orientations will soon be required' warning
I wonder if this is because on iPadOS users can now resize apps, so forcing them to a certain orientation is redundant now? If you want to lock in an orientation, you could maybe set the scene's minimumSize so users can't resize it smaller than a specific size that just happens to be a landscape window, such as 800 x 600? i.e.: class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene: UIWindowScene = scene as? UIWindowScene else { return } windowScene.sizeRestrictions?.minimumSize = CGSizeMake(800, 600) ... So, implement that, and tick all the orientation check boxes in the deployment info. Note, you should probably try and make sure your chosen window size makes your app look good on a smaller iPad (iPad mini) in portrait orientation. If necessary, compress or hide parts of the UI when the window is too small.
Topic: UI Frameworks SubTopic: General Tags:
2w
Reply to Camera Permissions Popup
No idea what you mean by "it freezes the Camera feed", and you don't exactly say how those cameraStatus and isAuthorized variables are used. I assume the initial value of isAuthorized is false ? You don't show that in the code. Do you need two variables? It seems: whenever isAuthorized is true, cameraStatus is .authorised; and vice versa, when isAuthorized is false, cameraStatus is notAuthorised. Except in the last case: @unknown default: where isAuthorized is true but cameraStatus is notAuthorised. Could this be your issue? Could you use just one variable to cut down on the complexity?
2w
Reply to Universal Control Copy Paste Issue
I agree with @Etresoft, but I would also point out that these forums aren't the place for your post. These are the Developer Forums, where developers of apps for Apple's platforms ask each other for hints and tips on coding. You are talking to other third-party developers here. While some Apple employees do visit these forums, they are likely to agree with @Etresoft in this instance, and, if they don't agree, they would probably point out that your issue is more suited to the Apple Support Forums. (And yes, I'm aware of your other post regarding being unable to post in the other forums, but that does not negate the fact that this is not the place for your post.)
2w
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.
3w
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:
3w
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.
3w
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)") } } }
3w
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 } }
3w
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/
3w
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.
3w