Combine 如何成功匹配两个发布服务器之间的失败映射(Never和URLError)

Combine 如何成功匹配两个发布服务器之间的失败映射(Never和URLError),combine,Combine,在浏览了联合收割机上的一些不同资源(包括Joseph Heck和Donny Wals的书)后,我接近了解DataTaskPublishers的链接,但未能将它们连接到一系列链接操作符中。第一个发布者的输出与第二个发布者的预期输入之间的错误不匹配,这一事实似乎让我心烦意乱。这两个发布者扩展在未连接时都可以工作,因此我确信这是缺乏将两者结合起来的能力。我本以为mapError()可以工作,但它不想编译 以下是设置: 给定两个自定义发布者: extension Publisher where Outp

在浏览了联合收割机上的一些不同资源(包括Joseph Heck和Donny Wals的书)后,我接近了解DataTaskPublishers的链接,但未能将它们连接到一系列链接操作符中。第一个发布者的输出与第二个发布者的预期输入之间的错误不匹配,这一事实似乎让我心烦意乱。这两个发布者扩展在未连接时都可以工作,因此我确信这是缺乏将两者结合起来的能力。我本以为mapError()可以工作,但它不想编译

以下是设置:

给定两个自定义发布者:

extension Publisher where Output == MKCoordinateRegion, Failure == URLError {

func toRegionDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, URLError> {
    return self
        .flatMap({ region -> URLSession.DataTaskPublisher in
                  ...
                  ... 
                  ...
                  return URLSession.shared.dataTaskPublisher(for: request)       
                  })
        .eraseToAnyPublisher()
    }
}
扩展发布服务器,其中输出==MKCoordinateRegion,失败==URLError{
func ToRegionDatask()->AnyPublisher{
回归自我
.flatMap({region->URLSession.DataTaskPublisher在中)
...
... 
...
返回URLSession.shared.dataTaskPublisher(用于:请求)
})
.删除任何发布者()
}
}

扩展发布服务器,其中输出==[String],失败==Never{
func toGeographiesDataTask()->中的任意发布服务器URLSession.DataTaskPublisher
...
...
...
返回URLSession.shared.dataTaskPublisher(用于:请求)
})
.删除任何发布者()
}
}

然后,我有一个函数,试图将这两个链接在一起,如下所示:

   let passthroughSubj = PassthroughSubject<MKCoordinateRegion,URLError>()

    passthroughSubj
    .toRegionDataTask()                                         // returns <DataTaskPublisher, URLError>
    .map { $0.data }                                            // returns <FlatMap, ?>
    .decode(type: ApiResponse.self, decoder:JSONDecoder())      // returns <ApiResonse, ?>
    .map {$0.body.data(using: .utf8)! }                         // returns <Data, ?>
    .decode(type: AmznResponse.self, decoder: JSONDecoder())    // returns <AmznResponse, ?>
    .map ({ response -> [AmznItem] in                           //
                return response.contents                        // returns <[AmznItem], ?>
    })
    .map ({ items -> [String] in                                // returns <[String], Never> ?
            var ids = [String]()
            for item in items {
                    ids.append(item.geoid)
            }
            return ids
            })
//
//        .toGeographiesDataTask()                                  // get error "Referencing instance method
//        .map { $0.data }                                          // 'toGeographiesDataTask()' on 'Publisher'
//        .decode(type: ApiResponse.self, decoder:JSONDecoder())    // requires the types 'Error' and 'Never'
//        .map {$0.body.data(using: .utf8)! }                       // be equivalent"
//        .decode(type: AmznResponse.self, decoder: JSONDecoder())
//        .map { $0.contents }
//
    .sink(receiveCompletion: { (completion) in
        switch completion {
        case .failure(let error):
            print(error)
        case .finished:
            print("DONE")
        }
        }, receiveValue: { data in
           print(data)
        })
    .store(in: &cancellables)

passthroughSubj.send(region1)
let passthroughSubj=PassthroughSubject()
直通UBJ
.ToRegionDatask()//返回
.map{$0.data}//返回
.decode(类型:ApiResponse.self,解码器:JSONDecoder())//返回
.map{$0.body.data(使用:.utf8)!}//返回
.decode(类型:AmznResponse.self,解码器:JSONDecoder())//返回
.map({response->[AmznItem]位于//
return response.contents//returns
})
.map(//returns中的{items->[String]?
变量ID=[String]()
对于项目中的项目{
附加(item.geoid)
}
返回ID
})
//
//.toGeographiesDataTask()//引用实例方法时出现“get error”
//.map{$0.data}/'toGeographiesDataTask()'在'Publisher'上
//.decode(类型:ApiResponse.self,解码器:JSONDecoder())//需要类型“Error”和“Never”
//.map{$0.body.data(使用:.utf8)!}///必须等效”
//.decode(类型:AmznResponse.self,解码器:JSONDecoder())
//.map{$0.contents}
//
.sink(receiveCompletion:{(completion)in
交换完成{
案例。失败(let错误):
打印(错误)
案例。完成:
打印(“完成”)
}
},receiveValue:{中的数据
打印(数据)
})
.store(在:&可取消项中)
passthroughSubj.send(区域1)
如果取消对第二个自定义发布服务器的注释,则会得到右侧显示的错误消息。我的理解是.map正在返回,但最终由于DataTaskPublisher可能失败,我需要将其映射到URLError。但是,似乎也没有编译.maperor的组合

我是不是错过了一些基本的东西?似乎是一个很容易解决的问题,但我没有发现任何突出的问题

我看到过使用.flatMap将这些链接在一起的示例,但由于我正在将一个自定义发布服务器的输出转换为第二个自定义发布服务器的输入,这似乎是不可能的


欢迎任何帮助或指点!谢谢。

映射操作符只转换
输出
,它保留
错误
不变。因此,如果我要在您的
输出
失败
对中填入空格,我将得到以下结果:

// returns <DataTaskPublisher, URLError>
// returns <Data, URLError>
// returns <ApiResonse, Error> (decode replaces the Failure with Error)
// returns <Data, Error>
// returns <AmznResponse, Error>
// returns <[AmznItem], Error>
// returns <[String], Error>
然后在
toGeographiesDataTask()
中,可以使用
maperro
替换数据任务发出的
urleror

func toGeographiesDataTask() ->  AnyPublisher<URLSession.DataTaskPublisher.Output, Error> {
    return self
        .flatMap({ ids -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> in
                   ...
                   ...
                   ...
                  return URLSession.shared.dataTaskPublisher(for: request)
                    .mapError({ $0 as Error})
                    .eraseToAnyPublisher()
                 })
         .eraseToAnyPublisher()
}
func-toGeographiesDataTask()->AnyPublisher{
回归自我
.flatMap({ids->AnyPublisher-in)
...
...
...
返回URLSession.shared.dataTaskPublisher(用于:请求)
.mapError({$0作为错误})
.删除任何发布者()
})
.删除任何发布者()
}
我认为这应该让链的其余部分也能工作,你应该以链末端的
作为
结束


虽然我没有在操场上尝试过,但我确信这会让你成功。

映射操作符只转换
输出
,它保留
错误
不变。因此,如果我要在您的
输出
失败
对中填入空格,我将得到以下结果:

// returns <DataTaskPublisher, URLError>
// returns <Data, URLError>
// returns <ApiResonse, Error> (decode replaces the Failure with Error)
// returns <Data, Error>
// returns <AmznResponse, Error>
// returns <[AmznItem], Error>
// returns <[String], Error>
然后在
toGeographiesDataTask()
中,可以使用
maperro
替换数据任务发出的
urleror

func toGeographiesDataTask() ->  AnyPublisher<URLSession.DataTaskPublisher.Output, Error> {
    return self
        .flatMap({ ids -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> in
                   ...
                   ...
                   ...
                  return URLSession.shared.dataTaskPublisher(for: request)
                    .mapError({ $0 as Error})
                    .eraseToAnyPublisher()
                 })
         .eraseToAnyPublisher()
}
func-toGeographiesDataTask()->AnyPublisher{
回归自我
.flatMap({ids->AnyPublisher-in)
...
...
...
返回URLSession.shared.dataTaskPublisher(用于:请求)
.mapError({$0作为错误})
.删除任何发布者()
})
.删除任何发布者()
}
我认为这应该让链的其余部分也能工作,你应该以链末端的
作为
结束

我没有
struct ApiResponse: Decodable {
    var body: String
}

// Does the abbreviation "Amzn" really improve the program?
struct AmazonResponse: Decodable {
    var contents: [AmazonItem]
}

struct AmazonItem: Decodable {
    var geoid: String
}
func apiRequest(for region: MKCoordinateRegion) -> URLRequest {
    // Your code here. fatalError gets this through the compiler.
    fatalError()
}

func geographiesRequest(forIds ids: [String]) -> URLRequest {
    // Your code here. fatalError gets this through the compiler.
    fatalError()
}
extension Publisher where Output == MKCoordinateRegion {
    func toRegionDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> {
        return self
            .map { apiRequest(for: $0) }
            .flatMap { URLSession.shared.dataTaskPublisher(for: $0) }
            .eraseToAnyPublisher()
    }
}
            .flatMap { URLSession.shared.dataTaskPublisher(for: $0) }
             ^
            .eraseToAnyPublisher()
             ^
            .eraseToAnyPublisher()
             ^
    func toRegionDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> {
        let x = self
            .map { apiRequest(for: $0) }
            .eraseToAnyPublisher()

        let y = x
            .flatMap { URLSession.shared.dataTaskPublisher(for: $0) }
            .eraseToAnyPublisher()

        return y
    }
extension Publisher where Output == MKCoordinateRegion {
    func toRegionDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> {
        let x = self
            .map { apiRequest(for: $0) }
            .mapError { $0 as Error }
         // ^^^^^^^^^^^^^^^^^^^^^^^^^
            .eraseToAnyPublisher()

        let y = x
            .flatMap { URLSession.shared.dataTaskPublisher(for: $0).mapError { $0 as Error } }
                                                                 // ^^^^^^^^^^^^^^^^^^^^^^^^
            .eraseToAnyPublisher()

        return y
    }
}
extension Publisher where Output == MKCoordinateRegion {
    func toRegionDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> {
        return self
            .map { apiRequest(for: $0) }
            .mapError { $0 as Error }
            .flatMap { URLSession.shared.dataTaskPublisher(for: $0).mapError { $0 as Error } }
            .eraseToAnyPublisher()
    }
}
extension Publisher where Output == [String] {
    func toGeographiesDataTask() -> AnyPublisher<URLSession.DataTaskPublisher.Output, Error> {
        return self
            .map { geographiesRequest(forIds: $0) }
            .mapError { $0 as Error }
            .flatMap { URLSession.shared.dataTaskPublisher(for: $0).mapError { $0 as Error } }
            .eraseToAnyPublisher()
    }
}
// Does the abbeviation "subj" really improve the program?
// The subject's Failure type could be anything here.
let subject = PassthroughSubject<MKCoordinateRegion, Error>()

var tickets: [AnyCancellable] = []

subject
    .toRegionDataTask()
    .map { $0.data }
    .decode(type: ApiResponse.self, decoder: JSONDecoder())
    .map { $0.body.data(using: .utf8)! }
    .decode(type: AmazonResponse.self, decoder: JSONDecoder())
    .map { $0.contents }
    .map { $0.map { $0.geoid } }
    .toGeographiesDataTask()
    .map { $0.data }
    .decode(type: ApiResponse.self, decoder: JSONDecoder())
    .map { $0.body.data(using: .utf8)! }
    .decode(type: AmazonResponse.self, decoder: JSONDecoder())
    .map { $0.contents }
    .sink(
        receiveCompletion: { print("completion: \($0)") },
        receiveValue: { print("value: \($0)") })
    .store(in: &tickets)

let region1 = MKCoordinateRegion()
subject.send(region1)