How to send NSData as parameter in POST method swift - ios

I'm trying to send a PDF file's NSData to server as a parameter in POST method, by converting PDF into NSData and then NSData to String,
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
let getPDFPath = paths.stringByAppendingPathComponent("resume.pdf")
let pdfdata = NSData(contentsOfFile: getPDFPath)
let dataString: String? = "\(pdfdata!)"
And uploading as parameter using NSURLSession like this,
let request = NSMutableURLRequest(URL: NSURL(string: "http://someurl/pdf.aspx")!)
request.HTTPMethod = "POST"
let postString = "pdfdata=\(dataString!)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
return }
if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
But it's always giving me Error :
Error Domain=NSURLErrorDomain Code=-1005 "The network connection was
lost." UserInfo={NSUnderlyingError=0x7f8d63f34250 {Error
Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)"
UserInfo={_kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}},
NSErrorFailingURLStringKey=http://someurl/pdf.aspx,
NSErrorFailingURLKey=http://someurl/pdf.aspx,
_kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-4, NSLocalizedDescription=The network connection was lost.}
Things i have tried:
Restarting Simulator
Reset All settings of Simulator
Same error with Alamofire
Tried different Simulators
App Transport Security is YES in info.plist
Only one PDF file (which is 21kb) gets uploaded, others not

It looks like your App Transport Security settings don't allow you to go to the specified URL.
Insert this in your app's plist file:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

Since iOS9 you are required to use https only for security reasons. You may edit the Info.plist file and add an exception for your domain.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>yourdomain.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
Read more about App Transport Security here:
https://developer.apple.com/library/prerelease/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33

Related

Swift URL with Custom Scheme in Query String Fails - Redirect_URI

I'm trying to call a URL using URLSession to log out of an oauth2 session in swift in an iOS app.
The URL is something like this:
Notice that some parameters are URL Encoded
https://xxxxxx.auth.us-east-1.amazoncognito.com/logout?client_id=49jaf4a848hakh&logout_uri=myapp%3A%2F%2F&redirect_uri=myapp%3A%2F%2F&response_type=token
I get a run time error using this URL in a data task that says:
Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL"
UserInfo={NSUnderlyingError=0x60000202ccf0
{Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"},
NSErrorFailingURLStringKey=myapp://, NSErrorFailingURLKey=myapp://,
NSLocalizedDescription=unsupported URL}
I already have "myapp" in my info.plist.
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.xxx.yyy</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
Below is the code to execute this simple URLSession data task
let s = URLSession.shared
let u = URL(string: "https://xxx.yyy.auth.us-east-1.amazoncognito.com/logout?client_id=49jaf4a848hakh&logout_uri=myapp%3A%2F%2F&redirect_uri=myapp%3A%2F%2F&response_type=token")!
let t = s.dataTask(with: u) { (d: Data?, r: URLResponse?, e:Error?) in
if e != nil {
print("error: \(e)")
}
let decoded = String(data: d!, encoding: .utf8)
print("Data: \(decoded)")
print("url Response: \(r)")
print("Breakpoint")
}.resume()
I've tried with both URL strings below and still get the same error
let u = URL(string: "https://xxx.yyy.auth.us-east-1.amazoncognito.com/logout?client_id=49jaf4a848hakh&logout_uri=myapp%3A%2F%2F&redirect_uri=myapp%3A%2F%2F&response_type=token")!
let u = URL(string: "https://xxx.yyy.auth.us-east-1.amazoncognito.com/logout?client_id=49jaf4a848hakh&logout_uri=myapp://&redirect_uri=myapp://&response_type=token")!
Why doesn't swift like "myapp://" or (myapp%3A%2F%2F) being a query string parameter?
Update
After some more experimentation, what’s really weird is that there is no issue if I only have 1 query string parameter with a scheme as a value. Seems like a bug that it doesn’t support more than 1 query string parameter with a scheme as a value.
This seems like an encoding issue. Have you tried encoding like this:
let escapedString = originalString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)

TIC SSL Trust Error while trying to read a txt file

I'm having this problem with reading from a txt file from a website without a certificate even though I have allowed it in Info.plist.
My Swift code looks like this:
let url = URL(string: "http://newskit.matsworld.io/domaci/text.txt")!
let task = URLSession.shared.downloadTask(with: url) { localURL, urlResponse, error in
if let localURL = localURL {
if let string = try? String(contentsOf: localURL) {
print(string)
}
}
}
task.resume()
And the Info.plist looks like this:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>http://newskit.matsworld.io</key>
<dict>
<key>ExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>IncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
I'm stuck with this and can't figure it out. Thanks for help!
The domain key is supposed to be just the domain without the scheme (http) and without subdomains if IncludesSubdomains is specified
<key>matsworld.io</key>

Swift: How to Make Https Request Using Server SSL Certificate

Hi I want to make Https Request in Swift. Currently im accessing local server through ip address. Local Server has one SSL Certificate by accessing the certificate want to make request to server currently im doing like this.
Alamofire.request(.GET, https://ipaddress//, parameters: [param], headers: headers)
.responseJSON { response in
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
I have used the above code for making request and in plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>192.168.2.223:1021(my local ip)</key>
<dict>
Include to allow subdomains-->
<key>NSIncludesSubdomains</key>
<true/>
<!--Include to allow insecure HTTP requests-->
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<!--Include to specify minimum TLS version-->
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
</dict>
in plist i have given like this but im still getting error like
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
IMPORTANT: DO NOT USE THIS IN PRODUCTION. Basically, with Alamofire, you can bypass the authentication for app development and testing purpose. Make sure you remove it before the app is on the App Store or in production:-
func bypassURLAuthentication() {
let manager = Alamofire.Manager.sharedInstance
manager.delegate.sessionDidReceiveChallenge = { session, challenge in
var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
var credential: NSURLCredential?
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
disposition = NSURLSessionAuthChallengeDisposition.UseCredential
credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
} else {
if challenge.previousFailureCount > 0 {
disposition = .CancelAuthenticationChallenge
} else {
credential = manager.session.configuration.URLCredentialStorage?.defaultCredentialForProtectionSpace(challenge.protectionSpace)
if credential != nil {
disposition = .UseCredential
}
}
}
return (disposition, credential)
}
}
Thank You!
Let me know if this helps. :)

Updating iOS 8 -> iOS 9 (New version of Swift) Causes REST API Issues (401 Error) using Alamofire

After I updated my code from iOS 8 to iOS 9, my alamofire rest calls started giving me 401 errors. The code works perfectly fine in iOS 8 but not in iOS 9.
Thanks for the help.
The fullUrl and token variables are set correctly.
let fullUrl = Config.baseUrl + endpoint
let manager = Manager.sharedInstance
manager.session.configuration.HTTPAdditionalHeaders = [
"Content-Type": "application/json",
"Authorization": "Basic \(token)"
]
manager.request(.GET, fullUrl).response {
(request, response, data, error) in
if let response = response {
print("GET REQUEST: \(response.statusCode)")
print(response.debugDescription)
// print(response.description)
if response.statusCode == 200 ||
response.statusCode == 201 {
let json = JSON(data: data!)
success?(json: json)
}
else if response.statusCode == 403 {
UserInfo.logOut()
failure?(error: .ExpiredToken)
}
else {
failure?(error: .InvalidLogin)
}
} else {
failure?(error: .ConnectionUnavailable)
// self.log(request, res: response, err: error)
}
}
In my info.plist I have also added the code to handle App Transport Security
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
Please let me know if there is any confusion with the question.
You might be running into the same issue I had: https://github.com/Alamofire/Alamofire/issues/798
Your URL might return a redirect, in wich case the "Authorization"-Header is not passed along. You can test this when you look at the data sent over the network using Wireshark or a similar tool.
If you don't want to do the network debugging, you can just try the following and see if you're request work.
BTW: You should not use HTTPAdditionalHeaders.
Try this:
// Configure special-handling of redirects (you only need to do this once)
Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
var redirectedRequest = request
if let
originalRequest = task.originalRequest,
headers = originalRequest.allHTTPHeaderFields,
authorizationHeaderValue = headers["Authorization"]
{
let mutableRequest = request.mutableCopy() as! NSMutableURLRequest
mutableRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: "Authorization")
redirectedRequest = mutableRequest
}
return redirectedRequest
}
// Your code, slightly adapted
let fullUrl = Config.baseUrl + endpoint
let headers = [
"Content-Type": "application/json",
"Authorization": userData.userAPIKey!,
]
Alamofire.request(.GET, fullUrl, parameters: nil, encoding: .URL, headers: headers)
.response { (request, response, data, error) -> Void in
if let response = response {
print("GET REQUEST: \(response.statusCode)")
print(response.debugDescription)
// print(response.description)
if response.statusCode == 200 ||
response.statusCode == 201 {
let json = JSON(data: data!)
success?(json: json)
}
else if response.statusCode == 403 {
UserInfo.logOut()
failure?(error: .ExpiredToken)
}
else {
failure?(error: .InvalidLogin)
}
} else {
failure?(error: .ConnectionUnavailable)
// self.log(request, res: response, err: error)
}
}
Yes, the possibilities is due to the ATS because iOS 9 and OSX 10.11 require TLS Version 1.2 SSL that enforce you for secure connections between an app and its back
end so you should use HTTPS exclusively. In addition, your communication through higher-level APIs needs to be encrypted using TLS version 1.2 with
forward secrecy. If you try to make a connection that doesn't follow this requirement, an error is thrown. If your app needs to make a request to an insecure domain you have to specify this domain in your app's
Info.plist file.
<Key> NSAppTransportSecurity </ key>
<Dict>
<Key> NSExceptionDomains </ key>
<Dict>
<Key> yourserver.com </ key>
<Dict>
<-! Include to allow subdomains ->
<Key> NSIncludesSubdomains </ key>
<True />
<-! Include to allow insecure HTTP requests ->
<Key> NSTemporaryExceptionAllowsInsecureHTTPLoads </ key>
<True />
<-! Include to specify minimum TLS version ->
<Key> NSTemporaryExceptionMinimumTLSVersion </ key>
<String> TLSv1.1 </ string>
</ Dict>
</ Dict>
</ Dict>
if you application needs to connect with unknown hosts you have to configure like this.
<Key> NSAppTransportSecurity </ key>
<Dict>
<-! Connect to anything (this is probably BAD) ->
<Key> NSAllowsArbitraryLoads </ key>
<True />
</ Dict>
I had the same problem, actually the sessionManager.session.configuration.HTTPAdditionalHeaders get overwritten by the requestSerializer when sending the request. So you must set the headers in the requestSerializer, not in the session.configuration.HTTPAdditionalHeaders.
Code that was working on iOS8, but not on iOS9:
sm.session.configuration.HTTPAdditionalHeaders = ["Authorization" : "Bearer \(token!)"]
New code for iOS9
sm.requestSerializer.setValue("Bearer \(token!)", forHTTPHeaderField: "Authorization")
I ran into a similar issue that was fixed by temporarily allowing Alamofire to accept invalid SSL certificates (not for shipped code, but it works until the server is equipped to handle it).
public let alamoFireManager = Manager(
serverTrustPolicyManager: ServerTrustPolicyManager(policies: [
"myserver.com": .DisableEvaluation
])
)
and to use it, call
alamofireManager.request(....
like you would a normal request.

IOS 9 Sending Post Request, Blocked by ATS, Bypass is not working

I'm trying to bypass Application Transport Security(ATS), its a new feature of IOS 9 and Xcode 7. However, I tried the info.plist bypass and I am still having problems. I tried the exact same code in Xcode 6 and the request does get sent successfully, so the request should be correct. This could just be a bug on the new Xcode but I was wondering if anyone else ran into the same issue. I'm pretty sure I'm following proper documentation: https://developer.apple.com/library/prerelease/ios/technotes/App-Transport-Security-Technote/index.html#//apple_ref/doc/uid/TP40016240
Info.plist(not complete, just part on ATS)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict/>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>http://127.0.0.1:5000</key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.1</string>
</dict>
</dict>
Request:
let postData = NSMutableData(data: "username=bobbyz".dataUsingEncoding(NSUTF8StringEncoding)!)
postData.appendData("&password=form".dataUsingEncoding(NSUTF8StringEncoding)!)
let request = NSMutableURLRequest(URL: NSURL(string: "http://127.0.0.1:5000/register")!,
cachePolicy: .UseProtocolCachePolicy,
timeoutInterval: 10.0)
request.HTTPMethod = "POST"
request.HTTPBody = postData
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? NSHTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
This just happened to me. Turns out I accidentally added the bypass information to my Unit Test Info.plist. Putting it in the correct Info.plist fixed the issue, as expected. I also used "localhost" instead of "127.0.0.1" and did not provide the port.
Using Xcode 7 Beta 4.

Resources