ios - SKCloudServiceController().requestUserToken 在 iOS 14.2 上卡住

标签 ios swift xcode swiftui swift4.2

我正在尝试从 SKCloudServiceController 运行以下功能,但出于某种原因,每次运行时,应用程序都会卡住。我已经测试了我的开发人员 token ,它确实有效。我正在运行 Xcode 12.2。也许有一个更新会使它不再工作?

我已经测试了 token 并且它有效。

class AppleMusicAPI {
    let developerToken = "b'eyJ0{...}RDlRSlFw'"

    func getUserToken() -> String {
        var userToken = String()
        let lock = DispatchSemaphore(value: 0)
        func requestAccess(_ completion: @escaping(String?) -> Void) {
            SKCloudServiceController().requestUserToken(forDeveloperToken: developerToken) { (receivedToken, error) in
                completion(receivedToken)
            }
        }
        requestAccess( { (completeToken) in
            if let token = completeToken {
                userToken = token
                lock.signal()
            }
        })
        lock.wait()
        return userToken
    }

    func fetchStorefrontID() -> String {
        let lock = DispatchSemaphore(value: 0)
        var storefrontID: String!
        let musicURL = URL(string: "https://api.music.apple.com/v1/me/storefront")!
        var musicRequest = URLRequest(url: musicURL)
        musicRequest.httpMethod = "GET"
        musicRequest.addValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
        musicRequest.addValue(getUserToken(), forHTTPHeaderField: "Music-User-Token")
        
        URLSession.shared.dataTask(with: musicRequest) { (data, response, error) in
            guard error == nil else { return }
            
            if let json = try? JSON(data: data!) {
                let result = (json["data"]).array!
                let id = (result[0].dictionaryValue)["id"]!
                storefrontID = id.stringValue
                lock.signal()
            }
        }.resume()
        
        lock.wait()
        return storefrontID
    }
    
    func searchAppleMusic(_ searchTerm: String!) -> [Song] {
        let lock = DispatchSemaphore(value: 0)
        var songs = [Song]()

        let musicURL = URL(string: "https://api.music.apple.com/v1/catalog/\(fetchStorefrontID())/search?term=\(searchTerm.replacingOccurrences(of: " ", with: "+"))&types=songs&limit=25")!
        var musicRequest = URLRequest(url: musicURL)
        musicRequest.httpMethod = "GET"
        musicRequest.addValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
        musicRequest.addValue(getUserToken(), forHTTPHeaderField: "Music-User-Token")
        
        URLSession.shared.dataTask(with: musicRequest) { (data, response, error) in
            guard error == nil else { return }
            if let json = try? JSON(data: data!) {
                let result = (json["results"]["songs"]["data"]).array!
                for song in result {
                    let attributes = song["attributes"]
                    let currentSong = Song(id: attributes["playParams"]["id"].string!, name: attributes["name"].string!, artistName: attributes["artistName"].string!, artworkURL: attributes["artwork"]["url"].string!)
                    songs.append(currentSong)
                }
                lock.signal()
            } else {
                lock.signal()
            }
        }.resume()
        
        lock.wait()
        return songs
    }
}

最佳答案

我对发生的事情有一个理论:由于在主线程上调用了 requestUserToken 函数,因此使用信号量会造成无限等待(在同一线程上调用 lock.wait() 和 lock.signal())。最终对我有用的是使用完成处理程序而不是信号量。所以我的 getUserToken 函数看起来像这样:

func getUserToken(completion: @escaping(_ userToken: String) -> Void) -> String {
    SKCloudServiceController().requestUserToken(forDeveloperToken: developerToken) { (userToken, error) in
          guard error == nil else {
               return
          }
          completion(userToken)
    }
}

在任何需要 userToken 的后续函数中,我将其作为参数传入:

func fetchStorefrontID(userToken: String, completion: @escaping(String) -> Void){
     var storefrontID: String!
     let musicURL = URL(string: "https://api.music.apple.com/v1/me/storefront")!
     var musicRequest = URLRequest(url: musicURL)
     musicRequest.httpMethod = "GET"
     musicRequest.addValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
     musicRequest.addValue(userToken, forHTTPHeaderField: "Music-User-Token")
        
     URLSession.shared.dataTask(with: musicRequest) { (data, response, error) in
          guard error == nil else { return }
            
          if let json = try? JSON(data: data!) {
              let result = (json["data"]).array!
              let id = (result[0].dictionaryValue)["id"]!
              storefrontID = id.stringValue
              completion(storefrontID)
          }
     }.resume() 
}

通过首先调用 getUserToken 然后在其完成处理程序中调用 fetchStorefrontID 来调用 fetchStorefrontID

getUserToken{ userToken in
    fetchStorefrontID(userToken){ storefrontID in
        print(storefrontID)
        //anything you want to do with storefrontID here
    }
}

这就是最终对我有用的东西。

关于ios - SKCloudServiceController().requestUserToken 在 iOS 14.2 上卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65057320/

相关文章:

ios - UITableView 上的 Swift 信号 SIGABRT 错误

ios - 当我更换计算机时,Xcode 出现证书问题

iphone - iOS中的多个按钮作为一个

objective-c - NSURLConnection 在禁用互联网连接的情况下没有给出错误?

ios - iOS 上的 OpenGL ES 2.0 对象拾取

c - 在 Xcode 中为 C 程序寻址

ios - 选择放置 .app 文件以进行 xcode 构建的位置

ios - 如何修复 UICollectionViewFlowLayout Transition 未将样式应用于单元格?

ios - Swift 包管理器与未版本化包有关的问题(例如 : firebase-ios-sdk)

ios - 当重复设置为 yes 时,UNUserNotification 不会从挂起的通知中删除 - Swift