I have been trying to write the Unit test cases in swift for making an API call but not able to figure out how to write - swift

I have been trying to write Unit test cases in swift for making an API call but being new in this am not able to figure out how do I write the Unit test case for the same. here's my code for which I want to write the unit test case
class QuotesModel: ObservableObject {
#Published var quotes = [Quote]()
#MainActor
func fetchData() async {
guard let url = URL(string: "https://breakingbadapi.com/api/quotes") else {
print("Invalid URL")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
quotes = try JSONDecoder().decode([Quote].self, from: data)
} catch {
print(error)
}
// print(quotes)
}
}
I have been trying to write the unit testcase for this but am not able to figure out how do I do it. Can someone help me with this?

URLSession.shared is a singleton. There's nothing wrong with using singletons. But if you use them directly, your dependencies become hard-wired and you give up control. And URLSession is an "awkward dependency" that makes testing harder. Let's replace it.
So change your code so that it uses URLSession.shared when in production, but something else during testing — something your tests can control. Let's introduce a protocol, as I describe in https://qualitycoding.org/swift-mocking/
protocol URLSessionProtocol {
// We will add more here
}
Use an extension to make URLSession conform to this new protocol:
extension URLSession: URLSessionProtocol {}
Change your production code to use an instance of this protocol instead of directly calling URLSession.shared. But provide URLSession.shared as the default value. For example, we can add an argument to your method:
func fetchData(urlSession: URLSessionProtocol = URLSession.shared) async { … }
To make it possible to use urlSession, the protocol needs the URLSession method you use:
protocol URLSessionProtocol {
func data(for request: URLRequest, delegate: URLSessionTaskDelegate?) async throws -> (Data, URLResponse)
}
Unfortunately, we can't directly add the = nil default value for the delegate argument directly in the protocol. The easiest way to resolve this is to pass it explicitly from your calling code, which will now look like
let (data, _) = try await urlSession.data(from: url, delegate: nil)
(We could also add an extension to the protocol.)
Now test code can provide a different implementation which records how it was called. (How many times was it called? Did you use the correct URL?) And it returns canned data controlled by the test.
…All this to say: Don't write code and expect to be able to write microtests for it, as-is. This is hard. Instead, shape your code to make it easy to write microtests.

Related

Use generic protocols as variable type

I would like to create a generic protocol for a data fetching service like this:
protocol FetchDataDelegate: AnyObject {
associatedtype ResultData
func didStartFetchingData()
func didFinishFetchingData(with data: ResultData)
func didFinishFetchingData(with error: Error)
}
class Controller: UIViewController, FetchDataDelegate {
func didStartFetchingData() {
//...
}
func didFinishFetchingData(with data: ResultData) {
//...
}
func didFinishFetchingData(with error: Error) {
//...
}
}
class NetworkManager {
weak var delegate: FetchDataDelegate?
//...
}
The Controller class would have a reference to a NetworkManager instance, and through this, I would like to start the network operations. When the operation is ended I would like to call the appropriate delegate function to pass the result back to the controller. But with this setup I got the following error:
Protocol 'FetchDataDelegate' can only be used as a generic constraint because it has Self or associated type requirements
The question is what should I do to use this generic protocol as a variable type? Or if is not possible what would be the correct way? Thanks!
While this is closely related to the question David Smith linked (and you should read that as well), it's worth answering separately because it's a different concrete use case, and so we can talk about it.
First, imagine you could store this variable. What would you do with it? What function in NetworkManager could call delegate.didFinishFetchingData? How would you generate the ResultData when you don't know what it is?
The point is that this isn't what PATs (protocols with associated types) are for. It's not their goal. Their goal is to help you add extensions to other types, or to restrict which kinds of types can be passed to generic algorithms. For those purposes, they're incredibly powerful. But what you want are generics, not protocols.
Instead of creating delegates, you should use generic functions to handle the result of a specific call, rather than trying to nail down each view controller to a specific result type (which isn't very flexible anyway). For example, in the simplest, and least flexible way that still gives you progress reporting:
struct APIClient {
func fetch<Model: Decodable>(_: Model.Type,
with urlRequest: URLRequest,
completion: #escaping (Result<Model, Error>) -> Void)
-> Progress {
let session = URLSession.shared
let task = session.dataTask(with: urlRequest) { (data, _, error) in
if let error = error {
completion(.failure(error))
}
else if let data = data {
let decoder = JSONDecoder()
completion(Result {
try decoder.decode(Model.self, from: data)
})
}
}
task.resume()
return task.progress
}
}
let progress = APIClient().fetch(User.self, with: urlRequest) { user in ... }
This structure is the basic approach to this entire class of problem. It can be made much, much more flexible depending on your specific needs.
(If your didStartFetchingData method is very important, in ways that Progress doesn't solve, leave a comment and I'll show how to implement that kind of thing. It's not difficult, but this answer is pretty long already.)
Possible duplicate of this
In summary, you cannot use generic protocols as variable types, it would have to be leveraged as a generic constraint since you wouldn't know the type of ResultData at compilation time

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.

enums with Associated Values + generics + protocol with associatedtype

I'm trying to make my API Service as generic as possible:
API Service Class
class ApiService {
func send<T>(request: RestRequest) -> T {
return request.parse()
}
}
So that the compiler can infer the response type from the request categories .auth and .data:
let apiService = ApiService()
// String
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")))
// Int
let intResponse = apiService.send(request: .data(.content(id: "123")))
I tried to come up with a solution using generics and a protocol with associated type to handle the parsing in a clean way. However I'm having trouble associating the request cases with the different response types in a way that it's simple and type-safe:
protocol Parseable {
associatedtype ResponseType
func parse() -> ResponseType
}
Endpoints
enum RestRequest {
case auth(_ request: AuthRequest)
case data(_ request: DataRequest)
// COMPILER ERROR HERE: Generic parameter 'T' is not used in function signature
func parse<T: Parseable>() -> T.ResponseType {
switch self {
case .auth(let request): return (request as T).parse()
case .data(let request): return (request as T).parse()
}
}
enum AuthRequest: Parseable {
case login(email: String, password: String)
case signupWithFacebook(token: String)
typealias ResponseType = String
func parse() -> ResponseType {
return "String!!!"
}
}
enum DataRequest: Parseable {
case content(id: String?)
case package(id: String?)
typealias ResponseType = Int
func parse() -> ResponseType {
return 16
}
}
}
How is T not used in function signature even though I'm using T.ResponseType as function return?
Is there a better still clean way to achieve this?
I'm trying to make my API Service as generic as possible:
First, and most importantly, this should never be a goal. Instead, you should start with use cases, and make sure that your API Service meets them. "As generic as possible" doesn't mean anything, and only will get you into type nightmares as you add "generic features" to things, which is not the same thing as being generally useful to many use cases. What callers require this flexibility? Start with the callers, and the protocols will follow.
func send<T>(request: RestRequest) -> T
Next, this is a very bad signature. You don't want type inference on return types. It's a nightmare to manage. Instead, the standard way to do this in Swift is:
func send<ResultType>(request: RestRequest, returning: ResultType.type) -> ResultType
By passing the expected result type as a parameter, you get rid of the type inference headaches. The headache looks like this:
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")))
How is the compiler to know that stringResponse is supposed to be a String? Nothing here says "String." So instead you have to do this:
let stringResponse: String = ...
And that's very ugly Swift. Instead you probably want (but not really):
let stringResponse = apiService.send(request: .auth(.signupWithFacebook(token: "9999999999999")),
returning: String.self)
"But not really" because there's no way to implement this well. How can send know how to translate "whatever response I get" into "an unknown type that happens to be called String?" What would that do?
protocol Parseable {
associatedtype ResponseType
func parse() -> ResponseType
}
This PAT (protocol w/ associated type) doesn't really make sense. It says something is parseable if an instance of it can return a ResponseType. But that would be a parser not "something that can be parsed."
For something that can be parsed, you want an init that can take some input and create itself. The best for that is Codable usually, but you could make your own, such as:
protocol Parseable {
init(parsing data: Data) throws
}
But I'd lean towards Codable, or just passing the parsing function (see below).
enum RestRequest {}
This is probably a bad use of enum, especially if what you're looking for is general usability. Every new RestRequest will require updating parse, which is the wrong place for this kind of code. Enums make it easy to add new "things that all instances implement" but hard to add "new kinds of instances." Structs (+ protocols) are the opposite. They make it easy to add new kinds of the protocol, but hard to add new protocol requirements. Requests, especially in a generic system, are the latter kind. You want to add new requests all the time. Enums make that hard.
Is there a better still clean way to achieve this?
It depends on what "this" is. What does your calling code look like? Where does your current system create code duplication that you want to eliminate? What are your use cases? There is no such thing as "as generic as possible." There are just systems that can adapt to use cases along axes they were prepared to handle. Different configuration axes lead to different kinds of polymorphism, and have different trade-offs.
What do you want your calling code to look like?
Just to provide an example of what this might look like, though, it'd be something like this.
final class ApiService {
let urlSession: URLSession
init(urlSession: URLSession = .shared) {
self.urlSession = urlSession
}
func send<Response: Decodable>(request: URLRequest,
returning: Response.Type,
completion: #escaping (Response?) -> Void) {
urlSession.dataTask(with: request) { (data, response, error) in
if let error = error {
// Log your error
completion(nil)
return
}
if let data = data {
let result = try? JSONDecoder().decode(Response.self, from: data)
// Probably check for nil here and log an error
completion(result)
return
}
// Probably log an error
completion(nil)
}
}
}
This is very generic, and can apply to numerous kinds of use cases (though this particular form is very primitive). You may find it doesn't apply to all your use cases, so you'd begin to expand on it. For example, maybe you don't like using Decodable here. You want a more generic parser. That's fine, make the parser configurable:
func send<Response>(request: URLRequest,
returning: Response.Type,
parsedBy: #escaping (Data) -> Response?,
completion: #escaping (Response?) -> Void) {
urlSession.dataTask(with: request) { (data, response, error) in
if let error = error {
// Log your error
completion(nil)
return
}
if let data = data {
let result = parsedBy(data)
// Probably check for nil here and log an error
completion(result)
return
}
// Probably log an error
completion(nil)
}
}
Maybe you want both approaches. That's fine, build one on top of the other:
func send<Response: Decodable>(request: URLRequest,
returning: Response.Type,
completion: #escaping (Response?) -> Void) {
send(request: request,
returning: returning,
parsedBy: { try? JSONDecoder().decode(Response.self, from: $0) },
completion: completion)
}
If you're looking for even more on this topic, you may be interested in "Beyond Crusty" which includes a worked-out example of tying together parsers of the kind you're discussing. It's a bit dated, and Swift protocols are more powerful now, but the basic message is unchanged and the foundation of things like parsedBy in this example.

Swift - Protocol can only be used as a generic constraint because it has Self or associated type requirements

I'm working on an app which needs to query multiple APIs. I've come up with classes for each API provider (and in more extreme cases, a class for each specific API Endpoint). This is because each API query is expected to return a very strict type of response, so if an API can, for instance, return both user profiles and profile pictures, I only want a response to be specific to either of those.
I've implemented it roughly in the following manner:
protocol MicroserviceProvider {
associatedtype Response
}
protocol ProfilePictureMicroserviceProvider: MicroserviceProvider {
func getPicture(by email: String, _ completion: (Response) -> Void)
}
class SomeProfilePictureAPI: ProfilePictureMicroserviceProvider {
struct Response {
let error: Error?
let picture: UIImage?
}
func getPicture(by email: String, _ completion: (Response) -> Void) {
// some HTTP magic
// will eventually call completion(_:) with a Response object
// which either holds an error or a UIImage.
}
}
Because I want to be able to Unit Test classes that will rely on this API, I need to be able to inject that profile picture dependency dynamically. By default it will use SomeProfilePictureAPI but when running tests I will be able to replace that with a MockProfilePictureAPI which will still adhere to ProfilePictureMicroserviceProvider.
And because I'm using associated types, I need to make classes that depend on ProfilePictureMicroserviceProvider generic.
At first, I naively did try to write my view controller like such
class SomeClass {
var profilePicProvider: ProfilePictureMicroserviceProvider
}
But that just led the frustratingly famous 'Protocol ProfilePictureMicroserviceProvider can only be used as a generic constraint because it has Self or associated type requirements' compile-time error.
Now I've been reading up on the issue over the last couple days, trying to wrap my head around Protocols with Associated Types (PATS), and figured I'd take the route of generic classes like such:
class SomeClass<T: ProfilePictureMicroserviceProvider> {
var profilePicProfider: T = SomeProfilePictureAPI()
}
But even then I get the following error:
Cannot convert value of type 'SomeProfilePictureAPI' to specified type 'T'
Even though having T being constrained to the ProfilePictureMicroserviceProvider protocol, and having SomeProfilePictureAPI adhere to it...
Basically the main idea was to reach 2 objectives: enforce Microservice structure with mandatory Response type, and make each Microservice mock-able for unit tests of dependent classes.
I'm now stuck with choosing either one of the two as I can't seem to make it work. Any help telling me what I'm doing wrong would be most welcome.
I've also had a look at type-erasure. But this to me seems very whacky and quite an effort for something that looks wrong on many aspects.
So basically my question is two-fold: how can I enforce my Microservices to define their own Response type ? And how can I easily replace them by mock microservices in classes that depend on them ?
You have to turn these requirements around;
Instead of injecting a MicroServiceProvider into each request, you should write a generic MicroService 'Connector' Protocol that should define what it expects from each request, and what each request expects it to return.
You can then write a TestConnector which conforms to this protocol, so that you have complete control over how your requests are handled. The best part is, your requests won't even need to be modified.
Consider the following example:
protocol Request {
// What type data you expect to decode and return
associatedtype Response
// Turn all the data defined by your concrete type
// into a URLRequest that we can natively send out.
func makeURLRequest() -> URLRequest
// Once the URLRequest returns, decode its content
// if it succeeds, you have your actual response object
func decode(incomingData: Data?) -> Response?
}
protocol Connector {
// Take in any type conforming to Request,
// do whatever is needed to get back some potential data,
// and eventually call the handler with the expected response
func perform<T: Request>(request: T, handler: #escaping (T.Response?) -> Void)
}
These are essentially the bare minimum requirements to setup such a framework. In real life, you'll want more requirements from your Request protocol (such as ways to define the URL, request headers, request body, etc).
The best part is, you can write default implementations for your protocols. That removes a lot of boilerplate code! So for an actual Connector, you could do this:
extension Connector {
func perform<T: Request>(request: T, handler: #escaping (T.Response?) -> Void) {
// Use a native URLSession
let session = URLSession()
// Get our URLRequest
let urlRequest = request.makeURLRequest()
// define how our URLRequest is handled
let task = session.dataTask(with: urlRequest) { data, response, error in
// Try to decode our expected response object from the request's data
let responseObject = request.decode(incomingData: data)
// send back our potential object to the caller's completion block
handler(responseObject)
}
task.resume()
}
}
Now, with that, all you need to do is implement your ProfilePictureRequest like this (with extra example class variables):
struct ProfilePictureRequest: Request {
private let userID: String
private let useAuthentication: Bool
/// MARK: Conform to Request
typealias Response = UIImage
func makeURLRequest() -> URLRequest {
// get the url from somewhere
let url = YourEndpointProvider.profilePictureURL(byUserID: userID)
// use that URL to instantiate a native URLRequest
var urlRequest = URLRequest(url: url)
// example use: Set the http method
urlRequest.httpMethod = "GET"
// example use: Modify headers
if useAuthentication {
urlRequest.setValue(someAuthenticationToken.rawValue, forHTTPHeaderField: "Authorization")
}
// Once the configuration is done, return the urlRequest
return urlRequest
}
func decode(incomingData: Data?) -> Response? {
// make sure we actually have some data
guard let data = incomingData else { return nil }
// use UIImage's native data initializer.
return UIImage(data: data)
}
}
If you then want to send a profile picture request out, all you then need to do is (you'll need a concrete type that conforms to Connector, but since the Connector protocol has default implementations, that concrete type is mostly empty in this example: struct GenericConnector: Connector {}):
// Create an instance of your request with the arguments you desire
let request = ProfilePictureRequest(userID: "JohnDoe", useAuthentication: false)
// perform your request with the desired Connector
GenericConnector().perform(request) { image in
guard let image = image else { return }
// You have your image, you can now use that instance whichever way you'd like
ProfilePictureViewController.current.update(with: image)
}
And finally, to set up your TestConnector, all you need to do is:
struct TestConnector: Connector {
// define a convenience action for your tests
enum Behavior {
// The network call always fails
case alwaysFail
// The network call always succeeds with the given response
case alwaysSucceed(Any)
}
// configure this before each request you want to test
static var behavior: Behavior
func perform<T: Request>(request: T, handler: #escaping (T.Response?) -> Void) {
// since this is a test, you don't need to actually perform any network calls.
// just check what should be done
switch Self.behavior {
case alwaysFail:
handler(nil)
case alwaysSucceed(let response):
handler(response as! T)
}
}
}
With this, you can easily define Requests, how they should configure their URL actions and how they decode their own Response type, and you can easily write mocks for you connectors.
Of course, keep in mind that the examples given in this answer are quite limited in how they can be used. I would highly suggest you to take a look at this library I wrote. It extends this example in a much more structured way.

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.