I want to build an analytics class for my application, and i am using singleton.
If I run this, the tagEvent function immediately runs rather than first running the openSession(), so sessionId returns nil.
How can I create a class like this with proper initialisation and use it application wide like singleton instances.
Analytics.swift
final class Analytics {
public static let instance = Analytics()
private var baseUrl = "http://localhost"
public var deviceId: String?
private init(){
self.deviceId = SomeFunctionGetsDeviceID()
}
func openSession(){
// make an API call to create a session and save sessionId to UserDefaults
if let url = URL(string: self.baseUrl + "/session"){
let params:[String:Any] = ["deviceId": "\(self.deviceId!)"]
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField:"Content-Type")
request.httpMethod = "POST"
request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: [])
AnalyticsSessionManager.sharedManager.request(request as URLRequestConvertible).validate().responseObject(completionHandler: { (response: DataResponse<SessionOpenResponse>) in
if response.result.value != nil {
UserDefaults.standard.set(response.result.value?.sessionId, forKey: "sessionId")
}
})
}
}
func closeSession(){
// make an API call to close a session and delete sessionId from UserDefaults
...
}
func tagEvent(eventName: String, attributes: [String : String]? = nil) {
if let url = URL(string: self.baseUrl + "/event"),
let sessionId = UserDefaults.standard.string(forKey: "sessionId"){
...
// make an API call to create an event with that sessionId
}
}
}
AppDelegate.swift
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Analytics.instance.openSession()
Analytics.instance.tagEvent(eventName: "App Launch", attributes:
["userID":"1234"])
}
My best guess is the openSession function is doing work asynchronously and the tagEvent call comes in BEFORE the asynchronous code has completed. There are a couple of ways around this:
1) Add synchronization so the tagEvent code will wait for the openSession call to complete (if in progress). If not in progress, perhaps it should automatically call openSession, wait for completion, then execute the code in that function
2) Add a completion handler from openSession and inside that enclosure you can call tagEvent such as:
func openSession(completionHandler: #escapaing (Bool) -> ()){
// make an API call to create a session and save sessionId to UserDefaults
...
UserDefaults.standard.set(someSessionID, forKey: "sessionId")
// when done with async work
completionHandler(true)
}
Then in your app delegate:
Analytics.instance.openSession() { (success)
Analytics.instance.tagEvent(eventName: "App Launch", attributes:["userID":"1234"])
}
3) * This is the way I would fix it * I would not make a call to openSession to be required outside of the class. I would add a flag to the Analytics class:
private var initialized = false
In the openSession function, set this after everything is done
initialized = true
In the tagEvent function:
func tagEvent(eventName: String, attributes: [String : String]? = nil) {
// Check for initialization
if (!initialized) {
openSession() { (success) in
// perform your tagEvent code
})
} else {
if let url = URL(string: self.baseUrl + "/event"),
let sessionId = UserDefaults.standard.string(forKey: "sessionId"){
...
// make an API call to create an event with that sessionId
}
}
}
You can implement an "internal" class to provide a static reference to your Analytics class, as follows (note: remove final keyword):
class Analytics {
// properties ...
class var sharedInstance: Analytics {
struct Static {
static let instance: Analytics = Analytics()
}
return Static.instance
}
// other methods (init(), openSession(), closeSession(), tagEvent())
}
Next call methods from your Analytics class in any other class, as follows:
Analytics.sharedInstance.openSession()
Analytics.sharedInstance.closeSession()
Analytics.sharedInstance.tagEvent( ... )
Related
I'm calling a Firestore query that does come back, but I need to ensure completion before moving on with the rest of the code. So I need a completion handler...but for the life of me I can't seem to code it.
// get user info from db
func getUser() async {
self.db.collection("userSetting").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let userTrust = document.data()["userTrust"] as! String
let userGrade = document.data()["userGrade"] as! String
let userDisclaimer = document.data()["userDisclaimer"] as! String
var row = [String]()
row.append(userTrust)
row.append(userGrade)
row.append(userDisclaimer)
self.userArray.append(row)
// set google firebase analytics user info
self.userTrustInfo = userTrust
self.userGradeInfo = userGrade
}
}
}
}
Called by:
internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
db = Firestore.firestore()
Database.database().isPersistenceEnabled = true
Task {
do {
let userInfo = await getUser()
}
} return true }
I used a Task as didFinishLauncingWithOptions is synchronous and not asynchronous
However, the getUser() still isn't completed before didFinishLauncingWithOptions moves on.
I need the data from getUser as the very next step uses the data in the array, and without it I get an 'out of bounds exception' as the array is still empty.
Also tried using dispatch group within the func getUser(). Again with no joy.
Finally tried a completion handler:
func getUser(completion: #escaping (Bool) -> Void) {
self.db.collection("userSetting").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let userTrust = document.data()["userTrust"] as! String
let userGrade = document.data()["userGrade"] as! String
let userDisclaimer = document.data()["userDisclaimer"] as! String
var row = [String]()
row.append(userTrust)
row.append(userGrade)
row.append(userDisclaimer)
self.userArray.append(row)
// set google firebase analytics user info
self.userTrustInfo = userTrust
self.userGradeInfo = userGrade
completion(true)
}
}
}
}
Nothing works. The getUser call isn't completed before the code moves on. Can someone please help. I've searched multiple times, looked at all linked answers but I can not make this work.I'm clearly missing something easy, please help
read this post: Waiting for data to be loaded on app startup.
It explains why you should never wait for data before returning from
function application(_:didFinishLaunchingWithOptions).
To achieve what you need, you could use your first ViewController as a sort of splashscreen (that only shows an image or an activity indicator) and call the function getUser(completion:) in the viewDidLoad() method the ViewController.
Example:
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
MyFirestoreDatabaseManager.shared.getUser() { success in
if success {
//TODO: Navigate to another ViewController
} else {
//TODO: Show an error
}
}
}
}
Where obviously MyFirestoreDatabaseManager.shared is the object on which you defined the getUser(completion:) method.
(In your example, I think that you defined that function in the AppDelegate. In that case, you should mark your getUser(completion:) method and all related variables as static. Then replace MyFirestoreDatabaseManager.shared with AppDelegate).
Not 100% sure what you would like to accomplish as I can't see all your code, but try something similar to this, replacing Objects for what you are trying to return from the documents.
You don't want your user's data spread across multiple documents. With Firebase you pay for every document you have to get. Ideally you want all your user's settings within one firebase document. Then create a UserInfo struct that you can decode to using the library CodeableFirebase or the decoder of your choice.
// Create user struct
struct UserInfo: Codable {
var userId: String
var userTrust: String
var userGrade: String
var userDisclaimer: String
}
// get user info from db and decode using CodableFirebase
func getUser() async throws -> UserInfo {
let doc = try await self.db.collection("users").document("userIdHere")
let userInfo = try FirestoreDecoder().decode(UserInfo.self, from: doc.data())
return UserInfo
}
Then you can do this...
Task {
do {
let userInfo = try await getUser()
let userTrust = userInfo.userTrust
let userGrade = userInfo.userGrade
let userDisclaimer = userInfo.userDisclaimer
}
}
I'm using a Series 6 emulator on watchOS7 and I'm trying to upload some JSON data to a remote server using an URLSession background task. However the delegate functions are not being called so I cannot clean up any local data from the upload and handle any errors. I got the original idea from my implementation from this WWDC video WWDC Video. I've looked at many posts on the Internet but nothing I've tried seems to work. Here is my code:
UploadSession class
class UploadSession: NSObject, Identifiable, URLSessionDelegate {
var backgroundTasks = [WKURLSessionRefreshBackgroundTask]()
private lazy var urlSession: URLSession = {
let config = URLSessionConfiguration.background(withIdentifier: "my.app.watchextension")
config.sessionSendsLaunchEvents = true
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
func enqueueBackgroundTask(idToken: String, uploadData: Data, url: URL) throws {
//build the JSON object we want to send in this post request
let tempDir = FileManager.default.temporaryDirectory
let localURL = tempDir.appendingPathComponent("throwaway")
try? uploadData.write(to: localURL)
//set up the URLRequest
var request = URLRequest(url: url)
request.httpMethod = K.Upload.httpPost
request.setValue(K.Upload.jsonContent, forHTTPHeaderField: K.Upload.contentType)
request.setValue("\(K.Upload.bearer)\(idToken)", forHTTPHeaderField: K.Upload.authorization)
request.timeoutInterval = K.Upload.httpUploadTimeout
//keep a reference to this class
BackgroundURLSessions.shared.sessions["my.app.watchextension"] = self
//create the upload task and kick it off
let task = urlSession.uploadTask(with: request, fromFile: localURL)
task.earliestBeginDate = Date().advanced(by: 120)//when setting this to zero the upload runs straight away.
task.resume()
}
func addBackgroundRefreshTask(_ task: WKURLSessionRefreshBackgroundTask) {
backgroundTasks.append(task)
}
//gets called when the task background task completes
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
//PROBLEM. This delegate method is never called
if let sessionId = session.configuration.identifier {
//TODO delete any local data copies
//set the session to nil so the system doesn't try to execute it again
BackgroundURLSessions.shared.sessions[sessionId] = nil
}
for task in backgroundTasks {
task.setTaskCompletedWithSnapshot(false)
}
}
//gets called if the background task throws an error
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
//PROBLEM. This delegate method is never called
}
}
BackgroundURLSessions class
class BackgroundURLSessions: NSObject {
static let shared: BackgroundURLSessions = BackgroundURLSessions()
var sessions = [String: UploadSession]()
override private init() {
super.init()
}
}
Extension Delegate class
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
// Use a switch statement to check the task type
switch task {
case let urlSessionTask as WKURLSessionRefreshBackgroundTask:
// Be sure to complete the URL session task once you’re done.
if let session = BackgroundURLSessions.shared.sessions[urlSessionTask.sessionIdentifier] {
session.addBackgroundRefreshTask(urlSessionTask)
} else {
urlSessionTask.setTaskCompletedWithSnapshot(false)
}
default:
// make sure to complete unhandled task types
task.setTaskCompletedWithSnapshot(false)
}
}
}
Any help really appreciated as I'm really struggling to get this work and it's a vital part of the application. Thanks.
In the Combine framework, I have found following text
The Combine framework provides a declarative approach for how your app
processes events. Rather than potentially implementing multiple
delegate callbacks or completion handler
Can somebody tell me what is the difference between completion handler and callback in Swift?
A delegate callback is when you have a delegate that you know in advance implements a method (e.g. because it adopts a protocol), and you call that method by name.
A completion handler is when someone hands you a function and you just call it blindly by reference.
to be clear actually you can achieve the same functionality with both ways however the there are completely different approach for designing your app
let me clarify with simple example the difference between both with the same function is making network call
delegate protocol
// enum to define the request type
enum RequestTypes {
case UserRegister
case UserLogin
}
protocol ServiceDelegate {
func didCompleteRequest(responseModel: AnyObject, tag: RequestTypes)
}
// you can also add default impl to the methods here
extension ServiceDelegate {
func didCompleteRequest(responseModel: AnyObject, tag: RequestTypes){}
}
class BaseService<ResponseModel: Codable> {
var session: URLSession!
var delegate: ServiceDelegate?
// MARK: Rebuilt Methods
func FireRequest(){
// Request Preparation
let serviceUrl = URL(string: /* your url */)!
var request = URLRequest(url: serviceUrl)
request.httpMethod = "GET"
// Firing the request
session = URLSession.init(configuration: URLSessionConfiguration.default)
session.dataTask(with: request) { (data, response, error) in
if let data = data {
do {
guard let object = try? JSONDecoder().decode(ResponseModel.self , from: data) else {/* handle error or call delegate error method here */ return }
delegate?.didCompleteRequest(responseModel: object, tag: .UserLogin)
}
}
}.resume()
}
}
class ViewController: UIViewController, ServiceDelegate {
override func viewDidLoad() {
super.viewDidLoad()
fetchNewData()
}
func fetchNewData(){
let service = BaseService<YourModel>()
service.delegate = self
service.FireRequest()
}
func didCompleteRequest(responseModel: AnyObject, tag: RequestTypes) {
if tag == /* the tag you are waiting */ .UserLogin {
// YourModel is available here
}
}
}
completion handler
class BaseService<ResponseModel: Codable> {
var session: URLSession!
// MARK: Rebuilt Methods
func FireRequest(completion: ((ResponseModel?) -> Void)?){
// Request Preparation
let serviceUrl = URL(string: /* your url */)!
var request = URLRequest(url: serviceUrl)
request.httpMethod = "GET"
// Firing the request
session = URLSession.init(configuration: URLSessionConfiguration.default)
session.dataTask(with: request) { (data, response, error) in
if let data = data {
do {
guard let object = try? JSONDecoder().decode(ResponseModel.self , from: data) else {/* handle error or call delegate error method here */ return }
DispatchQueue.main.async {
completion?(object)
}
}
}
}.resume()
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
fetchNewData()
}
func fetchNewData(){
let service = BaseService<YourModel>()
service.FireRequest(completion: { [weak self] (response) in
// yourModel Available here once the request completed
})
}
}
A delegate callback is one to one communication between various ViewControllers and classes. It basically lets you know that a particular change has been done in particular view or any where else and now you can make change after this action.
While completion handler is a block executed after completing a particular process or task.
Callback is a way to sending data back to some other function on some particular occasion. there are 2 ways to implement callbacks in swift.
Using Protocols / Delegate
Using Completion Handler
Using Protocols / Delegate Example:
Declare Protocol
protocol MyDelegate {
public method(param: String);
}
Your ViewController should extend the delegate
class YourViewController: MyDelegate {
// Your Other methods
func method(param: String) {
// Do your stuff
}
}
Now in your other classes you can send callback to ViewController through delegate object like
delegate.method(param: "your_param");
Using Completion Handler Example:
public func method(param: String, completionHandler: #escaping (_ param: String) -> Void)
{
...
// now you can send data back to the caller function using completionHandler on some particular occasion
completionHandler("param");
}
We can call this function like
method(param: String, completionHandler: { (result, alreadyUserId) in
// here you will receive callback
});
Callbacks and Completion Handlers are synonymous when referring to asynchronous methods.
I’ve found the main difference being in how its used in defining what’s returned to the caller where a callback is used when referring to a method where the scope is returned to the previous calling method and a completion handler refers to a method when it returns some Result type to the caller.
I have tried to use NSURLProtocol to log all requests in a Swift 2.3 project. However not all URL requests are being logged. Specifically all the Alamofire requests are not being recorded.
Sample code
class AppDelegate: NSObject, NSApplicationDelegate{
func applicationDidFinishLaunching(aNotification: NSNotification) {
NSURLProtocol.registerClass(TestURLProtocol)
Alamofire.request(.GET, SomeURL).responseSwiftyJSON({ (request, response, json, error) in })
}
}
class TestURLProtocol: NSURLProtocol {
override class func canInitWithRequest(request: NSURLRequest) -> Bool {
print("request \(request.URL!)") // never called
return false
}
}
I think this is because Alamofire uses the new URLSession API, which is not affected by the NSURLProtocol.registerProtocol call.
You have to create a URLSession with URLSessionConfiguration that has its protocolClasses array set to [TestURLProtocol.self].
But with this you would have to use a custom SessionManager everywhere to log the requests, instead of using the implicit Alamofire.request I think.
What I ended up using was the pod OHHTTPStubs. I added the following code to my app delegate to log every host being used.
func applicationDidFinishLaunching(aNotification: NSNotification) {
var hosts = [String: Int]()
stub({ req in
if let url = req.URL, let host = url.host{
var count = 1
if let c = hosts[host]{
count = c + 1
}
hosts[host] = count
print("Request #\(count): Host = \(host)")
}
return false
},
response:{_ in return OHHTTPStubsResponse()}
);
}
First of all, I am just a beginner who is currently developing an app with the Swift language, so please don't mind my question too much because I really need to know and I am having trouble with maintaining the code that I constructed.
It's about the async delegate pattern.
Here is my API class. Assume that there are many API classes like that which makes async calls.
protocol InitiateAPIProtocol{
func didSuccessInitiate(results:JSON)
func didFailInitiate(err:NSError)
}
class InitiateAPI{
var delegate : InitiateAPIProtocol
init(delegate: InitiateAPIProtocol){
self.delegate=delegate
}
func post(wsdlURL:String,action:String,soapMessage : String){
let request = NSMutableURLRequest(URL: NSURL(string: wsdlURL)!)
let msgLength = String(soapMessage.characters.count)
let data = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
request.HTTPMethod = "POST"
request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
request.addValue(action, forHTTPHeaderField: "SOAPAction")
request.HTTPBody = data
let task = session.dataTaskWithRequest(request) {
data, response, error in
if error != nil {
self.delegate.didFailInitiate(error!)
return
}
let jsonData = JSON(data: data)
self.delegate.didSuccessInitiate(jsonData)
}
task.resume()
}
func doInitiate(token : String){
let soapMessage = “”
// WSDL_URL is the main wsdl url i will request.
action = “”
post(WSDL_URL, action: action, soapMessage: soapMessage)
}
}
Here is my ViewController:
class ViewController : UIViewController,InitiateAPIProtocol{
var initiateAPI : InitiateAPI!
var token : String = “sWAFF1”
override func viewWillAppear(animated: Bool) {
// Async call start
initiateAPI = InitiateAPI(delegate:self)
initiateAPI.doInitiate(token)
}
// Here comes call back
func didSuccessInitiate(results: JSON) {
//handle results
}
func didFailInitiate(err: NSError) {
//handle errors
}
}
My problem is I said that there are many API classes like that, so if one view controller handles 4 API classes, I have to handle many protocol delegates methods as I extend the view controller. There will be many delegates method below of view controller. If other view controllers call the same API and have to handle the same delegates, I have a problem maintaining the code because every time I change some delegate parameters, I have to fix the code at all view controllers which use those API classes.
Is there any other good way to handle async call?
If my question seems a little complex, please leave a comment, I will reply and explain it clearly.
Delegates (OOP) and "completion handlers" (function like programming) just don't fit well together.
In order to increase comprehension and to make the code more concise, an alternative approach is required. One of this approach has been already proposed by #PEEJWEEJ using solely completion handlers.
Another approach is using "Futures or Promises". These greatly extend the idea of completion handlers and make your asynchronous code look more like synchronous.
Futures work basically as follows. Suppose, you have an API function that fetches users from a remote web service. This call is asynchronous.
// Given a user ID, fetch a user:
func fetchUser(id: Int) -> Future<User> {
let promise = Promise<User>()
// a) invoke the asynchronous operation.
// b) when it finished, complete the promise accordingly:
doFetchAsync(id, completion: {(user, error) in
if error == nil {
promise.fulfill(user!)
} else {
promise.reject(error!)
}
})
return.promise.future
}
First, the important fact here is, that there is no completion handler. Instead, the asynchronous function returns you a future. A future represents the eventual result of the underlying operation. When the function fetchUser returns, the result is not yet computed, and the future is in a "pending" state. That is, you cannot obtain the result immediately from the future. So, we have to wait?? - well not really, this will be accomplished similar to an async function with a completion handler, i.e. registering a "continuation":
In order to obtain the result, you register a completion handler:
fetchUser(userId).map { user in
print("User: \(user)")
}.onFailure { error in
print("Error: \(error)")
}
It also handles errors, if they occur.
The function map is the one that registered the continuation. It is also a "combinator", that is it returns another future which you can combine with other functions and compose more complex operations.
When the future gets finally completed, the code continues with the closure registered with the future.
If you have two dependent operations, say OP1 generates a result which should be used in OP2 as input, and the combined result should be returned (as a future), you can accomplish this in a comprehensive and concise manner:
let imageFuture = fetchUser(userId).flatMap { user in
return user.fetchProfileImage()
}
imageFuture.onSuccess { image in
// Update table view on main thread:
...
}
This was just a very short intro into futures. They can do much more for you.
If you want to see futures in action, you may start the Xcode playgrounds "A Motivating Example" in the third party library FutureLib (I'm the author). You should also examine other Future/Promise libraries, for example BrightFutures. Both libraries implement Scala-like futures in Swift.
Have you looked into NSNotificationCenter?
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/
You'll be able to post events from your api class, then each view controller would subscribe to the events and be notified accordingly
Does that make sense? There are lots of good examples of this pattern:
https://stackoverflow.com/a/24049111/2678994
https://stackoverflow.com/a/28269217/2678994
I've updated your code below:
class InitiateAPI{
//
// var delegate : InitiateAPIProtocol
// init(delegate: InitiateAPIProtocol){
// self.delegate=delegate
// }
func post(wsdlURL:String,action:String,soapMessage : String){
let request = NSMutableURLRequest(URL: NSURL(string: wsdlURL)!)
let msgLength = String(soapMessage.characters.count)
let data = soapMessage.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
request.HTTPMethod = "POST"
request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.addValue(msgLength, forHTTPHeaderField: "Content-Length")
request.addValue(action, forHTTPHeaderField: "SOAPAction")
request.HTTPBody = data
let task = session.dataTaskWithRequest(request) {
data, response, error in
if error != nil {
// self.delegate.didFailInitiate(error!)
/* Post notification with error */
NSNotificationCenter.defaultCenter().postNotificationName("onHttpError", object: error)
return
}
let jsonData = JSON(data: data)
// self.delegate.didSuccessInitiate(jsonData)
/* Post notification with json body */
NSNotificationCenter.defaultCenter().postNotificationName("onHttpSuccess", object: jsonData)
}
task.resume()
}
func doInitiate(token : String){
let soapMessage = “”
// WSDL_URL is the main wsdl url i will request.
action = “”
post(WSDL_URL, action: action, soapMessage: soapMessage)
}
}
Your view controller class:
class ViewController : UIViewController { //,InitiateAPIProtocol{
var initiateAPI : InitiateAPI!
var token : String = “sWAFF1”
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.didSuccessInitiate(_:)), name: "onHttpSuccess", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.didFailInitiate(_:)), name: "onHttpError", object: nil)
}
override func viewWillAppear(animated: Bool) {
// Async call start
initiateAPI = InitiateAPI(delegate:self)
initiateAPI.doInitiate(token)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
/* Remove listeners when view controller disappears */
NSNotificationCenter.defaultCenter().removeObserver(self, name: "onHttpSuccess", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: "onHttpError", object: nil)
}
// Here comes call back
func didSuccessInitiate(notification : NSNotification) { //results: JSON) {
if let payload = notification.object as? JSON {
//handle results
}
}
func didFailInitiate(notification : NSNotification) { //err: NSError) {
if let payload = notification.object as? NSError {
//handle errors
}
}
}
Instead of using a delegate, you could (should?) use closers/functions:
func post(/*any other variables*/ successCompletion: (JSON) -> (), errorCompletion: (NSError) ->()){
/* do whatever you need to*/
/*if succeeds*/
successCompletion("")
/*if fails*/
errorCompletion(error)
}
// example using closures
post({ (data) in
/* handle Success*/
}) { (error) in
/* handle error */
}
// example using functions
post(handleData, errorCompletion: handleError)
func handleData(data: JSON) {
}
func handleError(error: NSError) {
}
This would also give you the option to handle all the errors with one function.
Also, it's ideal to parse your JSON into their desired objects before returning them. This keeps your ViewControllers clean and makes it clear where the parsing will occur.