iOS: Intermittently slow network requests - ios

I have an iOS application that has two requests that have large response objects and typically take ~100-200ms to complete when connectivity is good. When connectivity is average these requests seem to get hung-up somewhere ~20% of the time and I've had them take anywhere from 30 to 60s. I'll then restart the app and try the request again seconds later and it will complete in the expected time (100-200 ms)
I've tested the api that the endpoints are querying with other clients (cURL, Postman) and haven't had any issue so I'm fairly certain this has to do with my front-end configuration. I'm using the Alamofire library to handle the requests.
Here's the code for one of the requests:
func login(_ params: [String: String], completion: #escaping (Response<UserResponseSuccess, UserResponseFail>) -> Void) {
Alamofire.request(self.url!, method: .post, parameters: params).responseJSON {
response in
if response.result.isSuccess {
let respJSON: JSON = JSON(response.result.value!)
if response.response?.statusCode == 200 {
// do stuff with json
let resp = UserResponseSuccess()
completion(Response.success(resp))
} else {
logRequestError()
let resp = UserResponseFail()
completion(Response.failure(resp))
}
} else {
logServerError()
let resp = UserResponseFail()
completion(Response.failure(resp))
}
}
}
And here's the request log:
2019-08-17 14:03:16.832462-0600 Debug - foo[17418:3273774] CredStore - performQuery - Error copying matching creds. Error=-25300, query={
class = inet;
"m_Limit" = "m_LimitAll";
ptcl = htps;
"r_Attributes" = 1;
sdmn = "foo.bar.com";
srvr = "foo.bar.com";
sync = syna;
}
$ curl -v \
-X POST \
-H "User-Agent: Debug - foo/1.3 (com.foo.bar.debug; build:4; iOS 12.4.0) Alamofire/4.7.3" \
-H "Content-Type: application/x-www-form-urlencoded; charset=utf-8" \
-H "Accept-Language: en-US;q=1.0" \
-H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \
-d "email=foo#foo.com&password=bar123" \
"https://rtj.foo.com/users/login"
The requests either go through instantly or don't hit the server. I could probably have these timeout and then retry the request but I don't want to cover up a probable bug.
This also seems to happen more often on a cell network than wifi.
Update: I ran the same query with URLSession instead of Alamofire and received similar results. The error output from the query is:
error:
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."
UserInfo={NSUnderlyingError=0x28228fe10 {Error
Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo= .
{_kCFStreamErrorCodeKey=60, _kCFStreamErrorDomainKey=1}},
NSErrorFailingURLStringKey=https://foo.bar.com/users/login,
NSErrorFailingURLKey=https://foo.bar.com/users/login,
_kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=60,
NSLocalizedDescription=The request timed out.}
Versioning: Alamofire: 4.7.3, Xcode version: 10.3, Swift version: 5

Related

Why I can't access my nodejs local server from iOs simulator

I am trying to make an http request from my xcode to my local server running on nodejs. The following in a part of my iOs code.
let url = URL(string: "http://localhost.com/signup")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
}
task.resume()
I recieve the following the response
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."
Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be
found.
I receive the first error if I use localhost.com and I receive the second error if I change the request to www.localhost.com
in this case you have to use your IP to get access to your localhost
ex: let url = URL(string: "http://192.168.1.105/signup")!
so Instead of using (localhost.com/...) you have to set the IP of your computer where you have LOCAL SERVER.

Swift/iOS URL request to Node.js API endpoint

I have standard code in Swift like below:
private func testFormUrlEncodedRequest() {
let headers = [
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json"
]
let postData = NSMutableData(data: "user_id=5874ae8ae9a98c2d6cef1da8".data(using: String.Encoding.utf8)!)
postData.append("&offset=0".data(using: String.Encoding.utf8)!)
postData.append("&limit=20".data(using: String.Encoding.utf8)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://www.example.com/endpoint")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
ViewController.log(request: request as! URLRequest)
print((request as URLRequest).curlString)
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
ViewController.log(data: data, response: response as? HTTPURLResponse, error: error)
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
}
But the problem is that it hangs on, and then request times out with error.
REST API is written in Node.js and gets error in body-parser module like request aborted.
I can make successfully the same request with POSTMAN or curl (from Terminal) and I get correct response.
Code on server which I have no access to seems to be also rather standard, and was used in previous projects where it was tested to work correctly with iOS apps.
I have no idea why this request goes ok with POSTMAN and doesn't work with URLSession in Swift.
Any help will be beneficial.
Here is error message printed to console I am getting:
Optional(Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x6000013196e0
{Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}},
NSErrorFailingURLStringKey=http://example.com/api/endpoint, NSErrorFailingURLKey=http://example.com/api/endpoint,
_kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.})
This request gives error in such cases:
1. form-url-encoded params in HTTP request body
2. raw application/json params in HTTP request body
3. It works if params are passed in query params
4. It crashes with request aborted error on server side (body-parser module)
5. node.js uses standard app.use()
// support parsing of application/json type post data
app.use(bodyParser.json());
//support parsing of application/x-www-form-urlencoded post data
app.use(bodyParser.urlencoded({ extended: true }));
It uses http without SSL but in Info.plist there is App Transport Security > Allow Arbitrary Loads set to YES etc.
UPDATE:
This is error on server side
BadRequestError: request aborted
at IncomingMessage.onAborted (/Users/michzio/Click5Interactive/Reusable Parts/NetworkApi/node_modules/raw-body/index.js:231:10)
at emitNone (events.js:86:13)
at IncomingMessage.emit (events.js:188:7)
at abortIncoming (_http_server.js:381:9)
at socketOnClose (_http_server.js:375:3)
at emitOne (events.js:101:20)
at Socket.emit (events.js:191:7)
at TCP.Socket._destroy.cb._handle.close [as _onclose] (net.js:510:12)
Node.js Test Code:
const express = require('express');
const port = 9001;
const app = express();
const bodyParser = require('body-parser');
var todos = [{id:1, title:'buy the milk'}, {id:2, title:'rent a car'}, {id:3, title:'feed the cat'}];
var count = todos.length;
app.use(bodyParser.urlencoded({ extended: true }))
app.use(bodyParser.json());
app.get('/test', (request, response) => {
console.log("-----")
console.log(request.params);
console.log(request.body);
console.log(request.query);
console.log("-----")
response.status(200).json( todos );
});
app.listen(port);
It seems that GET + query params works, and POST + body params (url-from-encoded or application/json) also works correctly.
So it doesn't work for GET body params url-form encoded and GET body params application/json. Is it some limitation of URLSession/URLRequest in Swift. POSTMAN can pass params in body with GET and server receives it in request.body !
UPDATE 2!
Yes, it seems that in Android/Kotlin with OkHttpClient there even is not possible to define Request Body with GET method. And there is also this error. Maybe this only works with POSTMAN and curl, and should not be used in real application scenario to join GET and body params.
public fun makeNetworkRequest(v: View) {
object : Thread() {
override fun run() {
val client = OkHttpClient()
val mediaType = MediaType.parse("application/json")
val body = RequestBody.create(mediaType, "{ \"test\" : \"nowy\", \"test2\" : \"lol\" }")
/*
val request = Request.Builder()
.url("http://10.0.2.2:9001/test")
.get()
.addHeader("Content-Type", "application/json")
.build()
*/
val mySearchUrl = HttpUrl.Builder()
.scheme("http")
.host("10.0.2.2")
.port(9001)
.addPathSegment("test")
.addQueryParameter("q", "polar bears")
.build()
val request = Request.Builder()
.url(mySearchUrl)
.addHeader("Accept", "application/json")
.method("GET", body)
.build()
val response = client.newCall(request).execute()
Log.d("RESPONSE", response.toString())
}
}.start()
}

Data task with URL timed out

I am trying to access a website. I am using a dataTaskWithURL request.
Here is my code:
let task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) in
print("2")
if let urlContent = data {
print("3")
let stringContent = NSString(data: urlContent, encoding: NSUTF8StringEncoding)!
let arr = stringContent.componentsSeparatedByString("<b>The computer's solutions will appear below</b><br>")
let second = arr[1]
let newArr = second.componentsSeparatedByString("</tr></table></center><p align=\"center\">")
let results = newArr[0]
self.resultsLabel.text = results
self.stopActivity()
} else {
print(error)
self.stopActivity()
}
})
task.resume()
I have also tried running this code on the main block:
dispatch_async(dispatch_get_main_queue(), {() -> Void in
// Code
})
However, neither of these has been successful. I repeatedly get the following error
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x7ff0a3f4c6e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=THE URL I AM TRYING TO ACCESS, NSErrorFailingURLKey=THE URL I AM TRYING TO ACCESS, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.})
I have tried looking at this post and this one as well. However, I have not been successful with them. The website loads fairly quickly. How do I get rid of this error?
It could be caused by a number of things; If the issue is happening in the Simulator, I suggest restarting it as per the suggestions in the SO posts below:
Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost."
Error: Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."
NSURLErrorDomain Code=-1001 error when a http post request is sent
Undocumented NSURLErrorDomain error codes (-1001, -1003 and -1004) using StoreKit
How to use NSURLSessionDataTask in Swift
It might be help to print response as well to see if the HTTP headers can provide a clue.
I realized that I was using https:// instead of http://.

SoundCloud API: GET request fails with code -1005, using iOS/Alamofire

I'm working on an iOS app where SoundCloud users log in with OAuth in a web view and then the app makes HTTP requests to the SoundCloud API via Alamofire. I've successfully authenticated the user and stored their token (using ABMSoundCloudAPI), but GET requests to https://api.soundcloud.com/me are failing with a -1005 error, "The network connection was lost." This seems to be a common problem with iOS as discussed here, however resetting the simulator doesn't solve the problem for me and the problem also occurs when using a device. I've also tried:
Removing and re-adding the wifi network
Retrying the request programmatically if it fails
Adding a header with "Connection": "Close"
I see the same error in every case. Are there other headers I should try? I'm using these libraries via Cocoapods:
ABMSoundCloudAPI (0.2.1)
AFNetworking (2.6.1)
AFOAuth2Manager (2.2.0)
Alamofire (3.1.2)
SwiftyJSON (2.3.1)
Here is my code:
var retryCount = 0
func getUserInfo(token:String) {
let headers = ["Connection": "Close"]
Alamofire.request(.GET, "https://api.soundcloud.com/me?oauth_token=\(token)", parameters: ["client_id":clientId], encoding: .JSON, headers: headers)
.responseJSON { response in
guard response.result.error == nil else {
print("error calling GET on /me")
print(response.result.error)
if self.retryCount < 2 {
if let token = self.defaults.stringForKey("sc_key_token") {
self.getUserInfo(token)
++self.retryCount
}
}
return
}
guard let value = response.result.value else {
print("Error: did not receive data")
return
}
let user = JSON(value)
print("User info: " + user.description)
}
}
Error message:
Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={NSUnderlyingError=0x126248c10 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={_kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=https://api.soundcloud.com/me?oauth_token=USER_TOKEN, NSErrorFailingURLKey=https://api.soundcloud.com/me?oauth_token=USER_TOKEN, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-4, NSLocalizedDescription=The network connection was lost.}
It seems that this was caused by the request encoding. When I switched from .JSON to .URL, the 1005 error went away.

Restkit Authorization Header disappears

I already searched everywhere and this issue that should have been trivial is taking longer than I wanted therefore I am reaching you for help.
I am using swift and integrated restkit using cocoapods.
Despite, as you can see the screenshots/log, the header is fine however the outgoing http packet is not consistent.
AppDelegate:
RKlcl_configure_by_name("RestKit/Network", RKlcl_vTrace.value);
RKlcl_configure_by_name("RestKit/ObjectMapping", RKlcl_vTrace.value);
ObjectManagerCode:
let objectManager: RKObjectManager = RKObjectManager(baseURL: NSURL(string: Endpoints.BaseUrl.toString()))
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
let username = "TestUser"
let password = "password"
objectManager.HTTPClient.setAuthorizationHeaderWithUsername(username, password: password)
objectManager.HTTPClient.setDefaultHeader("whatIWantForChristmas", value: "You")
objectManager.HTTPClient.allowsInvalidSSLCertificate = true
Request:
var requestUrl = cds.objectManager!.requestWithObject(
nil,
method: RKRequestMethod.GET,
path: endpoint.path,
parameters: endpoint.parameters())
cds.objectManager!.getObjectsAtPath(
endpoint.path,
parameters: endpoint.parameters(),
success:
{
(RKObjectRequestOperation, RKMappingResult) -> Void in
println(RKObjectRequestOperation.description)
Logger.Info("Success");
},
failure: {
(RKObjectRequestOperation, error) -> Void in
Logger.Error("Error: \(error.description)")
println(RKObjectRequestOperation.HTTPRequestOperation.description)
})
Log:
T restkit.network:RKObjectRequestOperation.m:178 GET 'http://website/api?format=json':
request.headers={
Accept = "application/json";
"Accept-Language" = "en;q=1, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5";
Authorization = "Basic VGVzdFVzZXI6cGFzc3dvcmQ=";
"User-Agent" = "malaria-ios/1 (iPhone Simulator; iOS 8.3; Scale/2.00)";
whatIWantForChristmas = You;
}
...
E restkit.network:RKObjectRequestOperation.m:576 Object request
failed:Underlying HTTP request operation failed with error:
Error Domain=org.restkit.RestKit.ErrorDomain Code=-1011 "Expected status code in (200), got 403"
UserInfo=0x7ff54ae4f690 {NSLocalizedRecoverySuggestion={"detail":"Authentication credentials were not provided."}
What I figured out:
I can send anything in the header as long as it isn't "Authorization", if I change to "Authorization2", ok. The header "WhatIWantForChristmas" is also there. The authorization isn't despite being present in the log! It seems that the underlying software filters.
Out of paranoia I tried turning off firewall, cleaning project, reseting iOS simulator and nada. The application I used to see the HTTP packets is Charles.
What am I doing wrong?
Url for the screenshot pf Charles: http://s7.postimg.org/pfwn7kyq2/Screen_Shot_2015_06_20_at_21.jpg

Resources