接收迅速 |发出连续网络请求的最佳方式?
RxSwift | Best way to make consecutive network requests?
我最近一直在练习 RxSwift,但是我 运行 在发出网络请求时遇到了问题。
问题是如何进行连续的网络请求。
例如,在 Github api 中,我应该使用 https://api.github.com/user/starred/{\owner}/{\repository_name}
来检查用户是否为存储库加注星标。
应该在之后发送我收到请求的数据,但我很难实现它。
这是我到目前为止尝试过的方法:
import RxSwift
// Struct used to encode response
struct RepositoryResponse: Codable {
let items: [Item]
enum CodingKeys: String, CodingKey {
case items
}
struct Item: Codable {
let fullName: String
enum CodingKeys: String, CodingKey {
case fullName = "full_name"
}
}
}
// Actual data for further use
struct Repository {
let item: RepositoryResponse.Item
var fullName: String {
return item.fullName
}
var isStarred: Bool
init(_ item: RepositoryData, isStarred: Bool) {
self.item = item
self.isStarred = isStarred
}
}
// Url components
var baseUrl = URLComponents(string: "https://api.github.com/search/repositories") // base url
let query = URLQueryItem(name: "q", value: "flutter") // Keyword. flutter for this time.
let sort = URLQueryItem(name: "sort", value: "stars") // Sort by stars
let order = URLQueryItem(name: "order", value: "desc") // Desc order
baseUrl?.queryItems = [query, sort, order]
// Observable expected to return Observable<[Repository]>
Observable<URL>.of((baseUrl?.url)!)
.map { URLRequest(url: [=10=]) }
.flatMap { request -> Observable<(response: HTTPURLResponse, data: Data)> in
return URLSession.shared.rx.response(request: request)
}
.filter { response, data in
return 200..<300 ~= response.statusCode
}
.map { _, data -> [RepositoryResponse.Item] in
let decoder = JSONDecoder()
if let decoded = try? decoder.decode(RepositoryResponse.self, from: data) {
return decoded.items
} else {
print("ERROR: decoding")
return [RepositoryResponse.Item]()
}
}
.map { items -> [Repository] in
let repos = items.map { item -> Repository in
var isStarred: Bool?
/// I want to initialize Repository with isStarred value
/// What should I do in here?
return Repository(item, isStarred: isStarred)
}
return repos
}
我计划做的是通过 Github 搜索 api 获取存储库,然后检查用户是否为每个存储库加星标。
所以我制作了 Repository
结构,它有两个变量,每个变量包含存储库的名称和星级状态。
这里出现问题。要初始化存储库结构,我应该获得星级。
我已经尝试了一种补全方式,但似乎 return 完成前 return 的值。
private func isRepoStarred(name: String, completion: @escaping (Bool) -> Void) {
let isStarredCheckerUrl = URL(string: "https://api.github.com/user/starred/\(name)")!
URLSession.shared.dataTask(with: isStarredCheckerUrl) { _, response, _ in
guard let response = response as? HTTPURLResponse else {
return
}
let code = response.statusCode
if code == 404 {
return completion(false)
} else if code == 204 {
return completion(true)
} else {
return completion(false)
}
}
}
我尝试过的另一种方法是让 Single
可观察,但不知道如何使用它。
func isRepoStarredObs(name: String) -> Single<Bool> {
return Single<Bool>.create { observer in
let isStarredCheckerUrl = URL(string: "https://api.github.com/user/starred/\(name)")!
let task = URLSession.shared.dataTask(with: isStarredCheckerUrl) { _, response, _ in
guard let response = response as? HTTPURLResponse else {
return
}
let code = response.statusCode
if code == 404 {
observer(.success(false))
} else if code == 204 {
observer(.success(true))
} else {
observer(.failure(NSError(domain: "Invalid response", code: code)))
}
}
task.resume()
return Disposables.create { task.cancel() }
}
}
如果您有任何想法,请告诉我。谢谢。
这获得了星标状态:
func isRepoStarred(name: String) -> Observable<Bool> {
URLSession.shared.rx.data(request: URLRequest(url: URL(string: "https://api.github.com/user/starred/\(name)")!))
.map { data in
var result = false
// find out if repo is starred here and return true or false.
return result
}
}
这是您的搜索。
func searchRepositories() -> Observable<RepositoryResponse> {
var baseUrl = URLComponents(string: "https://api.github.com/search/repositories") // base url
let query = URLQueryItem(name: "q", value: "flutter") // Keyword. flutter for this time.
let sort = URLQueryItem(name: "sort", value: "stars") // Sort by stars
let order = URLQueryItem(name: "order", value: "desc") // Desc order
baseUrl?.queryItems = [query, sort, order]
return URLSession.shared.rx.data(request: URLRequest(url: baseUrl!.url!))
.map { data in
try JSONDecoder().decode(RepositoryResponse.self, from: data)
}
}
这就是您提出请求所需的全部内容。
要合并它们,您可以这样做:
let repositories = searchRepositories()
.flatMap {
Observable.zip([=12=].items.map { item in
isRepoStarred(name: item.fullName).map { Repository(item, isStarred: [=12=]) }
})
}
一般来说,最好尽可能减少 flatMap 中的代码量。这是一个更好地分解代码的版本。这个版本也可能更容易理解发生了什么。
let repositories = searchRepositories()
.map { [=13=].items }
let starreds = repositories
.flatMap { items in
Observable.zip(items.map { isRepoStarred(name: [=13=].fullName) })
}
let repos = Observable.zip(repositories, starreds) { items, starreds in
zip(items, starreds)
.map { Repository([=13=], isStarred: ) }
}
我最近一直在练习 RxSwift,但是我 运行 在发出网络请求时遇到了问题。
问题是如何进行连续的网络请求。
例如,在 Github api 中,我应该使用 https://api.github.com/user/starred/{\owner}/{\repository_name}
来检查用户是否为存储库加注星标。
应该在之后发送我收到请求的数据,但我很难实现它。
这是我到目前为止尝试过的方法:
import RxSwift
// Struct used to encode response
struct RepositoryResponse: Codable {
let items: [Item]
enum CodingKeys: String, CodingKey {
case items
}
struct Item: Codable {
let fullName: String
enum CodingKeys: String, CodingKey {
case fullName = "full_name"
}
}
}
// Actual data for further use
struct Repository {
let item: RepositoryResponse.Item
var fullName: String {
return item.fullName
}
var isStarred: Bool
init(_ item: RepositoryData, isStarred: Bool) {
self.item = item
self.isStarred = isStarred
}
}
// Url components
var baseUrl = URLComponents(string: "https://api.github.com/search/repositories") // base url
let query = URLQueryItem(name: "q", value: "flutter") // Keyword. flutter for this time.
let sort = URLQueryItem(name: "sort", value: "stars") // Sort by stars
let order = URLQueryItem(name: "order", value: "desc") // Desc order
baseUrl?.queryItems = [query, sort, order]
// Observable expected to return Observable<[Repository]>
Observable<URL>.of((baseUrl?.url)!)
.map { URLRequest(url: [=10=]) }
.flatMap { request -> Observable<(response: HTTPURLResponse, data: Data)> in
return URLSession.shared.rx.response(request: request)
}
.filter { response, data in
return 200..<300 ~= response.statusCode
}
.map { _, data -> [RepositoryResponse.Item] in
let decoder = JSONDecoder()
if let decoded = try? decoder.decode(RepositoryResponse.self, from: data) {
return decoded.items
} else {
print("ERROR: decoding")
return [RepositoryResponse.Item]()
}
}
.map { items -> [Repository] in
let repos = items.map { item -> Repository in
var isStarred: Bool?
/// I want to initialize Repository with isStarred value
/// What should I do in here?
return Repository(item, isStarred: isStarred)
}
return repos
}
我计划做的是通过 Github 搜索 api 获取存储库,然后检查用户是否为每个存储库加星标。
所以我制作了 Repository
结构,它有两个变量,每个变量包含存储库的名称和星级状态。
这里出现问题。要初始化存储库结构,我应该获得星级。
我已经尝试了一种补全方式,但似乎 return 完成前 return 的值。
private func isRepoStarred(name: String, completion: @escaping (Bool) -> Void) {
let isStarredCheckerUrl = URL(string: "https://api.github.com/user/starred/\(name)")!
URLSession.shared.dataTask(with: isStarredCheckerUrl) { _, response, _ in
guard let response = response as? HTTPURLResponse else {
return
}
let code = response.statusCode
if code == 404 {
return completion(false)
} else if code == 204 {
return completion(true)
} else {
return completion(false)
}
}
}
我尝试过的另一种方法是让 Single
可观察,但不知道如何使用它。
func isRepoStarredObs(name: String) -> Single<Bool> {
return Single<Bool>.create { observer in
let isStarredCheckerUrl = URL(string: "https://api.github.com/user/starred/\(name)")!
let task = URLSession.shared.dataTask(with: isStarredCheckerUrl) { _, response, _ in
guard let response = response as? HTTPURLResponse else {
return
}
let code = response.statusCode
if code == 404 {
observer(.success(false))
} else if code == 204 {
observer(.success(true))
} else {
observer(.failure(NSError(domain: "Invalid response", code: code)))
}
}
task.resume()
return Disposables.create { task.cancel() }
}
}
如果您有任何想法,请告诉我。谢谢。
这获得了星标状态:
func isRepoStarred(name: String) -> Observable<Bool> {
URLSession.shared.rx.data(request: URLRequest(url: URL(string: "https://api.github.com/user/starred/\(name)")!))
.map { data in
var result = false
// find out if repo is starred here and return true or false.
return result
}
}
这是您的搜索。
func searchRepositories() -> Observable<RepositoryResponse> {
var baseUrl = URLComponents(string: "https://api.github.com/search/repositories") // base url
let query = URLQueryItem(name: "q", value: "flutter") // Keyword. flutter for this time.
let sort = URLQueryItem(name: "sort", value: "stars") // Sort by stars
let order = URLQueryItem(name: "order", value: "desc") // Desc order
baseUrl?.queryItems = [query, sort, order]
return URLSession.shared.rx.data(request: URLRequest(url: baseUrl!.url!))
.map { data in
try JSONDecoder().decode(RepositoryResponse.self, from: data)
}
}
这就是您提出请求所需的全部内容。
要合并它们,您可以这样做:
let repositories = searchRepositories()
.flatMap {
Observable.zip([=12=].items.map { item in
isRepoStarred(name: item.fullName).map { Repository(item, isStarred: [=12=]) }
})
}
一般来说,最好尽可能减少 flatMap 中的代码量。这是一个更好地分解代码的版本。这个版本也可能更容易理解发生了什么。
let repositories = searchRepositories()
.map { [=13=].items }
let starreds = repositories
.flatMap { items in
Observable.zip(items.map { isRepoStarred(name: [=13=].fullName) })
}
let repos = Observable.zip(repositories, starreds) { items, starreds in
zip(items, starreds)
.map { Repository([=13=], isStarred: ) }
}