First time using Bolts and I can almost understand how they work, however I'm sure there has to be a cleaner way to do what I'm doing.
In my example, I want to download a JSON with categories, import them to Core Data, then download a JSON with ingredients and import them as well. Things need to happen in this order since my parser requires the Category
objects to exist already before parsing the Ingredient
objects.
I am using BoltsSwift in order to avoid the nested blocks so I made my "downloader" and "importer" return a Task
.
The network call (using Moya):
static func downloadJSON(target: MoyaAPI) -> BoltsSwift.Task<JSONArray> {
let taskCompletionSource = TaskCompletionSource<JSONArray>()
requestJSONArray(target, success: { (jsonArray) in
taskCompletionSource.set(result: jsonArray)
}, error: { (error) in
taskCompletionSource.set(error: error)
}, failure: { (moyaError) in
taskCompletionSource.set(error: moyaError)
})
return taskCompletionSource.task
}
The parsing part (using Groot):
func importCategories(from json: JSONArray) -> BoltsSwift.Task<[Category]> {
let taskCompletionSource = TaskCompletionSource<[Category]>()
do {
let categories: [Category] = try objects(fromJSONArray: json, inContext: managedObjectContext)
taskCompletionSource.set(result: categories)
} catch {
taskCompletionSource.set(error: error)
}
return taskCompletionSource.task
}
So later when I try to actually chain these 4 tasks together, I came up with this:
NetworkAdapter.downloadJSON(target: .categories).continueOnSuccessWithTask { (json) -> Task<[Category]> in
return importer.importCategories(from: json)
}.continueOnSuccessWith { (categories) in
return NetworkAdapter.downloadJSON(target: .ingredients).continueOnSuccessWithTask(continuation: { (json) -> Task<[Ingredient]> in
return importer.importIngredients(from: json)
})
}
This works but I have a feeling the syntax can be more readable. Can someone explain what is the proper way to chain these 4 tasks nicely?
You should be able to pull out the nested task following your second JSON download to the top level, making the code much cleaner:
NetworkAdapter.downloadJSON(target: .categories)
.continueOnSuccessWithTask { (json) in
return importer.importCategories(from: json)
}
.continueOnSuccessWithTask { (_) in
return NetworkAdapter.downloadJSON(target: .ingredients)
}
.continueOnSuccessWithTask { (json) in
return importer.importIngredients(from: json)
}
(If the compiler gets confused, you might have to re-add the return type declarations inside the closures.)
If the above works for you, you can further slim down your code using inline closures:
NetworkAdapter.downloadJSON(target: .categories)
.continueOnSuccessWithTask { importer.importCategories(from: $0) }
.continueOnSuccessWith { NetworkAdapter.downloadJSON(target: .ingredients) }
.continueOnSuccessWithTask { importer.importIngredients(from: $0) }