Why I sometime get 'The connection was lost' error? - ios

so, I am making an app and want to GET data from my own server using alamofire. but I don't know why, I SOMETIME get the error
HTTP load failed (error code: -1005 [1:57]) .
finished with error -
code: -1005. error: The network connection was lost.
and this only happen when I try to get data to my own server. I try to compare to get data from other server, from darksky API, and I never get the error. here is the code I use to get data from my own server and to darksky API.
the error usually occurred when .....
after I start the app, and I push the home button ( so the app enter the background state), and when I back to the app, and at this time, I usually get the error.
so maybe I got the error when prepareDataBeforeEnterForeground is triggered. not always happened, and I don't know why it happened only to my server, and I need to know how to fix this.
does the error comes from my code or from the server?
import UIKit
import Alamofire
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
getData()
getPrepareDataBeforeEnterForeground() // using notification center
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getData()
}
func getData() {
// to make request to my own server OR to DarkSky API
let url = URL(string: "https://")
let parameters : [String:String] = ["xxx": "xxxx"]
Alamofire.request(url!, method: .get, parameters: parameters).responseJSON { (response) in
switch response.result {
case .failure(let error):
print("Error: \(error.localizedDescription)")
case .success(let value) :
// parse the data
}
}
}
func getPrepareDataBeforeEnterForeground() {
// to trigger some function after the enter the background state
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.prepareDataBeforeEnterForeground), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil)
}
#objc func prepareDataBeforeEnterForeground() {
getData()
}
}

For reference, your error code correlates to:
kCFURLErrorNetworkConnectionLost = -1005
From my own personal experience, I get this error when the parameters I'm trying to send to the server are incorrect or malformed.
In your case, I can't seem to see where you are accepting parameters or defining it, so that might be your issue.
If its coming from your server, you will have to check the error logs for the server software (Apache, nginx, etc) (if you have them).
Here is an example of a valid request:
let parameters: Parameters = [
"foo": "bar",
"baz": ["a", 1],
"qux": [
"x": 1,
"y": 2,
"z": 3
]
]
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters)
To add on, it doesn't appear that you are sending any headers either, so you could be causing your server to reject the request completely.

Related

Getting a EXC_BAD_ACCESS error trying to setup the Stripe Integration Code

So my goal is to successfully integrate this API without getting bad access errors. I understand that this error means one of three things, the object is not initialized, the object has already been released, or something else very unlikely.
I really can't figure out where this can possibly be causing an issue, I have Zombies enabled and it doesn't print anything in the console when the app crashes.
UPDATED -> In MyAPIClient.swift file, I have this method:
class MyAPIClient: NSObject, STPCustomerEphemeralKeyProvider {
var baseURL: String = "https://us-central1-attend-41f12.cloudfunctions.net/createEphemeralKeys"
let ticketVC: TicketFormViewController = TicketFormViewController()
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
ticketVC.getUsersStripeCustomerID { (customerid) in
if let id = customerid {
Alamofire.AF.request(self.baseURL, method: .post, parameters: [
"api_version": apiVersion,
"customerId": id
])
.validate(statusCode: 200..<300)
.responseJSON { (responseJSON) in
switch responseJSON.result {
case .success(let json):
completion(json as? [String: Any], nil)
case .failure(let error):
print("Error creating customer Key (retrieving ephemeral key) with Alamofire. See: MyAPIClient - func createCustomerKey")
completion(nil, error)
}
}
}
}
}
}
I need this so I can setup the STPPaymentContext as well as the STPCustomerContext, I do those in the TicketFormViewController():
let customerContext = STPCustomerContext(keyProvider: MyAPIClient())
var paymentContext: STPPaymentContext
init() {
self.paymentContext = STPPaymentContext(customerContext: customerContext)
super.init(nibName: nil, bundle: nil)
self.paymentContext.delegate = self
self.paymentContext.hostViewController = self
self.paymentContext.paymentAmount = 5000
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
I have the delegate methods in the extension of the VC, but I don't think those are apart of the issue, the methods are empty anyways. I'm not sure if this is fully correct, I was basically just following along with the Stripe API Docs, but it seems I've done something wrong, if anybody can help me through this that would be great.
EDIT Here is an image of the error:
In the console, I also get this:
warning: could not execute support code to read Objective-C class data in the process. This may reduce the quality of type information available.
And lastly, the Zombie message:
objc[19951]: Class _NSZombie_NSURLRequestInternal is implemented in both ?? (0x17023a250) and ?? (0x169773350). One of the two will be used. Which one is undefined.
This error gets called right when I segue to the vc that contains all the Stripe STPPaymentContext integration code as well as the initialization code. I'm not even sure if my method even does make a request to the Firebase Cloud Function. I segue by pressing a button, I've put breakpoints leading up to the button being pressed and I can't see anything that could be possibly causing the app to crash like this.

How to retry URL requests indefinitely until success with Alamofire 5?

Is there any recommendation on using Alamofire's RequestRetrier to keep retrying a request until it succeeds?
The issue I'm running into is every time a retry happens it increases the memory of the app. I have a scenario that a user might make a request offline and it should eventually succeed when they regain connection.
Is it not good practice to continually retry a request? Should I use a Timer instead to to recreate the network call if it fails a few times? Should I use some other mechanism that waits for network activity and then sends up a queue of requests (any suggestions on what that would be would be appreciated!)?
Here is an example to see the memory explode. You just need to put your iPhone in airplane mode (so there is no connection) and the memory will keep going up rapidly since the call will happen a lot in this case.
import UIKit
import Alamofire
class ViewController: UIViewController {
let session = Session(configuration: .default)
override func viewDidLoad() {
super.viewDidLoad()
sendRequest()
}
func sendRequest() {
session.request("https://www.google.com", method: .get, interceptor: RetryHandler())
.responseData { (responseData) in
switch responseData.result {
case .success(let data):
print(String(data: data, encoding: .utf8) ?? "nope")
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
class RetryHandler: RequestInterceptor {
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: #escaping (RetryResult) -> Void) {
// keep retrying on failure
completion(.retry)
}
}(

API Request Fails immediately in iOS share extension

Hi I'm using a share extension to post some data to my server using API request (Alamofire), the problem is that the request fails immediately and I don't know how to make it work, I read on some articles that I must use URLSession to send the request in the background but I couldn't find any example to how to make it work with alamofire, here is my code in share extension ViewController:
override func didSelectPost() {
MessageHTTPHelper.submitMessage(contains: contentText, completion: { (response) in
self.showAlert(title: "Result", message: response.result.isSuccess ? "SUCCESS" : "FAILURE")
})
}
The MessageHTTPHelper.submitMessage is a helper function that I defined and it works in the main app perfectly
I don't care about the response, I just want to send the request without any callbacks, can you please give me an example of sending a request in iOS share extension?
After lots of search and tests and fails, finally, this solution worked for me!
and here is my code in didSelectPost()
let body: Parameters = [
"version": Configs.currentReleaseVersion,
"content": cleanTextContent
]
let request = HTTPHelper.makeHTTPRequest(route: "message",
headers: HTTPHelper.defaultAuthHTTPHeaders,
verb: .post,
body: body,
apiV1Included: true)
let queue = DispatchQueue(label: "com.example.background", qos: .background, attributes: .concurrent)
request.responseJSON(queue: queue, options: .allowFragments) { (response) in
if response.result.isFailure {
guard let message = response.error?.localizedDescription else {
self.dismiss()
return
}
self.showAlert(title: "Error", message: message)
}
}
The HTTPHepler.makeHTTPRequest is just a helper method which creates an Alamofire DataRequest Instance with given parameters and returns it

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