Spartan is a lightweight, elegant, and easy to use Spotify Web API wrapper library for iOS and macOS written in Swift 3. Under the hood, Spartan makes request to the Spotify Web API. Those requests are then turned into clean, friendly, and easy to use objects.
- ✅ Make any request that the Spotify Web API allows.
- ❌ Authorization flows that help provide you with an authorization token. The Spotify iOS SDK can help assist you with that.
- iOS 9.0+ / macOS 10.11+
- xCode 9.0+
To integrate Spartan into your xCode project using CocoaPods, specify it in your Podfile
:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0' # If you targeting iOS
platform :osx, '10.11' # If you targeting macOS
use_frameworks!
pod 'Spartan'
Then, run the following command:
$ pod install
This will download any library dependencies you do not already have in your project.
public static var authorizationToken: String?
This is the token that is included with each request that requires OAuth authentication. If the request you make requires OAuth and this in nil, invalid, or expired, the request will fail. This can be left nil
if you do not plan on making any requests that require OAuth authentication.
Each time your token is refreshed, simply update it:
Spartan.authorizationToken = newToken
public static var loggingEnabled: Bool = true
When enabled, before each request starts and after each request finishes, helpful statements will be printed to the console.
A successful request will look something like this:
🔵 [SpartanRequestLogger] GET https://api.spotify.com/v1/me
⚪️ [SpartanRequestLogger] GET https://api.spotify.com/v1/me (200 OK) 0.140969038009644 seconds
While a failed/invalid request will look something like this:
🔵 [SpartanRequestLogger] GET https://api.spotify.com/v1/me
🔴 [SpartanRequestLogger] GET https://api.spotify.com/v1/me (401 Unauthorized) 0.81086003780365 seconds
This can be enabled/disabled at any time anywhere in your code simply by:
Spartan.loggingEnabled = true/false
For more information on each request, please click the link associated with it. Spotify provides excellent documentation for each api request that they support. Spartan supports almost all query parameter fields that Spotify allows.
Quite a few requests return a PagingObject
. This is an offset based paging object that is a container for a set of objects. It contains a key called items whose value is an array of the requested objects. It can be used to get the previous set of items or the next set of items for a future call.
For example:
_ = Spartan.search(query: "Five for Fighting", type: .track, success: { (pagingObject: PagingObject<Track>) in
self.pagingObject = pagingObject
}, failure: { (error) in
print(error)
})
Then later, if you need to load more data (scrolled to bottom of UITableView/UICollectionView, etc...) more data can be loaded/reloaded with these two methods:
if pagingObject.canMakeNextRequest {
pagingObject.getNext(success: { (pagingObject) in
// Update the paging object
self.pagingObject = pagingObject
}, failure: { (error) in
print(error)
})
}
if pagingObject.canMakePreviousRequest {
pagingObject.getPrevious(success: { (pagingObject) in
// Update the paging object
self.pagingObject = pagingObject
}, failure: { (error) in
print(error)
})
}
_ = Spartan.getAlbum(id: albumId, market: .us, success: { (album) in
// Do something with the album
}, failure: { (error) in
print(error)
})
_ = Spartan.getAlbums(ids: albumIds, market: .us, success: { (albums) in
// Do something with the albums
}, failure: { (error) in
print(error)
})
_ = Spartan.getTracks(albumId: albumId, limit: 20, offset: 0, market: .us, success: { (pagingObject) in
// Get the tracks via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getArtist(id: artistId, success: { (artist) in
// Do something with the artist
}, failure: { (error) in
print(error)
})
_ = Spartan.getArtists(ids: artistIds, success: { (artists) in
// Do something with the artists
}, failure: { (error) in
print(error)
})
_ = Spartan.getTrack(id: track, market: .us, success: { (track) in
// Do something with the track
}, failure: { (error) in
print(error)
})
_ = Spartan.getTracks(ids: trackIds, market: .us, success: { (tracks) in
// Do something with the tracks
}, failure: { (error) in
print(error)
})
_ = Spartan.getArtistAlbums(artistId: artistId, limit: 20, offset: 0, albumTypes: [.album, .single, .appearsOn, .compilation], market: .us, success: { (pagingObject) in
// Get the albums via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getArtistsTopTracks(artistId: artistId, country: .us, success: { (tracks) in
// Do something with the tracks
}, failure: { (error) in
print(error)
})
_ = Spartan.getArtistsRelatedArtists(artistId: artistId, success: { (artists) in
// Do something with the artists
}, failure: { (error) in
print(error)
})
_ = Spartan.search(query: query, type: .album, success: { (pagingObject: PagingObject<SimplifiedAlbum>) in
// Get the albums via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.search(query: query, type: .artist, success: { (pagingObject: PagingObject<SimplifiedArtist>) in
// Get the artists via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.search(query: query, type: .playlist, success: { (pagingObject: PagingObject<SimplifiedPlaylist>) in
// Get the playlists via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.search(query: query, type: .track, success: { (pagingObject: PagingObject<SimplifiedTrack>) in
// Get the tracks via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getUser(id: userId, success: { (user) in
// Do something with the user
}, failure: { (error) in
print(error)
})
_ = Spartan.getAudioAnaylsis(trackId: trackId, success: { (audiAnalysis) in
// Do something with the audio analysis
}, failure: { (error) in
print(error)
})
_ = Spartan.getAudioFeatures(trackId: trackId, success: { (audioFeaturesObject) in
// Do something with the audio features object
}, failure: { (error) in
print(error)
})
_ = Spartan.getAudioFeatures(trackId: trackId, success: { (audioFeaturesObject) in
// Do something with the audio features object
}, failure: { (error) in
print(error)
})
_ = Spartan.getAudioFeatures(trackIds: trackIds, success: { (audioFeaturesObject) in
// Do something with the audio features objects
}, failure: { (error) in
print(error)
})
_ = Spartan.getFeaturedPlaylists(locale: locale, country: .us, timestamp: timestamp, limit: 20, offset: 0, success: { (featuredPlaylistsObject) in
// Do something with the featured playlists object
}, failure: { (error) in
print(error)
})
_ = Spartan.getNewReleases(country: .us, limit: 20, offset: 0, success: { (pagingObject) in
// Get the albums via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getCategories(success: { (pagingObject) in
// Get the categories via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getCategorysPlaylists(categoryId: categoryId, locale: locale, country: .us, limit: 20, offset: 0, success: { (pagingObject) in
// Get the playlists via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getMe(success: { (user) in
// Do something with the user object
}, failure: { (error) in
print(error)
})
_ = Spartan.getMyFollowedArtists(success: { (pagingObject) in
// Get artists via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.follow(ids: artistIds, type: .artist, success: {
// Artists are now followed
}, failure: { (error) in
print(error)
})
_ = Spartan.follow(ids: userIds, type: .user, success: {
// Users are now followed
}, failure: { (error) in
print(error)
})
_ = Spartan.unfollow(ids: artistIds, type: .artist, success: {
// Artists are now unfollowed
}, failure: { (error) in
print(error)
})
_ = Spartan.unfollow(ids: userIds, type: .user, success: {
// Users are now unfollowed
}, failure: { (error) in
print(error)
})
_ = Spartan.getIsFollowing(ids: artistIds, type: .artist, success: { (followingBools) in
// Do something with the followingBools
}, failure: { (error) in
print(error)
})
_ = Spartan.getIsFollowing(ids: userIds, type: .user, success: { (followingBools) in
// Do something with the followingBools
}, failure: { (error) in
print(error)
})
_ = Spartan.followPlaylist(ownerId: ownerId, playlistId: playlistId, isPublic: true, success: {
// Playlist is now followed
}, failure: { (error) in
print(error)
})
_ = Spartan.unfollowPlaylist(ownerId: ownerId, playlistId: playlistId, success: {
// Playlist is now unfollowed
}, failure: { (error) in
print(error)
})
_ = Spartan.getSavedTracks(limit: 20, offset: 0, market: .us, success: { (pagingObject) in
// Get the saved tracks via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.saveTracks(trackIds: trackIds, success: {
// Tracks are now saved
}, failure: { (error) in
print(error)
})
_ = Spartan.removeSavedTracks(trackIds: trackIds, success: {
// Tracks are now removed
}, failure: { (error) in
print(error)
})
_ = Spartan.tracksAreSaved(trackIds: trackIds, success: { (savedBools) in
// Do something with the savedBools
}, failure: { (error) in
print(error)
})
_ = Spartan.getSavedAlbums(limit: 20, offset: 0, market: .us, success: { (pagingObject) in
// Get the saved albums via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.saveAlbums(albumIds: albumIds, success: {
// Albums are now saved
}, failure: { (error) in
print(error)
})
_ = Spartan.removeSavedAlbums(albumIds: albumIds, success: {
// Albums are now removed
}, failure: { (error) in
print(error)
})
_ = Spartan.albumsAreSaved(albumIds: albumIds, success: { (savedBools) in
// Do something with the savedBools
}, failure: { (error) in
print(error)
})
_ = Spartan.albumsAreSaved(albumIds: albumIds, success: { (savedBools) in
// Do something with the savedBools
}, failure: { (error) in
print(error)
})
_ = Spartan.albumsAreSaved(albumIds: albumIds, success: { (savedBools) in
// Do something with the savedBools
}, failure: { (error) in
print(error)
})
_ = Spartan.getMyTopArtists(limit: 20, offset: 0, timeRange: .mediumTerm, success: { (pagingObject) in
// Get the artists via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getMyTopTracks(limit: 20, offset: 0, timeRange: .mediumTerm, success: { (pagingObject) in
// Get the tracks via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getUsersPlaylists(userId: userId, limit: 20, offset: 0, success: { (pagingObject) in
// Get the playlists via pagingObject.playlists
}, failure: { (error) in
print(error)
})
_ = Spartan.getMyPlaylists(limit: 20, offset: 0, success: { (pagingObject) in
// Get the playlists via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.getUsersPlaylist(userId: userId, playlistId: playlistId, fields: fields, market: .us, success: { (playlist) in
// Do something with the playlist
}, failure: { (error) in
print(error)
})
_ = Spartan.getPlaylistTracks(userId: userId, playlistId: playlistId, limit: 20, offset: 0, fields: fields, market: .us, success: { (pagingObject) in
// Get the playlist tracks via pagingObject.items
}, failure: { (error) in
print(error)
})
_ = Spartan.createPlaylist(userId: userId, name: name, isPublic: true, isCollaborative: true, success: { (playlist) in
// Do something with the playlist
}, failure: { (error) in
print(error)
})
_ = Spartan.changePlaylistDetails(userId: userId, playlistId: playlistId, name: name, isPublic: false, isCollaborative: false, success: {
// The playlist details are now changed
}, failure: { (error) in
print(error)
})
_ = Spartan.addTracksToPlaylist(userId: userId, playlistId: playlistId, trackUris: trackUris, success: { (snapshot) in
// Do something with the snapshot
}, failure: { (error) in
print(error)
})
_ = Spartan.removeTracksFromPlaylist(userId: userId, playlistId: playlistId, trackUris: trackUris, success: { (snapshot) in
// Do something with the snapshot
}, failure: { (error) in
print(error)
})
_ = Spartan.reorderPlaylistsTracks(userId: userId, playlistId: playlistId, rangeStart: rangeStart, rangeLength: rangeLength, insertBefore: insertBefore, snapshotId: snapshotId, success: { (snapshot) in
// Tracks are now reordered
}, failure: { (error) in
print(error)
})
_ = Spartan.replacePlaylistsTracks(userId: userId, playlistId: playlistId, trackUris: trackUris, success: {
// Tracks are now replaced in playlist
}, failure: { (error) in
print(error)
})
_ = Spartan.getUsersAreFollowingPlaylists(ownerId: ownerId, playlistId: playlistId, userIds: userIds, success: { (followings) in
// Do something with the followings
}) { (error) in
print(error)
}
SpartanError objects have a type and error message to help determine what kind of error occurred.
public class SpartanError: NSObject, Mappable {
private(set) open var type: SpartanErrorType!
private(set) open var errorMessage:String!
}
If a request suceeds but is invalid, the erorrMessage will be the error message returned directly from Spotify.
For example, if the Spotify error response is:
{
"error": {
"status": 401,
"message": "Invalid access token"
}
}
The SpartanError object within a failure callback will be:
_ = Spartan.getMe(success: { (user) in
}, failure: { (error) in
print(error.errorType) // .unauthorized
print(error.errorMessage) // "Invalid access token"
})
So if your access token is expired, you could do something like this:
_ = Spartan.getMe(success: { (user) in
}, failure: { (error) in
if error.errorType == .unauthorized {
// Refresh your access token and try again
}
})
- Alamofire: Elegant HTTP Networking in Swift
- AlamofireObjectMapper: An Alamofire extension which converts JSON into Swift objects
Since Spartan is built on top of the Spotify Web API, click here for more information
Dalton Hinterscher, [email protected]
Spartan is available under the MIT license. See the LICENSE file for more info.