Alamofire Nonblocking Connection

Alamofire nonblocking connection

You don't want to do the networking inside your model object. Instead, you want to handle the networking layer in some more abstract object such as a Service of class methods. This is just a simple example, but I think this will really get you heading in a much better architectural direction.

import Alamofire

struct User {
let name: String
let companyName: String
}

class UserService {

typealias UserCompletionHandler = (User?, NSError?) -> Void

class func getUser(completionHandler: UserCompletionHandler) {
let loginRequest = Alamofire.request(.GET, "login/url")
loginRequest.responseJSON { request, response, json, error in
if let error = error {
completionHandler(nil, error)
} else {
println("Login Succeeded!")

let userRequest = Alamofire.request(.GET, "user/url")
userRequest.responseJSON { request, response, json, error in
if let error = error {
completionHandler(nil, error)
} else {
let jsonDictionary = json as [String: AnyObject]
let user = User(
name: jsonDictionary["name"]! as String,
companyName: jsonDictionary["companyName"]! as String
)

completionHandler(user, nil)
}
}
}
}
}
}

UserService.getUser { user, error in
if let user = user {
// do something awesome with my new user
println(user)
} else {
// figure out how to handle the error
println(error)
}
}

Since both the login and user requests are asynchronous, you cannot start using the User object until both requests are completed and you have a valid User object. Closures are a great way to capture logic to run after the completion of asynchronous tasks. Here are a couple other threads on Alamofire and async networking that may also help you out.

  • Handling Multiple Network Calls
  • Returning a Value with Alamofire

Hopefully this sheds some light.

How to handle multiple network call in Alamofire

Your assessment is 100% correct. At the moment, the two options you laid out are really the only possible approaches. I agree with you that your second option is much better than the first given your use case.

If you wish to combine ReactiveCocoa with Alamofire, then that's certainly possible, but hasn't been done yet to my knowledge. You could also investigate whether PromiseKit would be able to offer some assistance, but it hasn't been glued together with Alamofire yet either. Trying to combine either of these libraries with the Alamofire response serializers will not be a trivial task by any means.

Switching gears a bit, I don't really think ReactiveCocoa or PromiseKit are very well suited for your use case since you aren't chaining service calls, you are running them in parallel. Additionally, you still need to run all your parsing logic and determine whether each one succeeded or failed and then update your application accordingly. What I'm getting at is that Option 2 is going to be your best bet by far unless you want to go to all the effort of combining PromiseKit or ReactiveCocoa with Alamofire's response serializers.

Here's what I would suggest to keep things less complicated.

import Foundation
import Alamofire

class ParallelServiceCaller {
var firstServiceCallComplete = false
var secondServiceCallComplete = false

func startServiceCalls() {
let firstRequest = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["first": "request"])
firstRequest.responseString { request, response, dataString, error in
self.firstServiceCallComplete = true
self.handleServiceCallCompletion()
}

let secondRequest = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["second": "request"])
secondRequest.responseString { request, response, dataString, error in
self.secondServiceCallComplete = true
self.handleServiceCallCompletion()
}
}

private func handleServiceCallCompletion() {
if self.firstServiceCallComplete && self.secondServiceCallComplete {
// Handle the fact that you're finished
}
}
}

The implementation is really clean and simple to follow. While I understand your desire to get rid of the completion flags and callback function, the other options such as ReactiveCocoa and/or PromiseKit are still going to have additional logic as well and may end up making things more complicated.

Another possible option is to use dispatch groups and semaphores, but that really adds complexity, but could get you much closer to a ReactiveCocoa or PromiseKit styled approach.

I hope that helps shed some light.

Swift Alamofire SwiftyJSON Asynchronous/Synchronous Class Methods

You need to set a callback function to be called whenever you want inside your loginPassword() function.

This could be a way of achieving it:

func loginPassword(callback: ((isOk: Bool)->Void)?) -> String{
var loginJSON = ["auth": ["passwordCredentials": ["username": _username, "password": _password]]];
var returnJSON: String

request(.POST, baseUrl, parameters: loginJSON, encoding: .JSON)
.responseJSON { (request, response, data, error) in
if let anError = error{
// got an error in getting the data, need to handle it
println("error calling POST on /posts")
println(error)

callback?(isOk: false)
}
else if let data: AnyObject = data{
// handle the results as JSON, without a bunch of nested if loops
let post = JSON(data)
// to make sure it posted, print the results
println("JSON Returned")

callback?(isOk: true)
}
}
}

and then...

override func viewDidLoad() {
super.viewDidLoad()

var identity = Identity(username: "John Apleseed", apiKey: "213123123")

identity.loginPassword { (isOK) -> Void in
if (isOK) {
//do good stuff here
}else{
// do error handling here
}

}
}

UPDATE

Also, your calling function can look like this:

override func viewDidLoad() {
super.viewDidLoad()

var identity = Identity(username: "John Apleseed", apiKey: "213123123")
identity.loginPassword(handlePasswordRequest)
}

and you can add as many callback handlers as you may need without messing around with a bunch of nested closures...

private func handlePasswordRequest(isOK: Bool){
if (isOK) {
//do good stuff here
}else{
// do error handling here
}
}

UPDATE 2

If you are in need to call a callback deep into the hierarchy of calls, then you will need to pass the callback as a parameter of every previous closures.

UPDATE 3

I would give a try to RxAlamofire and all about RxSwift

making an asynchronous alamofire request synchronous

It's quite easy to implement a completion block in Swift.

This is your function with a completion block

func checkIfUserExistsInDB(userName: String, completion:(String) -> Void)
{
Alamofire.request(.POST, "http://blablabla.com/getuserdata", parameters: ["queryValue": userName,], encoding:.JSON).responseJSON { request, response, result in
switch result {
case .Success(let JSON):
let info = String(data: JSON.dataUsingEncoding(NSUTF8StringEncoding)!, encoding: NSUTF8StringEncoding)!
completion(info)

case .Failure(let data, _):
if let errorData = data, info = String(data: errorData, encoding: NSUTF8StringEncoding) {
completion(info)
}
}
}
}

and can be called with (info is the asynchronously returned string)

checkIfUserExistsInDB("string") { (info) in
print(info)
}

Checking for multiple asynchronous responses from Alamofire and Swift

Better than that looping process, which would block the thread, you could use dispatch group to keep track of when the requests were done. So "enter" the group before issuing each of the requests, "leave" the group when the request is done, and set up a "notify" block/closure that will be called when all of the group's tasks are done.

For example, in Swift 3:

let group = DispatchGroup()

group.enter()
retrieveDataFromURL(url1, parameters: firstParameters) {
group.leave()
}

group.enter()
retrieveDataFromURL(url2, parameters: secondParameters) {
group.leave()
}

group.notify(queue: .main) {
print("both requests done")
}

Or, in Swift 2:

let group = dispatch_group_create()

dispatch_group_enter(group)
retrieveDataFromURL(url1, parameters: firstParameters) {
dispatch_group_leave(group)
}

dispatch_group_enter(group)
retrieveDataFromURL(url2, parameters: secondParameters) {
dispatch_group_leave(group)
}

dispatch_group_notify(group, dispatch_get_main_queue()) {
print("both requests done")
}

The other approach is to wrap these requests within an asynchronous NSOperation subclass (making them cancelable, giving you control over constraining the degree of concurrency, etc.), but that's more complicated, so you might want to start with dispatch groups as shown above.

Proper model for multiple Alamofire requests for multiple websites

There are many ways to design this type of abstraction. I tend to lean towards simplicity as much as possible in my architectural designs if possible. A great pattern here is to use a Service object with class methods to handle calling your different services, parsing the result and calling a success or failure closure.

You can also use a completion handler that doesn't split the success and failure into two things, but then you need to handle the failure or success in your caller objects which I don't really like. Here's an example of the Service design in action.

FirstNewsService

import Alamofire

struct News {
let title: String
let content: String
let date: NSDate
let author: String
}

class FirstNewsService {

typealias NewsSuccessHandler = ([News]) -> Void
typealias NewsFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void

// MARK: - Fetching News Methods

class func getNews(#success: NewsSuccessHandler, failure: NewsFailureHandler) {
login(
success: { apiKey in
FirstNewsService.fetch(
apiKey: apiKey,
success: { news in
success(news)
},
failure: { response, json, error in
failure(response, json, error)
}
)
},
failure: { response, json, error in
failure(response, json, error)
}
)
}

// MARK: - Private - Helper Methods

private class func login(#success: (String) -> Void, failure: (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) {
let request = Alamofire.request(.GET, "login/url")
request.responseJSON { _, response, json, error in
if let error = error {
failure(response, json, error)
} else {
// NOTE: You'll need to parse here...I would suggest using SwiftyJSON
let apiKey = "12345678"
success(apiKey)
}
}
}

private class func fetch(
#apiKey: String,
success: ([News]) -> Void,
failure: (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void)
{
let request = Alamofire.request(.GET, "fetch/url")
request.responseJSON { _, _, json, error in
if let error = error {
failure(response, json, error)
} else {
// NOTE: You'll need to parse here...I would suggest using SwiftyJSON
let news = [News]()
success(news)
}
}
}
}

Inside a View Controller

override func viewDidLoad() {
super.viewDidLoad()

FirstNewsService.getNews(
success: { news in
// Do something awesome with that news
self.tableView.reloadData()
},
failure: { response, json, error in
// Be flexible here...do you want to retry, pull to refresh, does it matter what the response status code was?
println("Response: \(response)")
println("Error: \(error)")
}
)
}

Feel free to mod the design however you like to tailor it to your use cases. None of this pattern is set in stone. It just gives you a common way to construct different services. @mattt also has some really cool patterns (Router and CRUD) in the Alamofire README which I would highly recommend reading through. They are definitely more complicated though and still require a Service type of object to maximize code reuse.

Hopefully that helps shed some light.

Connecting wifi without internet not recognised with NetworkReachabilityManager or Reachability

How did you check connection? did you try to reach any host something like below.

do {
Network.reachability = try Reachability(hostname: "www.google.com")
do {
try Network.reachability?.start()
} catch let error as Network.Error {
print(error)
} catch {
print(error)
}
} catch {
print(error)
}


Related Topics



Leave a reply



Submit