Swift 成功与失败的方法链接
Swift Method Chaining with Success & Failure
我正在尝试在我的代码中实现成功和失败调用的方法链,但我似乎无法实际调用 onSuccess
方法。
- 视图控制器调用
getProduct(_:)
函数。
getProduct(_:)
进行 API 调用,然后使用检索到的 json 调用 storeProduct(_:)
storeProduct(_:)
呼叫 fetchProduct(_:)
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
}
我最初为每个函数初始化了可链接的 class 但这有它自己的问题,我认为(可能是错误的)我应该只初始化一次可链接的 class 并传递它的引用。
关于我要去的地方的一些输入wrong/what我可以尝试下一步会很棒。
根据@john elements的推荐,我决定使用PromiseKit
这不需要太多的代码更改,下面是函数现在的样子(仍然需要做一些代码清理,但它有效!):
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)
}
}
}
我正在尝试在我的代码中实现成功和失败调用的方法链,但我似乎无法实际调用 onSuccess
方法。
- 视图控制器调用
getProduct(_:)
函数。 getProduct(_:)
进行 API 调用,然后使用检索到的 json 调用 storeProduct(_:)
呼叫fetchProduct(_:)
fetchProduct(_:)
调用doSuccess(_:)
但这永远不会回到之前调用的onSuccess
。
storeProduct(_:)
一些代码片段
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
}
我最初为每个函数初始化了可链接的 class 但这有它自己的问题,我认为(可能是错误的)我应该只初始化一次可链接的 class 并传递它的引用。
关于我要去的地方的一些输入wrong/what我可以尝试下一步会很棒。
根据@john elements的推荐,我决定使用PromiseKit
这不需要太多的代码更改,下面是函数现在的样子(仍然需要做一些代码清理,但它有效!):
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)
}
}
}