【问题标题】:Repeat Network Request Multiple Times With Swift Combine使用 Swift Combine 多次重复网络请求
【发布时间】:2020-11-19 10:14:26
【问题描述】:

我正在尝试创建一个组合管道,该管道使用 Unsplash API 获取五个随机照片 url,然后从每个 url 下载一张照片。我遇到的问题是图像大多相同。有时(我不知道为什么)其中一张图片会与其他四张不同。

任何帮助将不胜感激。除了 UnSplash Api 密钥之外,我还包含了所有必要的代码。

static func fetchRandomPhotos() -> AnyPublisher<UIImage, Error>{
        let string = createURL(path: "/photos/random")

        return [1,2,3,4,5]
            .publisher
            .flatMap{ i -> AnyPublisher<UnsplashImageResults, Error> in
                return self.downloadAndDecode(string, type: UnsplashImageResults.self)
            }
            .flatMap{ result -> AnyPublisher<UIImage, Error> in
                print(result.urls.thumb)
                let url = URL(string: result.urls.thumb)!
                return URLSession.shared.dataTaskPublisher(for: url)
                    .map { UIImage(data: $0.data)! }
                    .mapError{_ in NetworkError.invalidURL}
                    .eraseToAnyPublisher()
            }
            .receive(on: RunLoop.main)
            .eraseToAnyPublisher()
    }

struct UnsplashURLs: Decodable{
    let full: String
    let regular: String
    let small: String
    let thumb: String
}

struct UnsplashImageResults: Decodable{
    let urls: UnsplashURLs
}
enum NetworkError: LocalizedError{
    case invalidURL

    var errorDescription: String? {
        switch self {
        case .invalidURL:
            return "The url is invalid"
        }
    }
}
static private func createURL(path: String)-> String{
        var components = URLComponents()
        components.scheme     = "https"
        components.host       = "api.unsplash.com"
        components.path       = path
        components.queryItems = [
            URLQueryItem(name: "client_id", value: "YOUR API KEY HERE")
        ]
        
        return components.string!
    }
    
    static private func downloadAndDecode<T:Decodable>(_ urlString: String, type: T.Type) -> AnyPublisher<T, Error>{
        guard let url = URL(string: urlString) else{
            return Fail(error: NetworkError.invalidURL).eraseToAnyPublisher()
        }
        return URLSession.shared.dataTaskPublisher(for: url)
            .tryMap() { element -> Data in
                guard let httpResponse = element.response as? HTTPURLResponse,
                      httpResponse.statusCode == 200 else {
                    throw URLError(.badServerResponse)
                }
                return element.data
            }
            .decode(type: T.self, decoder: JSONDecoder())
            .receive(on: RunLoop.main)
            .eraseToAnyPublisher()
    }
class UnsplashImagesViewModel: ObservableObject{
    var subscriptions = Set<AnyCancellable>()
    @Published var images = [UIImage]()
    
    init(){
        UnsplashAPI.fetchRandomPhotos()
            .sink { (_) in
                
            } receiveValue: { image in
                self.images.append(image)
            }.store(in: &subscriptions)

    }
}

struct UnsplashImagesGrid: View {
    @StateObject private var model = UnsplashImagesViewModel()
    
    var body: some View {
        List(model.images, id: \.self){ image in
            Image(uiImage: image)
        }
    }
}

【问题讨论】:

    标签: swift combine


    【解决方案1】:

    如果您同时发送多个请求,Unsplash 会返回相同的图像。您可以通过在请求之间引入延迟来测试此行为。

    无论如何,您应该使用Unsplash random APIcount 参数来请求特定数量的图像。我对您的代码进行了一些更改,以接收UIImage 的数组。我希望这能让您了解如何进一步改进它。

     static func fetchRandomPhotos() -> AnyPublisher<[UIImage], Error>{
                let string = createURL(path: "/photos/random")
        
                return Just(())
                    .print()
                    .flatMap{ i -> AnyPublisher<[UnsplashImageResults], Error> in
                        return self.downloadAndDecode(string, type: UnsplashImageResults.self)
                    }
                    .flatMap{ results -> AnyPublisher<[UIImage], Error> in
                        let images = results.map { result -> AnyPublisher<UIImage, Error> in
                            let url = URL(string: result.urls.thumb)!
                            return URLSession.shared.dataTaskPublisher(for: url)
                                .map { UIImage(data: $0.data)! }
                                .mapError{_ in NetworkError.invalidURL}
                                .eraseToAnyPublisher()
                            
                        }
                         return Publishers.MergeMany(images)
                             .collect()
                             .eraseToAnyPublisher()
                    }
                    .receive(on: RunLoop.main)
                    .eraseToAnyPublisher()
            }
        
        static private func createURL(path: String)-> String{
                ...
                components.queryItems = [
                    URLQueryItem(name: "client_id", value: "ID"),
                    URLQueryItem(name: "count", value: "5")
                ]
                ...
            }
    
            static private func downloadAndDecode<T:Decodable>(_ urlString: String, type: T.Type) -> AnyPublisher<[T], Error>{
                ...
                    .decode(type: [T].self, decoder: JSONDecoder())
                ...
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-17
      相关资源
      最近更新 更多