Skip to content

Commit

Permalink
Release 0.4 (#3)
Browse files Browse the repository at this point in the history
* Bump dependencies version

* bump swift-tools-version to 5.6

* Update test manifests and linux main files to include recente unit tests

* Add support to retrieve catalog resources

* Refactor library & catalog requests to use a more intuitive convention and follow DRY principle

* Update Readme file to reflect latest changes
  • Loading branch information
jjotaum authored Jul 12, 2022
1 parent 29f990d commit 15f4448
Show file tree
Hide file tree
Showing 16 changed files with 4,181 additions and 118 deletions.
10 changes: 2 additions & 8 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
// swift-tools-version:5.6

import PackageDescription

Expand All @@ -12,19 +11,14 @@ let package = Package(
.watchOS(.v6)
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "AmuseKit",
targets: ["AmuseKit"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "3.0.0"),
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "4.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "AmuseKit",
dependencies: ["KeychainAccess"]),
Expand Down
87 changes: 68 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,61 +32,60 @@ amuseProvider.setUserToken("USER_TOKEN")
amuseProvider.setUserCountryCode("USER_COUNTRY_CODE")
```


### Retrieve All User's Library Playlists.
### Retrieve multiple Albums from Apple Music catalog by ids.

```swift
amuseProvider.libraryPlaylists()
amuseProvider.catalog(.albums, ids: ["123", "456", "789"])
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Retrieve All User's Library Albums.
### Retrieve multiple Artists from Apple Music catalog by ids.

```swift
amuseProvider.libraryAlbums()
amuseProvider.catalog(.artists, ids: ["123", "456", "789"])
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Retrieve All User's Library Artists.
### Retrieve multiple Music Videos from Apple Music catalog by ids.

```swift
amuseProvider.libraryArtists()
amuseProvider.catalog(.musicVideos, ids: ["123", "456", "789"])
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Retrieve All User's Library Songs.
### Retrieve multiple Playlists from Apple Music catalog by ids.

```swift
amuseProvider.librarySongs()
amuseProvider.catalog(.playlists, ids: ["123", "456", "789"])
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Retrieve All User Library Music Videos.
### Retrieve multiple Songs from Apple Music catalog by ids.

```swift
amuseProvider.libraryMusicVideos()
amuseProvider.catalog(.songs, ids: ["123", "456", "789"])
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Search on User's Library.
### Search on Apple Music catalog.

```swift
amuseProvider.librarySearch(searchTerm: "YOUR_QUERY_TEXT")
amuseProvider.catalogSearch(searchTerm: "YOUR_QUERY_TEXT")
.sink { _ in
} receiveValue: { response in
// content will be found under results properties.
Expand All @@ -97,21 +96,71 @@ amuseProvider.librarySearch(searchTerm: "YOUR_QUERY_TEXT")
}
```

### Search on User's Library for specific types.
### Search on Apple Music catalog for specific types.

```swift
amuseProvider.librarySearch([.playlists, .songs], searchTerm: "YOUR_QUERY_TEXT")
amuseProvider.catalogSearch([.playlists, .songs], searchTerm: "YOUR_QUERY_TEXT")
.sink { _ in
} receiveValue: { response in
print(response.results?.playlists)
print(response.results?.songs)
}
```

### Search on Apple Music catalog.
### Retrieve All User's Library Albums.

```swift
amuseProvider.catalogSearch(searchTerm: "YOUR_QUERY_TEXT")
amuseProvider.library(.albums)
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Retrieve All User's Library Artists.

```swift
amuseProvider.library(.artists)
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Retrieve All User Library Music Videos.

```swift
amuseProvider.library(.musicVideos)
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Retrieve All User's Library Playlists.

```swift
amuseProvider.library(.playlists)
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Retrieve All User's Library Songs.

```swift
amuseProvider.library(.songs)
.sink { _ in
} receiveValue: { response in
print(response.data)
}
```

### Search on User's Library.

```swift
amuseProvider.librarySearch(searchTerm: "YOUR_QUERY_TEXT")
.sink { _ in
} receiveValue: { response in
// content will be found under results properties.
Expand All @@ -122,10 +171,10 @@ amuseProvider.catalogSearch(searchTerm: "YOUR_QUERY_TEXT")
}
```

### Search on Apple Music catalog for specific types.
### Search on User's Library for specific types.

```swift
amuseProvider.catalogSearch([.playlists, .songs], searchTerm: "YOUR_QUERY_TEXT")
amuseProvider.librarySearch([.playlists, .songs], searchTerm: "YOUR_QUERY_TEXT")
.sink { _ in
} receiveValue: { response in
print(response.results?.playlists)
Expand Down
2 changes: 1 addition & 1 deletion Sources/AmuseKit/Models/Resource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
// A resource—such as an album, song, or playlist—in the Apple Music catalog or iCloud Music Library.
// https://developer.apple.com/documentation/applemusicapi/resource

public protocol Resource: Codable {
protocol Resource: Codable {
associatedtype Attributes
associatedtype Relationships
var attributes: Attributes? { get }
Expand Down
1 change: 0 additions & 1 deletion Sources/AmuseKit/Models/Resources/Catalog/Album.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public extension AmuseKit.Album {

struct Attributes: Codable {
public let artistName: String
public let albumName: String?
public let artwork: AmuseKit.Artwork
public let contentRating: String?
public let copyright: String?
Expand Down
2 changes: 1 addition & 1 deletion Sources/AmuseKit/Models/Resources/Catalog/Song.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
public extension AmuseKit {

/// A resource object that represents a song.
/// https://developer.apple.com/documentation/applemusicapi/songs-um8.
/// https://developer.apple.com/documentation/applemusicapi/songs
/// Latest revision Feb 21 2022.

struct Song: Resource {
Expand Down
78 changes: 21 additions & 57 deletions Sources/AmuseKit/Networking/DataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@
import Combine
import Foundation
import class KeychainAccess.Keychain
import AppKit

public extension AmuseKit {
typealias LibraryPlaylistResponse = ResponseRoot<LibraryPlaylist, EmptyCodable>
typealias LibraryAlbumResponse = ResponseRoot<LibraryAlbum, EmptyCodable>
typealias LibraryArtistResponse = ResponseRoot<LibraryArtist, EmptyCodable>
typealias LibrarySongResponse = ResponseRoot<LibrarySong, EmptyCodable>
typealias LibraryMusicVideoResponse = ResponseRoot<LibraryMusicVideo, EmptyCodable>

class DataProvider {
public typealias CatalogSearchTypes = Set<CatalogResourceType>
Expand Down Expand Up @@ -49,49 +45,45 @@ public extension AmuseKit {
public func setUserCountryCode(_ userCountryCode: String) {
self.userCountryCode = userCountryCode
}

// MARK: - Library Methods

public func libraryPlaylists() throws -> AnyPublisher<AmuseKit.LibraryPlaylistResponse, Error> {
try libraryRequest(.library(.playlists))
}

public func libraryAlbums() throws -> AnyPublisher<AmuseKit.LibraryAlbumResponse, Error> {
try libraryRequest(.library(.albums))
}

public func libraryArtists() throws -> AnyPublisher<AmuseKit.LibraryArtistResponse, Error> {
try libraryRequest(.library(.artists))
}
// MARK: - Catalog Methods

public func librarySongs() throws -> AnyPublisher<AmuseKit.LibrarySongResponse, Error> {
try libraryRequest(.library(.songs))
func catalog<Resource: Codable>(_ resourceType: CatalogResourceConvertible<Resource>, ids: [String]) throws -> AnyPublisher<AmuseKit.ResponseRoot<Resource, EmptyCodable>, Error> {
guard let developerToken = storage.developerToken else {
throw AmuseKit.AmuseError.missingDevToken
}

var request = try Router.catalog(countryCode: userCountryCode, resourceType: resourceType.rawValue).asURLRequest(
[.init(name: "ids", value: ids.joined(separator: ","))]
)
request.setValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
request.setValue(storage.userToken, forHTTPHeaderField: "Music-User-Token")
return try service.publisher(with: request)
}

public func libraryMusicVideos() throws -> AnyPublisher<AmuseKit.LibraryMusicVideoResponse, Error> {
try libraryRequest(.library(.musicVideos))
public func catalogSearch(_ resourceTypes: CatalogSearchTypes = .all, limit: Int = 25, searchTerm: String) throws -> AnyPublisher<AmuseKit.SearchResponse, Error> {
try searchRequest(.search(countryCode: userCountryCode), rawTypes: resourceTypes.map({ $0.rawValue }), limit: limit, searchTerm: searchTerm)
}

// MARK: - Library Methods

private func libraryRequest<T: Codable>(_ router: Router) throws -> AnyPublisher<T, Error> {
func library<Resource: Codable>(_ resourceType: LibraryResourceConvertible<Resource>) throws -> AnyPublisher<AmuseKit.ResponseRoot<Resource, EmptyCodable>, Error> {
guard let developerToken = storage.developerToken else {
throw AmuseKit.AmuseError.missingDevToken
}

var request = try router.asURLRequest([])
var request = try Router.library(resourceType.rawValue).asURLRequest([])
request.setValue("Bearer \(developerToken)", forHTTPHeaderField: "Authorization")
request.setValue(storage.userToken, forHTTPHeaderField: "Music-User-Token")
return try service.publisher(with: request)
}

// MARK: - Search Methods

public func librarySearch(_ resourceTypes: LibrarySearchTypes = .all, limit: Int = 25, searchTerm: String) throws -> AnyPublisher<AmuseKit.LibrarySearchResponse, Error> {
try searchRequest(.librarySearch, rawTypes: resourceTypes.map({ $0.rawValue }), limit: limit, searchTerm: searchTerm)
}

public func catalogSearch(_ resourceTypes: CatalogSearchTypes = .all, limit: Int = 25, searchTerm: String) throws -> AnyPublisher<AmuseKit.SearchResponse, Error> {
try searchRequest(.search(countryCode: userCountryCode), rawTypes: resourceTypes.map({ $0.rawValue }), limit: limit, searchTerm: searchTerm)
}

// MARK: - Private Methods

private func searchRequest<T: Codable>(_ router: Router, rawTypes: [String], limit: Int, searchTerm: String) throws -> AnyPublisher<T, Error> {
guard let developerToken = storage.developerToken else {
Expand All @@ -109,31 +101,3 @@ public extension AmuseKit {
}
}
}

public extension AmuseKit {
enum CatalogResourceType: String, AmuseOption {
case albums, artists, curators, playlists, songs, stations
case appleCurators = "apple-curators"
case musicVideos = "music-videos"
}

enum LibraryResourceType: String, AmuseOption {
case albums = "library-albums"
case artists = "library-artists"
case musicVideos = "library-music-videos"
case playlists = "library-playlists"
case songs = "library-songs"
}
}

public extension Set where Element == AmuseKit.CatalogResourceType {
static var all: Set<Element> {
return Set(Element.allCases)
}
}

public extension Set where Element == AmuseKit.LibraryResourceType {
static var all: Set<Element> {
return Set(Element.allCases)
}
}
Loading

0 comments on commit 15f4448

Please sign in to comment.