Swift callback inline function usage - swift

Using Swift I can do a callback like the following:
userService.validateToken("6beba35f", success: onSuccess, failure: onFailure)
func onSuccess(status_code: Int, data: String)
{
var dd : String = ""
}
func onFailure(status_code: Int, data: String)
{
var dd : String = ""
}
but i would like to have the functions on the same line as the call:
Example 1:
userService.validateToken("6beba35f",
success: (Int, String) -> ()
{
},
failure: (Int, String) -> ()
{
})
Example 2:
userService.validateToken("6beba35f",
success: (Int, String)
{
},
failure: (Int, String)
{
})
both give errors. I think im close with Example 1 but it keeps giving me an error "Expected , seperator" and when i have it add the ","
success: (Int, String), -> ()
but the error keeps saying "Expected , separator"
Any ideas on what the solutions is?
Including function
func validateToken(token: String, success: (Int, String) -> Void, failure: (Int, String) -> Void)
{
if(Network.isOnline())
{
var url: String = Commons.plistValue("Base URL") + "/security/" + token
Alamofire.request(.GET, url)
.responseJSON { (request, response, json, error) in
let jsonData: JSON = JSON(json!)
let statusCode: Int = response!.statusCode
if(statusCode == 202)
{
success(statusCode, jsonData["status"]["message"].stringValue)
}
else
{
failure(statusCode, jsonData["status"]["message"].stringValue)
}
}
}
else
{
failure(-1, "No Internet Connection")
}
}
Usage Fix
userService.validateToken("6beba35f",
success: { status_code, data in
(
println(status_code)
)
},
failure: { status_code, data in
(
println(status_code)
)
})

There are several ways to declare or use closures. The simplest one you're looking for is probably:
{ status_code, data in
println(status_code)
}
This needs to be used in such a way that the compiler can infer the type of status_code, data, and determine that there should be no return value. For instance, you can pass it as a function parameter (what you want), or assign it to a variable with appropriate type hints.

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

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

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

PromiseKit 6, Alamofire, Xcode 10.2/Swift 5

I'm having trouble writing the following code:
public
func
get(organization inSID: String)
-> Promise<Organization>
{
URLSession.showNetworkActivity()
return firstly
{
let req = buildRequest(path: "/api/\(inUUID)", date: inDate, headers: [.organizationSID : inSID])
self.mgr.request(req).responseJSON()
}
.map()
{ inData, inResp in
return Organization(sid: "")
}
.ensure
{
URLSession.hideNetworkActivity()
}
}
I get an error on firstly: Ambiguous reference to member 'firstly(execute:)'
After adding import PMKAlamofire to the top of my file, and being more explicit, I get this to compile:
public
func
get(organization inSID: String)
-> Promise<Organization>
{
URLSession.showNetworkActivity()
return firstly
{ () -> Promise<(json: Any, response: PMKAlamofireDataResponse)> in
let req = buildRequest(path: "/api/v2/organizations/\(inSID)", headers: [.organizationSID : inSID])
return self.mgr.request(req).responseJSON()
}
.map()
{ inResp in
return Organization(sid: "")
}
.ensure
{
URLSession.hideNetworkActivity()
}
}
Note the added explicit () -> Promise<(json: Any, response: PMKAlamofireDataResponse)> and the explicit return statement in the firstly closure. I don't know if this is now required by Swift 5 or it still can't properly infer types.

PromiseKit 6 error in cannot convert error

Firstly, I'm aware that the implementation has changed in v6 and and I using the seal object as intended, the problem I'm having is that even when following the example to the letter it still gives me the old Cannot convert value of type '(_) -> CustomerLoginResponse' to expected argument type '(_) -> _' error.
Here is my function that returns the promise:
static func makeCustomerLoginRequest(userName: String, password: String) -> Promise<CustomerLoginResponse>
{
return Promise
{ seal in
Alamofire.request(ApiProvider.buildUrl(), method: .post, parameters: ApiObjectFactory.Requests.createCustomerLoginRequest(userName: userName, password: password).toXML(), encoding: XMLEncoding.default, headers: Constants.Header)
.responseXMLObject { (resp: DataResponse<CustomerLoginResponse>) in
if let error = resp.error
{
seal.reject(error)
}
guard let Xml = resp.result.value else {
return seal.reject(ApiError.credentialError)
}
seal.fulfill(Xml)
}
}
}
and here is the function that is consuming it:
static func Login(userName: String, password: String) {
ApiClient.makeCustomerLoginRequest(userName: userName, password: password).then { data -> CustomerLoginResponse in
}
}
You might have to provide more information if you want to chain more than one promises. In v6, you need to use .done if you don't want to continue the promises chain. If you have only one promise with this request then below is the correct implementation.
static func Login(userName: String, password: String) {
ApiClient.makeCustomerLoginRequest(userName: userName, password: password)
.done { loginResponse in
print(loginResponse)
}.catch { error in
print(error)
}
}
Remember, you have to return a promise if you are using .then until you break the chain by using .done. If you want to chain multiple promises then your syntax should look like this,
ApiClient.makeCustomerLoginRequest(userName: userName, password: password)
.then { loginResponse -> Promise<CustomerLoginResponse> in
return .value(loginResponse)
}.then { loginResponse -> Promise<Bool> in
print(loginResponse)
return .value(true)
}.then { bool -> Promise<String> in
print(bool)
return .value("hello world")
}.then { string -> Promise<Int> in
print(string)
return .value(100)
}.done { int in
print(int)
}.catch { error in
print(error)
}

Migration to Swift 2 "Use of unresolved identifier '***'"

I'm upgrading my project from Swift 1.2 to Swift 2.
I use this occasion to upgrade lot of the lib that I use, in particular Alamofire.
But now I got this error on many of my request:
Use of unresolved identifier 'notifTypeJSON'
Here is the code of one of the func:
func getNotifications(failure failure: (NSError) -> (), success: ([Notification]) -> ()) {
Alamofire.request(Router.Notifications)
.validate()
.responseJSON { response in
if let error = response.result.error {
failure(error)
} else {
var json = JSON(response.data!)
let status = json["error"].intValue
if status != 0 {
failure(self.createError(status))
} else {
var notifications = [Notification]()
let notificationsList = json["notification"]
for (index: String, notifTypeJSON: JSON) in notificationsList {
if let notifJSON = notifTypeJSON[NotificationTypes.Generic.rawValue].dictionaryObject {
notifications.append(GenericNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Follow.rawValue].dictionaryObject {
notifications.append(FollowNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Comment.rawValue].dictionaryObject {
notifications.append(CommentNotification(json: notifJSON))
}
else if let notifJSON = notifTypeJSON[NotificationTypes.Like.rawValue].dictionaryObject {
notifications.append(LikeNotification(json: notifJSON))
}
}
DDLogInfo("SeetyAPI getNotifications() success")
success(notifications)
}
}
}
}
In Swift 2 the way we loop over a dictionary with a tuple has changed: the types have to be in a separate tuple.
Example Before:
for (key:String, value:Int) in xxx {
Example After:
for (key, value):(String, Int) in xxx {
So for you, you would need to replace this:
for (index: String, notifTypeJSON: JSON) in notificationsList {
With this:
for (index, notifTypeJSON):(String, JSON) in notificationsList {