Не так давно, с релизом iOS 9.3, в свет вышел Apple Music API — набор средств для управления встроенным яблочным плеером. Теперь разработчик может увидеть, оплатил ли пользователь подписку и в какой стране она доступна. Однако, увидеть, кто что слушает нам не позволено — только воспроизводить и добавлять музыку в каталог.
Наш путь состоит из четырёх шагов:
- Получить данные о состоянии Apple Music;
- Сделать запрос о нашей музыке через iTunes Search API;
- Воспроизвести и добавить музыку в медиатеку;
- Избежать проблем и пройти ревью в App Store;
А теперь подробнее.
Всё начинается с запроса у пользователя доступа к его медиатеке, используя новый в iOS 9.3 класс:
SKCloudServiceController.requestAuthorization { status in
if status == .Authorized {
// Зеленый свет
}
}
Собственно, этот статус бывает и таким:
SKCloudServiceAuthorizationStatus
.Authorized // Наша цель
.Denied // Пользователь решил вам не доверять, а если передумает — шлите его в настройки конфиденциальности
.Restricted // Доступ запрещен, да так, что пользователь ничего с этим поделать не может. Такое бывает, например, когда устройства в режиме обучения или как-то особо заблокировано
.NotDetermined // Да, кто-ж его знает!
Далее узнаем, подписан ли пользователь на Apple Music и включена ли у него медиатека iCloud:
let controller = SKCloudServiceController()
controller.requestCapabilitiesWithCompletionHandler { (capabilities, error) in
if capabilities.rawValue >= SKCloudServiceCapability.AddToCloudMusicLibrary.rawValue {
// можем всё!
}
else if capabilities == SKCloudServiceCapability.MusicCatalogPlayback {
// можем только играть музыку
// видимо, пользователь выключил медиатеку iCloud
}
else {
// подписки нет
}
}
Так же нужно выяснить, в какой стране находится пользователь. Это очень важно: мало того, часть музыки может быть недоступна во всех странах, так ещё и уникальные id у треков и альбомов различаются!
controller.requestStorefrontIdentifierWithCompletionHandler { (identifier, error) in
if error == nil {
// Погоди-ка, идентификатор?
}
}
Казалось бы, что может быть проще, верно? Нет. На самом интересном месте, идентификатор оказывается шестизначным номером (например, для России — 143469). К счастью, я наткнулся на отличную штуку: StorefrontAssistant, которая помогает из этого жуткого номера получить значения в нужном и удобном формате, таком как «RU», «PT» или «ES».
2. iTunes Search API
Инструмент поиска в музыкальном магазине iTunes. Нам нужен для получения id треков, которые мы собираемся играть.
Рассмотрим 2 примера: поисковой запрос по альбомам и получение id отдельных треков в этом альбоме.
Поисковый запрос в iTunes отправляется на itunes.apple.com/search с дополнительными аргументами. Некоторые из них обязательны: вы должны указать страну в виде «country=RU» и сам текст «term=Geogaddi», по которому будет произведён поиск.
Другие интересные нам параметры включают в себя:
- media (music, movie, podcast — что мы ищем)
- entity (album/song/musicVideo — то, что вы хотите получить)
- limit (по умолчанию 50 — количество результатов)
Подробную информацию ищите в документации.
Результаты выдаются в JSON формате и для парсинга я воспользовался библиотекой SwiftyJSON.
// Ищем "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
}
}
{
"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":"Электронная музыка"}]
}
Теперь можем запросить список треков. Это называется Lookup и запрос идёт по другому адресу: itunes.apple.com/lookup, но не забудьте указать страну, иначе получите пустой результат.
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. Играем!
Теперь, когда у нас есть id нужных нам треков, самое страшное — позади:
// запускаем музыку
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 и дополнительные требования к приложению
На данный момент всё ещё достаточное количество пользователей по той или иной причине не перешло на iOS 9. Для того, чтобы избежать проблем с поддержкой старых версий, в Swift 2 появился отличный, а главное безопасный инструмент для проверки версии системы.
func play(ids:[String]) {
if #available(iOS 9.3, *) {
// всё отлично
let player = MPMusicPlayerController.systemMusicPlayer()
player.setQueueWithStoreIDs(ids)
player.play()
}
else {
// сдаём назад
openInItunesStore()
}
}
В этом примере, я проверяю, установлена ли версия iOS 9.3 и позднее. Если да, то я запускаю мои треки в Apple Music, если нет — открываю альбом в iTunes Store, где пользователь сможет их купить. Кстати, это ещё один способ заработать, если использовать партнёрскую программу Apple.
Говоря об дополнениях в проверке приложения перед выпуском в App Store, хочется заметить несколько моментов:
- Ваше приложение не должно нелегально скачивать музыку на устройство пользователя будь то Apple Music или другие сервисы.
- Вы не должны продавать услуги Apple Music внутри приложения.
- Вы не должны включать музыку без явного действия пользователя.
- Необходимо предоставить пользователю стандартные элементы управления музыкальным плеером: такие кнопки как play, pause и skip.
Итог
Хотя, буквально всё, что нам позволено — это искать музыку и включать её не покидая приложение, пользователю это может быть удобно. Однако, для тех, кто не хочет пользоваться Apple Music, это сомнительное удовольствие. Да, кстати, для тестирования кода мне пришлось купить подписку.
Автор: ysoftware