I'm dealing with a strange issue where I'm not able to hit some API's through iOS simulator in XCode.
Version: Xcode 10.3
I've tried using the following:
https://finnhub.io/api/v1/stock/profile2?symbol=GOOGL&token=
https://www.alphavantage.co/query?function=OVERVIEW&symbol=IBM&apikey=demo
I'm able to hit both endpoints through the browser, replacing both with a completely unrelated api: https://cat-fact.herokuapp.com/facts works, I'm able to see the response immediately as expected.
I've added the following to my Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Code:
let apiBaseUrl = "https://www.alphavantage.co/query?function=OVERVIEW&symbol=IBM&apikey=demo"
func getStock(symbol: String) {
if let url = URL(string: apiBaseUrl) {
let task = URLSession.shared.dataTask(with: url, completionHandler: {
data, res, err in
if let error = err {
print(error)
} else {
var result: Any! = nil
do
{
result = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments)
}
catch{
print("exception: ")
}
print(result)
}
})
task.resume()
}
}
In my console I see the following:
HTTP load failed (error code: -999 [1:89])
PAC result block not invoked
Received XPC error Connection invalid for message type 3 kCFNetworkAgentXPCMessageTypePACQuery
I solved this issue as below :
I went into
System Preferences > Network > Advanced > Proxies
and unticked "Auto Proxy Discovery"
My calls now work.
Related
Using Alamofire for JSON response from remote.
I have had a very weird issue. While running my app on a device connected through cable is working fine and getting all response. But when the device gets disconnected through cable response time getting increased to 5 minutes to 30 minutes and if I connect the device to charging or to MacBook again it's work fine but stuck if not connected.
func getData()
{
let baseUrl = URL.init(fileURLWithPath: "")
let headers:HTTPHeaders = ["Content-Type":"application/json"]
Alamofire.request(baseUrl, method: .get, parameters: nil, encoding: URLEncoding.default, headers: headers)
.responseJSON
{ response in
switch(response.result) {
case .success(_):
if response.result.value != nil{
let jsonData = response.result.value as? Dictionary<String,AnyObject>
print(jsonData)
}
case .failure(_):
print(response.result.error!)
break
}
}
}
Are you using any other class for Session manager? If yes then that may cause problem. So remove that one.
can you share your api request ?.
One possible solution to add Transport security as:
Add this code in info.plist, inside the root tag
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
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
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>)
I am trying to obtain NSData from a server using contentsOfURL. When the iPhone is connected to wifi, it works fine. But when not connected to wifi, my app crashes with an EXC_BREAKPOINT message. How can I handle or circumvent this issue of crashing when not connected?
do {
let varOption = try NSData(contentsOfURL: NSURL(string: urlToRequest)!, options:NSDataReadingOptions.DataReadingMappedIfSafe)
} catch {
print("error encountered")
}
Is there a requirement to use NSData's contentsOfURL? Why not make an async web request using NSURLSession?
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: urlToRequest)!) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
// if data && !error, do the thing
}.resume()
You can use this tutorial to create a function to check if iPhone is connected to Internet.
http://www.brianjcoleman.com/tutorial-check-for-internet-connection-in-swift/
After implementing this function you can use it to validate the connection and then put your code inside an if statement block
if isConnectedToNetwork() { //<-- This is the function implemented in tutorial posted before.
//Here we put your code
do {
let varOption = try NSData(contentsOfURL: NSURL(string: urlToRequest)!, options:NSDataReadingOptions.DataReadingMappedIfSafe)
} catch {
print("error encountered")
}
}
I am trying to get rest data to iOS app, and I use:
var rest_url = "http://192.168.0.1:8000/rest/users/"
let url: NSURL = NSURL(string: rest_url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
if(error != nil) {
println(error.localizedDescription)
}
println(data)
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary!
But I think I can't access my server like this. Does anyone know how I can access my server from the iOS simulator?
Do you have App Transport Security Settings in your Info.plist file?
If no then for debugging purpose you can set them like
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Such settings allow issuing requests to any server.
But don't do so for a release version. It is insecure.
Make sure that your IP of your Mac is 192.168.0.1. So your url could be
var rest_url = "http://YOUR MAC IP:8000/rest/users/"
If you have a server running on the machine where you iOS simulator is running, then you need to choose 'http://127.0.0.1' as the URL.
In your case it will be :
var rest_url = "http://127.0.0.1:8000/rest/users/"
For people finding this thread because they can't connect to localhost due to an invalid certificate: in your URLSessionDelegate you should respond to the URLAuthenticationChallenge with the following delegate method:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
func defaultAction() { completionHandler(.performDefaultHandling, nil) }
// Due to localhost using an invalid certificate, we need to manually accept it and move on
guard challenge.protectionSpace.host.hasPrefix("localhost") else { return defaultAction() }
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust else { return defaultAction() }
guard let trust = challenge.protectionSpace.serverTrust else { return defaultAction() }
completionHandler(.useCredential, URLCredential(trust: trust))
}
Maybe you can replace 192.168.0.1 with localhost, when debugging with iOS simulator (that is, real devices should use your server's IP).
I also cannot access my test server using IP address on simulator. But when I am using localhost or 120.0.0.1, the simulator can work well with my test server.