Pass scope to a named function, rather than a closure - swift

I would like to separate the data processing of my NSURLSession into a separate method.
When making a URL request, I rely on the enclosing scope to provide me the user's callback.
This works:
static func makeRequest(url: String, callback: APICallback) {
let urlObject = NSURL(string: url)
var request = createRequest(urlObject!, method: "GET") // internal
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
// do some basic parsing, error checking, then...
callback(data, nil)
}
task.resume()
}
There's rather a lot of basic parsing and error checking I'd like to do at the application level, however, so I want to define and pass a function instead of a closure to the dataTaskWithRequest method:
static func makeRequest(url: String, callback: APICallback) {
let urlObject = NSURL(string: url)
var request = createRequest(urlObject!, method: "GET") // internal
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request, completionHandler: gotResponse)
task.resume()
}
static private func gotResponse (nsdata: NSData!, response: NSURLResponse!, err: NSError!) -> Void {
// Do my parsing and handling here, instead.
// OOPS! Don't have access to the callback. :(
}
This all leads me to my question, which, despite the lengthy example, is about language features. Can I pass some captured scope to this method? In Javascript I could accomplish this using Function.prototype.bind, but I'm not sure how to do it in Swift.

This seems like a good example of when to use a curried method. Declare the function like this with a first parameter of an APICallback. Note the brackets.
static private func gotResponse(callback: APICallback)(nsdata: NSData!, response: NSURLResponse!, err: NSError!) -> Void {
// use callback: like normal
}
Then, use it like this:
var task = session.dataTaskWithRequest(request, completionHandler: gotResponse(callback))
(apologies, the syntax might not be 100% correct since your code isn’t stand alone so I can’t test it fully)
Curried functions are a little finicky and had some bugs in 1.1 (though they got fixed in 1.2), so instead of using the language support for them, you could try hand-rolling if the above doesn’t work, something like:
static private func gotResponse(callback: APICallback) -> (nsdata: NSData!, response: NSURLResponse!, err: NSError!) -> Void {
return { data, response, error in
// put the common code, capturing callback:, in here...
}
}

Related

Creating a property of strict generic type of "self"

I want to create a property on a class that uses the class type as a generic parameter, and I'm having difficulty working it out.
open class ResponseProcessor {
required public init() {
}
var success: ((_ responseProcessor: ResponseProcessor) -> Void)?
func process() {
success?(self)
}
}
class TestProcessor: ResponseProcessor {
var result: String?
override func process() {
result = "Some Result"
super.process()
}
}
open class Request<ResponseProcessorType: ResponseProcessor> {
var success: ((_ responseProcessor: ResponseProcessor) -> Void)?
func doRequest() {
let responseProcessor = ResponseProcessorType.init()
responseProcessor.success = success
responseProcessor.process()
}
}
class TestRequest: Request<TestProcessor> {
}
let testRequest = TestRequest()
testRequest.success = { (responseProcessor) in
// This line reports an error, but I want it to know what
// type the responseProcessor is.
print(responseProcessor.result)
}
testRequest.doRequest()
I want to be able to assign SubRequest to the .request variable, but I can't because of strict generic typing.
So I'd like to be able to say "the request property on a ResponseProcessor should be of type Request<WhateverThisClassIs>, but I can't work out how to express that, or declare it in a way that works.
It should work out that testProcessor.request is of type HTTPRequest<TestProcessor>, but obviously that isn't happening.
I'm not sure if this is going to answer your question or not, but maybe it will put you on a better road. To your stated question, the answer is there is no generic covariance in Swift. What you're trying to write is not possible. Generic covariance wouldn't actually fix your code, because you have a lot of other type problems here (your latest version is probably violating Liskov's Substitution Principle, which means it breaks the meaning of class inheritance). But I don't think you actually want what you're trying to write at all.
I suspect you're writing a pluggable and testable networking stack. That's really common. He's a fairly simple one; they can get much more powerful if you tear this apart a bit more.
First, the low-level networking stack itself should consume URLRequests and return Data. That's all. It should not try to deal with model types. This is where people always go off the rails. So a Request is an URLRequest and a completion handler:
struct Request {
let urlRequest: URLRequest
let completion: (Result<Data, Error>) -> Void
}
And a client consumes those.
final class NetworkClient {
func fetch(_ request: Request) {
URLSession.shared.dataTask(with: request.urlRequest) { (data, _, error) in
if let error = error { request.completion(.failure(error)) }
else if let data = data { request.completion(.success(data)) }
}.resume()
}
}
Now we generally don't want to talk to URLSession when we're testing. We want to throw back pre-canned data probably. So we make one of those.
final class TestClient {
enum ClientError: Error {
case underflow
}
var responses: [Result<Data, Error>]
init(responses: [Result<Data, Error>]) { self.responses = responses }
func fetch(_ request: Request) {
if let response = responses.first {
responses.removeFirst()
request.completion(response)
} else {
request.completion(.failure(ClientError.underflow))
}
}
}
I'm marking things final class because these are sensibly reference types, but I want to make it clear that I'm not using class inheritance anywhere here. (Feel free to leave "final" off in your own code; it's a bit pedantic and usually not needed.)
How are these two things alike? They share a protocol:
protocol Client {
func fetch(_ request: Request)
}
Great. Now I can do things like:
let client: Client = TestClient(responses: [])
No associated types means that Client is perfectly fine as a type.
But getting back Data is kind of ugly. We want a type, like User.
struct User: Codable, Equatable {
let id: Int
let name: String
}
How do we do that? We just need a way to construct a Request that fetches a Decodable:
extension Request {
init<Model: Decodable>(fetching: Model.Type,
from url: URL,
completion: #escaping (Result<Model, Error>) -> Void) {
self.urlRequest = URLRequest(url: url)
self.completion = { data in
completion(Result {
try JSONDecoder().decode(Model.self, from: data.get())})
}
}
}
Notice how Request still doesn't know anything about models? And Client doesn't know anything about models. There's just this Request initializer that takes a Model type and wraps it up in a way that can accept Data and spit back a Model.
You can take this approach miles further. You can write a Client that wraps a Client and modifies the request, adding headers for example.
struct AddHeaders: Client {
let base: Client
let headers: [String: String]
func fetch(_ request: Request) {
var urlRequest = request.urlRequest
for (key, value) in headers {
urlRequest.addValue(value, forHTTPHeaderField: key)
}
base.fetch(Request(urlRequest: urlRequest,
completion: request.completion))
}
}
let client = AddHeaders(base: NetworkClient(),
headers: ["Authorization": "Token ...."])
There are no subclasses here, no generic types, just one protocol (which has no associated types), and one generic method. But you can plug in a wide variety of back-ends, and compose together any operation that can be made to match one of a handful of transforms (Request -> Request, Request -> Data, Data -> Void).
I hope this matches some of what you're getting at with your question. Best of luck.

Swift POST Request in same Thread

Hope you can help me. I want a swift function that make a post request and return the json data
so here is my class
import Foundation
class APICall {
//The main Url for the api
var mainApiUrl = "http://url.de/api/"
func login(username: String, password: String) -> String {
let post = "user=\(username)&password=\(password)";
let action = "login.php";
let ret = getJSONForPOSTRequest(action: action, post: post)
return ret;
}
//Function to call a api and return the json output
func getJSONForPOSTRequest(action: String, post: String) -> String {
var ret: String?
let apiUrl = mainApiUrl + action;
let myUrl = URL(string: apiUrl);
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
let postString = post;
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(error)")
return
}
print("response=\(response)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let login = parseJSON["Login"] as? String
print("login: \(login)")
ret = login
}
} catch {
print(error)
}
}
task.resume()
return ret!;
}
}
But ret is nil. In the debugger is see the inner of the task is called later by another thread?
How can if fix that?
Thank you guys
The data task completion closure is called on another thread and after the execution of the method is completed so you need to re-jig your code a bit. Instead of having a String return value for your getJSONForPOSTRequest, don't return anything and instead have an additional argument that is a closure and call that from within your dataTask closure instead.
func getJSONForPOSTRequest(action: String, post: String, completion: (string: String) -> Void) {
// ...
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
// ... (Convert data to string etc.)
completion(string: myString)
}
task.resume()
}
Remember, doing this means that the completion handler will be called once the network request completes and not right away.
EDIT:
Lets take this from the beginning. When you download something from the network in iOS you typically use NSURLSession. NSURLSession has a number of methods available to it for different means of interacting with the network, but all of these methods use a different thread, typically a background thread, which will do work independently of the rest of your code.
With this in mind, when you call the dataTask method you will notice that you have to add a completion closure as one of the parameters (notice in your example you are using something called a 'trailing closure' which is a closure that is the last argument in the method call that doesn't fall within the parenthesis of the method with the rest of the arguments). Think of a closure as a piece of code that is executed at a different time, it's not executed in line with the rest of the code around it (See the Swift documentation on closures here). In this case the closure will be called once the network request has been completed. Network requests aren't instant so we typically use a background thread to execute them while the user is shown an activity indicator etc and can still use the app. If we waited until the network request completed on the same thread as the rest of our code then it results in the app appearing laggy and even frozen which is terrible for users.
So going back to your example at hand; when you call your getJSONForPOSTRequest method the code within that method will complete and return before the network request has completed which is why we don't need to use a return value. Once the network request has completed your closure code will get called. Because the closure is called later it's also being called from an entirely different place within the code, in this case it's called from within iOS's network code. Because if this if you return a value from within the closure you will be trying to return the value to the network code which isn't what you want, you want to return the value to your own code.
To return the value of the network response to your code you need to define a closure (or a delegate, but I'm not going to go into that here) yourself. If you look at the example code above I've removed the return value from your getJSONForPOSTRequest method and added a new argument called 'completion', and if you look at the type of that argument you can see it's (string: String) -> Void, this defines a closure that passes in a string (the string that you will have downloaded from the network). Now that we have a closure thats within your method we can use this to call back to the caller of the getJSONForPOSTRequest with the data we have downloaded form the network.
Lets take your login method and see how we use getJSONForPOSTRequest within it:
func login(username: String, password: String, completion: (success: Bool) -> Void) {
let post = "user=\(username)&password=\(password)";
let action = "login.php";
let ret = getJSONForPOSTRequest(action: action, post: post) { string in
// This will be called once the network has responded and 'getJSONForPOSTRequest' has processed the data
print(string)
completion(success: true)
}
}
See that again we aren't returning anything directly from the login method as it has to rely on the a-synchronousness of calling off to the network.
It might feel by now that you are starting to get into something called 'callback hell', but this is the standard way to deal with networking. In your UI code you will call login and that will be the end of the chain. For example here is some hypothetical UI code:
func performLogin() {
self.activityIndicator.startAnimating()
self.apiCaller.login(username: "Joe", password: "abc123") { [weak self] success in
print(success)
// This will get called once the login request has completed. The login might have succeeded of failed, but here you can make the decision to show the user some indication of that
self?.activityIndicator.stopAnimating()
self?.loginCompleted()
}
}
Hopefully that clarifies a few things, if you have any other questions just ask.

In Swift, how does one manipulate an object inside a typealias?

I have a typealias I would like to manipulate one of the objects in it after receiving it as s one completion handler and before sending it to another. So
typealias GalleryResponse = (gallery: MLGallery?, error: NSError?) -> ()
and the function i’d like to intervene on the typealias:
func getGalleryForDiscover(onCompletion: galleryResponse) {
let endpointURL = kGalleryURL + kMetaDataFilter + kLimitURL20
/// Would like to do something here with the MLGallery object in the galleryResponse closure.
makeRequestToCurbsAt(endpointURL, completionHandler: onCompletion)
}
How do I get to that MLGallery object - manipulate it - and then send it on?
You provide makeRequestToCurbsAt with its own completion handler, you then
manipulate the MLGallary instance passed to that handler, and then pass it on to the original handler. Like such:
func getGalleryForDiscover(onCompletion: galleryResponse) {
let endpointURL = kGalleryURL + kMetaDataFilter + kLimitURL20
makeRequestToCurbsAt(endpointURL, completionHandler: {
(gallery: MLGallery?, error: NSError?) in
// do something with gallery
// invoke the original
onCompletion (gallery: gallery, error: error)
})
}
Note: Really, really, your typealias identifier should be capitalized as GalleryResponse.

Confusing closures and completion handles

Im a new programmer and am very lost.
I am taking this online iOS dev course and I was configuring collection view cell.
However, closures and completion handles were used and it was never mentioned before.
import UIKit
class PersonCell: UICollectionViewCell {
#IBOutlet weak var img: UIImageView!
func configureCell(imgUrl: String) {
if let url = NSURL(string: imgUrl) {
downloadImg(url)
}
}
func downloadImg(url: NSURL) {
getDataFromURL(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else {return}
self.img.image = UIImage(data: data)
}
}
}
func getDataFromURL(url: NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError?) -> Void)) {
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
completion(data: data, response: response, error: error)
} .resume()
}
}
Can someone explain to me what the completion handler is doing after the "getDataFromURL" function. Also what are the closures doing? is "(data, response, error)" getting passed around? How does swift know that "data" is suppose to be NSData and etc in the "(data, response, error)"?
What does the closure after the "dataTaskWithURL" do (is it setting up the completion handler"?
Thank you!
These are good questions!
A closure is simply a collection (aka block) of lines of code that you can treat like a variable and execute like a function. You can refer to a closure with a variable name and you can pass a closure around as a parameter in function calls just like any other variable, eventually executing the code when appropriate. A closure can accept certain parameters to use in its code and it can include a return value.
Example:
This is a closure that accepts two strings as parameters and returns a string.
let closure: (String, String) -> String = { (a: String, b: String) -> String in
return a + b
}
Thus, the following will print "Hello Jack!":
print(closure("Hello ", "Jack!"))
A closure also has a variable type (just like "hello" is a String and 1 is an Int). The variable type is based on the parameters that the closure accepts and the value that the closure returns. Thus, since the closure above accepts two strings as parameters and returns a string, its variable type is (String, String) -> String. Note: when nothing is returned (i.e. the return type is Void), you can omit the return type (so (Int, String) -> Void is the same thing as (Int, String)).
A completion handler is a closure that you can pass to certain functions. When the function completes, it executes the closure (e.g. when a view finished animating onto the screen, when a file finished downloading, etc.).
Example:
"Done!" will be printed when the view controller is finished presenting.
let newClosure: () -> Void = { () -> Void in
print("Done!")
}
let someViewController = UIViewController(nibName: nil, bundle: nil)
self.presentViewController(someViewController, animated: true, completion: newClosure)
Let's focus on the getDataFromURL function you wrote first. It takes two parameters: a variable of type NSData and a closure of type (NSData?, NSURLResponse?, NSError?) -> Void. Thus, the closure (which is named completion) takes three parameters of types NSData?, NSURLResponse?, and NSError?, and returns nothing, because this is how you defined the closure in the function declaration.
You then call getDataFromURL. If you read the documentation, you'll see that the closure you pass to this function as the second parameter is executed when the load task is complete. The function declaration for dataTaskWithURL is what defines the variable types that the closure accepts and returns. Within this closure, you are then calling the closure you passed to the getDataFromURL function.
Within this latter closure (the one you define in downloadImg when you are calling getDataFromURL), you are checking to see if the data that you downloaded is not nil, and if not, you are then setting the data as an image in a UIImageView. The dispatch_async(dispatch_get_main_queue(), ...) call simply ensures that you are setting the new image on the main thread, as per Apple's specifications (you can read more about threads elsewhere).
make an typealias to understand this is easy :
typealias Handle = (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void
//the func should be
func getDataFromURL(url: NSURL, completion: Handle)
//when you call it. it needs an url and an Handle
getDataFromURL(url:NSURL, completion: Handle)
// so we pass the url and handle to it
getDataFromURL(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else {return}
self.img.image = UIImage(data: data)
}
}
//setp into the func
func getDataFromURL(url: NSURL, completion: Handle){
// call async net work by pass url
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
// now data / response / error we have and we invoke the handle
completion(data: data, response: response, error: error)
} .resume()
}
hope it be helpful :D

The meaning of urlSession.dataTaskWithRequest(request)

When I read the book about swift in the Network Development chapter, I met some code which I cannot understand. The code is as follows:
let sessionTask = urlSession.dataTaskWithRequest(request) {
(data, response, error) in
handler(response, data)
}
the prototype of this function in swift is:
public func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask
As you can see, the prototype has 2 parameters, one is request, another is completionHandler. But in the above code, it also has one parameter. And also I cannot understand the code in the curly braces, where do the 3 variable data, response, error come from? I cannot find any definition of the 3 variables. Who can help me understand the code, thanks in advance.
It is called a trailing closure, it's a cleaner way of passing a function to another function if that function is the last argument. The same code can be written as:
let sessionTask = NSURLSession.sharedSession()
let request = NSURLRequest()
sessionTask.dataTaskWithRequest(request, completionHandler: {(data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
})
If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is a closure expression that is written outside of (and after) the parentheses of the function call it supports
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID102
func aFunction(callback: (done: Bool) -> Void) {
let finished = true
callback(done: finished)
}
aFunction { (done) -> Void in
print("we are done \(done)")
}