在 Swift Combine Publisher 中,我遇到了符合 'RangeReplaceableCollection' 的错误

In Swift Combine Publisher, I am getting conform to 'RangeReplaceableCollection' error

对于我正在处理的 Swift 发布者解决方案,我收到以下编译时错误:

Instance method 'store(in:)' requires that 'Set<[AnyCancellable]>' conform to 'RangeReplaceableCollection'

我的代码块如下:

public class APIService {
    static let baseURL = URL(string: "https://api.recommendations.samba.tv")!
    var apiKey: String

    var logger = Logger(label: "com.samba.tv.recommendations")

    private var subscriptions = Set<AnyCancellable>()

    private let jsonDecoder: JSONDecoder = {
        let jsonDecoder = JSONDecoder()
        jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-mm-dd"
        jsonDecoder.dateDecodingStrategy = .formatted(dateFormatter)
        return jsonDecoder
    }()


    /// Defines the type of API error returbed,
    /// An enumerating type to classify the types of errors that can occur. Returns an `Error`, such as `noResponse`. Specific errors with messages will return both an error message and code.
    enum APIError: Error, LocalizedError {
        case missingCredentials
        case urlError(URLError)
        case responseError(Int)
        case decodingError(DecodingError)
        case genericError

        var localizedDescription: String {
            switch self {
            case .missingCredentials:
                return "Missing credentials"
            case .urlError(let error):
                return error.localizedDescription
            case .decodingError(let error):
                return error.localizedDescription
            case .responseError(let status):
                return "Bad response code: \(status)"
            case .genericError:
                return "An unknown error has been occured"
            }
        }
    }

    /// Obtain Endpoint complete with baseURL
    ///
    /// An enumeration type, with an in-line function. This method constructs the entire URL, using the `baseURL` variable declared prior.
    /// By passing the specific enum type, such as `.popular(param)`, along with a `Popular` parameter, which is optional.
    enum Endpoint {
        case popular(popularParam: Popular?)

        func path() -> URLComponents {
            switch self {
            case let .popular(popularParam):
                var queryItems = [URLQueryItem]()
                let urlString = "\(baseURL)/popularity"
                var components = URLComponents(url: URL(string: urlString)!, resolvingAgainstBaseURL: true)!

                if let countryCode = popularParam?.country {
                    queryItems.append(URLQueryItem(name: "country_code", value: countryCode.rawValue))
                }
                if let namespace = popularParam?.namespace {
                        queryItems.append(URLQueryItem(name: "response_namespace", value: namespace.rawValue))
                }
                if let contentType = popularParam?.contentType {
                            queryItems.append(URLQueryItem(name: "content_type", value: contentType.rawValue))
                }
                if let genres = popularParam?.genres {
                    let genreArrayString = genres.compactMap({String([=12=].rawValue)}).joined(separator: ",")
                                queryItems.append(URLQueryItem(name: "genre_ids", value: genreArrayString))
                }
                components.queryItems = queryItems
                return components
            }
        }
    }

    init(key: String) {
        apiKey = key
    }

    /// Generic `GET` function, facilitating calls by passing in an `Endpoint` along with any parameters.
    /// It returns a `Result<T, APIError>`.
    ///  The method also inserts the required header parameters, including `apiKey`, and will fail if it is not valid, or missing.
    func GET<T: Codable>(endpoint: Endpoint,
                         params: [String: String]?) -> Future<[T], APIError> {

//        guard let apiKey = APIService.shared.apiKey else {
//            throw APIError.missingCredentials
//        }

        return Future<[T], APIError> { promise in


            var components = endpoint.path()
            var request = URLRequest(url: components.url!)
            request.addValue("Bearer \(self.apiKey)", forHTTPHeaderField: "Authorization")
            request.addValue("application/json", forHTTPHeaderField: "accept")

            if let params = params {
                for (_, value) in params.enumerated() {
                    components.queryItems?.append(URLQueryItem(name: value.key, value: value.value))
                }
            }

            request.httpMethod = "GET"

            URLSession.shared.dataTaskPublisher(for: request)
            .tryMap{ data, response -> Data in
                guard let httpResponse = response as? HTTPURLResponse, 200...299 ~= httpResponse.statusCode else {
                    throw APIError.responseError((response as? HTTPURLResponse)?.statusCode ?? 500)
                }
                return data
            }
            .decode(type: PaginatedResponse.self, decoder: self.jsonDecoder)
            .receive(on: RunLoop.main)
                .sink(receiveCompletion: { (completion) in
                    if case let .failure(error) = completion {
                        switch error {
                        case let urlError as URLError:
                            promise(.failure(.urlError(urlError)))
                        case let decodingError as DecodingError:
                            promise(.failure(.decodingError(decodingError)))
                        case let apiError as APIError:
                            promise(.failure(apiError))
                        default:
                            promise(.failure(.genericError))
                        }
                    }
                }, receiveValue: { promise(.success([=12=].results)) })
                .store(in: &self.subscriptions)
            }

    }

}

错误出现在最后一行,.store(in:....

问题

如何解决错误?

额外引用类

PaginatedResponse.swift

import Foundation

struct PaginatedResponse<T: Codable>: Codable {
    let paginator: Paginator
    let results: [T]
}

ContentItem.swift

struct ContentItem: Codable, Equatable {
    enum ContentID: Codable {
        case text(String)
        case number(Int)

        case empty
    }
    enum SeriesID: Codable {
        case text(String)
        case number(Int)

        case empty
    }

    enum CodingKeys: String, CodingKey {
        case contentID = "content_id"
        case seriesID = "series_id"
        case rank = "rank"
        case score = "score"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        rank = try values.decode(Int.self, forKey: .rank)
        score = try values.decode(Float.self, forKey: .score)
        seriesID = try values.decode(SeriesID.self, forKey: .seriesID)
        contentID = try values.decode(ContentID.self, forKey: .contentID)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(score, forKey: .score)
        try container.encode(rank, forKey: .rank)
        try container.encode(seriesID, forKey: .seriesID)
        try container.encode(contentID, forKey: .contentID)
    }


    let rank: Int
    let score: Float
    let seriesID: SeriesID
    let contentID: ContentID

}

extension ContentItem.SeriesID: Equatable, Hashable {

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .text(let text):
            try container.encode(text)
        case .number(let number):
            try container.encode(number)
        case .empty:
            break
        }
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(self.hashValue)
    }


    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let text = try? container.decode(String.self) {
            self = .text(text)
        } else if let number = try? container.decode(Int.self) {
            self = .number(number)
        } else {
            //assertionFailure("Unknown id type")
            self = .empty
        }
    }

}

extension ContentItem.ContentID: Equatable, Hashable {

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case .text(let text):
            try container.encode(text)
        case .number(let number):
            try container.encode(number)
        case .empty:
            break
        }
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(self.hashValue)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let text = try? container.decode(String.self) {
            self = .text(text)
        } else if let number = try? container.decode(Int.self) {
            self = .number(number)
        } else {
//            assertionFailure("Unknown id type")
            self = .empty
        }
    }

}

我试过 var subscriptions = [AnyCancellable]() 似乎成功了。不确定它为什么起作用,但如果有人对此有解释,那么后代在这里添加会很好。