Playing apple music

Not so long ago, with the release of iOS 9.3, the Apple Music API was released - a set of tools to control the built-in apple player. Now the developer can see if the user has paid for the subscription and in which country it is available. However, we are not allowed to see who is listening to what - just play and add music to the catalog.

Our path consists of four steps:

  • Get status data for Apple Music;
  • Make a request for our music through the iTunes Search API;
  • Play and add music to your music library;
  • Avoid problems and review in the App Store;

And now in more detail.

1. SKCloudServiceController

It all starts with a user requesting access to his library using the new class in iOS 9.3:

SKCloudServiceController.requestAuthorization { status in
    if status == .Authorized {
        // Зеленый свет
    }
}

Actually, this status also happens like this:

SKCloudServiceAuthorizationStatus
.Authorized // Наша цель
.Denied // Пользователь решил вам не доверять, а если передумает — шлите его в настройки конфиденциальности
.Restricted // Доступ запрещен, да так, что пользователь ничего с этим поделать не может. Такое бывает, например, когда устройства в режиме обучения или как-то особо заблокировано
.NotDetermined // Да, кто-ж его знает!

Next, we’ll find out if the user is subscribed to Apple Music and whether their iCloud library is enabled:

let controller = SKCloudServiceController()
controller.requestCapabilitiesWithCompletionHandler { (capabilities, error) in
    if capabilities.rawValue >= SKCloudServiceCapability.AddToCloudMusicLibrary.rawValue {
        // можем всё!
    }
    else if capabilities == SKCloudServiceCapability.MusicCatalogPlayback {
        // можем только играть музыку
        // видимо, пользователь выключил медиатеку iCloud
        }
    else { 
        // подписки нет 
    }
}

You also need to find out in which country the user is located. This is very important: not only that, some of the music may not be available in all countries, but the unique id of tracks and albums are different!

controller.requestStorefrontIdentifierWithCompletionHandler { (identifier, error) in
    if error == nil {
        // Погоди-ка, идентификатор?
    }
}

It would seem that it could be easier, right? Not. At the most interesting place, the identifier turns out to be a six-digit number (for example, for Russia - 143469). Fortunately, I came across a great thing: StorefrontAssistant , which helps to get values ​​from this creepy number in the desired and convenient format, such as "RU", "PT" or "ES".

2. iTunes Search API

A search tool in the iTunes music store. We need to get the id tracks that we are going to play.

Consider 2 examples: a search query on albums and getting the id of individual tracks in this album.

A search query in iTunes is sent to itunes.apple.com/searchwith additional arguments. Some of them are required: you must specify the country in the form of "country = RU" and the text "term = Geogaddi" itself, which will be searched.

Other options we are interested in include:

  • media (music, movie, podcast - what we are looking for)
  • entity (album / song / musicVideo - what you want to get)
  • limit (default 50 - the number of results)

For more information, see the documentation .

The results are issued in JSON format and for parsing I used the SwiftyJSON library .

And here is an example of querying and parsing results
// Ищем "Geogaddi"...
let request = "https://itunes.apple.com/search?media=music&entity=album&country=\(countryCode)&term=Geogaddi"
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: request)!) { (data, response, error) in
    guard let data_ = data where error == nil else {
        // Ошибка. Печатаем описание и выходим
        print(error?.localizedDescription)
        return
    }
    let json = JSON(data: data_)
    guard let results = json["results"].array else {
        // JSON вернулся не таким, как мы ожидали
        print("JSON: Ну, что за беда?")
        return
    }
    //  Вот наш список альбомов, пройдёмся по нему
    for albumJSON in results {
        let title = albumJSON["collectionName"].string
        let itunesUrl = albumJSON["collectionViewUrl"].string
        let artworkUrl = albumJSON["artworkUrl100"].string
        let releaseDate = albumJSON["releaseDate"].string
        let id = albumJSON["collectionId"].int
    }
}


By the way, here is the same JSON
{
"resultCount":1,
"results": [
{"wrapperType":"collection", "collectionType":"Album", "artistId":2989314, "collectionId":281208446, "amgArtistId":224179, "artistName":"Boards of Canada", "collectionName":"Geogaddi", "collectionCensoredName":"Geogaddi", "artistViewUrl":"https://itunes.apple.com/ru/artist/boards-of-canada/id2989314?uo=4", "collectionViewUrl":"https://itunes.apple.com/ru/album/geogaddi/id281208446?uo=4", "artworkUrl60":"http://is1.mzstatic.com/image/thumb/Music/v4/9c/f1/ca/9cf1cae7-c295-2531-3918-267d6ee9ce08/source/60x60bb.jpg", "artworkUrl100":"http://is1.mzstatic.com/image/thumb/Music/v4/9c/f1/ca/9cf1cae7-c295-2531-3918-267d6ee9ce08/source/100x100bb.jpg", "collectionPrice":99.00, "collectionExplicitness":"notExplicit", "trackCount":23, "copyright":"℗ 2002 Warp Records Limited", "country":"RUS", "currency":"RUB", "releaseDate":"2002-02-19T08:00:00Z", "primaryGenreName":"Электронная музыка"}]
}


Now we can request a list of tracks. This is called Lookup and the request goes to a different address: itunes.apple.com/lookup , but do not forget to specify the country, otherwise you will get an empty result.

The code for our lookup request
let request = "https://itunes.apple.com/lookup?media=music&entity=song&country=\(countryCode_)&limit=200&id=\(GeogaddiID)"
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: request)!) { (data, response, error) in
    guard let data_ = data where error == nil else {
        print(error?.localizedDescription)
        return
    }
    let json = JSON(data: data_)
    guard let results = json["results"].array else {
        print("JSON, ты такой внезапный!")
        return
    }
    for songJSON in results {
        // lookup по альбому выдает нам не только список треков,
        // но и информацию о самом альбоме
        if songJSON["wrapperType"].stringValue == "track" {
            let title = songJSON["trackName"].string
            let id = songJSON["trackId"].int
            let artist = songJSON["artistName"].string
            let time = songJSON["trackTimeMillis"].int
            let trackNumber = songJSON["trackNumber"].int
            let discNumber = songJSON["discNumber"].int
        }
    }
}


3. We play!

Now that we have the id of the tracks we need, the worst is behind:

// запускаем музыку
func play(ids:[String]) {
    let player = MPMusicPlayerController.systemMusicPlayer()
    player.setQueueWithStoreIDs(ids) // вставьте ваши id здесь
    player.play()
}
// добавляем треки в медиатеку пользователя
func addToLibrary(id:String) {
    let library = MPMediaLibrary.defaultMediaLibrary()
    library.addItemWithProductID(id) { (entity, error) in
        guard error == nil else {
            print(error?.localizedDescription)
            return
        }
        // успех!
    }
}

4. iOS 9.3 and additional requirements for the application

At the moment, still a sufficient number of users for one reason or another have not switched to iOS 9. In order to avoid problems with supporting older versions, Swift 2 has an excellent, and most importantly safe tool for checking the system version.

func play(ids:[String]) {
        if #available(iOS 9.3, *) {
            // всё отлично
            let player = MPMusicPlayerController.systemMusicPlayer()
            player.setQueueWithStoreIDs(ids)
            player.play()
        }
        else {
            // сдаём назад
            openInItunesStore()
        }
    }

In this example, I check if iOS version 9.3 and later is installed. If so, then I launch my tracks in Apple Music, if not, I open an album in the iTunes Store where the user can buy them. By the way, this is another way to earn money if you use the Apple affiliate program .

Speaking about the additions in checking the application before release in the App Store, I want to note a few points:

  • Your application should not illegally download music to a user's device, be it Apple Music or other services.
  • You should not sell Apple Music services within the app.
  • You should not turn on music without explicit user action.
  • It is necessary to provide the user with standard music player controls: buttons such as play, pause and skip.

Summary

Although, literally all that we are allowed to do is to search for music and turn it on without leaving the application, it can be convenient for the user. However, for those who do not want to use Apple Music, this is a dubious pleasure. Yes, by the way, for testing the code I had to buy a subscription.

Also popular now: