Swift协议与关联类型(PAT)

时间:2016-07-05 17:01:25

标签: ios iphone swift-protocols

我想要实现的是等待所有服务调用完成。我知道可以使用GCD完成,但我正在寻找更多面向对象的方法。这是我到目前为止:

第一个服务应该通知代表完成,所以我们需要一个协议:

protocol ParallelServiceDelegate: class {
    func serviceDidCompleted()
}

服务是Alamofire的请求,我们的回复是:

enum ServiceResult {
    case Success(NSDictionary)
    case Failure(NSError)
}

我的设计是在这个方法上添加Facade(包装器)。这是摘要:

import ObjectMapper

protocol ParallelService: class {

    associatedtype ItemType: Mappable

    var item: ItemType? { get }
    var error: NSError? { get }

    var isCompleted: Bool { get }

    weak var delegate: ParallelServiceDelegate? { get set }

    // TODO: Pass params

    func start()
    func handleRequestCompletion(result: ServiceResult)
}

private var psiAssociationKey: UInt8 = 0
private var pseAssociationKey: UInt8 = 0
private var psdAssociationKey: UInt8 = 0

extension ParallelService {

    var item: ItemType? {
        return _item
    }

    var error: NSError? {
        return _error
    }

    var isCompleted: Bool {
        return item != nil || error != nil
    }

    weak var delegate: ParallelServiceDelegate? {
        get {
            let object = objc_getAssociatedObject(self, &psdAssociationKey)
            let wrapper = object as? WeakWrapper<ItemType?>
            return wrapper?.value as? ParallelServiceDelegate
        }
        set(newValue) {
            objc_setAssociatedObject(self,
                                     &psdAssociationKey,
                                     WeakWrapper(value: newValue),
                                     .OBJC_ASSOCIATION_RETAIN)
        }
    }

    func handleRequestCompletion(result: ServiceResult) {
        switch result {
        case .Success(let json):
            _item = map(json, object: ItemType.self)
        case .Failure(let error):
            _error = error
        }
    }

    // Degfault is nothing
    func start() {}

    // MARK: - Private

    private var _item: ItemType? {
        get {
            let object = objc_getAssociatedObject(self, &psiAssociationKey)
            let wrapper = object as? WeakWrapper<ItemType?>
            return wrapper?.value as? ItemType
        }
        set(newValue) {
            objc_setAssociatedObject(self,
                                     &psiAssociationKey,
                                     WeakWrapper(value: newValue),
                                     .OBJC_ASSOCIATION_RETAIN)
        }
    }

    private var _error: NSError? {
        get {
            let object = objc_getAssociatedObject(self, &pseAssociationKey)
            return object as? NSError
        }
        set(newValue) {
            objc_setAssociatedObject(self,
                                     &pseAssociationKey,
                                     newValue,
                                     .OBJC_ASSOCIATION_RETAIN)
        }
    }

}

具体服务门面实施:

class EmployeesParallelService: ParallelService {

    typealias ItemType = Employee

    func start() {
        Service.emploeesList(callback: handleRequestCompletion)
    }

}

class InformationParallelService: ParallelService {

    typealias ItemType = Information

    func start() {
        Service.information(callback: handleRequestCompletion)
    }

}

服务呼叫者 - 对它刚刚启动的所有服务一无所知,并等待它们完成:

class ParallelServiceCaller {

    private var services: [ParallelService] = []

    // MARK: - Lifecycle

    func addParallelService(service: ParallelService) {
        service.delegate = self
        self.services.append(service)
    }

    // MARK: - Public

    func start() {
        for service in services {
            service.start()
        }
    }

}

extension ParallelServiceCaller: ParallelServiceDelegate {

    func serviceDidCompleted() {
        for service in services {
            // !!! wait
            if !service.isCompleted {
                return
            }
        }

        // TODO: Notify delegate
    }

}

后期我想这样使用它:

let caller = ParallelServiceCaller()
caller.addParallelService(EmployeesParallelService())
caller.addParallelService(InformationParallelService())
caller.start()

但是我在ParallelServiceCaller类的实现中遇到了问题。我收到以下错误:

  

协议'ParallelService'只能用作通用约束   因为它有自我或相关的类型要求

知道如何避免此错误吗?

更新07/07/16:

我仍然无法理解如何使用PAT。但是我采取了稍微不同的方法,现在我使用访客模式。这是我的游乐场代码,它可能对某人有帮助:

//: Playground - noun: a place where people can play

import UIKit

// Mocks

enum ServiceResult {
    case Success(NSDictionary)
    case Failure(NSError)
}

protocol Mappable { }

typealias CompletionHandlerType = (result: ServiceResult) -> Void

class Service {
    class func emploeesList(start: Int? = nil,
                            max: Int? = nil,
                            timestamp: Int? = nil,
                            callback: CompletionHandlerType) {
        callback(result: .Success(NSDictionary()))
    }

    class func information(timestamp: Int? = nil,
                           callback: CompletionHandlerType) {
        callback(result: .Failure(NSError(domain: "", code: 1, userInfo: nil)))
    }
}

class EmployeesList: Mappable {}
class Information: Mappable {}

// Actual Implementation

// Visitor
protocol ParallelServiceCallerProtocol {
    func call(service: EmployeesListParallelService)
    func call(service: InformationParallelService)
}

// Element
protocol ParallelServiceProtocol {
    func start(visitor: ParallelServiceCallerProtocol)
}

// Concrete Elements
class EmployeesListParallelService: ParallelServiceProtocol {
    func start(visitor: ParallelServiceCallerProtocol) { visitor.call(self) }
}

class InformationParallelService: ParallelServiceProtocol {
    func start(visitor: ParallelServiceCallerProtocol) { visitor.call(self) }
}

// Concrete Visitor Delegate - defines callback for async tasks
protocol ParallelServiceCallerDelegateProtocol: class {
    func didCompleteParellelServiceWithResult(service: ParallelServiceProtocol, result: ServiceResult)
}

// Concrete Visitor - make API calls
class ParallelServiceCaller <T: ParallelServiceCallerDelegateProtocol>: ParallelServiceCallerProtocol {
    private unowned let delegate: T

    init(delegate: T) {
        self.delegate = delegate
    }

    func call(service: EmployeesListParallelService) {
        Service.emploeesList { [unowned self] (result) in
            self.delegate.didCompleteParellelServiceWithResult(service, result: result)
        }
    }

    func call(service: InformationParallelService) {
        Service.information { (result) in
            self.delegate.didCompleteParellelServiceWithResult(service, result: result)
        }
    }
}

// Service Result In Context

enum SynchronizationServiceResult {
    case Employees(ServiceResult)
    case Information(ServiceResult)
}

// Concrete Visitor - Wraps API Result And Gives Context

class ParallelServiceParser: ParallelServiceCallerProtocol {
    var result: SynchronizationServiceResult?

    private let serviceResult: ServiceResult

    init(serviceResult: ServiceResult) {
        self.serviceResult = serviceResult
    }

    func call(service: EmployeesListParallelService) {
        result = .Employees(serviceResult)
    }

    func call(service: InformationParallelService) {
        result = .Information(serviceResult)
    }
}

// Delegate that notifies for completion of all calls

protocol ParallelServiceManagerDelegateProtocol: class {
    func didCompleteAllServicesWithResults(results: [SynchronizationServiceResult])
}

// Manager - starts all calls and adds context to returned results - knows nothing about calls

class ParallelServiceManager<T where T: ParallelServiceManagerDelegateProtocol> {
    private let services: [ParallelServiceProtocol]
    private unowned let delegate: T
    // Keep Caller Visitors in Memory or they will be dealocated
    private var callers: [ParallelServiceCaller<ParallelServiceManager>] = []

    private var completed: [SynchronizationServiceResult] = [] {
        didSet {
            if completed.count == services.count {
                self.delegate.didCompleteAllServicesWithResults(completed)
                self.callers.removeAll()
            }
        }
    }

    init(services: [ParallelServiceProtocol], delegate: T) {
        self.services = services
        self.delegate = delegate
    }

    func start() {
        visitAllServices { (service) in
            let caller =
                ParallelServiceCaller<ParallelServiceManager>(delegate: self)
            service.start(caller)
            self.callers.append(caller)
        }
    }

    private func visitAllServices(perform: ParallelServiceProtocol -> () ) {
        for service in self.services {
            perform(service)
        }
    }
}

extension ParallelServiceManager: ParallelServiceCallerDelegateProtocol {
    func didCompleteParellelServiceWithResult(service: ParallelServiceProtocol,
                                              result: ServiceResult) {
        // No need to persist parser visitor
        let caller = ParallelServiceParser(serviceResult: result)
        service.start(caller)
        completed.append(caller.result!)
    }
}

// Example Usage

class SynchronizationService {
    private lazy var services: [ParallelServiceProtocol] = {
        return [EmployeesListParallelService(), InformationParallelService()]
    }()

    func start() {
        let manager = ParallelServiceManager<SynchronizationService>(services: services, delegate: self)
        manager.start()
    }
}

extension SynchronizationService: ParallelServiceManagerDelegateProtocol {
    func didCompleteAllServicesWithResults(results: [SynchronizationServiceResult]) {
        for result in results {
            switch result {
            case .Employees(let result):
                // TODO:
                print("\(result)") // Should Return Success
            case .Information(let result):
                // TODO:
                print("\(result)") // Should Return Failure
            }
        }
    }
}

let sync = SynchronizationService()
sync.start()

0 个答案:

没有答案