The Network Connection was lost from Alamofire - ios

I got this error many times in my project and it very irritates me because I have full internet connectivity though I get this error repeatedly.
What is the solution...?
I am using
Swift - 3.3
Alamofire - 4.7.3
API Calling Code:
class func post(_ URL: String, withParams params: [String : AnyObject], onView parentView: UIViewController, hnadler completion: #escaping ([AnyHashable: Any]!) -> Void) {
var URLString = String()
URLString = APIConstants.kServerURL + URL
var headers = [String: String]()
headers["Content-Type"] = "application/x-www-form-urlencoded"
Alamofire.request(URLString,method: .post, parameters: params , headers : headers)
.validate(contentType: ["application/vnd.api+json"])
.responseJSON { response in
switch response.result {
case .success( _):
var completionVarible = [NSObject : AnyObject]()
completionVarible = response.result.value as! [AnyHashable: Any]! as [NSObject : AnyObject]
completion(completionVarible)
case .failure(let error):
self.handleFailureResponse(Error: error as NSError?, parentView: parentView)
}
}
}

If the alert appears immediately you may try to change the cache policy to
.reloadIgnoringCacheData

I don't know exactly why this error occurs but I have also faced this error, so I have put one solution. This error has some unique error code. So check that error code and ignore alert over there or you can again try this API call if this error code you get.

I find one solution to this issue if you using Alamofire.
First import Alamofire in your common class otherwise you can create a separate class for check internet connection.
import Alamofire
class Connectivity {
class func isConnectedToInternet() ->Bool {
return NetworkReachabilityManager()!.isReachable
}
}
Call below method before you API call
if !Connectivity.isConnectedToInternet() {
ServiceHandler.ShowAlert(message: "Check your internet connectivity.", title: "Error", parentView: self) //This is my comman method for display alert.
return
}

Related

Postman giving different response

I am a beginner in swift iOS and I am doing login module of an application in iOS but I am stuck at one thing I have login api but when I am checking response in postman when I am sending parameters as "raw" than it is showing user logged in but when I am sending the same parameters as "form-data" than it is showing wrong id and password....can anyone tell me how to send parameters as "raw" so that I can get correct response?? Thanks for your help!!
Please try this method if you are using Alamofire library for API call.
func request(_ method: HTTPMethod
, _ URLString: String
, parameters: [String : AnyObject]? = [:]
, headers: [String : String]? = [:]
, onView: UIView?, vc: UIViewController, completion:#escaping (Any?) -> Void
, failure: #escaping (Error?) -> Void) {
Alamofire.request(URLString, method: method, parameters: parameters, encoding: JSONEncoding.default, headers: headers)
.responseJSON { response in
switch response.result {
case .success:
completion(response.result.value!)
case .failure(let error):
failure(error)
}
}
}
Also remember you need to pass Application/JSON header while calling this method.
["Content-Type": "application/json"]
From: http://toolsqa.com/postman/post-request-in-postman/
Check if your raw is using the correct format type as specified below.

How to pass data between two ViewController using closure

I want to know how to pass data using closure. I know that there are three types of data pass approaches:
delegate
Notification Center
closure
I want proper clarification of closure with an example.
Well passing data with blocks / closures is a good and reasonable approach and much better than notifications.
Below is the same code for it.
First ViewController (where you make object of Second ViewController)
#IBAction func push(sender: UIButton) {
let v2Obj = storyboard?.instantiateViewControllerWithIdentifier("v2ViewController") as! v2ViewController
v2Obj.completionBlock = {[weak self] dataReturned in
//Data is returned **Do anything with it **
print(dataReturned)
}
navigationController?.pushViewController(v2Obj, animated: true)
}
Second ViewController (where data is passed back to First VC)
import UIKit
typealias v2CB = (infoToReturn :String) ->()
class v2ViewController: UIViewController {
var completionBlock:v2CB?
override func viewDidLoad() {
super.viewDidLoad()
}
func returnFirstValue(sender: UIButton) {
guard let cb = completionBlock else {return}
cb(infoToReturn: "any value")
}
}
This example explains use of service call with Alamofire and send the response back to calling View Controller with closure.
Code in Service Wrapper Class:
Closure declaration
typealias CompletionHandler = (_ response: NSDictionary?, _ statusCode: Int?, _ error: NSError?) -> Void
Closure implementation in method
func doRequestFor(_ url : String, method: HTTPMethod, dicsParams : [String: Any]?, dicsHeaders : [String: String]?, completionHandler:#escaping CompletionHandler) {
if !NetworkReachablity().isNetwork() {
return
}
if (dicsParams != nil) {print(">>>>>>>>>>>>>Request info url: \(url) --: \(dicsParams!)")}
else {print(">>>>>>>>>>>>>Request info url: \(url)")}
Alamofire.request(url, method: method, parameters: dicsParams, encoding:
URLEncoding.default, headers: dicsHeaders)
.responseJSON { response in
self.handleResponse(response: response, completionHandler: completionHandler)
}
}
Code at calling view controller:
ServiceWrapper().doRequestFor(url, method: .post, dicsParams: param, dicsHeaders: nil) { (dictResponse, statusCode, error) in
}

Detecting if there is data transfer in iOS app with Swift

I was wanting to know if anyone knows how to detect if there is data transfer or not within an iOS app. I am using a Swift Reachability library to detect if there is wifi or cellular connection, but this does not let me know if there is actual data being transferred.
My app starts out with a WKWebView, and in the case there is no data being transferred to load the webview that takes up the entire screen, I would like to present an alertController to the user.
Reachability lets me know if there is a connection to wifi or a cellular network, but they don't help with letting me know if there is any data being transferred. I'm testing with my wifi on, but with no network connection, and I'm unable to present any alertController as connection is always passing true for isReachable().
Does anyone know how to go about this? Thank you.
Alamofire Provide the functionaly to check the network status. So here it is in detail with some other functionality.
I have created an APIClient Manager class in my project.
import UIKit
import Alamofire
import AlamofireNetworkActivityIndicator
// Completion handeler
typealias TSAPIClientCompletionBlock = (response: NSHTTPURLResponse?, result: AnyObject?, error: NSError?) -> Void
// MARK: -
class TSAPIClient: Manager {
// MARK: - Properties methods
private let manager = NetworkReachabilityManager(host: "www.apple.com") // could be any website, Just to check connectivity
private var serviceURL: NSURL?
// MARK: - init & deinit methods
init(baseURL: NSURL,
configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration(),
delegate: SessionDelegate = SessionDelegate(),
serverTrustPolicyManager: ServerTrustPolicyManager? = nil) {
super.init(configuration: configuration, delegate: delegate, serverTrustPolicyManager: serverTrustPolicyManager)
var aURL = baseURL
// Ensure terminal slash for baseURL path, so that NSURL relativeToURL works as expected
if aURL.path?.characters.count > 0 && !aURL.absoluteString.hasSuffix("/") {
aURL = baseURL.URLByAppendingPathComponent("")
}
serviceURL = baseURL
NetworkActivityIndicatorManager.sharedManager.isEnabled = true
observeNetworkStatus()
}
// MARK: - Public methods
func serverRequest(methodName: String, parameters: [String : AnyObject]? , isPostRequest: Bool, headers: [String : String]?, completionBlock: TSAPIClientCompletionBlock) -> Request {
let url = NSURL(string: methodName, relativeToURL: serviceURL)
print("\(url)")
let request = self.request(isPostRequest ? .POST : .GET, url!, parameters: parameters, encoding: isPostRequest ? .JSON : .URL, headers: headers)
.validate(statusCode: 200..<600)
.responseJSON { response in
switch response.result {
case .Success:
completionBlock(response: response.response, result: response.result.value, error: nil
)
break
case .Failure(let error):
completionBlock(response: response.response, result: nil, error: error)
break
}
}
return request
}
func cancelAllRequests() {
session.getAllTasksWithCompletionHandler { tasks in
for task in tasks {
task.cancel()
}
}
}
// this will contiously observe network changes
func observeNetworkStatus() {
manager?.listener = { status in
print("Network Status Changed: \(status)")
if status == .NotReachable {
} else if status == .Unknown {
} else {
// status is reachable
// posting a notification for network connectivity NSNotificationCenter.defaultCenter().postNotificationName(kNetworkStatusConnectedNotification, object: nil)
}
}
manager?.startListening()
}
// If you want to check network manually for Example before pushing a viewController.
func isNetworkReachable() -> Bool {
return manager?.isReachable ?? false
}
}
kNetworkStatusConnectedNotification is a constant declared as following
let kNetworkStatusConnectedNotification = "kNetworkStatusConnectedNotification"
Now, Where you want to observe the network changes in your application, register the notification in that viewController as follows
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(HomeViewController.applicationIsConnectedToNetwok), name: kNetworkStatusConnectedNotification, object: nil)
and in the selector do whatever you want to do when you get network connectivity
func applicationIsConnectedToNetwok() {
// e.g LoadData
}
I have written this method in my Session class and I am using it to check connectivity mannualy
func isNetworkReachable() -> Bool {
return YourAPIClient.sharedClient.isNetworkReachable()
}
I have added some comments in the code for your understanding. I hope it helps.

HTTP requests being blocked by a previous one using Alamofire

I'm having some trouble with an HTTP request performed from our iOS app to our API. The problem with this request is that it usually takes 30-40s to complete. I don't need to handle the response for now, so I just need to fire it and forget about it. I don't know if the problem is in my code or in the server, that's why I'm asking here.
I'm using Alamofire and Swift 2.2 and all the other requests are working just fine. This is an screenshot from Charles proxy while I was trying to debug it: Charles screenshot
As you can see, the request that blocks the others is the refreshchannels. When that request fires (#6 and #25), the others are blocked and don't finish until the refreshchannels finishes.
Here is the code that triggers that request and also the APIManager that i've built on top of Alamofire:
// This is the method that gets called when the user enables the notifications in the AppDelegate class
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
// Recieve the APNSToken and handle it. I've removed it to make it shorter
// This sends a POST to our API to store some data
APIManager().registerForPushNotifications(parametersPush) { (result) in
switch result {
case .Success (let JSON):
// This is the slow call that blocks the other HTTP requests
APIManager().refreshChannels { _ in } // I don't need to handle the response for now
case .Failure: break
}
}
}
And the manager:
//This is my custom manager to handle all the networking inside my app
class APIManager {
typealias CompletionHandlerType = (Result) -> Void
enum Result {
case Success(AnyObject?)
case Failure(NSError)
}
let API_HEADERS = Helper.sharedInstance.getApiHeaders()
let API_DOMAIN = Helper.sharedInstance.getAPIDomain()
//MARK: Default response to a request
func defaultBehaviourForRequestResponse(response: Response<AnyObject, NSError>, completion: CompletionHandlerType) {
print("Time for the request \(response.request!.URL!): \(response.timeline.totalDuration) seconds.")
switch response.result {
case .Success (let JSON):
if let _ = JSON["error"]! {
let error = NSError(domain: "APIError", code: response.response!.statusCode, userInfo: JSON as? [NSObject : AnyObject])
completion(Result.Failure(error))
} else {
completion(Result.Success(JSON))
}
case .Failure (let error):
completion(Result.Failure(error))
}
}
func refreshChannels(completion: CompletionHandlerType) {
Alamofire.request(.PUT, "\(API_DOMAIN)v1/user/refreshchannels", headers: API_HEADERS).responseJSON { response in
self.defaultBehaviourForRequestResponse(response, completion: completion)
}
}
}
Any help will be appreciated. Have a nice day!

URLSessionUploadTask getting automatically cancelled instantly

I'm having this weird issue in which a newly created URLSessionUploadTask gets cancelled instantly. I'm not sure if it's a bug with the current beta of Xcode 8.
I suspect it might be a bug because the code I'm about to post ran fine exactly once. No changes were made to it afterwards and then it simply stopped working. Yes, it literally ran once, and then it stopped working. I will post the error near the end.
I will post the code below, but first I will summarize how the logic here works.
My test, or user-exposed API (IE for use in Playgrounds or directly on apps), calls the authorize method. This authorize method will in turn call buildPOSTTask, which will construct a valid URL and return a URLSessionUploadTask to be used by the authorize method.
With that said, the code is below:
The session:
internal let urlSession = URLSession(configuration: .default)
Function to create an upload task:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
let fullURL: URL
if let gets = getParams {
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
} else {
fullURL = URL(string: path, relativeTo: baseURL)!
}
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data? = nil
if let posts = postParams {
do {
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
} catch let error as NSError {
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
}
}
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
}
The authentication function, which uses a task created by the above function:
public func authorize(withCode code: String?, completion: AccessTokenExchangeCompletionHandler) {
// I have removed a lot of irrelevant code here, such as the dictionary building code, to make this snippet shorter.
let obtainTokenTask = buildPOSTTask(onURLSession: self.urlSession, appendingPath: "auth/access_token", withPostParameters: nil, getParameters: body, httpHeaders: nil) { (data, response, error) in
if let err = error {
completion(error: err)
} else {
print("Response is \(response)")
completion(error: nil)
}
}
obtainTokenTask.resume()
}
I caught this error in a test:
let testUser = Anilist(grantType: grant, name: "Test Session")
let exp = expectation(withDescription: "Waiting for authorization")
testUser.authorize(withCode: "a valid code") { (error) in
if let er = error {
XCTFail("Authentication error: \(er.localizedDescription)")
}
exp.fulfill()
}
self.waitForExpectations(withTimeout: 5) { (err) in
if let error = err {
XCTFail(error.localizedDescription)
}
}
It always fails instantly with this error:
Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED,
NSLocalizedDescription=cancelled,
NSErrorFailingURLStringKey=https://anilist.co/api/auth/access_token?client_secret=REMOVED&grant_type=authorization_code&redirect_uri=genericwebsitethatshouldntexist.bo&client_id=ibanez-hod6w&code=REMOVED}
Here's a few things to keep in mind:
The URL used by the session is valid.
All credentials are valid.
It fails instantly with a "cancelled" error, that simply did not happen before. I am not cancelling the task anywhere, so it's being cancelled by the system.
It also fails on Playgrounds with indefinite execution enabled. This is not limited to my tests.
Here's a list of things I have tried:
Because I suspect this is a bug, I first tried to clean my project, delete derived data, and reset all simulators. None of them worked.
Even went as far restarting my Mac...
Under the small suspicion that the upload task was getting deallocated due to it not having any strong pointers, and in turn calling cancel, I also rewrote authorize to return the task created by buildPOSTTask and assigned it to a variable in my test. The task was still getting cancelled.
Things I have yet to try (but I will accept any other ideas as I work through these):
Run it on a physical device. Currently downloading iOS 10 on an iPad as this is an iOS 10 project. EDIT: I just tried and it's not possible to do this.
I'm out of ideas of what to try. The generated logs don't seem to have any useful info.
EDIT:
I have decided to just post the entire project here. The thing will be open source anyway when it is finished, and the API credentials I got are for a test app.
ALCKit
After struggling non-stop with this for 6 days, and after googling non-stop for a solution, I'm really happy to say I have finally figured it out.
Turns out that, for whatever mysterious reason, the from: parameter in uploadTask(with:from:completionHandler) cannot be nil. Despite the fact that the parameter is marked as an optional Data, it gets cancelled instantly when it is missing. This is probably a bug on Apple's side, and I opened a bug when I couldn't get this to work, so I will update my bug report with this new information.
With that said, everything I had to do was to update my buildPOSTTask method to account for the possibility of the passed dictionary to be nil. With that in place, it works fine now:
internal func buildPOSTTask(onURLSession urlSession: URLSession, appendingPath path: String, withPostParameters postParams: [String : String]?, getParameters getParams: [String : String]?, httpHeaders: [String : String]?, completionHandler completion: URLSessionUploadTaskCompletionHandler) -> URLSessionUploadTask {
let fullURL: URL
if let gets = getParams {
fullURL = buildURL(appendingPath: path, withGetParameters: gets)
} else {
fullURL = URL(string: path, relativeTo: baseURL)!
}
var request = URLRequest(url: fullURL)
request.httpMethod = "POST"
var postParameters: Data
if let posts = postParams {
do {
postParameters = try JSONSerialization.data(withJSONObject: posts, options: [])
} catch let error as NSError {
fatalError("[\(#function) \(#line)]: Could not build POST task: \(error.localizedDescription)")
}
} else {
postParameters = Data()
}
let postTask = urlSession.uploadTask(with: request, from: postParameters, completionHandler: completion)
return postTask
}
Are you by any chance using a third party library such as Ensighten? I had the exact same problem in XCode 8 beta (works fine in XCode 7) and all of my blocks with nil parameters were causing crashes. Turns out it was the library doing some encoding causing the issue.
For me, this was a weak reference causing the issue, so I changed
completion: { [weak self] (response: Result<ResponseType, Error>)
to
completion: { [self] (response: Result<ResponseType, Error>)

Resources