Skip to content

Commit

Permalink
Add iOS 16+ methods to library song and artist
Browse files Browse the repository at this point in the history
  • Loading branch information
rudrankriyam committed Aug 29, 2022
1 parent 311f82b commit 0b981a4
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 72 deletions.
26 changes: 21 additions & 5 deletions Sources/MusadoraKit/History/MusicHistoryEndpoints.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,27 @@ public enum MusicHistoryEndpoints {

var path: String {
switch self {
case .heavyRotation: return "history/heavy-rotation"
case .recentlyAdded: return "library/recently-added"
case .recentlyPlayed: return "recent/played"
case .recentlyPlayedTracks: return "recent/played/tracks"
case .recentlyPlayedStations: return "recent/radio-stations"
case .heavyRotation:
return "history/heavy-rotation"
case .recentlyAdded:
return "library/recently-added"
case .recentlyPlayed:
return "recent/played"
case .recentlyPlayedTracks:
return "recent/played/tracks"
case .recentlyPlayedStations:
return "recent/radio-stations"
}
}

var maximumLimit: Int {
switch self {
case .heavyRotation, .recentlyPlayed, .recentlyPlayedStations:
return 10
case .recentlyAdded:
return 25
case .recentlyPlayedTracks:
return 30
}
}
}
4 changes: 1 addition & 3 deletions Sources/MusadoraKit/History/MusicRecentlyAddedRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

/// A protocol for music items that your app can fetch by
/// using a recently added request.
public protocol MusicRecentlyAddedRequestable : MusicItem {
public protocol MusicRecentlyAddedRequestable: MusicItem {
}

/// A request that your app uses to fetch items the user has recently added.
Expand Down Expand Up @@ -46,9 +46,7 @@ extension MusicRecentlyAddedResponse: Sendable {
}

extension MusicRecentlyAddedResponse: Decodable where MusicItemType: Decodable {

}

extension MusicRecentlyAddedResponse: Encodable where MusicItemType: Encodable {

}
22 changes: 22 additions & 0 deletions Sources/MusadoraKit/Library/LibraryArtists.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,31 @@ import MusicKit
import MediaPlayer

public extension MusadoraKit {

#if compiler(>=5.7)
/// Fetch an artist from the user's library by using its identifier.
/// - Parameters:
/// - id: The unique identifier for the artist.
/// - Returns: `Artist` matching the given identifier.
///
/// - Note: This method fetches the artist locally from the device when using iOS 16+
/// and is faster because it uses the latest `MusicLibraryRequest` structure.
/// For iOS 15 devices, it uses the custom structure `MusicLibraryResourceRequest`
/// that fetches the data from Apple Music API.
@available(iOS 16.0, tvOS 16.0, watchOS 9.0, *)
@available(macOS, unavailable)
@available(macCatalyst, unavailable)
static func libraryArtist(id: MusicItemID) async throws -> Artist {
var request = MusicLibraryRequest<Artist>()
request.filter(matching: \.id, equalTo: id)
let response = try await request.response()

guard let artist = response.items.first else {
throw MusadoraKitError.notFound(for: id.rawValue)
}
return artist
}
#else
static func libraryArtist(id: MusicItemID) async throws -> Artist {
let request = MusicLibraryResourceRequest<Artist>(matching: \.id, equalTo: id)
let response = try await request.response()
Expand All @@ -22,6 +43,7 @@ public extension MusadoraKit {
}
return artist
}
#endif

/// Fetch all artists from the user's library in alphabetical order.
/// - Parameters:
Expand Down
58 changes: 35 additions & 23 deletions Sources/MusadoraKit/Library/LibrarySong.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,37 +9,32 @@ import MusicKit
import MediaPlayer

public extension MusadoraKit {

#if compiler(>=5.7)
/// Fetch a song from the user's library by using its identifier.
/// - Parameters:
/// - id: The unique identifier for the song.
/// - Returns: `Song` matching the given identifier.
///
/// - Note: This method fetches the song locally from the device when using iOS 16+
/// and is faster because it uses the latest `MusicLibraryRequest` structure.
/// iOS 15 uses the custom structure `MusicLibraryResourceRequest` that
/// fetches the data from Apple Music API.
/// For iOS 15 devices, it uses the custom structure `MusicLibraryResourceRequest`
/// that fetches the data from Apple Music API.
@available(iOS 16.0, tvOS 16.0, watchOS 9.0, *)
@available(macOS, unavailable)
@available(macCatalyst, unavailable)
static func librarySong(id: MusicItemID) async throws -> Song {
#if compiler(>=5.7)
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
var request = MusicLibraryRequest<Song>()
request.filter(matching: \.id, equalTo: id)
let response = try await request.response()
var request = MusicLibraryRequest<Song>()
request.filter(matching: \.id, equalTo: id)
let response = try await request.response()

guard let song = response.items.first else {
throw MusadoraKitError.notFound(for: id.rawValue)
}
return song
} else {
return try await fetchLibrarySong(id: id)
guard let song = response.items.first else {
throw MusadoraKitError.notFound(for: id.rawValue)
}
#else
return try await fetchLibrarySong(id: id)
#endif
return song
}
}

public extension MusadoraKit {
static private func fetchLibrarySong(id: MusicItemID) async throws -> Song {
#else
static func librarySong(id: MusicItemID) async throws -> Song {
let request = MusicLibraryResourceRequest<Song>(matching: \.id, equalTo: id)
let response = try await request.response()

Expand All @@ -48,19 +43,34 @@ public extension MusadoraKit {
}
return song
}
}
#endif

public extension MusadoraKit {
#if compiler(>=5.7)
/// Fetch all songs from the user's library in alphabetical order.
/// - Parameters:
/// - limit: The number of songs returned.
/// - Returns: `Songs` for the given limit.
///
/// - Note: This method fetches the song locally from the device when using iOS 16+
/// and is faster because it uses the latest `MusicLibraryRequest` structure.
/// For iOS 15 devices, it uses the custom structure `MusicLibraryResourceRequest`
/// that fetches the data from Apple Music API.
@available(iOS 16.0, tvOS 16.0, watchOS 9.0, *)
@available(macOS, unavailable)
@available(macCatalyst, unavailable)
static func librarySongs(limit: Int? = 50) async throws -> Songs {
let request = MusicLibraryRequest<Song>()
let response = try await request.response()
return response.items
}
#else
static func librarySongs(limit: Int? = nil) async throws -> Songs {
var request = MusicLibraryResourceRequest<Song>()
request.limit = limit
let response = try await request.response()
return response.items
}
#endif

/// Fetch multiple songs from the user's library by using their identifiers.
/// - Parameters:
Expand All @@ -78,7 +88,9 @@ public extension MusadoraKit {
/// - id: The unique identifier for the song.
/// - Returns: `Song` matching the given identifier.
@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
static func localLibrarySong(id: MusicItemID, with properties: SongProperties = .all) async throws -> Song {
@available(macOS, unavailable)
@available(macCatalyst, unavailable)
static func librarySong(id: MusicItemID, with properties: SongProperties = .all) async throws -> Song {
var request = MusicLibraryRequest<Song>()
request.filter(matching: \.id, equalTo: id)
let response = try await request.response()
Expand Down
2 changes: 1 addition & 1 deletion Sources/MusadoraKit/Models/AppleMusicURLComponents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct AppleMusicURLComponents {
components.scheme = "https"
components.host = "api.music.apple.com"
}

var queryItems: [URLQueryItem]? {
get {
components.queryItems
Expand Down
48 changes: 24 additions & 24 deletions Sources/MusadoraKit/Models/MusadoraKitError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ public enum RatingsError: Error, Equatable {
extension RatingsError: CustomStringConvertible {
public var description: String {
switch self {
case .idMissing:
return "One or more ID must be specified to fetch the ratings for it."
case .typeMissing:
return "The music item type must be specified to fetch its ratings."
case .idMissing:
return "One or more ID must be specified to fetch the ratings for it."
case .typeMissing:
return "The music item type must be specified to fetch its ratings."
}
}
}
Expand Down Expand Up @@ -47,33 +47,33 @@ public enum MusadoraKitError: Error, Equatable {
extension MusadoraKitError: CustomStringConvertible {
public var description: String {
switch self {
case let .notFound(id):
return "The specified music item could not be found for \(id)."
case .typeMissing:
return "One or more types must be specified for fetching top results in search suggestions."
case let .recommendationOverLimit(limit):
return "Value must be an integer less than or equal to 30, but was: \(limit)."
case let .historyOverLimit(limit, overLimit):
return "Value must be an integer less than or equal to \(limit), but was: \(overLimit)."
case let .notFound(id):
return "The specified music item could not be found for \(id)."
case .typeMissing:
return "One or more types must be specified for fetching top results in search suggestions."
case let .recommendationOverLimit(limit):
return "Value must be an integer less than or equal to 30, but was: \(limit)."
case let .historyOverLimit(limit, overLimit):
return "Value must be an integer less than or equal to \(limit), but was: \(overLimit)."
}
}
}

extension MusadoraKitError: LocalizedError {
public var errorDescription: String? {
switch self {
case let .notFound(id):
return NSLocalizedString("The specified music item could not be found for \(id).",
comment: "Resource Not Found")
case .typeMissing:
return NSLocalizedString("One or more types must be specified for fetching top results in search suggestions.",
comment: "Missing Parameter")
case let .recommendationOverLimit(limit):
return NSLocalizedString("Value must be an integer less than or equal to 30, but was: \(limit).",
comment: "Invalid Parameter Value")
case let .historyOverLimit(limit, overLimit):
return NSLocalizedString("Value must be an integer less than or equal to \(limit), but was: \(overLimit).",
comment: "Invalid Parameter Value")
case let .notFound(id):
return NSLocalizedString("The specified music item could not be found for \(id).",
comment: "Resource Not Found")
case .typeMissing:
return NSLocalizedString("One or more types must be specified for fetching top results in search suggestions.",
comment: "Missing Parameter")
case let .recommendationOverLimit(limit):
return NSLocalizedString("Value must be an integer less than or equal to 30, but was: \(limit).",
comment: "Invalid Parameter Value")
case let .historyOverLimit(limit, overLimit):
return NSLocalizedString("Value must be an integer less than or equal to \(limit), but was: \(overLimit).",
comment: "Invalid Parameter Value")
}
}
}
32 changes: 16 additions & 16 deletions Sources/MusadoraKit/Models/UserMusicItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ extension UserMusicItem: MusicItem {
let id: MusicItemID

switch self {
case let .album(album): id = album.id
case let .playlist(playlist): id = playlist.id
case let .station(station): id = station.id
case let .track(track): id = track.id
case let .album(album): id = album.id
case let .playlist(playlist): id = playlist.id
case let .station(station): id = station.id
case let .track(track): id = track.id
}

return id
Expand Down Expand Up @@ -55,18 +55,18 @@ extension UserMusicItem: Decodable {
let type = try values.decode(HistoryMusicItemTypes.self, forKey: .type)

switch type {
case .album, .libraryAlbum:
let album = try Album(from: decoder)
self = .album(album)
case .playlist, .libraryPlaylist:
let playlist = try Playlist(from: decoder)
self = .playlist(playlist)
case .station:
let station = try Station(from: decoder)
self = .station(station)
case .song, .librarySong, .musicVideo, .libraryMusicVideo:
let track = try Track(from: decoder)
self = .track(track)
case .album, .libraryAlbum:
let album = try Album(from: decoder)
self = .album(album)
case .playlist, .libraryPlaylist:
let playlist = try Playlist(from: decoder)
self = .playlist(playlist)
case .station:
let station = try Station(from: decoder)
self = .station(station)
case .song, .librarySong, .musicVideo, .libraryMusicVideo:
let track = try Track(from: decoder)
self = .track(track)
}
}
}
Expand Down

0 comments on commit 0b981a4

Please sign in to comment.