Posts

Post not yet marked as solved
0 Replies
83 Views
It seems that all macOS apps currently going through App Review which use ASWebAuthenticationSession (or SwiftUI webAuthenticationSession) are being rejected, because: Reason given: Guideline 4.0 - Design The user is taken to the default web browser to sign in or register for an account, which provides a poor user experience. See here for more details: https://forums.developer.apple.com/forums/thread/750400 If webAuthenticationSession is no longer permitted for production apps, what is the current recommended approach for implementing OAuth 2 authentication?
Posted
by dutton.
Last updated
.
Post not yet marked as solved
0 Replies
132 Views
Hi. My app has a single IAP which I currently promote separately on the App Store, as well as allowing purchase in-app. My app currently implements the paymentQueue delegate method which makes the separate promotional listing possible. I want to stop separately promoting my IAP, leaving my app as the only way to purchase the IAP in future. How can I remove the separate listing for my IAP on the App Store? Can I simply delete the promotional artwork for the IAP in App Store Connect? Do I need to release a new binary which no longer supports the paymentQueue delegate? Can I simply unselect “This promotion will be shown to all app store users…” in App Store Visibility and not bother to implement the SKProductStorePromotionController update visibility delegate? Please advise! Thanks in advance.
Posted
by dutton.
Last updated
.
Post not yet marked as solved
2 Replies
397 Views
For the 15th time today I've failed to copy some text in Xcode because because, as of Xcode 15, what used to be: Right-click -> Copy has now become: Right-click -> Copy -> Copy The former is standard on literally every other app I use on every platform, including macOS so I'm probably going to carry on making this mistake forever. Could someone on the Xcode team please explain why this was thought to be a good idea? Either that, or change it back?
Posted
by dutton.
Last updated
.
Post not yet marked as solved
0 Replies
667 Views
The following line of code worked fine in macOS 13.3 / Catalyst: try await MPMediaLibrary.default().addItem(withProductID: "1678639572") Since the 13.4 update it now throws an error: The operation couldn’t be completed. (MPErrorDomain error 11.) Although the Apple Music track with catalog Id 1678639572 does seem to get added to the library regardless. MPError.errorCode 11 is an undocumented value. Can someone please advise what error code 11 actually means, and if there's a workaround? I've raised this as feedback id FB12196635.
Posted
by dutton.
Last updated
.
Post marked as solved
29 Replies
19k Views
Since today, I'm getting this message (including the typo) every single time I try to upload a new build of my app to both the macOS & iOS app stores. If I upload the iOS version first, I'll get the error when I upload the macOS version. If I upload the macOS version first, I get the error on the iOS version. It's a catalyst app and it's the same build across both platforms. Common build numbers between iOS & macOS have always been allowed, and I released a binary to both iOS & macOS app stores with the same build number last week. Any idea what's going on?
Posted
by dutton.
Last updated
.
Post not yet marked as solved
6 Replies
1k Views
Our app is a Catalyst app with a single non-consumable in-app purchase. Over the last few weeks we've had a number of users complaining that they've purchased the app on macOS but when they come to restore the same purchase on their iOS devices, our app tells them that there's "No purchase found to restore". This is the code that is giving the message: public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { guard queue.transactions.count > 0 else { self.setError(text: "No purchase found to restore") return } ... So the delegate function is getting called, but the transaction queue is empty. We never have problems with iOS purchasers restoring purchases to iOS devices. Nor do we have issues when macOS purchasers restore to macOS devices. It's only an issue when macOS purchasers restore to iOS devices. We now advise users experiencing this issue to go through the purchase route on their iOS devices. This will give them an error telling them that they have already purchased this IAP, but then subsequently the IAP seems to be available to restore on the iOS App Store. This should not be necessary. Please advise if there's something else we should be doing to fix this long term. I've also raised Feedback on this (FB11733455).
Posted
by dutton.
Last updated
.
Post marked as solved
2 Replies
1.2k Views
Creating playlists on iPadOS 16.1 isn't working for me. The code: let playlist = try await MusicLibrary.shared.createPlaylist(name: caption, description: description, authorDisplayName: curator, items: items) is throwing the error "Attempting to retrieve handler for an unsupported configuration". The full error is: The same code is working fine on an iPhone running iOS 16.0. Does anyone know how to get this working?
Posted
by dutton.
Last updated
.
Post not yet marked as solved
0 Replies
1.6k Views
I’ve been trying to debug an issue where tracks in playlists created using the new MusicLibrary.shared.createPlaylist function seem to be missing their artwork. For example: I’ve found that when I look at the track which MusicKit added in the above example, it’s not the one which I specified - it’s a different version (catalogId) of the same song. Interestingly it also seems to be marked as a library track (the track is definitely not in my library). Here is the code: let request = MusicCatalogResourceRequest<Song>(matching: \.id, equalTo: MusicItemID("1440800296")) let response = try await request.response() let initialCatalogId = response.items.first?.id.rawValue ?? "?" let playlist = try await MusicLibrary.shared.createPlaylist(name: "My Playlist", description: nil, authorDisplayName: "My App", items: response.items) while true {     guard let entry = try? await playlist.with([.entries], preferredSource: .library).entries?.first else {         continue     }     // This is just for debug purposes to work out what track actually got added:     let data = try JSONEncoder().encode(entry.playParameters)     guard let root = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {         continue     }     let newCatalogId = root["catalogId"] as? String ?? "?"     let isLibrary = root["isLibrary"] as? Bool ?? false     print(initialCatalogId, newCatalogId, isLibrary)     // Prints: 1440800296 309186793 true     break } If I’m doing something wrong either when saving the playlist or retrieving the catalogId of the track which was saved please let me know: I confess I’m finding it quite hard to navigate MusicKit Swift at the moment as examples & documentation seem quite thin on the ground. However if the above behaviour is the best I can expect then it’s a serious issue for me - the catalogId of the track I want to add, above, was carefully selected so if MusicKit is going to randomly swap this out then I can’t use it. I’ve created FB11589088 for this issue. @JoeKun, @david-apple: I’ve raised a number of issues with MusicKit on here recently and have received no responses at all. Appreciate you guys are really busy but I think a little help here would go a long way to making MusicKit the success it deserves to be. Please let me know if I should be doing this differently. Thanks!
Posted
by dutton.
Last updated
.
Post not yet marked as solved
0 Replies
667 Views
I'm creating playlists in iOS 16 with the following code: let playlist = try await MusicLibrary.shared.createPlaylist(name: caption, description: description, authorDisplayName: nil, items: items) I want to set the authorDisplayName to the current user, who used my app to curate the playlist. How can I do this? Looking at playlists in the Music app which were created using the Apple Music API the user's name was being defaulted automatically. But I can't seem to replicate this using MusicKit: I can't explicitly set the name as I don't think it's exposed to me, and if I set it to nil or empty then I get my app name, not the user's name. Thanks in advance.
Posted
by dutton.
Last updated
.
Post not yet marked as solved
0 Replies
1.4k Views
Hi. I have a situation where I need to create a playlist with MusicKit and then, shortly after, access that same playlist in the cloud using the Apple Music API. I'm creating the playlist without issue. The code looks like: let playlist = try await MusicLibrary.shared.createPlaylist(name: caption, description: description, authorDisplayName: nil, items: items) So far, so good. However when I look at the resulting ID for that playlist I get something like "454485423519848175", which doesn't look much like the Id that I need to access that playlist via the Apple Music API. If I immediately query the playlist again with something like: var playlistsRequest = MusicLibraryRequest<Playlist>.init() playlistsRequest.filter(matching: \.name, equalTo: caption) let playlistsResponse = try await playlistsRequest.response() print(playlistsResponse.items.first?.id ?? "") I get the same "454485423519848175" style of Id. However if I run the same code a few minutes later, I'll get a cloud Id of the form "p.QZ0L3h6PM8a", which is what I need. Current I'm having to poll the cloud version of the user's library until the playlist I've created appears, which is horrible. Is there a better way to access the cloud playlist Id directly at the point of creation? Thanks in advance!
Posted
by dutton.
Last updated
.
Post not yet marked as solved
2 Replies
940 Views
Hi. I'm trying out some of the new MusicKit functionality using Xcode 14 beta 3 and macOS 13 beta 3 and I'm getting the above error (or similar) whenever I use any of the new classes with a Mac Catalyst destination (an iOS 16 destination works fine). I'm guessing one of my build settings is incorrect on either my project or target, but it's not obvious. So far I've set: Project / IOS Deployment Target = 16.0 Target / Minimum Deployments / iOS = 16.0 Could someone please tell me what I'm missing? Thanks in advance!
Posted
by dutton.
Last updated
.
Post not yet marked as solved
1 Replies
1.3k Views
I have a SwiftUI app live on the App Store which uses ASWebAuthenticationSession to authenticate against several remote services. It all runs perfectly on iOS but I'm getting a stream of complaints from users running it on Monterey / Catalyst. There seem to be 2 main errors: The auth browser window doesn't anchor properly, so the window will pop-up but it's completely independent of my app, so can easily end-up behind my application (which then appears to have hung) Even worse, on some machines (mostly m1 iMacs) the window doesn't pop-up at all but the OAuth request to the browser sits in some queue somewhere and at a later point when the user happens to restart their browser they will be prompted to login for every single time they clicked on the "sign in" button in my app. I've seen lots of other reports of the 2nd problem which just seems to happen randomly so I don't have a repro. I've seen a similar number of different ways of implementing ASWebAuthenticationPresentationContextProviding which (I presume) ought to fix the former. Unfortunately none of them work. I'm including some minimal code to reproduce the former issue. This is my own amalgamation of several other approaches. It doesn't work - if you click on the "sign in" button the OAuth window will pop-up but it's completely independent and you can easily move the "anchor" window on top of it. Has anyone managed to get this working? Here's the code: import SwiftUI import AuthenticationServices import UIKit struct SignInView: View {     @StateObject var viewModel = SignInViewModel()     @State var window: UIWindow? = nil     var body: some View {         VStack(spacing: 16) {             Image(systemName: "person.circle")                 .resizable()                 .frame(width: 50, height: 50)                 .foregroundColor(.primary)             VStack(spacing: 8) {                 Text("You must be log in to proceed any further")                     .foregroundColor(.secondary)                     .font(.title3)                     .multilineTextAlignment(.center)                     .padding()                 Button {                     viewModel.signIn(window: self.window)                 } label: {                     Text("Sign In")                         .foregroundColor(.white)                         .padding()                         .clipShape(RoundedRectangle(cornerRadius: 8))                         .background(                                     HostingWindowFinder { window in                                         self.window = window                                    }                         )                 }             }         }     } } class SignInViewModel: NSObject, ObservableObject, ASWebAuthenticationPresentationContextProviding {     var window: UIWindow? = nil     func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {         return window ?? ASPresentationAnchor()     }     func signIn(window: UIWindow?) {         self.window = window         let authSession = ASWebAuthenticationSession(url: URL(string: "https://accounts.spotify.com/authorize")!, callbackURLScheme: "myapp-auth") { (url, error) in             if let error = error {                 print(error.localizedDescription)             } else if let url = url {                 print(url.absoluteString)             }         }         authSession.presentationContextProvider = self         authSession.prefersEphemeralWebBrowserSession = true         authSession.start()     } } struct HostingWindowFinder: UIViewRepresentable {     var callback: (UIWindow?) -> ()     func makeUIView(context: Context) -> UIView {         let view = UIView()         DispatchQueue.main.async { [weak view] in             self.callback(view?.window)         }         return view     }     func updateUIView(_ uiView: UIView, context: Context) {     } }
Posted
by dutton.
Last updated
.
Post not yet marked as solved
5 Replies
1.6k Views
Hi.Am currently struggling to get approval for our first version of an app and have hit a brick wall. Am hoping someone might be able to help point us in the right direction - any help would be greatly appreciated.Background: One of the first things our app does on startup is call SKProductsRequest to get it's IAP (it only has one). When we or any of our TestFlight users run our app everything works fine - the IAP is returned and when the user reaches the appropriate screen we give them a "Purchase" button along with localised pricing information. This has been 100% reliable.The issue: When our app gets run during the Review process it seems as though the SKProductsRequest is not returning any IAPs. If no IAPs are returned we display an error message instead of the "Purchase" button and we've now been rejected twice because the message, rather than the purchase button, is being displayed.Question: Is there anything we need to specifically code for when the app is being reviewed? Why might the response during review be different from when we are testing the app ourselves?Our initial app binary was submitted along side an IAP and the two were linked in App Store Connect when we submitted the binary. We have asked the Review team whether they have turned-over the IAP to their environment but they have referred us to Developer Support.Thanks in advance to to anyone who can give us any pointers!
Posted
by dutton.
Last updated
.
Post not yet marked as solved
6 Replies
1.2k Views
This is becoming a big issue for us. We have a MusicKit app developed using Catalyst and since we released the Mac version we’ve had a constant trickle of users who seem to be completely unable to use the app because SKCloudServiceController().requestCapabilities reports that they do not have .addToCloudMusicLibrary capability, although this is clearly incorrect and they have all the right permissions (the iOS app version works fine). After a LOT of investigation we’ve discovered that on a Mac where the logged-in user has: iCloud logged-in using Apple ID “user1” App Store & Book Store logged-in using Apple ID “user2” Apple Music/iTunes/Media logged-in under Apple ID “user3” SKCloudServiceController().requestCapabilities will sometimes report the Apple Music capabilities of “user2” or even “user1” rather than “user3”. This is very bad: it should only ever use the “user3” account. In one situation, user2 actually had an Apple Music account with the requisite permissions and our app ended-up creating playlists in user2’s account rather than user3’s account. We’ve reported this as a bug (FB9100381) but heard nothing back. Can someone please acknowledge that this is being looked-at? The issue does appear to be intermittent and some Mac’s work fine. However we find that a brand-new Mac set up with a single user account where that account uses different apple id’s, as above, the issue is quite easy to reproduce. iOS handles this 100% correctly. It’s only a MacOS (Big Sur) issue and we’re getting a constant trickle of unwarranted of 1* reviews because of it. Thanks!
Posted
by dutton.
Last updated
.
Post not yet marked as solved
1 Replies
896 Views
We’ve reported a bug for this and I’m really looking for workarounds in the meantime. The problem: if you use SKCloudServiceController on Catalina, instead of using the account / Apple Id of the current user’s iTunes/Media account as it should, it uses the account / Apple Id of the current user’s iCloud account instead. On most installs these accounts are one &amp; the same so the issue is not apparent. However there is a sizeable minority of Mac users who use different accounts for their itunes login and for these users any Catalyst apps which interact with Apple Music will end up interacting with the WRONG account (e.g. they will create playlists for the wrong user). iOS handles this correctly. So the question is: is there any way to work around this? Either at the MacOS level (apart from telling users not to use a different account) or at the SKCloudServiceController level (e.g. to specify which account to use)?
Posted
by dutton.
Last updated
.