简体   繁体   English

快速方法链成功与失败

[英]Swift Method Chaining with Success & Failure

I am trying to implement method chaining for success and failure calls in my code but I seem to be having trouble getting the onSuccess methods to actually be called. 我正在尝试为代码中的成功和失败调用实现方法链接,但是我似乎很难让onSuccess方法真正被调用。

  1. A view controller calls the getProduct(_:) function. 视图控制器调用getProduct(_:)函数。
  2. getProduct(_:) makes an API call and then calls storeProduct(_:) with the retrieved json getProduct(_:)进行API调用,然后使用检索到的json调用storeProduct(_:)
  3. storeProduct(_:) calls fetchProduct(_:) storeProduct(_:)调用fetchProduct(_:)
  4. fetchProduct(_:) calls doSuccess(_:) but this never gets back into the onSuccess of the previous calls. fetchProduct(_:)调用doSuccess(_:)但这永远不会返回到先前调用的onSuccess中。

Some Code Snippets 一些代码片段

BSProductChainable.swift 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 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
}

I initially initialised the chainable class per function but this had its own issues from which I thought (possibly incorrectly) that I should only initialise the chainable class once and pass around its reference. 我最初初始化每个函数的可链接类,但是这有其自身的问题,我认为(可能是错误地)我应该只初始化一次可链接类并传递其引用。

Some input as to where I am going wrong/what I could try next would be great. 关于我要去哪里的错误/下一步可以尝试的一些输入非常棒。

As recommended by @john elements, I decided to use PromiseKit 根据@john elements的建议,我决定使用PromiseKit

This didn't require to much of a code change and here are what the functions now look like (still need to do a bit of a code cleanup but it works!): 这不需要进行太多的代码更改,下面是函数的外观(仍然需要进行一些代码清除,但它可以工作!):

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)
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Swift - 方法链接 - Swift - Method chaining Swift 完成块,带有成功和失败处理程序 - Swift completion block with success and failure handler 用目标C编写的成功与失败块,迅速显示错误 - Success & failure block written in Objective C, showing error in swift 如何修复 swift 中成功和失败案例的 Alamofire 5 错误? - How to fix the Alamofire 5 error for success and failure cases in swift? RESTKit-postObject:path:parameters:success:failure:方法总是失败 - RESTKit - postObject:path:parameters:success:failure: method always fails 在方法中包装AFNetworking的成功失败块 - Wrapping success failure blocks from AFNetworking within a method 如何模拟AFHTTPClient类和测试方法getPath:parameters:success:failure :? - How to mock AFHTTPClient class and test method getPath:parameters:success:failure:? 使用BFTask链接调用成功/错误 - Calling success/error with BFTask chaining 如何设置成功/失败块,以便可以使用Swift将字符串传递给它? - How do I set up a success/failure block so that I can pass a string into it using Swift? Swift 5:成功/失败块中的错误“'catch' 块无法访问,因为 'do' 块中没有引发错误” - Swift 5: Error in success/failure block "'catch' block is unreachable because no errors are thrown in 'do' block"
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM