ios - 成功与失败的 Swift 方法链接

标签 ios swift method-chaining

我正在尝试在我的代码中实现成功和失败调用的方法链,但我似乎无法获得 onSuccess。实际调用的方法。

  1. View Controller 调用 getProduct(_:)功能。
  2. getProduct(_:)进行 API 调用,然后调用 storeProduct(_:)使用检索到的 json
  3. storeProduct(_:)电话 fetchProduct(_:)
  4. fetchProduct(_:)电话 doSuccess(_:)但这永远不会回到 onSuccess以前的电话。

一些代码片段

BSProductChainable.swift

import Foundation

class BSProductChainable<SuccessParams, FailureParams> {

    var successClosure: ((SuccessParams) -> ())? = nil
    var failureClosure: ((FailureParams) -> ())? = nil

    func onSuccess(closure: (SuccessParams) -> ()) -> BSProductChainable {
        successClosure = closure
        return self
    }
    func onFailure(closure: (FailureParams) -> ()) -> BSProductChainable {
        failureClosure = closure
        return self
    }
    func doSuccess(params: SuccessParams) {
        if let closure = successClosure {
            closure(params)
        }
    }
    func doFailure(params: FailureParams) {
        if let closure = failureClosure {
            closure(params)
        }
    }
}

BSProductManagerSwift.swift

class BSProductManagerSwift: NSObject {

typealias productResponseChain = BSProductChainable<Product, NSError?>
typealias productsResponseChain = BSProductChainable<[Product], NSError?>

var serviceClient: BSNetworkingServiceClient!
var objectContext: NSManagedObjectContext!
var productChains: BSProductChainable<Product, NSError?>!
var productsChains: BSProductChainable<[Product], NSError?>!

convenience init(serviceClient: BSNetworkingServiceClient) {
    self.init()
    self.serviceClient = serviceClient
    self.objectContext = managedObjectContext
    self.productChains = BSProductChainable<Product, NSError?>()
    self.productsChains = BSProductChainable<[Product], NSError?>()
}

func getProduct(ean: String) -> productResponseChain {

    let urlString = BSConstants.BarcodeScanner.productEndpoint.stringByAppendingString(ean)
    serviceClient.GET(urlString, failure: { (error) in
        print("Could not get product")
    }) { (response) in
        if let json = response {
            self.storeProduct(json).onSuccess({ (returedProduct) in
                print("Stored product")
            })
        }
    }

    return productChains
}

func storeProduct(json: JSON) -> productResponseChain {

    fetchProduct(json["ean"].stringValue).onSuccess { (returedProduct) in
        self.productChains.doSuccess(returedProduct)
    }

    return productChains
}

func fetchProduct(ean: String) -> productResponseChain {

    let fetchRequest = NSFetchRequest(entityName: "Product")
    let predicateEAN = NSPredicate(format: "%K == %@", "ean", ean)
    let predicateMarket = NSPredicate(format: "%K == %@", "market", BSCountryManager.sharedInstance().getCurrentCountry().market)
    let predicateLocale = NSPredicate(format: "%K == %@", "locale", BSLocalizationManager.sharedManager().currentLocalization.localeIdentifier())
    let predicateCurrency = NSPredicate(format: "%K == %@", "currency", BSLocalizationManager.sharedManager().currentLocalization.country.currencyIdentifierDMW)
    let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateEAN, predicateMarket, predicateLocale, predicateCurrency])
    fetchRequest.predicate = compoundPredicate

    do {
        let matchingProuducts = try objectContext.executeFetchRequest(fetchRequest)

        if matchingProuducts.count == 0 {
            print("No matching products found")
            let entity = NSEntityDescription.entityForName("Product", inManagedObjectContext: objectContext)
            productChains.doSuccess(Product(entity: entity!, insertIntoManagedObjectContext: objectContext))
        } else {
            print("Found matching product")
            let d = matchingProuducts.first as! Product
            productChains.doSuccess(d)
        }
    } catch let error as NSError {
        print("Could not fetch \(error), \(error.userInfo)")
        productChains.doFailure(error)
    }

    return productChains
}

我最初为每个函数初始化了可链接的类,但这有其自身的问题,我认为(可能是错误的)我应该只初始化一次可链接的类并传递它的引用。

关于我出错的地方/我接下来可以尝试什么的一些输入会很棒。

最佳答案

根据@john elements 的推荐,我决定使用 PromiseKit

这不需要太多的代码更改,下面是函数现在的样子(仍然需要做一些代码清理,但它可以工作!):<​​/p>

func getProduct(ean: String) -> Promise<Product> {
    return Promise { fullfill, reject in
        let urlString = BSConstants.BarcodeScanner.productEndpoint.stringByAppendingString(ean)
        serviceClient.GET(urlString, failure: { (error) in
            reject(error!)
        }) { (response) in
            if let json = response {
                self.storeProduct(json).then ({ returnedProduct in
                    print("We stored the product: \(returnedProduct.ean)")
                    fullfill(returnedProduct)
                }).error { returnedError in
                    print("We had a problem storing the product: \(returnedError)")
                }
            }
        }
    }
}

func storeProduct(json: JSON) -> Promise<Product> {
    return Promise { fullfill, reject in
        fetchProduct(json["ean"].stringValue).then ({ returnedProduct in

            var storedProduct: Product!
            var isNewProduct = false

            print("Fetched Product: \(returnedProduct.ean)")

            isNewProduct = returnedProduct.valueForKey("ean") == nil
            storedProduct = returnedProduct
            storedProduct.setValue(json["name"].stringValue, forKey: "name")
            storedProduct.setValue(json["ean"].stringValue, forKey: "ean")
            storedProduct.setValue(json["image"].stringValue, forKey: "image")
            storedProduct.setValue(json["price"].doubleValue, forKey: "price")
            storedProduct.setValue(json["status"].intValue, forKey: "status")
            storedProduct.setValue(json["pdp"].stringValue, forKey: "pdp")
            storedProduct.setValue(BSCountryManager.sharedInstance().getCurrentCountry().market, forKey: "market")
            storedProduct.setValue(BSLocalizationManager.sharedManager().currentLocalization.localeIdentifier(), forKey: "locale")
            storedProduct.setValue(BSLocalizationManager.sharedManager().currentLocalization.country.currencyIdentifierDMW, forKey: "currency")

            do {
                try self.objectContext.save()
                print("Stored Product: \(returnedProduct.ean)")
                fullfill(returnedProduct)

                if isNewProduct {
                    NSNotificationCenter.defaultCenter().postNotificationName("DidAddScanEntry", object: nil)
                }

            } catch let error as NSError {
                print("Could not save \(error), \(error.userInfo)")
                reject(error)
            }

        }).error { returnedError in
            print("We had a problem fetching the product: \(returnedError)")
            reject(returnedError)
        }
    }
}

func fetchProduct(ean: String) -> Promise<Product> {
    return Promise { fullfill, reject in

        let fetchRequest = NSFetchRequest(entityName: "Product")
        let predicateEAN = NSPredicate(format: "%K == %@", "ean", ean)
        let predicateMarket = NSPredicate(format: "%K == %@", "market", BSCountryManager.sharedInstance().getCurrentCountry().market)
        let predicateLocale = NSPredicate(format: "%K == %@", "locale", BSLocalizationManager.sharedManager().currentLocalization.localeIdentifier())
        let predicateCurrency = NSPredicate(format: "%K == %@", "currency", BSLocalizationManager.sharedManager().currentLocalization.country.currencyIdentifierDMW)
        let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateEAN, predicateMarket, predicateLocale, predicateCurrency])
        fetchRequest.predicate = compoundPredicate

        do {
            let matchingProuducts = try objectContext.executeFetchRequest(fetchRequest)

            if matchingProuducts.count == 0 {
                print("No matching products found")
                let entity = NSEntityDescription.entityForName("Product", inManagedObjectContext: objectContext)
                fullfill(Product(entity: entity!, insertIntoManagedObjectContext: objectContext))
            } else {
                print("Found matching product")
                let d = matchingProuducts.first as! Product
                fullfill(d)
            }
        } catch let error as NSError {
            print("Could not fetch \(error), \(error.userInfo)")
            reject(error)
        }
    }
}

关于ios - 成功与失败的 Swift 方法链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38832681/

相关文章:

ios - swift :NSAttributedString 和表情符号

javascript - 如何编写供本地使用的 jquery 可链接函数?

javascript - 与子方法的方法链接

javascript - 如何在维护命名空间的同时创建类似 jQuery 的方法链?

如果选中编译,iOS MPMediaQuery.artistsQuery() 不会返回每个艺术家

ios - UICollectionView 和 MVVM

ios - UITabBarController 等待加载 UIViewController 直到选择 Xamarin

ios - 如何更改边框自定义边的颜色?

ios - 从 AppDelegate 调用 ViewController 的成员函数

ios - NSFetchedResultsController 复杂的获取请求和模型