Search code examples
swiftcombine

reduce(into: [:]) equivalent for Combine in Swift


I've been struggling to find an equivalent for reduce(into:_:) in Combine.

enum GenericError : Error {
    case with(message: String)
}

class Constants {
    fileprivate static let scheme = "sampleScheme"
    fileprivate static let host = "com.sampleHost"
    fileprivate static let authPath = "/auth"
    static let authQuery = "key"
}

func parse(_ url: URL, queryPathsFilterByKey: [String]) throws -> [String: String] {
    guard url.scheme == Constants.scheme,
          url.host == Constants.host,
          url.path == Constants.authPath,
          let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true),
          let queryItems = urlComponents.queryItems else {
        
        throw GenericError.with(message: "Invalid deep link.")
    }
    
    // Create a [String:String] from [UrlQueryItems]
    let query = queryItems.reduce(into: [:]) { result, item in
        result[item.name] = item.value
    }

    let result = query.filter { queryPathsFilterByKey.contains($0.key) }

    guard !result.isEmpty else {
            throw GenericError.with(message: "Error parsing query Items (deep linking)")
    }

    return result
}

what operator replaces reduce(into:) in Combine?


Solution

  • Found a way to do this

    func parse(_ url: URL, queryPaths: [String]) -> AnyPublisher<[String: String], GenericError> {
            guard url.scheme == Constants.scheme,
                  url.host == Constants.host,
                  url.path == Constants.authPath,
                  let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true),
                  let queryItems = urlComponents.queryItems else {
    
                return Fail(outputType: [String:String].self, failure: GenericError.with(message: "Invalid deep link"))
                    .eraseToAnyPublisher()
            }
            
            return queryItems
                .publisher
                .reduce ([String:String]()) { dictionary, value in
                    var dict = dictionary
                    dict[value.name] = value.value
                    return dict
                }
                .filter { $0.allSatisfy { queryPaths.contains($0.key) } }
                .flatMap { dictionary -> AnyPublisher<[String:String], GenericError> in
                    if(dictionary.isEmpty)
                    {
                        return Fail(outputType: [String:String].self, failure: GenericError.with(message: "Error parsing query Items (deep linking)"))
                            .eraseToAnyPublisher()
                    } else {
                        return Just(dictionary)
                            .setFailureType(to: GenericError.self)
                            .eraseToAnyPublisher()
                    }
                }
                .eraseToAnyPublisher()
        }
    

    Not sure if this is the best way but worked.