Super long wifi request travel time on iPhone 8 / iPhone X - ios

Not sure if it's ok to ask this here but I'm now confronting this frustrating problem and would like to ask for opinion on how to deal with this.
From some of the forum and discussions, looks like iPhone 8 and iPhone X has super slow internet issue when using wifi and running on iOS 11.4 / 11.4.1, and this results to the request travel time rediculously increases to almost 55 seconds (e.g. Hello world pinging test API) for a very simple request in my app. If I turn the wifi off and use 4G instead, the same request travel time is only 2 seconds(the API server is in the US while the app is oversea, so generally bearable), and the same request only travels 2 - 3 seconds on iPhone 6 plus running on iOS 11.2 / 11.4.1 in wifi mode.
I guess this is more a hardware or system side bug and on the app side we may not be able to do anything about this. However, as our client users who use iPhone 8 are unhappy about the waiting time and insist on solving it, and I also found that if I call the hello world API from safari browser of the iPhone, things are not that bad. Therefore, I would like to know if there is anything the app side can do (ex. detect this issue and do the workaround) to fix this or sooth the awful user experiences?
P.S. My app is written in Swift and I don't use third party library such as Alamofire to manage requests, simply use the build-in functionalities in Foundation. I would like to post some code on my sending request here, even if this may not help much.
func sendRequest(request: URLRequest, completion: #escaping (Int, Data?, Error?) -> Void) {
let task = URLSession.shared.dataTask(with: request) {data, response, error in
guard let data = data, error == nil else {
print("HTTP request error=\(String(describing: error))")
// use 999 to represend unknown error
completion(HTTPHelper.DEFAULT_STATUS_CODE, nil, error)
return
}
guard let httpStatus = response as? HTTPURLResponse else { // check for http errors
return
}
if(httpStatus.statusCode != 200) {
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(json)
} catch {
print("error serializing JSON: \(error)")
}
let responseString = String(data: data, encoding: .utf8) ?? ""
print(responseString)
completion(httpStatus.statusCode, data, nil)
}
task.resume()
}
And this is the function where the above request is called, the request travel time is the time elapsed between two [TIME FLAG]:
func test(completion: #escaping (Bool) -> Void) {
let url = httpHelper.myUrlBuilder()
let request = httpHelper.wrapGetRequest(url: url, withToken: .none)
NSLog("[TIME FLAG] Test request sent")
httpHelper.sendRequest(request: request as URLRequest) { statusCode, data, error in
NSLog("[TIME FLAG] Test response get")
guard error == nil && self.httpHelper.validateStatusCode(statusCode: statusCode) else {
completion(false)
return
}
completion(true)
}
}
Thanks for any kind of answers and feedbacks.

Related

Network request intermittently failing with error -1009 The internet connection appears to be offline

I have multiple request that I call on the viewDidLoad method of my initial view controller. Sometimes these requests are failing with Error Code -1009 The internet connection appears to be offline. for some user and others are not having it. Sometimes users who get the error can use it after sometime with no issues. This usually happens on a cellular network. And other apps are working in their phone and not ours.
All of these requests are using the same method on my Service class. First I have an enum of the APIs and I convert them to a URLRequest using Alamofire's URLRequestConvertible protocol. Then I pass this request into Alamofire and handle the response.
func get<T:Codable>(_ api: ServiceAPI, resultType: T.Type, completion: #escaping (_ result: T?, _ error: Error?) -> ()) {
Alamofire.request(api).validate().responseJSON { response in
switch response.result {
case .success(let json):
print("\(api.path):\n\(json)")
do {
if let data = response.data {
let jsonDecoder = JSONDecoder()
let dictionary = try jsonDecoder.decode([String: T].self, from: data)
if let result = dictionary["result"] {
completion(result, nil)
} else {
completion(nil, self.resultNotFoundError)
}
}
} catch {
completion(nil, error)
}
case .failure(let error):
error.trackMixPanelEvent()
completion(nil, error)
}
}
}
Since I am creating an instance of the Service class and calling the get method on it for each request, is it possible that the request is being deallocated? What could also be the cause of such intermittent network error?
To also point out, all of the web service requests are using POST method.

Apple Siri Intent stops playback without apparent reason

I would like to improve the integration of my App FM-Pod with Siri intents shortcuts. I've done this App to listen the radio on the HomePod and, as of now, I've been able to start the playback, change the stations, etc. but I'm facing a strange issue which causes the audio playback to stop alone after about 1 minute...
Does anyone knows the reason? What's wrong?
Here is the code in Swift for starting the playback, leveraging on AVAudioPlayer:
open func handle(intent: StartFMPodIntent, completion: #escaping (StartFMPodIntentResponse) -> Void) {
DataManager.getStationDataWithSuccess(filename: "favorites") { (data) in
if debug { print("Stations JSON Found") }
guard let data = data, let jsonDictionary = try? JSONDecoder().decode([String: [RadioStation]].self, from: data), let stationsArray = jsonDictionary["station"]
else {
if debug { print("JSON Station Loading Error") }
return
}
HPRIntentHandler.stations = stationsArray
if !FRadioPlayer.shared.isPlaying {
FRadioPlayer.shared.radioURL = URL(string: HPRIntentHandler.stations![0].streamURL0!)
let response = StartFMPodIntentResponse(code: .success, userActivity: nil)
response.stationName = stationsArray[0].name
completion(response)
}
}
}
According to https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html#//apple_ref/doc/uid/TP40014214-CH2-SW2
Extension is killed after code is finished running.
Since the getStationDataWithSuccess run in Asynchronously, its code reaches the end before the asynchronous function finishes running.
You have to find different place to handle this.

Twitter Kit RestAPI does not support IPv6? - Rejected from Apple

My app is rejected due to not supporting IPv6 network support.
Here is message from Apple:
We discovered one or more bugs in your app when reviewed on iPhone
running iOS 11 on Wi-Fi connected to an IPv6 network.
Submitting a Tweet through the app does not post it on the feed or on
the Twitter web-site.
My app is very simple and posts text using RestAPI call. Here is code snippet.
let client = TWTRAPIClient(userID: userid)
let url = "https://api.twitter.com/1.1/statuses/update.json";
let message:[String:Any] = ["status": text]
var error: NSError?
let request = client.urlRequest(withMethod: "POST", url: url, parameters: message, error: &error)
DispatchQueue.main.async {
client.sendTwitterRequest(request) { (response, data, ConnectionError) -> Void in
if let error = connectionError {
print("Error: \(error.localizedDescription)")
DispatchQueue.main.async {
completion(error)
}
} else {
DispatchQueue.main.async {
completion(nil)
}
}
}
}
Did anyone experience this?
Removing AFNetworking from Podfile fixed the issue

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>)

Alamofire requests are slow

I'm using Alamofire in my project, my problem is requests takes a long while to load (10 seconds at least), is there a way to speed it up?
That's one of the requests I'm handling
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, TripsEndPointURL, parameters: nil)
.responseJSON { (request, response, JSONData, error) in
if (error != nil) {
NSLog("Error: \(error)")
}
else {
TripsTableList = JSON(JSONData!)
self.TripsTableView.reloadData()
}
}
}
You're using Alamofire correctly. I'd imagine this is a problem with the server, or your connection. Try replacing the endpoint URL with the url of any small image, from a different server, just to experiment with downloading it. If it's still slow, it's your connection. If it's faster, it's the server.

Resources