Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
84 views
in Technique[技术] by (71.8m points)

ios - Add additional logic to chain of publisher transformation without affecting Publisher type

Kotlin’s coroutines provide the ability to write very flat code. I am trying to work on converting some of the async iOS code to utilize Combine to flatten it out on my end as well.

Kotlin looks like:

private suspend fun getData(
        networkCall: Boolean = true,
        networkRequest: suspend () -> Either<FieldError, List<JobModel>>,
        localRequest: suspend () -> Either<FieldError, List<JobModel>>,
        cache: suspend (data: List<JobModel>) -> Unit
): Either<FieldError, List<JobModel>> {
    // getting of Jobs and passing in pair if result was from network
    var resultNetwork = false
    var result: Either<FieldError, List<JobModel>> = Left(GenericError)
    if (!networkCall) {
        result = localRequest()
    }
    // if it was not a network call and failed we will force network call
    if (networkCall || result.isLeft()) {
        resultNetwork = true
        result = networkRequest()
    }

    if (result is Either.Right && resultNetwork) {
        cache(result.b)
    }

    return result

}

Swift WIP looks like:

public func getData(isNetworkCall: AnyPublisher<Bool, Error>,
                    networkRequest: AnyPublisher<[Job], Error>,
                    localRequest: AnyPublisher<[Job], Error>,
                    cache: ([Job]) -> AnyPublisher<Void, Error>) -> AnyPublisher<[Job], Error>? {
    
    let getJobsRequest =  isNetworkCall.flatMap { (isCall) in
        return isCall
            ? networkRequest
            //.also { jobs in cache(jobs) }
            .catch{ _ in return localRequest }
            .eraseToAnyPublisher()
            : localRequest
    }.eraseToAnyPublisher()
    
    return getJobsRequest
}

How do I add this logic of caching data as part of this AnyPublisher? I would like to have it cache in the midst of this logic transformation. Ideally there could be an also function that appends logic when the transaction is being completed by the subscriber.

Solution:

private func getData(isNetworkCall: AnyPublisher<Bool, Error>,
                         networkRequest: AnyPublisher<[Job], Error>,
                         localRequest: AnyPublisher<[Job], Error>,
                         cache: @escaping ([Job]) -> AnyPublisher<Void, Error>) -> AnyPublisher<[Job], Error> {
    
    // Sequence of steps for when we should do a network call
    let networkCallFlow = networkRequest
        .flatMap { jobs in // cache jobs from network
            cache(jobs)
                .replaceError(with: ()) // fire and forget cache, replace error with Void to continue
                .map { _ in jobs } // need to give back jobs to flatMap
                .setFailureType(to: Error.self) // match failure type
        }
        .catch { _ in localRequest } // return local if network fails
        .eraseToAnyPublisher()
    
    // Sequence of steps for when we should get from local
    let localCallFlow = localRequest
        .catch { _ in networkCallFlow } // do network flow if local call fails
        .eraseToAnyPublisher()

    return isNetworkCall
        .flatMap { $0 ? networkCallFlow : localCallFlow }
        .eraseToAnyPublisher()
}
question from:https://stackoverflow.com/questions/65831741/add-additional-logic-to-chain-of-publisher-transformation-without-affecting-publ

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can chain it with flatMap.

It would have been easier if cache was also a AnyPublisher<[Job], Error>, then you could have had the following:

return isCall
    ? networkRequest
        .flatMap { jobs in cache(jobs) }
        .catch{ _ in return localRequest }
        .eraseToAnyPublisher()
    : localRequest

Otherwise, you'd need to map its Void returned value back to jobs:

return isCall
    ? networkRequest
        .flatMap { jobs in 
            cache(jobs).map { _ in jobs }
        }
        .catch{ _ in return localRequest }
        .eraseToAnyPublisher()
    : localRequest

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...