daltron / spartan Goto Github PK
View Code? Open in Web Editor NEWAn Elegant Spotify Web API Library Written in Swift for iOS and macOS
License: MIT License
An Elegant Spotify Web API Library Written in Swift for iOS and macOS
License: MIT License
The getArtists() function returns a list of "Album"s.
I'm not sure if something changed in Spotify API, but performing some requests causes bad crashes. For example, requesting a track with func getTrack(id: String
returns invalid object with missing values, for example durationMs
is nil, but it never should be. A lot of users are reporting that app started crashing.
I am executing the following code and I am getting a Fatal error: Unexpectedly found nil while unwrapping an Optional value
that references line 57 of SpartanError. Please see the following screenshot for more detailed thread information. This is the only function that does this. It appears that the SpartanError is trying to map a status code and that it is actually nil. Any help on this would be great.
Code
_ = Spartan.getAudioFeatures(trackIds: tracksIDs, success: { (audioFeatures) in
print(audioFeatures.first?.tempo)
}, failure: { (error) in
Logger.error(error)
})
Console Output
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/audio-features?ids=7sO5G9EABYOXQKNPNiE9NR%2C0NKh1STZG1VgnVwntJF3ze%2C4dVpf9jZjcORqGTLUaeYj9%2C2Xqd0wUttjueBfdcltADOv%2C0Gux2yTMWYOlcBUNjGJu5p%2C2vjmyyAnDUzilFYxOlTdO1%2C1OmcAT5Y8eg5bUPv9qJT4R%2C6foxplXS0YEq8cO374Lty4%2C5u6vkDnOyaf8LsteDAj2ub%2C1XRgIKC5TPwo7nWGyKqgG0%2C7zLGHiDWd9T1Rxw4PQCb13%2C1YZfcVLbbJwfizR5cDOp3q%2C7r6LNJT2LqpLpEyZQJPygt%2C57IRaiAB4hBZu3gnNVZC0v%2C6GNifiuBPrKFpwNBYnooFm%2C3ncgNpxLoBQ65ABk4djDyd%2C2vaMWMPMgsWX4fwJiKmdWm%2C0taOCiup4HNG9LmbduVlJj%2C59J5nzL1KniFHnU120dQzt%2C4EsYkJjHKMejYLp54woB9c
⚪️ [AlamoRecordLogger] GET https://api.spotify.com/v1/audio-features?ids=7sO5G9EABYOXQKNPNiE9NR%2C0NKh1STZG1VgnVwntJF3ze%2C4dVpf9jZjcORqGTLUaeYj9%2C2Xqd0wUttjueBfdcltADOv%2C0Gux2yTMWYOlcBUNjGJu5p%2C2vjmyyAnDUzilFYxOlTdO1%2C1OmcAT5Y8eg5bUPv9qJT4R%2C6foxplXS0YEq8cO374Lty4%2C5u6vkDnOyaf8LsteDAj2ub%2C1XRgIKC5TPwo7nWGyKqgG0%2C7zLGHiDWd9T1Rxw4PQCb13%2C1YZfcVLbbJwfizR5cDOp3q%2C7r6LNJT2LqpLpEyZQJPygt%2C57IRaiAB4hBZu3gnNVZC0v%2C6GNifiuBPrKFpwNBYnooFm%2C3ncgNpxLoBQ65ABk4djDyd%2C2vaMWMPMgsWX4fwJiKmdWm%2C0taOCiup4HNG9LmbduVlJj%2C59J5nzL1KniFHnU120dQzt%2C4EsYkJjHKMejYLp54woB9c (200 OK) 16.6 seconds
(lldb)
Request Output
I ran the request in a Paw and I got the following output. As you can see the second element is null. The Spotify API documentation states, "If an object is not found, a null value is returned in the appropriate position. " The track exists in the Spotify library, however audio features for it do not exist. It is possible that Spartan is not properly handling this null value and is searching for an error code that does not exist (because the request was successful) hence the unexpected nil value. Is there a way to get the output of all non null values?
{
"audio_features": [
{
"danceability": 0.880,
"energy": 0.428,
"key": 9,
"loudness": -8.280,
"mode": 1,
"speechiness": 0.206,
"acousticness": 0.149,
"instrumentalness": 0.0000507,
"liveness": 0.114,
"valence": 0.333,
"tempo": 100.007,
"type": "audio_features",
"id": "7sO5G9EABYOXQKNPNiE9NR",
"uri": "spotify:track:7sO5G9EABYOXQKNPNiE9NR",
"track_href": "https://api.spotify.com/v1/tracks/7sO5G9EABYOXQKNPNiE9NR",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/7sO5G9EABYOXQKNPNiE9NR",
"duration_ms": 172800,
"time_signature": 4
},
null,
{
"danceability": 0.797,
"energy": 0.844,
"key": 11,
"loudness": -5.482,
"mode": 1,
"speechiness": 0.275,
"acousticness": 0.0651,
"instrumentalness": 0,
"liveness": 0.0870,
"valence": 0.520,
"tempo": 170.142,
"type": "audio_features",
"id": "4dVpf9jZjcORqGTLUaeYj9",
"uri": "spotify:track:4dVpf9jZjcORqGTLUaeYj9",
"track_href": "https://api.spotify.com/v1/tracks/4dVpf9jZjcORqGTLUaeYj9",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/4dVpf9jZjcORqGTLUaeYj9",
"duration_ms": 173600,
"time_signature": 4
},
{
"danceability": 0.837,
"energy": 0.791,
"key": 1,
"loudness": -3.824,
"mode": 1,
"speechiness": 0.251,
"acousticness": 0.0199,
"instrumentalness": 0,
"liveness": 0.0836,
"valence": 0.379,
"tempo": 176.003,
"type": "audio_features",
"id": "2Xqd0wUttjueBfdcltADOv",
"uri": "spotify:track:2Xqd0wUttjueBfdcltADOv",
"track_href": "https://api.spotify.com/v1/tracks/2Xqd0wUttjueBfdcltADOv",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/2Xqd0wUttjueBfdcltADOv",
"duration_ms": 211006,
"time_signature": 4
},
{
"danceability": 0.595,
"energy": 0.431,
"key": 10,
"loudness": -7.282,
"mode": 1,
"speechiness": 0.486,
"acousticness": 0.0101,
"instrumentalness": 0.0000703,
"liveness": 0.164,
"valence": 0.107,
"tempo": 125.200,
"type": "audio_features",
"id": "0Gux2yTMWYOlcBUNjGJu5p",
"uri": "spotify:track:0Gux2yTMWYOlcBUNjGJu5p",
"track_href": "https://api.spotify.com/v1/tracks/0Gux2yTMWYOlcBUNjGJu5p",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/0Gux2yTMWYOlcBUNjGJu5p",
"duration_ms": 227707,
"time_signature": 3
},
{
"danceability": 0.689,
"energy": 0.761,
"key": 1,
"loudness": -4.939,
"mode": 1,
"speechiness": 0.130,
"acousticness": 0.0485,
"instrumentalness": 0,
"liveness": 0.136,
"valence": 0.698,
"tempo": 78.514,
"type": "audio_features",
"id": "2vjmyyAnDUzilFYxOlTdO1",
"uri": "spotify:track:2vjmyyAnDUzilFYxOlTdO1",
"track_href": "https://api.spotify.com/v1/tracks/2vjmyyAnDUzilFYxOlTdO1",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/2vjmyyAnDUzilFYxOlTdO1",
"duration_ms": 157643,
"time_signature": 4
},
{
"danceability": 0.580,
"energy": 0.531,
"key": 5,
"loudness": -6.631,
"mode": 0,
"speechiness": 0.0776,
"acousticness": 0.128,
"instrumentalness": 0.000127,
"liveness": 0.143,
"valence": 0.141,
"tempo": 159.786,
"type": "audio_features",
"id": "1OmcAT5Y8eg5bUPv9qJT4R",
"uri": "spotify:track:1OmcAT5Y8eg5bUPv9qJT4R",
"track_href": "https://api.spotify.com/v1/tracks/1OmcAT5Y8eg5bUPv9qJT4R",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/1OmcAT5Y8eg5bUPv9qJT4R",
"duration_ms": 218320,
"time_signature": 4
},
{
"danceability": 0.950,
"energy": 0.533,
"key": 5,
"loudness": -7.022,
"mode": 1,
"speechiness": 0.148,
"acousticness": 0.247,
"instrumentalness": 0,
"liveness": 0.114,
"valence": 0.675,
"tempo": 119.869,
"type": "audio_features",
"id": "6foxplXS0YEq8cO374Lty4",
"uri": "spotify:track:6foxplXS0YEq8cO374Lty4",
"track_href": "https://api.spotify.com/v1/tracks/6foxplXS0YEq8cO374Lty4",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/6foxplXS0YEq8cO374Lty4",
"duration_ms": 124056,
"time_signature": 4
},
{
"danceability": 0.782,
"energy": 0.436,
"key": 2,
"loudness": -7.033,
"mode": 1,
"speechiness": 0.155,
"acousticness": 0.331,
"instrumentalness": 0.0000130,
"liveness": 0.342,
"valence": 0.243,
"tempo": 82.994,
"type": "audio_features",
"id": "5u6vkDnOyaf8LsteDAj2ub",
"uri": "spotify:track:5u6vkDnOyaf8LsteDAj2ub",
"track_href": "https://api.spotify.com/v1/tracks/5u6vkDnOyaf8LsteDAj2ub",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/5u6vkDnOyaf8LsteDAj2ub",
"duration_ms": 268933,
"time_signature": 4
},
{
"danceability": 0.890,
"energy": 0.633,
"key": 11,
"loudness": -5.475,
"mode": 1,
"speechiness": 0.168,
"acousticness": 0.0232,
"instrumentalness": 0.000343,
"liveness": 0.0993,
"valence": 0.425,
"tempo": 139.948,
"type": "audio_features",
"id": "1XRgIKC5TPwo7nWGyKqgG0",
"uri": "spotify:track:1XRgIKC5TPwo7nWGyKqgG0",
"track_href": "https://api.spotify.com/v1/tracks/1XRgIKC5TPwo7nWGyKqgG0",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/1XRgIKC5TPwo7nWGyKqgG0",
"duration_ms": 233087,
"time_signature": 4
},
{
"danceability": 0.577,
"energy": 0.593,
"key": 9,
"loudness": -4.816,
"mode": 0,
"speechiness": 0.137,
"acousticness": 0.800,
"instrumentalness": 0,
"liveness": 0.623,
"valence": 0.575,
"tempo": 120.611,
"type": "audio_features",
"id": "7zLGHiDWd9T1Rxw4PQCb13",
"uri": "spotify:track:7zLGHiDWd9T1Rxw4PQCb13",
"track_href": "https://api.spotify.com/v1/tracks/7zLGHiDWd9T1Rxw4PQCb13",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/7zLGHiDWd9T1Rxw4PQCb13",
"duration_ms": 303781,
"time_signature": 5
},
{
"danceability": 0.901,
"energy": 0.529,
"key": 2,
"loudness": -4.443,
"mode": 1,
"speechiness": 0.191,
"acousticness": 0.0519,
"instrumentalness": 0,
"liveness": 0.179,
"valence": 0.146,
"tempo": 138.003,
"type": "audio_features",
"id": "1YZfcVLbbJwfizR5cDOp3q",
"uri": "spotify:track:1YZfcVLbbJwfizR5cDOp3q",
"track_href": "https://api.spotify.com/v1/tracks/1YZfcVLbbJwfizR5cDOp3q",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/1YZfcVLbbJwfizR5cDOp3q",
"duration_ms": 307660,
"time_signature": 4
},
{
"danceability": 0.860,
"energy": 0.561,
"key": 6,
"loudness": -5.757,
"mode": 0,
"speechiness": 0.185,
"acousticness": 0.274,
"instrumentalness": 0,
"liveness": 0.108,
"valence": 0.354,
"tempo": 152.984,
"type": "audio_features",
"id": "7r6LNJT2LqpLpEyZQJPygt",
"uri": "spotify:track:7r6LNJT2LqpLpEyZQJPygt",
"track_href": "https://api.spotify.com/v1/tracks/7r6LNJT2LqpLpEyZQJPygt",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/7r6LNJT2LqpLpEyZQJPygt",
"duration_ms": 189480,
"time_signature": 4
},
{
"danceability": 0.658,
"energy": 0.736,
"key": 9,
"loudness": -6.097,
"mode": 0,
"speechiness": 0.308,
"acousticness": 0.113,
"instrumentalness": 0,
"liveness": 0.0978,
"valence": 0.804,
"tempo": 143.966,
"type": "audio_features",
"id": "57IRaiAB4hBZu3gnNVZC0v",
"uri": "spotify:track:57IRaiAB4hBZu3gnNVZC0v",
"track_href": "https://api.spotify.com/v1/tracks/57IRaiAB4hBZu3gnNVZC0v",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/57IRaiAB4hBZu3gnNVZC0v",
"duration_ms": 160000,
"time_signature": 4
},
{
"danceability": 0.749,
"energy": 0.671,
"key": 6,
"loudness": -5.151,
"mode": 1,
"speechiness": 0.0599,
"acousticness": 0.680,
"instrumentalness": 0,
"liveness": 0.167,
"valence": 0.756,
"tempo": 81.994,
"type": "audio_features",
"id": "6GNifiuBPrKFpwNBYnooFm",
"uri": "spotify:track:6GNifiuBPrKFpwNBYnooFm",
"track_href": "https://api.spotify.com/v1/tracks/6GNifiuBPrKFpwNBYnooFm",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/6GNifiuBPrKFpwNBYnooFm",
"duration_ms": 268864,
"time_signature": 4
},
{
"danceability": 0.921,
"energy": 0.467,
"key": 1,
"loudness": -8.443,
"mode": 1,
"speechiness": 0.119,
"acousticness": 0.0149,
"instrumentalness": 0.000238,
"liveness": 0.334,
"valence": 0.287,
"tempo": 135.995,
"type": "audio_features",
"id": "3ncgNpxLoBQ65ABk4djDyd",
"uri": "spotify:track:3ncgNpxLoBQ65ABk4djDyd",
"track_href": "https://api.spotify.com/v1/tracks/3ncgNpxLoBQ65ABk4djDyd",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/3ncgNpxLoBQ65ABk4djDyd",
"duration_ms": 191252,
"time_signature": 4
},
{
"danceability": 0.717,
"energy": 0.753,
"key": 7,
"loudness": -4.609,
"mode": 0,
"speechiness": 0.311,
"acousticness": 0.293,
"instrumentalness": 0,
"liveness": 0.158,
"valence": 0.631,
"tempo": 169.857,
"type": "audio_features",
"id": "2vaMWMPMgsWX4fwJiKmdWm",
"uri": "spotify:track:2vaMWMPMgsWX4fwJiKmdWm",
"track_href": "https://api.spotify.com/v1/tracks/2vaMWMPMgsWX4fwJiKmdWm",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/2vaMWMPMgsWX4fwJiKmdWm",
"duration_ms": 174012,
"time_signature": 4
},
{
"danceability": 0.721,
"energy": 0.710,
"key": 7,
"loudness": -9.294,
"mode": 1,
"speechiness": 0.295,
"acousticness": 0.000655,
"instrumentalness": 0,
"liveness": 0.359,
"valence": 0.509,
"tempo": 108.024,
"type": "audio_features",
"id": "0taOCiup4HNG9LmbduVlJj",
"uri": "spotify:track:0taOCiup4HNG9LmbduVlJj",
"track_href": "https://api.spotify.com/v1/tracks/0taOCiup4HNG9LmbduVlJj",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/0taOCiup4HNG9LmbduVlJj",
"duration_ms": 156057,
"time_signature": 5
},
{
"danceability": 0.785,
"energy": 0.622,
"key": 8,
"loudness": -6.741,
"mode": 1,
"speechiness": 0.254,
"acousticness": 0.0133,
"instrumentalness": 0,
"liveness": 0.154,
"valence": 0.466,
"tempo": 78.475,
"type": "audio_features",
"id": "59J5nzL1KniFHnU120dQzt",
"uri": "spotify:track:59J5nzL1KniFHnU120dQzt",
"track_href": "https://api.spotify.com/v1/tracks/59J5nzL1KniFHnU120dQzt",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/59J5nzL1KniFHnU120dQzt",
"duration_ms": 235535,
"time_signature": 4
},
{
"danceability": 0.930,
"energy": 0.673,
"key": 10,
"loudness": -7.314,
"mode": 0,
"speechiness": 0.0733,
"acousticness": 0.0112,
"instrumentalness": 0,
"liveness": 0.138,
"valence": 0.219,
"tempo": 134.984,
"type": "audio_features",
"id": "4EsYkJjHKMejYLp54woB9c",
"uri": "spotify:track:4EsYkJjHKMejYLp54woB9c",
"track_href": "https://api.spotify.com/v1/tracks/4EsYkJjHKMejYLp54woB9c",
"analysis_url": "https://api.spotify.com/v1/audio-analysis/4EsYkJjHKMejYLp54woB9c",
"duration_ms": 188563,
"time_signature": 4
}
]
}
I have been noticing request time out issues with Spartan specifically when the user's device network reachability status changes. I recommend that you add support for the Alamofire RequestRetrier
protocol that allows the session manager to automatically retry requests if they fail for a specific reason.
I'm trying to create a playlist for the current user and received the error "SpartanError: Insufficient client scope". I have a valid auth token that I refresh and can get the current user's info fine. The scopes I have selected are:
static let SCOPE = ["user-read-email", "user-top-read", "playlist-modify-public", "playlist-modify-private"]
My Code for token request:
static func accessCodeToAccessToken(code: String, completion: @escaping (Result<Tokens, Error>) -> Void) -> Request {
Request.buildRequest(method: .post,
header: Header.POSTHeader.buildHeader(),
baseURL: SpotifyBaseURL.authBaseURL.rawValue,
path: EndingPath.token.buildPath(),
params: Parameters.codeForToken(accessCode: code).buildParameters()) { (result) in
result.decoding(Tokens.self, completion: completion)
}
}
My code calling request in view controller:
private func getSpotifyAccessCode() {
let urlRequest = client.getSpotifyAccessCodeURL()
print(urlRequest)
let scheme = "auth"
let session = ASWebAuthenticationSession(url: urlRequest, callbackURLScheme: scheme) { (callbackURL, error) in
guard error == nil, let callbackURL = callbackURL else { return }
let queryItems = URLComponents(string: callbackURL.absoluteString)?.queryItems
guard let requestAccessCode = queryItems?.first(where: { $0.name == "code" })?.value else { return }
print(" Code \(requestAccessCode)")
// exchanges access code to get access token and refresh token
self.client.call(request: .accessCodeToAccessToken(code: requestAccessCode, completion: { (token) in
switch token {
case .failure(let error):
print("ERROR: ", error)
case .success(let token):
UserDefaults.standard.set(token.accessToken, forKey: "token")
UserDefaults.standard.set(token.refreshToken, forKey: "refresh_token")
print("SUCCESS")
DispatchQueue.main.async {
self.goToApp()
}
}
}))
}
session.presentationContextProvider = self
session.start()
}
Create playlist code:
func createPlaylist(user: UserModel) {
let name = "Test"
let createPlaylist = Spartan.createPlaylist(userId: user.id, name: name, isPublic: true) { (playlist) in
print("PLAYLIST CREATED")
} failure: { (error) in
print("Error creating playlist: ", error)
}
}
Sorry if this isn't enough information please let me know.
Is there any was to encode and save Paging Objects using user defaults?
Hi Daltron,
I found an issue with playlist images. After getting root user's playlists using Spartan.getMyPlaylists(), for playlists that don't have an image, I get a random image from one of my other playlists. Moreover, all playlists after that one return random images from other playlists even though they have their own image on Spotify. Any help on this would be much appreciated!
Thanks :)
I was using the getArtists(ids: [String]...)
method with chunks of artist identifiers, each counting 50, as API states that's the limit and in some cases the app crashes. See attached screenshot.
The crash is only happening with some artists, as users reported. Not all of them experienced the crash.
Hi,
Yesterday the pagingObject.canMakeNextRequest
condition stopped working as expected.
It does end at offset=850
but according to the Spotify Web API Console there should be a total of 95902
results.
If I remove the canMakeNextRequest
condition and call searchNewAlbums
regardless of this value, it runs to the 10000 offset limit just fine.
Here is the code snippet:
_ = Spartan.search(query: "tag:new", type: .album, market: .de, limit: 50, offset: offset, success: { (pagingObject: PagingObject<SimplifiedAlbum>) in
// temporary workaround for null objects in search results
if (pagingObject.items != nil){
for album in pagingObject.items {
self.releasesIdSet.insert(album.id as! String)
}
if pagingObject.canMakeNextRequest {
self.searchNewAlbums(offset: (offset+pagingObject.limit))
}
} else {
print("PAGING OBJECT == NIL")
// TODO do error handling
}
}, failure: { (error) in
print(error)
// TODO do error handling
})
The last get request until it finishes and there is no error message:
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/search?limit=50&market=DE&offset=850&q=tag%3Anew&type=album
I am wondering if there is a way to get the track id from a Paging Object. Here is the method I am using:
playSpotifyURI("spotify:track:" + track, startingWith: 0, startingWithPosition: 0)
This works if track
is 58s6EuEYJdlb0kO7awm3Vp
I use this to get an array of tracks:
Spartan.search(query: search, type: .playlist, success:...
This returns a Paging Object which I get the items
array from. The items
array contains an href
which looks like this https://api.spotify.com/v1/users/spotify/playlists/37i9dQZF1DX4y8h9WqDPAE/tracks
. Unfortunately this does not contain the exact track id, it only gives the playlist id.
Would it be possible to add the album art url (where it exists) to the SimpleTrack class?
I'm trying to use if object.canMakeNextRequest
to get all possible artists returned by getMyFollowedArtists
however the second request doesn't appear to be returning an object with any items in it. I've tested my code with the getMyTopArtists
method and it works properly. I've also manually changed the limit on getMyFollowedArtists
to ensure that the API will return more than the default limit, and it does.
Here's a sample of my code just to see what I am doing.
var artists: PagingObject<Artist>? {
didSet {
print("Added more to artists variable.")
}
}
func grabSpotifyArtists(token: String?) {
Spartan.authorizationToken = token
Spartan.loggingEnabled = true
_ = Spartan.getMyFollowedArtists(limit: 10, after: nil, success: { (object) in
self.artists = object
self.printArtists()
self.fetchAllItems()
}, failure: { (error) in
print(error)
})
}
func fetchAllItems() {
if let artists = self.artists {
if artists.canMakeNextRequest {
artists.getNext(success: { (object) in
self.artists = object
self.printArtists()
self.fetchAllItems()
}) { (error) in
print(error)
}
}
}
}
func printArtists() {
if let artists = self.artists?.items {
for artist in artists {
print(artist.name)
}
}
}
Sample output:
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/me/following?type=artist&limit=10
⚪️ [AlamoRecordLogger] GET https://api.spotify.com/v1/me/following?type=artist&limit=10 (200 OK) 0.15 seconds
Added more to artists variable.
Optional("Handsome Furs")
Optional("Nevermen")
Optional("White Denim")
Optional("Quilt")
Optional("The Poison Control Center")
Optional("The Courtneys")
Optional("Meat Wave")
Optional("The Spook School")
Optional("Savages")
Optional("Twin Peaks")
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/me/following?type=artist&after=1xD85sp0kecIVuMwUHShxs&limit=10
⚪️ [AlamoRecordLogger] GET https://api.spotify.com/v1/me/following?type=artist&after=1xD85sp0kecIVuMwUHShxs&limit=10 (200 OK) 0.08 seconds
Added more to artists variable.
What I'd expect to happen is the what happens if I use the getMyTopArtists
method like so... changed code following way:
func grabSpotifyArtists(token: String?) {
Spartan.authorizationToken = token
Spartan.loggingEnabled = true
_ = Spartan.getMyTopArtists(limit: 10, offset: 0, timeRange: .longTerm, success: { (object) in
self.artists = object
self.printArtists()
self.fetchAllItems()
}, failure: { (error) in
print(error)
})
}
Output:
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=0&time_range=long_term
⚪️ [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=0&time_range=long_term (200 OK) 0.67 seconds
Added more to artists variable.
Optional("Guided By Voices")
Optional("Lana Del Rey")
Optional("Meat Wave")
Optional("YACHT")
Optional("White Denim")
Optional("Spoon")
Optional("Electric Six")
Optional("Television")
Optional("Quilt")
Optional("Savages")
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=10&time_range=long_term
⚪️ [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=10&time_range=long_term (200 OK) 0.19 seconds
Added more to artists variable.
Optional("Sleater-Kinney")
Optional("Queens of the Stone Age")
Optional("Local H")
Optional("Nick Lowe")
Optional("EMA")
Optional("Kishi Bashi")
Optional("Swearin\'")
Optional("BOYTOY")
Optional("Saint Motel")
Optional("Sleigh Bells")
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=20&time_range=long_term
⚪️ [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=20&time_range=long_term (200 OK) 0.19 seconds
Added more to artists variable.
Optional("HAIM")
Optional("Wampire")
Optional("White Fence")
Optional("The Spook School")
Optional("Vampire Weekend")
Optional("Wolf Alice")
Optional("Woods")
Optional("Algebra Suicide")
Optional("Foxygen")
Optional("Twin Peaks")
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=30&time_range=long_term
⚪️ [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=30&time_range=long_term (200 OK) 0.18 seconds
Added more to artists variable.
Optional("Joanna Gruesome")
Optional("Chastity Belt")
Optional("The High Strung")
Optional("Metz")
Optional("Archie Bronson Outfit")
Optional("Alex Winston")
Optional("Faith No More")
Optional("Posse")
Optional("Todd Terje")
Optional("Museum Mouth")
🔵 [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=40&time_range=long_term
⚪️ [AlamoRecordLogger] GET https://api.spotify.com/v1/me/top/artists?limit=10&offset=40&time_range=long_term (200 OK) 0.18 seconds
Added more to artists variable.
Optional("Andrew Bird")
Optional("Phoenix")
Optional("Evans The Death")
Optional("California X")
Optional("Nine Inch Nails")
Optional("Richard Hell")
Optional("Harry Nilsson")
Optional("FIDLAR")
Optional("Kitten")
Optional("Rockpile")
I am creating an app that exports playlists to Spotify. Currently, I have an array of Spotify song URIs. I have no knowledge of how the Spotify SDK works and I was wondering how I would use Spartan to a) set up a connection with the user and 2) create a playlist from the app.
I want to access the 'spotifyDescription' of Playlist, however as I searched through the methods, the 'Playlist' class is not even used anywhere.
When using Spartan method getTrack(id: String...)
Track object is received but most of its properties are nil
, when I checked the uri
of the object it turns out that actually an Album
object was received but when Spartan tried to map it to Track
most of the properties were nil.
See the Xcode Quicklook of the object
You can see that the object is in fact an album.
I think I'm calling the method correctly
Although this method gives wrong results, using getTracks(ids: [String]...)
works correctly and I'll be using it to get single tracks from the API
Thank you for creating such an awesome Spotify Client! There's a small issue with getNext being called on a pagingObject dealing with tracks fetched for a playlist. Spartan.getPlaylistTracks() works fine and loads 20 tracks. However after retrieving the pagingObject and calling getNext on it I get fatal error: unexpectedly found nil while unwrapping an Optional value 2017-07-09 19:55:16.220157-0500 Sink[12993:3968613] fatal error: unexpectedly found nil while unwrapping an Optional value
. To confirm I called getNext right after fetching the pagingObject as follows ` let _ = Spartan.getPlaylistTracks(userId: userId, playlistId: playlistId, success: { (tracksPage) in
if tracksPage.canMakeNextRequest {
tracksPage.getNext(success: { (trackpage) in
print("successfully fetched \(trackpage)")
}, failure: { (error) in
print(error.description)
print(error.errorMessage)
})
}
//completionHandler(tracksPage)
}) { (error) in
print(error.description)
print(error.errorMessage)
}
` and I get the error. I've also attached the stack trace below.
I am quite certain you'd be able to recreate the error if you fetch tracks for playlist that has number of songs over the the limit of 1 pagingObject and calling getNext on the pagingObject. Any fix or direction would be greatly appreciated!
Hello,
Just writing to say that the Podfile spec should probably be updated for AlamofireObjectMapper 5 so that users can use swift 4
[!] Unable to satisfy the following requirements:
- `AlamofireObjectMapper (from `https://github.com/tristanhimmelman/AlamofireObjectMapper.git`, branch `swift-4`)` required by `Podfile`
- `AlamofireObjectMapper (= 5.0.0)` required by `Podfile.lock`
- `AlamofireObjectMapper (~> 4.0.1)` required by `Spartan (1.0)`
Spartan.getTrack(id: "11dFghVXANMlKmJXsNCbNl", success: { (track) in self.currentSong = track // FIXME: gets the album instead of the song self.miniPlayer?.configure(song: self.currentSong) }) { (error) in print("Error while fetching the track: \(error.description)") }
The above code is the one I'm currently using to retrieve a Track, but no album data is filled in it, is this behaviour intentional?
This is the Track returned by Spartan
Using the latest version available in Cocoapods, Swift 4.0 and Xcode 10.1
Kindly share working demo project. This Demo project is completely blank.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.