Swift/https: NSURLSession/NSURLConnection HTTP load failed - ios

Unfortunately this morning my XCode updated to version 7 and the iOS app I was developing with http now wants https. So, following many tutorials, I configured my MAMP server in order to use https/ssl creating a dummy certificate. Now in my iOS app URLs are like the following:
static var webServerLoginURL = "https://localhost:443/excogitoweb/mobile/loginM.php"
static var webServerGetUserTasks = "https://localhost:443/excogitoweb/mobile/handleTasks.php"
static var webServerGetUsers = "https://localhost:443/excogitoweb/mobile/handleUsers.php"
static var webServerGetProjects = "https://localhost:443/excogitoweb/mobile/handleProjects.php"
and they work fine if I try to access them in my browser.
I was used to access the database and php files with NSURLSession.sharedSession().dataTaskWithRequest() which now raises the error in title. For example, here's the line where error is raised:
if let responseJSON: [[String: String]] = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())) as? [[String: String]] {
...
}
and this is the complete error message:
2015-09-21 16:41:48.354 ExcogitoWeb[75200:476213] CFNetwork SSLHandshake failed (-9824)
2015-09-21 16:41:48.355 ExcogitoWeb[75200:476213] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
fatal error: unexpectedly found nil while unwrapping an Optional value
I would like to know how to fix this. I've read some useful answers here but there are many things I still don't understand and if anyone would help/explain me I'd be very grateful.

Add this to your app's Info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

fatal error: unexpectedly found nil while unwrapping an Optional value
usually means that you're doing something not so good, and by looking at your if
if let responseJSON: [[String: String]] = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())) as? [[String: String]] {
I can see that there's a data!, but that data object is nil. You really should unwrap the optionals before using them, especially when working with remote data.
Then you have a network error, that's probably related to the ATS Apple added in iOS 9.
See another answer on how to temporarily disable ATS.
https://stackoverflow.com/a/30748166/421755
edit: I see now that you added ssl to your localhost, that's good. However it's not enough for ATS to work, since it needs TLS 1.2 and not self-signed certificates.

Really old question, but I thought I would respond. This usually happens when you try to unwrap an http object and it comes back nil.
Make sure that you unwrap your url and give it a default value.
example :
let jsonURL = "your https link to your json"
guard let url = URL(string: jsonURL) else { return } // dont force unwrap
hope this helps!

Related

GET and POST requests fail when going to my custom server

I have a linux based node.js server running on BlueHost that I have configured to respond to basic GET and POST requests. I have tested these requests extensively in Postman and they all seem to work great. I figured the transition to doing these requests from an app would not be too difficult but I am currently unable to get either of them to work.
I am using boilerplate code based on this repository. As an example here is the code I am using for the get request
guard let url = URL(string: "publicIP/test") else { print("Invalid url"); return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let res = response {
print(res)
}
if let d = data {
print(d)
do {
let json = try JSONSerialization.jsonObject(with: d, options: [])
print(json)
} catch {
print(error)
print("ERRORED")
}
}
}.resume()
This code works great for the https://jsonplaceholder.typicode.com/users endpoint however whenever I try substituting it with my public ip (just like in Postman) and I keep getting the following error.
2018-02-18 01:14:35.633518-0800 Application[1248:321560] Task <2E64810D-C10E-4D45-82F5-9C9E37A5FE54>.<1> finished with error - code: -1002
Keep in mind this information is not passed throughout the error parameter as "ERRORED" is not printed.
Can you add this to your info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
That error should not be related to using HTTP instead of HTTPS. App Transport Security failures return error code -1022.
The error code -1002 indicates an invalid URL. Perhaps your HTTP file contains a structurally invalid URL (e.g. missing scheme, a scheme other than http/https, etc.)?
For additional debugging, set this environment variable
CFNETWORK_DIAGNOSTICS=1
in your Xcode project and re-run the app. Once you know what URL is failing, the problem will likely become more obvious.
If it isn't, file a bug.
You can check the list of error code on below link
Apple Documentation NSURLErrorDomain Error Codes List
The issue was not any of the example code but merely a misunderstanding of what a url string should look like. I figured that I would need to match the url of the webpage as shown in the address bar of my web-browser but that is not exactly true because http does not show even if it is a part of the address in chrome.
The solution was simply to prepend http:// to the front of my url.

alamofire SSL errors on self-signed local certificates

I have a HTTP REST server with a self-signed certificate. I want to talk to this server from an iOS Swift app, using alamofire. The current code that I have is:
```
let Almgr : Alamofire.SessionManager = {
// Create the server trust policies
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"localhost": .disableEvaluation
]
// Create custom manager
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let man = Alamofire.SessionManager(
configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
return man
}()
Almgr.request(url, method: .post, parameters: params).responseJSON {
response in
if response.result.isSuccess {
print("Success")
} else {
print("Failure")
}
}
With the above code snippet, I am always getting an error when I try to make the http call Almgr.request. The error message is:
2017-12-30 18:24:20.114486+0530 myApp[58036:2721102] ATS failed system trust
2017-12-30 18:24:20.114625+0530 myApp[58036:2721102] System Trust failed for [1:0x600000178a80]
2017-12-30 18:24:20.114814+0530 myApp[58036:2721102] TIC SSL Trust Error [1:0x600000178a80]: 3:0
2017-12-30 18:24:20.115142+0530 myApp[58036:2721102] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
2017-12-30 18:24:20.115274+0530 myApp[58036:2721102] Task <4E3D9E88-B9CE-48C4-850C-5A3E7C9A6A72>.<1> HTTP load failed (error code: -1200 [3:-9802])
2017-12-30 18:24:20.115469+0530 myApp[58036:2721231] Task <4E3D9E88-B9CE-48C4-850C-5A3E7C9A6A72>.<1> finished with error - code: -1200
Any idea how to get this fixed ? I do not want any checks to be done if the url is localhost on port 8000. I have even tried with adding port to the serverTrustPolicies definition but that does not make any difference and I still get error. Any help ?
Update: My problem I believe is related to https://developer.apple.com/library/content/qa/qa1948/_index.html but not found the way to fix yet.
1.
Your approach of modifying server trust policies should work when providing the port. Also see this post. Maybe you are testing your app with the simulator and trying to connect to a web server on the same machine? This can cause all kinds of connection problems (or why are you trying to connect to localhost anyway?).
2.
You should never set NSAllowsLocalNetworking or similar parameters. It breaks SSL and you never know what may happen, even in the local network. If absolutely necessary, you should just make exceptions for single hosts and ports as stated above.
3.
You should never use self signed certificates because this also breaks SSL. It is very easy to obtain a valid certificate using Let's Encrypt. Though, in some cases it is just not possible to obtain a valid certificate. You should then create your own certificate authority and export the CA root certificate to your device. This way, you also make an exception for only one specific host.
Please note that security is crucial in all applications. Please only make exceptions if you exactly know what you are doing.
I have figured out the solution. We need to edit the Info.plist file and add the following section:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
to let iOS allow local networking without https errors.
I was facing same problems. What I figured out and what works:
let almgr:Alamofire.SessionManager = {
//getcertificates is my own method where i create certificate with data from my .cer file
let certificates = getCertificates()
let trustPolicy = ServerTrustPolicy.pinCertificates(certificates: certificates, validateCertificateChain: true, validateHost: true)
// Here was the problem. I had to modify that dict: (localhost with port and with .disableEvaluation)
let serverTrustPolicies = ["liper:8000":trustPolicy, "liper":.disableEvaluation]
let serverTrustPolicyManager = ServerTrustPolicyManager(policies: serverTrustPolicies)
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let man = Alamofire.SessionManager(configuration: URLSessionConfiguration.default, serverTrustPolicyManager: serverTrustPolicyManager)
return man
}()

NSJSONSerialization Failing

I am making a simple request to the Flickr API. I know that the request URL is correct and that the URL task returns data. However, when I try:
let jsonObject: AnyObject! = try NSJSONSerialization.JSONObjectWithData(data, options: [])
in a do, catch block, the operation fails but doesn't crash the app. I have tried changing the options to MutableContainers and AllowFragments but nothing seems to work.
Take the data and convert it to a string like this:
let responseString = String(data: data, encoding: NSUTF8StringEncoding)
Then paste that through a validator like jsonlint.com to see if you're actually getting valid JSON.
I suspect you aren't getting a valid HTTP response from the API, and so maybe you're getting HTML back (or plain text) that describes the error?

Websocket : Starscream "masked and rsv data is not currently supported"

I am developing an iOS app which required to connect with web socket server.
I can successfully connect to server but when I send request on it, it drop off the connection.
I am using Starscream library for web socket.
As per server support team:
it does not support protocol compression, but in the headers below they're requesting "permessage-deflate" from us. We'll accept uncompressed messages just fine (it's just a flag on the packet) but due to the extension they asked for, messages we send out will be compressed with that flag set.
I send request as following using Swift
let dict = ["Parameter1":"value1","Parameter2":"value2"]
do {
let data = try NSJSONSerialization.dataWithJSONObject(dict, options: NSJSONWritingOptions(rawValue: 0))
var jsonString: String = String(data: data, encoding: UInt())!
self.socket.writeString(jsonString);
} catch {
print(error)
}
It disconnect with server and print following message.
"websocket is disconnected: Optional("masked and rsv data is not currently supported")"
What the server support team meant is that the request from your WebSocket client application contained an HTTP header like below.
Sec-WebSocket-Extensions: permessage-deflate
So, if your application has a line like below, remove it.
socket.headers["Sec-WebSocket-Extensions"] = "permessage-deflate"
This error might also be thrown if the server doesn't accept the incoming connection (regardless of the reasons), or if the server crashed.
Basically, when this message shows up, the best action would be to check what is going on the server as you might be wasting time trying improve client code (it happened to me :)
For those facing this issue when trying to connect to the backend WebSocket, make sure the front end and the backend version of the socket.io are compatible. Running the following command fixed the issue for me.
pod update
Updated the both to the latest and solved the issue.
this will fix your issue I believe. just add "wamp" in the header like this.
*
var request = URLRequest(url: URL(string: URL)!)
request.setValue(["wamp"].joined(separator: ","), forHTTPHeaderField: "Sec-WebSocket-Protocol")
socket = WebSocket(request: request)
socket.delegate = self
socket.connect()

Alamofire not executing correctly in Swift

I have an Alamofire request that is suppose to download a file, but it will not execute the code it self. Here is the Alamofire Code:
var testNumbers: Int = 0
var testString: String = "Hi"
Alamofire.download(.GET, "http://www.sayweee.com/admin_groupbuy/export_deal_orders/71w4o?format=csv") { temporaryURL, response in
let fileManager = NSFileManager.defaultManager()
let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename
print("1")
testNumbers = 1
print(directoryURL.URLByAppendingPathComponent(pathComponent!))
print("blah")
testString = "blah"
print("2")
testNumbers = 2
return directoryURL.URLByAppendingPathComponent(pathComponent!)
}
print(testNumbers)
print(testString)
Executing this code will print this in the console:
0
Hi
I am pretty sure this means that the code within the {} are not getting executed. I've read another on this subject, and learned that Alamofire is "asynchronous", as they said in this post. I've tried to get rid of everything in viewDidLoad() method and viewDidAppear() method, and made sure I didn't have any endless loops. Even after that, the results are the same. Any ideas or suggestion on why this is happening to me? I tried to look here and on Google, but I only found one post related to this topic, the one linked above.
First of all, I put your code to run in Demo Alamofire found running results and what you said is the same, as shown in the figure below:
0
Hi
2015-12-30 14:31:29.873 iOS Example[3966:142688] App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
According to the prompt you will see that the problem:
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
The solution is as follows(In the project file "info.plist" ):
In project's "info.plist" file to add a "NSAppTransportSecurity" dictionary, an element is added in the dictionary.Key for "NSAllowsArbitraryLoads", the value is: YES, make it back to the HTTP protocol for the time being.
The problem solution links:
Transport security has blocked a cleartext HTTP
The final results are as follows:
0
Hi
1
file:///Users/yangshebing/Library/Developer/CoreSimulator/Devices/151CB429-29B3-46D0-AFF5-37D5B8D9E4FC/data/Containers/Data/Application/756A32D1-64C5-48CF-B652-D3009D80780D/Documents/71w4o.html
blah
2
Specific problems you can go to query the apple official documentation, query iOS9 ATS adaptation problem.
The hope can help you!

Resources