You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift同一函数中用双URLSession请求获取Spotify两类数据?

如何在Swift中通过Spotify API同时获取热门歌手与热门曲目数据

步骤1:定义组合数据结构

首先创建一个结构体,用来封装热门歌手(top artists)和热门曲目(top tracks)的数据,让completionHandler可以一次性返回两类结果:

// 假设你已经有解析Spotify返回数据的Artist和Track模型(符合Codable)
struct TopMusicData {
    let artists: [Artist]
    let tracks: [Track]
}

方式一:用Async/Await实现(iOS 15+ / macOS 12+ 推荐)

如果项目支持Swift并发特性,这种方式代码更简洁易维护,无需手动管理异步任务的同步:

func fetchTopMusicData(timeRange: String = "medium_term", limit: Int = 20) async throws -> TopMusicData {
    let session = URLSession.shared
    let accessToken = "你的Spotify访问令牌" // 需要确保令牌拥有user-top-read权限
    
    // 1. 请求并解析热门歌手数据
    guard let artistsURL = URL(string: "https://api.spotify.com/v1/me/top/artists?time_range=\(timeRange)&limit=\(limit)") else {
        throw URLError(.badURL)
    }
    var artistsRequest = URLRequest(url: artistsURL)
    artistsRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
    let (artistsData, _) = try await session.data(for: artistsRequest)
    let artistsResponse = try JSONDecoder().decode(TopArtistsResponse.self, from: artistsData)
    let topArtists = artistsResponse.items
    
    // 2. 请求并解析热门曲目数据
    guard let tracksURL = URL(string: "https://api.spotify.com/v1/me/top/tracks?time_range=\(timeRange)&limit=\(limit)") else {
        throw URLError(.badURL)
    }
    var tracksRequest = URLRequest(url: tracksURL)
    tracksRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
    let (tracksData, _) = try await session.data(for: tracksRequest)
    let tracksResponse = try JSONDecoder().decode(TopTracksResponse.self, from: tracksData)
    let topTracks = tracksResponse.items
    
    // 返回组合数据
    return TopMusicData(artists: topArtists, tracks: topTracks)
}

// 配套的解析模型示例(需符合Codable)
struct TopArtistsResponse: Codable {
    let items: [Artist]
}

struct TopTracksResponse: Codable {
    let items: [Track]
}

struct Artist: Codable {
    let id: String
    let name: String
    let images: [SpotifyImage]?
}

struct Track: Codable {
    let id: String
    let name: String
    let artists: [Artist]
    let album: Album
}

struct SpotifyImage: Codable {
    let url: String
    let height: Int?
    let width: Int?
}

struct Album: Codable {
    let id: String
    let name: String
    let images: [SpotifyImage]?
}

调用示例:

Task {
    do {
        let topMusicData = try await fetchTopMusicData()
        // 处理获取到的歌手和曲目数据
        print("热门歌手:\(topMusicData.artists.map { $0.name })")
        print("热门曲目:\(topMusicData.tracks.map { $0.name })")
    } catch {
        print("请求失败:\(error.localizedDescription)")
    }
}

方式二:用DispatchGroup实现回调式请求(兼容iOS 14及以下)

如果需要兼容旧系统,可使用DispatchGroup来同步两个异步请求的完成状态:

func fetchTopMusicData(timeRange: String = "medium_term", limit: Int = 20, completion: @escaping (Result<TopMusicData, Error>) -> Void) {
    let session = URLSession.shared
    let accessToken = "你的Spotify访问令牌"
    let dispatchGroup = DispatchGroup()
    
    var topArtists: [Artist]?
    var topTracks: [Track]?
    var requestError: Error?
    
    // 发起热门歌手请求
    dispatchGroup.enter()
    guard let artistsURL = URL(string: "https://api.spotify.com/v1/me/top/artists?time_range=\(timeRange)&limit=\(limit)") else {
        completion(.failure(URLError(.badURL)))
        return
    }
    var artistsRequest = URLRequest(url: artistsURL)
    artistsRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
    session.dataTask(with: artistsRequest) { data, _, error in
        defer { dispatchGroup.leave() }
        if let error = error {
            requestError = error
            return
        }
        guard let data = data else {
            requestError = URLError(.cannotParseResponse)
            return
        }
        do {
            let response = try JSONDecoder().decode(TopArtistsResponse.self, from: data)
            topArtists = response.items
        } catch {
            requestError = error
        }
    }.resume()
    
    // 发起热门曲目请求
    dispatchGroup.enter()
    guard let tracksURL = URL(string: "https://api.spotify.com/v1/me/top/tracks?time_range=\(timeRange)&limit=\(limit)") else {
        completion(.failure(URLError(.badURL)))
        return
    }
    var tracksRequest = URLRequest(url: tracksURL)
    tracksRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
    session.dataTask(with: tracksRequest) { data, _, error in
        defer { dispatchGroup.leave() }
        if let error = error {
            requestError = error
            return
        }
        guard let data = data else {
            requestError = URLError(.cannotParseResponse)
            return
        }
        do {
            let response = try JSONDecoder().decode(TopTracksResponse.self, from: data)
            topTracks = response.items
        } catch {
            requestError = error
        }
    }.resume()
    
    // 等待两个请求完成后返回结果
    dispatchGroup.notify(queue: .main) {
        if let error = requestError {
            completion(.failure(error))
            return
        }
        guard let artists = topArtists, let tracks = topTracks else {
            completion(.failure(URLError(.unknown)))
            return
        }
        completion(.success(TopMusicData(artists: artists, tracks: tracks)))
    }
}

调用示例:

fetchTopMusicData { result in
    switch result {
    case .success(let data):
        print("热门歌手:\(data.artists.map { $0.name })")
        print("热门曲目:\(data.tracks.map { $0.name })")
    case .failure(let error):
        print("请求失败:\(error.localizedDescription)")
    }
}

关键注意事项

  • 权限要求:你的Spotify访问令牌必须拥有user-top-read权限,否则API会返回403错误。
  • 令牌有效性:确保访问令牌未过期,可通过Spotify的授权流程刷新令牌。
  • 模型适配:根据你需要的字段调整Artist、Track等模型的属性,完全匹配Spotify API返回的JSON结构。

内容的提问来源于stack exchange,提问作者ezhovich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 21:07:38