Convert [Future<Object,Error>] to Future<[Object],Error> - swift

Is there any way I can parse through the code below and return the value of it as an array Future<[Object],Error> . I'm using the BrightFutures future implementation
return apiService.getArrayObject()
.flatMap(NetworkQueue.context) { (arrayObjects: [ArrayObject]) -> Future<[Object], Error> in
let objects = arrayObjects.map {
apiService.getObject(of: $0.objectId)
}
return objects // ERROR since objects is [Future<Object,Error>] rather than needed output
}

Use sequence
return apiService.getArrayObject()
.flatMap(NetworkQueue.context) { (arrayObjects: [ArrayObject]) -> Future<[Object], Error> in
let objects: [Future<Object, Error>] = arrayObjects.map {
apiService.getObject(of: $0.objectId)
}
let sequenceFuture: Future<[Object], NoError> = objects
.sequence()
return sequenceFuture
}

Related

Asynchronous iteration using Swift Combine

I am trying to do multiple async operations, in sequence, on an array of data. However I am having problems with the return values of map.
Here is the test code:
import Combine
func getLength(_ string: String) -> Future<Int,Error> {
return Future<Int,Error>{ promise in
print("Length \(string.count)")
promise(.success(string.count))
}
}
func isEven(_ int: Int) -> Future<Bool,Error> {
return Future<Bool,Error>{ promise in
print("Even \(int % 2 == 0)")
promise(.success(int % 2 == 0))
}
}
let stringList = ["a","bbb","c","dddd"]
func testStrings(_ strings:ArraySlice<String>) -> Future<Void,Error> {
var remaining = strings
if let first = remaining.popFirst() {
return getLength(first).map{ length in
return isEven(length)
}.map{ even in
return testStrings(remaining)
}
} else {
return Future { promise in
promise(.success(()))
}
}
}
var storage = Set<AnyCancellable>()
testStrings(ArraySlice<String>(stringList)).sink { _ in } receiveValue: { _ in print("Done") }.store(in: &storage)
This generates the following error:
error: MyPlayground.playground:26:11: error: cannot convert return expression of type 'Publishers.Map<Future<Int, Error>, Future<Void, Error>>' to return type 'Future<Void, Error>'
}.map{ even in
I thought we could use map to convert from one publisher type to the other, but it seems it's wrapped inside a Publishers.Map. How do I get rid of this?
Thanks!
Well it seems that this works:
import Combine
func getLength(_ string: String) -> Future<Int,Error> {
return Future<Int,Error>{ promise in
print("Length \(string.count)")
promise(.success(string.count))
}
}
func isEven(_ int: Int) -> Future<Bool,Error> {
return Future<Bool,Error>{ promise in
print("Even \(int % 2 == 0)")
promise(.success(int % 2 == 0))
}
}
let stringList = ["a","bbb","c","dddd"]
func testStrings(_ strings:ArraySlice<String>) -> AnyPublisher<Void,Error> {
var remaining = strings
if let first = remaining.popFirst() {
return getLength(first).flatMap{ length in
return isEven(length)
}.flatMap{ even in
return testStrings(remaining)
}.eraseToAnyPublisher()
} else {
return Future<Void,Error> { promise in
promise(.success(()))
}.eraseToAnyPublisher()
}
}
var storage = Set<AnyCancellable>()
testStrings(ArraySlice<String>(stringList)).sink { _ in } receiveValue: { _ in print("Done") }.store(in: &storage)

Vapor Swift - Obtaining data from two Models

I have the next function on Vapor:
func getPartidosHandler(_ req: Request) throws -> Future<[PartidoWSData]> {
return Partido.query(on: req).filter(\.estado == nil).all().map(to: [PartidoWSData].self) { partidos in
var partidosWS: [PartidoWSData] = []
for partido in partidos {
// Something here
}
return partidosWS
}
}
And the next struct PartidoWSData:
struct PartidoWSData: Content {
let idPartido: String
let fecha: String
let sede1: Future<Sede>
let sede2: Future<Sede>
}
My model Partido has two references to Sede, "sede1" and "sede2".
What I want is that the function gives an array of PartidoWSData struct, where I can see two properties of "Partido", "idPartido" and "fecha", and the two Sede related to the model.
How can I do that?
Thanks!
I'm not sure exactly what type of relation exists between Partido and Sedebecause the model wasn't included here, but assuming it's a Parent/Child relation, you should be able to do something like:
func getPartidosHandler(_ req: Request) throws -> Future<[PartidoWSData]> {
return Partido.query(on: req).filter(\.estado == nil).all().flatMap { partidos -> Future<[PartidoWSData]> in
let partidoIDs = try partidos.map { try $0.requireID() }
return Sede.query(on: req).filter(\.partidoID ~~ partidoIDs).map { sedes -> [PartidoWSData] in
return partidos.map { partido -> PartidoWSData in
return PartidoWSData(
id: partido.id
sedes: sedes.filter { $0.partidoID == partido.id }
)
}
}
}
}
The key is using the ~~ operator to do an x IN (...) predicate, following by using Array.filter to get the appropriate results.

Swift 4 Using Generics as Return Value

I have a protocol like so:
protocol ModelProtocol{
func parse<T.Model>(data: Data) -> Array<T>? {}
}
The return is an array of option values. The method takes in data, parses it and returns the array of parsed objects from API data.
The type of data that the API returns is called MyData that has an array as the value of the dictionary.
I parse the JSON like so
func parse<T>(data: Data) -> Array<T>? {
do {
let newJSONDecoder = JSONDecoder()
let menu = try newJSONDecoder.decode(MyData.self, from:data)
let dataArray = menu.data //array
let modelArray = Array<T>()
for object in dataArray {
modelArray.append(object)
}
return modelArray
}
catch {
print("error while parsing:\(error.localizedDescription)")
return nil
}
}
I get error on the line where I append into the array to be returned.
Cannot invoke 'append' with an argument list of type '(MyData.Drinks)'
Ultimately I want the returned array to have objects of the type that is in the array MyData.data- in this case, the type is Drinks. But more broadly, I want the method to return any type that is in any JSON payload. The goal is to create a method that can take in any data and return any object as parsed object of type X in the array.
How do I do this?
First of all the code does not compile:
Protocol methods must not have bodies
so you have to remove the braces:
protocol ModelProtocol{
func parse<T : Decodable>(data: Data) -> Array<T>?
}
To solve your problem create MyData also as generic
struct MyData<T : Decodable> : Decodable {
let data : [T]?
}
and declare parse
func parse<T : Decodable>(data: Data) -> Array<T>? {
do {
let newJSONDecoder = JSONDecoder()
let menu = try newJSONDecoder.decode(MyData<T>.self, from:data)
return menu.data
}
catch {
print("error while parsing: ", error)
return nil
}
}
print always the entire error to get detailed information about the decoding error. localizedDescription is too broad.
If data is expected to be non-optional make parse throw and hand over the decoding error
protocol ModelProtocol{
func parse<T : Decodable>(data: Data) throws -> Array<T>
}
struct MyData<T : Decodable> : Decodable {
let data : [T]
}
func parse<T : Decodable>(data: Data) throws -> Array<T> {
let newJSONDecoder = JSONDecoder()
let menu = try newJSONDecoder.decode(MyData<T>.self, from:data)
return menu.data
}

Nested do catch swift 3.0

I'd like to use consecutive try statements. If one returns an error I'd like to proceed to the next one, otherwise return the value.
The code below seems to work fine, however I'll end up with a big nested do catch pyramid. Is there a smarter/better way to do it in Swift 3.0?
do {
return try firstThing()
} catch {
do {
return try secondThing()
} catch {
return try thirdThing()
}
}
If the actual errors thrown from those function calls are not needed
then you can use try? to convert the result to an optional,
and chain the calls with the nil-coalescing operator ??.
For example:
if let result = (try? firstThing()) ?? (try? secondThing()) ?? (try? thirdThing()) {
return result
} else {
// everything failed ...
}
Or, if the error from the last method should be thrown if everything fails,
use try? for all but the last method call:
return (try? firstThing()) ?? (try? secondThing()) ?? (try thirdThing())
If Martin's answer is too terse for your taste you can just go with individual catch blocks.
do {
return try firstThing()
} catch {}
do {
return try secondThing()
} catch {}
do {
return try thirdThing()
} catch {}
return defaultThing()
As each throwing function's result is immediately returned no nesting is necessary.
Another way to do this is to write a function that takes all your throwing functions as an argument. It returns the first one that was successfully executed or nil.
func first<T>(_ values: (() throws -> T)...) -> T? {
return values.lazy.flatMap({ (throwingFunc) -> T? in
return try? throwingFunc()
}).first
}
The lazy ensures that the values are only called until it finds the first match. Doing it this way, you can also add a lot of cases very quickly.
You can use the function like this
return first(firstThing, secondThing, thirdThing) ?? "Default"
I also included the code I used to test this in playground:
enum ThingError: Error {
case zero
}
func firstThing() throws -> String {
print("0")
throw ThingError.zero
return "0"
}
func secondThing() throws -> String {
print("1")
return "1"
}
func thirdThing() throws -> String {
print("2")
return "B"
}
func first<T>(_ values: (() throws -> T)...) -> T? {
return values.lazy.flatMap({ (throwingFunc) -> T? in
return try? throwingFunc()
}).first
}
func tryThings() -> String {
return first(firstThing, secondThing, thirdThing) ?? "Default"
}
tryThings() // prints "0" and "1"

Using Do/Catch in Swift

I am working on an app and want to get data back from a function. However sometimes data is missing or is different from the kind of that I want to retrieve. I am new to Swift and I can't find a way to write a function that does a little bit of processing and returns this data. When this data is missing, the function should give back a string "Not Found". Like this:
func processData(data:String) {
do {
//processing
var result = processedData
} catch {
var result = "Not Found"
}
return result
}
It would be very nice if somebody could help me.
You should check if the result is nil.
func processData(data: String?) -> String {
guard let result = data else {
return "Not Found"
  }
return result
}
The most concise way of doing it would be using the guard-let construct:
func processData(data: String?) -> String {
// Assuming getProcessedData(:) returns your processed data
guard let result = getProcessedData(data) else {
return "Not found"
}
return result
}
Also, your function is missing a return type. You must specify the return type like -> TYPE in all functions that return some value.
Those answer were written till mine are right. There is one way: with handler check get result and use by your point.
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?, completionHandler: #escaping (_ result: String? , _ error: Error?) -> Void ) {
guard let data = data else {
// Data is missing
throw nil, Errors.noData
}
// Do other things, and throw if necessary
result = data
return result, nil
}
// example of calling this function
process(data: "A data to process"){(result, error) -> Void in
//do any stuff
/*if error == nil {
}*/
}
A good practice in swift would be to use correctly the throws errors
This is an example inspired from yours :
enum Errors: Error {
case noData
case unknownError
}
func progress(data: String?) throws -> String {
guard let data = data else {
// Data is missing
throw Errors.noData
}
// Do other things, and throw if necessary
result = data
return result
}
do {
try process(data: "A data to process")
} catch {
print("An error occurred: \(error)")
}
You can try this code as is in a Swift Playgound
Your function needs to be explicit about returning something with e.g. -> String Also do-catch is for methods that can throw an error. It seems like you need to take a look at how to use optionals. Optionals can have a value or they can have no value.
fun processData(data: String) -> String {
var result: String?
// Do some processing and assign the result to result variable
guard let result = result else { return "Not Found" }
return result
}