I am trying to run the following function from SKCloudServiceController but for some reason every time it runs, the app just freezes. I have tested my developer token and it does work. I am running Xcode 12.2. Maybe there was an update which would make this not work anymore?
I've tested the token and it works.
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
}
}
I have a theory on what happened: since the requestUserToken function is called on the main thread, using a semaphore creates an infinite wait(lock.wait() and lock.signal() are called on the same thread). What eventually worked for me was using completion handlers instead of semaphores. So my getUserToken function looked like this:
And in any subsequent functions that need the userToken, I passed it in as a parameter:
Calling
fetchStorefrontID
by first calling getUserToken then calling fetchStorefrontID in its completion handlerThis is just what eventually worked for me.