What argument to pass in place of (String) -> void? - swift

Im trying to fix this from a very long time.
I can not understand what to pass to this function in place of "(String) -> void" as it was supposed to return a string :
var result = myobj.createData(request: request, with: (String) -> void)
The above code is calling the following function:
func createData(request:Crudpb_CreateRequest, with completion: #escaping (String) -> Void) {
DispatchQueue.main.async {
self.response = try! self.client.create(request)
completion(self.response.result)
}
}

When you call this function you need to pass a closure with the type (String) -> void
myobj.createData(request: request, with: { string in
print(string)
})
Or
var completion = { string in
print(string)
}
myobj.createData(request: request, with: completion)
You can store the result like this
var result = ""
myobj.createData(request: request, with: { string in
result = string
self.displayTextArea.text = result
print(result)
})

Related

Use of flatMap on a generic Publisher results in a compile error

I'm writing a transform function that would take network request results and try to parse them automatically using a dict to Model transformer(not Decodable due to several backend reasons).
So the chain should look like this:
func getModel -> Single<Model> {
return networkRequest(requestParameters).parse(modelTranslator)
}
The translator is a generic protocol:
public protocol Translator {
associatedtype Model
func translateFrom(dictionary json: [String: Any]) throws -> Model
}
Single is a wrapper around Deferred and Future:
public typealias Single<T> = Deferred<Future<T, Error>>
The problematic parse extension method here is:
public extension Publisher {
func parse<T: Translator, M>(translator: T) -> Single<M> where T.Model == M {
return self.flatMap { (data: Data) -> Single<M> in
return Deferred {
return Future<M, any Error> { promise in
guard
let json = try? JSONSerialization.jsonObject(with: data, options: []),
let dict = json as? [String : Any]
else {
let error: any Error = TranslatorError.invalidJSONObject
return promise(Result.failure(error))
}
do {
let translatedModel: M = translator.translateFrom(dictionary: dict)
return promise(Result.success(translatedModel))
} catch let error {
return promise(Result.failure(error))
}
}
}
}
}
}
It won't compile. It shows 2 errors on the .flatmap row:
No 'flatMap' candidates produce the expected contextual result type 'Single' (aka 'Deferred<Future<M, any Error>>')
No exact matches in call to instance method 'flatMap'
I believe that it has something to do with a type mismatch?
Could you please help me see the problem?
Thank you in advance!
You are trying too hard. A simple tryMap is all you need to parse your [String: Any] into the appropriate model type. Here is a complete example:
func getFoo(_ requestParameters: RequestParameters) -> AnyPublisher<Foo, Error> {
getModel(requestParameters, modelTranslator: FooTranslator())
}
func getModel<T>(_ requestParameters: RequestParameters, modelTranslator: T) -> AnyPublisher<T.Model, Error> where T: Translator {
networkRequest(requestParameters)
.tryMap { try modelTranslator.translateFrom(dictionary: $0) }
.eraseToAnyPublisher()
}
The above assumes the following declarations:
func networkRequest(_ params: RequestParameters) -> Single<[String: Any]> ...
struct FooTranslator: Translator {
func translateFrom(dictionary json: [String : Any]) throws -> Foo ...
}

Store Swift closures and cast them back to their original type

I'm trying to create a class that can save different closures (or methods) with an argument of a specific subtype of Decodable that should be called later. This way I can predefine what actions, or methods, can be called on that class in response to some input. For example, the line addCallback(setOption(_:), SetOptionRequest.self) should result in the subsequent call to try! performCallback("setOption", JSONEncoder().encode(SetOptionRequest()) to call setOption(data) where the argument data has type SetOptionRequest.
Here is the code I have so far (I took the bit about DecodableWrapper from here). The problem is that at runtime the cast callback.callback as! (ActionRequest) throws -> Void fails, since the type of the closure is not (ActionRequest) throws -> Void but (SetOptionRequest) throws -> Void. But I have no idea if and how I can cast the closure back to its original type. I considered using Selectors but I would like to keep the compile-time check that I'm binding methods with their correct argument type.
struct DecodableWrapper: Decodable {
static var baseType: ActionRequest.Type!
var base: ActionRequest
init(from decoder: Decoder) throws {
self.base = try DecodableWrapper.baseType.init(from: decoder)
}
}
open class Server {
private var actionCallbacks = [String: (callback: Any, dataType: ActionRequest.Type)]()
open func setup() {
addCallback(setOption, action: SetOptionRequestResponse.self)
}
public func addCallback<T: ActionRequest>(_ callback: #escaping (_ data: T) throws -> Void, action: T.Type) {
actionCallbacks[T.action] = (callback, T.self)
}
private func performCallback(action: String, data: Data) throws {
let callback = actionCallbacks[action]!
DecodableWrapper.baseType = callback.dataType
let data = try! JSONDecoder().decode(DecodableWrapper.self, from: data).base
try (callback.callback as! (ActionRequest) throws -> Void)(data)
}
private func setOption(_ data: SetOptionRequest) {
}
}
protocol ActionRequest {
static var action: String
}
struct Request: SetOptionRequest {
}
Thanks to this article, I came up with a solution. The trick is to store a custom closure that always accepts the same argument type (in this case Data), does whathever needs to be done with the generic type T and then calls the nested closure.
open class Server {
private var actionCallbacks = [String: (Data) throws -> Void]()
open func setup() {
addCallback(setOption)
}
public func addCallback<T: ActionRequest>(_ callback: #escaping (_ data: T) throws -> Void) {
actionCallbacks[T.action] = { data in
let data = try JSONDecoder().decode(T.self, from: data)
try callback(data)
}
}
private func performCallback(action: String, data: Data) throws {
if let callback = actionCallbacks[action] {
try callback(data)
}
}
private func setOption(_ data: SetOptionRequest) {
}
}
public protocol ActionRequest: Codable {
static var action: String { get }
}
struct SetOptionRequest: ActionRequest {
static var action = "setOption"
}

How to return a failure inside .map function in a Result

I have a method execute that calls an external API with a callback that receives Result<Data?,Error>. How can I map that optional success to an unwrapped result or an Error?
func execute(then handle: #escaping (Result<Data, Error>) -> Void) {
externalAPI.retrieveData { result in
let mappedResult = result
.map {
guard let data = $0 else {
throw NSError(domain: "", code: 0, description: "error")
}
return data
}
handle(mappedResult)
}
}
This code fails with Invalid conversion from throwing function of type '(Optional<Data>) throws -> _' to non-throwing function type '(Data?) -> NewSuccess'
I was able to do this with a simple switch (below), but I was wondering if throwing a failure inside the .map is possible.
func execute(then handle: #escaping (Result<Data, Error>) -> Void) {
externalAPI.retrieveData { result in
switch result {
case .failure(let error):
handle(.failure(error))
case .success(let data):
guard let data = data else {
handle(.failure(NSError(domain: "", code: 0, description: "error")))
return
}
handle(.success(data))
}
}
}
You can convert between throws functions and functions that return Result<Success, Error> by using Result(catching:) and .get().
Here's your original map call:
.map {
guard let data = $0 else {
throw NSError(domain: "", code: 0, description: "error")
}
return data
}
Result.map takes a Result and a function that converts (Success) -> NewSuccess, and returns a Result<NewSuccess, Failure>.
Your map takes a Data (Success), and returns Result<Data, Error> (NewSuccess). So the final type, by plugging in NewSuccess is: Result<Result<Data, Error>, Error>. That's more layers than you want. You want to flatten that to just Result<Data, Error>, and that's where flatMap comes in.
Your answer shows that, but you can also pull this out into a more general-purpose tool. It only works when Failure == Error, because throws is untyped, so you can't limit it to some subset of errors. But that's what you're doing anyway. Here's tryMap:
extension Result where Failure == Error {
func tryMap<NewSuccess>(_ transform: (Success) throws -> NewSuccess) -> Result<NewSuccess, Error> {
self.flatMap { value in
Result<NewSuccess, Error> { try transform(value) }
}
}
}
With that, you can rewrite this as:
func execute(then handle: #escaping (Result<Data, Error>) -> Void) {
externalAPI.retrieveData { result in
handle(result
.tryMap {
guard let data = $0 else {
throw NSError(domain: "", code: 0, description: "error")
}
return data
})
}
}
That said, I'd probably be tempted to write it this way:
func execute(then handle: #escaping (Result<Data, Error>) -> Void) {
externalAPI.retrieveData { result in
handle(result
.flatMap { maybeData in
maybeData.map(Result.success)
?? .failure(NSError(domain: "", code: 0, description: "error"))
})
}
}
Or if I wanted someone to be able to actually read it later:
func execute(then handle: #escaping (Result<Data, Error>) -> Void) {
externalAPI.retrieveData { result in
handle(result
.flatMap {
switch $0 {
case .some(let data): return .success(data)
case .none: return .failure(NSError(domain: "", code: 0, description: "error"))
}
}
)
}
}
The advantage of this switch over yours is that you don't have to unwrap and rewrap previous failures.
Apparently, this can be done using flatmap. So in my case:
func execute(then handle: #escaping (Result<Data, Error>) -> Void) {
externalAPI.retrieveData { result in
let mappedResult = result
.flatMap { data in
Result<Data, Error> {
guard let data = data else {
throw NSError(domain: "", code: 0, description: "error")
}
return data
}
}
handle(mappedResult)
}
}
It's a little confusing, but it is working for me.

Getting non--void error on function return [duplicate]

This question already has answers here:
Unexpected Non-Void Return Value In Void Function (Swift 2.0)
(2 answers)
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I'm getting this error and I know this problem has been addressed on here before by people not adding the return -> to the function. I do not understand why this is still giving me error.
Unexpected non-void return value in void function
I'm trying to return a String called message.
func ParseIt(proURL: String, startStr: String, stopStr: String) -> String {
let url = URL(string: "https://www.siteimfetchingfrom.com/827444000973")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error)
} else {
let htmlContent = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
//print(htmlContent)
// Get all Product Info
//var proName = "id=\"productName\" value=\""
if let contentArray = htmlContent?.components(separatedBy: startStr) {
//print(contentArray)
if contentArray.count > 0 {
//proName = "\" required"
let newContentArray = contentArray[1].components(separatedBy: stopStr)
if newContentArray.count > 0 {
let message = newContentArray[0]
//print(newContentArray)
print(newContentArray[0])
return message // Error happens Here
}
}
}
}
}
task.resume()
}
The line return message is written inside of a closure. A return statement written inside a closure will return from the closure, not the surrounding function.
Seeing how you are performing a web request and getting the response, you should have a completion handler instead of a return. You can't immediately return a string from ParseIt because the request will take time.
// notice the extra completion parameter and the removal of the return type
func ParseIt(proURL: String, startStr: String, stopStr: String, completion: #escaping (String) -> Void) {
let url = URL(string: "https://www.siteimfetchingfrom.com/827444000973")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
...
// replace the return statement with this:
completion(message)
}
task.resume()
}
You can call it like this:
ParseIt(proURL: ..., startStr: ..., stopStr: ...) {
result in
// do something with "result"
}
Look carefully where your return statement belongs. It's not returning from ParseInt, it's actually returning from the completion closure passed to URLSession.shared.dataTask. The return type of that completion handler is void.
func dataTask(with request: URLRequest, completionHandler: #escaping
(Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask

Trailing Closures on generics?

Hi I am trying to understand the following code from Alamofire. How can you initialise a struct with "{}" I know that you can call a closure with Trailing Closures. I know I am totally missing something, but what?
extension Request {
public func responseObject<T: ResponseObjectSerializable>(completionHandler: Response<T, NSError> -> Void) -> Self {
let responseSerializer = ResponseSerializer<T, NSError> { // What is this?
request, response, data, error in
guard error == nil else { return .Failure(error!) }
let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let result = JSONResponseSerializer.serializeResponse(request, response, data, error)
switch result {
case .Success(let value):
if let
response = response,
responseObject = T(response: response, representation: value)
{
return .Success(responseObject)
} else {
let failureReason = "JSON could not be serialized into response object: \(value)"
let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
return .Failure(error)
}
case .Failure(let error):
return .Failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
The struct ResponseSerializer from Alamofire
public struct ResponseSerializer<Value, Error: ErrorType>: ResponseSerializerType {
/// The type of serialized object to be created by this `ResponseSerializer`.
public typealias SerializedObject = Value
/// The type of error to be created by this `ResponseSerializer` if serialization fails.
public typealias ErrorObject = Error
/**
A closure used by response handlers that takes a request, response, data and error and returns a result.
*/
public var serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>
/**
Initializes the `ResponseSerializer` instance with the given serialize response closure.
- parameter serializeResponse: The closure used to serialize the response.
- returns: The new generic response serializer instance.
*/
public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>) {
self.serializeResponse = serializeResponse
}
}
Your question can be greatly pared down (and should have been). Here is the relevant declaration of ResponseSerializer:
public struct ResponseSerializer<Value, Error: ErrorType>: ResponseSerializerType {
public init(serializeResponse: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Result<Value, Error>) {
self.serializeResponse = serializeResponse
}
}
So this initializer, init(serializeResponse:), takes one parameter — a function taking four parameters and returning one parameter (of the specified types).
Thus, we can initialize like this:
func f (request:NSURLRequest?, response:NSHTTPURLResponse?, data:NSData?, error:NSError?) -> Result<Value, Error>) {
guard error == nil else { return .Failure(error!)
}
let responseSerializer = ResponseSerializer<T, NSError>(serializeResponse:f)
However, this can be condensed. We don't really need the function f for anything else, so we can supply it as an anonymous function; it doesn't need a name or a full declaration. Moreover, there is a "shortcut" rule for anonymous functions, that if an anonymous function is the last parameter to a function, it can be provided literally after the function's closing parentheses, with the parameter name omitted. And if the function takes no other parameters, its parentheses can be omitted altogether.
Well, this init is exactly such a function — it takes a function as its last (and only) parameter — so that is exactly what the code in question does:
let responseSerializer = ResponseSerializer<T, NSError> {
request, response, data, error in
guard error == nil else { return .Failure(error!)
}
If I read it all correctly the code above has a pattern similar to this:
// just a something
struct Blah {
var stuffs : (message:String) -> Void
init(closure:(message:String) -> Void) {
self.stuffs = closure
}
}
// an extension because the code above is also in an extension, but not needed at all
extension Blah {
// a function with a closure that also returns an instance of Self
func spawnChild(closure:(message:String) -> Void) -> Blah {
return Blah(closure: closure) // closure is passed to spawn
}
}
Tests :
let alpha = Blah { (message) -> Void in
print("alpha",message)
}
let beta = alpha.spawnChild { (message) -> Void in
print("beta", message)
}
alpha.stuffs(message: "parrent") // alpha parent
beta.stuffs(message: "spawn") // beta spawn
Remember that the trailing closure is just syntactic sugar for an input parameter that is a closure. So anything that takes input parameters can have a trailing closure.