NSURLConnection throws after updating to Swift 2.0 - swift

Before the Swift 2.0 Update this code worked perfectly to download my JSON File from the Server with a PHP Script:
let url = NSURL(string: webAdress)
let cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
var request = NSMutableURLRequest(URL: url!, cachePolicy: cachePolicy, timeoutInterval: 5.0)
var response: NSURLResponse? = nil
var error: NSError? = nil
let reply = NSURLConnection.sendSynchronousRequest(request, returningResponse:&response, error:&error)
After the Update Xcode asked me to do some changes. I did and the code had no Error, but it always throws...
let url = NSURL(string: webAdress)
let cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
let request = NSMutableURLRequest(URL: url!, cachePolicy: cachePolicy, timeoutInterval: 5.0)
var response: NSURLResponse? = nil
var reply = NSData()
do {
reply = try NSURLConnection.sendSynchronousRequest(request, returningResponse:&response)
} catch {
print("ERROR")
}
Looking forward to your solutions!

Here's an example using the new NSURLSession - apparently NSURLConnection has been deprecated in iOS 9.
let url = NSURL(string: webAddress)
let request = NSURLRequest(URL: url!, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5.0)
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
print(data)
print(response)
print(error)
})?.resume()
I think it's super clean, there's just not much documentation on it. Let me know if you have any trouble getting this to work.

Maximilian hi,
I have the same unsolved issue, the proposed solution by Sidetalker using NSURLSession.dataTaskWithRequest is not what you looking for since the NSURLSession API is highly asynchronous (according with Apple documentation) and the code you had implemented in swift 1.2 was synchronous.
in the other hand, NSURLConnection has been deprecated in iOS 9 therefore the code you wrote is probably not building, right?
my suggested solution is:
let url = NSURL(string: webAdress)
let request: NSURLRequest = NSURLRequest(URL: url!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
var responseCode = -1
let group = dispatch_group_create()
dispatch_group_enter(group)
session.dataTaskWithRequest(request, completionHandler: {(_, response, _) in
if let httpResponse = response as? NSHTTPURLResponse {
responseCode = httpResponse.statusCode
}
dispatch_group_leave(group)
})!.resume()
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
//rest of your code...
please let me know if its now OK

Related

Swift Ios13 Last Modified Date Function [duplicate]

I have been skimming the StackOverflow questions trying to figure out where I'm going wrong with my code, but I just can't seem to! I am trying to convert my Swift 1.2 project to Swift 2.0, and am having an issue with my class that downloads JSON data.
I am continually receiving the error Unexpected non-void return value in void function.
Here is the code, somewhat truncated, that I am using;
...
class func fetchMinionData() -> [Minion] {
var myURL = "http://myurl/test.json"
let dataURL = NSURL(string: myURL)
let request = NSURLRequest(URL: dataURL!, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5.0)
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
let minionJSON = JSON(data!)
var minions = [Minion]()
for (_, minionDictionary) in minionJSON {
minions.append(Minion(minionDetails: minionDictionary))
}
return minions
//THIS IS WHERE THE ERROR OCCURS
}).resume()
}
...
Perhaps I am overlooking something simple, but I'm unsure why my function would be considered void at all. Any thoughts would be immensely appreciated! Thank you!
You have a problem because your line:
return minions
does not return from your function. Instead, it returns from the completion handler in dataTaskWithRequest. And it shouldn't be doing so because that closure is a void function.
The problem which you have results from the fact that dataTaskWithRequest is an asynchronous operation. Which means that it can return later after executing your function.
So, you need to change your design pattern.
One way of doing that would be the following:
static var minions:[Minion] = [] {
didSet {
NSNotificationCenter.defaultCenter().postNotificationName("minionsFetched", object: nil)
}
}
class func fetchMinionData() {
var myURL = "http://myurl/test.json"
let dataURL = NSURL(string: myURL)
let request = NSURLRequest(URL: dataURL!, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5.0)
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
let minionJSON = JSON(data!)
var minions = [Minion]()
for (_, minionDictionary) in minionJSON {
minions.append(Minion(minionDetails: minionDictionary))
}
self.minions = minions
//THIS IS WHERE THE ERROR OCCURS
}).resume()
}
Then before calling your function you should register to listen for NSNotification with name "minionsFetched". And only after you get that notification you should process the minions as if they were fetched.
I fixed mine by creating a completion handler. You can do this instead of using notifications:
class func fetchMinionData(completionHandler: (minions: [Minion]) -> Void) {
var myURL = "http://myurl/test.json"
let dataURL = NSURL(string: myURL)
let request = NSURLRequest(URL: dataURL!, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5.0)
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
let minionJSON = JSON(data!)
var minions = [Minion]()
for (_, minionDictionary) in minionJSON {
minions.append(Minion(minionDetails: minionDictionary))
}
completionHandler(minions: minions)
//THIS IS WHERE YOUR PREVIOUS ERROR OCCURRED
}).resume()
}

a solution to retrieve information when they're no internet connection

On my app I have a form which the user can complete and send it to us with a SOAP request.
the form sended:
But I have a problem, I they're no internet connection I can't send the data by using my SOAP request.
So firstly I save the request in Core Data, so here it's ok... And when the user is on the app I do a scheduled request to see if they're a request in Core Data to send them.
But what about if the app is not running ? In Android I use background service, but with Apple it's impossible:
Android Services equivalent in iOS Swift
Someone have an idea to retrieve the data ?
Soap function:
func soapCall(fonction:String, soapMessageParamsTab:[SoapMessageParams], completion: #escaping (_ strData: NSString)-> ()/*, completion: #escaping (_ result: Int, _ resultContactWebId: Int)->()*/){
let soapRequest = AEXMLDocument()
let attributes = ["xmlns:SOAP-ENV" : "http://schemas.xmlsoap.org/soap/envelope/", "xmlns:ns1" : namespace, "xmlns:xsd" : "http://www.w3.org/2001/XMLSchema", "xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance", "xmlns:SOAP-ENC" : "http://schemas.xmlsoap.org/soap/encoding/", "SOAP-ENV:encodingStyle" : "http://schemas.xmlsoap.org/soap/encoding/"]
let envelope = soapRequest.addChild(name: "SOAP-ENV:Envelope", attributes: attributes)
let body = envelope.addChild(name: "SOAP-ENV:Body")
let sendLead = body.addChild(name: "ns1:"+fonction)
for obj in soapMessageParamsTab {
sendLead.addChild(name: obj.soapName, value: obj.soapValue, attributes: ["xsi:type" : "xsd:"+obj.soapType])
}
//sendLead.addChild(name: "xml", value: messageSoap, attributes: ["xsi:type" : "xsd:string"])
let soapMessage = soapRequest.xmlString
let messageSoapMinify = minify(soapMessage)
// URL Soap
let urlString = soapServiceURL
let url = URL(string: urlString)
var theRequest = URLRequest(url: url!)
// Taille SoapMessage
let msgLength = String(messageSoapMinify.characters.count)
// Soap Login
let username = soapUsername
let password = soapPassword
let loginString = NSString(format: "%#:%#", username, password)
let loginData: Data = loginString.data(using: String.Encoding.utf8.rawValue)!
let base64LoginString = loginData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
// Requete Soap
theRequest.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
theRequest.addValue("Keep-Alive", forHTTPHeaderField: "Connection")
theRequest.addValue(msgLength, forHTTPHeaderField: "Content-Length")
theRequest.addValue("urn:ResponseAction", forHTTPHeaderField: "SOAPAction")
theRequest.httpMethod = "POST"
theRequest.httpBody = messageSoapMinify.data(using: String.Encoding.utf8, allowLossyConversion: true)
theRequest.addValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
// print("Request is \(theRequest.allHTTPHeaderFields!)")
let session = Foundation.URLSession.shared
let task = session.dataTask(with: theRequest, completionHandler: {data, response, error -> Void in
if error != nil
{
print(error?.localizedDescription)
}
//print("Response Login: \(response)")
//print("Error Login: \(error)")
let strData = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
//print("Body Login: \(strData)")
completion(strData)
})
task.resume()
}
Using NSURLSession with background configuration you should be ok.
Note: If the app is explicitly killed by the user the scheduled request will be cancelled. This trick works just if the app was killed by the system for any reason (low memory, crash etc...). There is nothing you can do if the app was killed by the user. The background fetch might be a solution, but it will be decided by iOS if and when to awake the app for the opportunistic fetch.
This could help you:
https://blog.newrelic.com/2016/01/13/ios9-background-execution/
Edit:
This tutorial could be useful as well: https://www.raywenderlich.com/110458/nsurlsession-tutorial-getting-started

Swift Send Email with MailGun

Problem
I would like to use the MailGun service to send emails from a pure Swift app.
Research So Far
As I understand it, there are two methods to send an email via MailGun. One is to email MailGun with the emails, and MailGun will redirect it (See Send via SMTP). That will, as I understand it, not work, as iOS cannot programatically automatically send mail, and must use methods that require user intervention. As such, I should use the API directly. As I understand it, I need to open a URL to do this, and so I should use some form of NSURLSession, as per this SO answer
Code
MailGun provides documentation for Python, which is as follows:
def send_simple_message():
return requests.post(
"https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages",
auth=("api", "key-(Personal info)"),
data={"from": "Excited User <(Personal info)>",
"to": ["bar#example.com", "(Personal info)"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!"})
with (Personal info) being substituted for keys/information/emails.
Question
How do I do that in Swift?
Thanks!
In python, the auth is being passed in the header.
You have to do a http post request, passing both the header and the body.
This is a working code:
func test() {
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages")!)
request.HTTPMethod = "POST"
let data = "from: Excited User <(Personal info)>&to: [bar#example.com,(Personal info)]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.HTTPBody = data.dataUsingEncoding(NSASCIIStringEncoding)
request.setValue("key-(Personal info)", forHTTPHeaderField: "api")
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.URL!)")
print("response = \(response)")
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
people are getting 400 or 401 errors because none of the other answers construct the url correctly. here is some code that works in swift 5 and iOS15:
func sendEmail() {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = "https://api:YOUR_API_KEY#api.mailgun.net/v3/YOUR_DOMAIN/messages?"
let emailRecipient = "RECIPIENT#EMAIL.COM"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: mailgunAPIPath + "from=USER#YOUR_DOMAIN&to=\(emailRecipient)&subject=A%20New%20Test%21&text=\(emailMessage)")! as URL)
// POST and report back with any errors and response codes
request.httpMethod = "POST"
let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
requests.post sends an HTTP POST request, encoding key/value pairs as application/x-www-form-urlencoded. You need to do the same.
convert the set of key-value pairs into application/x-www-form-urlencoded as per How to escape the HTTP params in Swift
compose the request using the resulting string for data & send it as per iOS : http Post using swift
I spent hours trying to get the selected answer working, but to no avail.
Although I was finally able to get this working properly with a large HTTP response. I put the full path into Keys.plist so that I can upload my code to github and broke out some of the arguments into variables so I can have them programmatically set later down the road.
// Email the FBO with desired information
// Parse our Keys.plist so we can use our path
var keys: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Keys", ofType: "plist") {
keys = NSDictionary(contentsOfFile: path)
}
if let dict = keys {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = dict["mailgunAPIPath"] as? String
let emailRecipient = "bar#foo.com"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: mailgunAPIPath! + "from=FBOGo%20Reservation%20%3Cscheduler#<my domain>.com%3E&to=reservations#<my domain>.com&to=\(emailRecipient)&subject=A%20New%20Reservation%21&text=\(emailMessage)")!)
// POST and report back with any errors and response codes
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.URL!)")
print("response = \(response)")
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
The Mailgun Path is in Keys.plist as a string called mailgunAPIPath with the value:
https://API:key-<my key>#api.mailgun.net/v3/<my domain>.com/messages?
Hope this offers a solution to anyone else having issues with MailGun and wanting to avoid a 3rd party solution!
Swift 3 answer:
func test() {
let session = URLSession.shared
var request = URLRequest(url: URL(string: "https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages")!)
request.httpMethod = "POST"
let data = "from: Excited User <(Personal info)>&to: [bar#example.com,(Personal info)]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.httpBody = data.data(using: .ascii)
request.setValue("key-(Personal info)", forHTTPHeaderField: "api")
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}

HTTP POST request in Swift

How do I post the request on iOS? Actually when I logged into Facebook it fetches the user informations like username, from where there are login (latitude, longitude). Is it possible to use api
Link: http://buddysin.aumkiiyo.com/fbc
My code is:
#IBAction func btnAPI(sender: UIButton)
{
//startConnection()
connectToWebAPI()
}
func connectToWebAPI()
{
//setting up the base64-encoded credentials
let id = "1620912344817986"
//let password = "pass"
let loginString = NSString(format: "%#:%#", id)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(nil)
//creating the requestz
let url = NSURL(string: "http://buddysin.aumkiiyo.com/fbc")
var request = NSMutableURLRequest(URL: url!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession.sharedSession()
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let urlConnection = NSURLConnection(request: request, delegate: self)
request.HTTPMethod = "POST"
request.setValue(base64LoginString, forHTTPHeaderField: "Authorization")
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if (error != nil) {
println(error)
}
else {
// converting the data into Dictionary
var error: NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
println(jsonResult)
}
})
//fire off the request
task.resume()
}
while I run, the fatal error where displayed as
`fatal error: unexpectedly found nil while unwrapping an Optional value`
in the "jsonResult"
i think it is better to use Alomafire. As AFNetWorking in Objective-C it is a library which simplified a lot http request.
Visit this question to check for my post-request function (if
you don't want to use Alamofire for any reasons)
Visit this question to check for steps you need to do if you
want to add Alamofire to your XCode-project
If you need to get json-data from your server, use
SwiftyJSON. It's as simple as dragging SwiftyJSON.swift into
your project with checking "Copy items if needed" and using like
let jsonData = JSON(data: yourDataFromServer)
Also you can view this question to check out for steps to encode
json-post data to send it to server.
Hope I helped :)
You should find which varible due to this error:
for example data,
if let dataTemp = data as? NSDictionary {
}
FYI:
Here is a way of 'POST' method of AFNetworking in swift, below code should be in your connectToWebAPI method, wrap your url ready into NSURL.
let manager = AFHTTPRequestOperationManager(baseURL: NSURL(string: yourURL))
manager.POST("path", parameters: ["key":value], success: { (opeartion:AFHTTPRequestOperation!, data:AnyObject!) -> Void in
},failure: { (operation:AFHTTPRequestOperation!, error:NSError!) -> Void in
})
Tutorial to install AFNetworking.
https://github.com/AFNetworking/AFNetworking/wiki/Getting-Started-with-AFNetworking
It is quite easy to do with Alamofire
func postSomething(completionHandler: #escaping CompletionHandler){
let loginString = NSString(format: "%#:%#", id)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions(nil)
let headers: HTTPHeaders = [
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "base64LoginString",
]
let parameters: Parameters = [
"parameter": value,
"parameter2": value2
]
Alamofire.request("http://buddysin.aumkiiyo.com/fbc", method: .post, parameters: parameters, encoding: URLEncoding.default, headers: SFAppServicesManager.sharedInstance.genericHeader()).responseJSON { response in
if let JSON = response.result.value {
let userDictionary = JSON as! NSDictionary
completionHandler(true)
} else {
completionHandler(false)
}
}
}

Submit Decimal/Float in URL Request with Swift

How can a value such as "1.5" be passed in a a URL POST request using swift?
For example:
let number = "1.5"
let numberValue = number.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let server:String = "www.someserver.com"
let phpFile:String = "/php/SomePHPScript.php"
let baseURL = NSURL(string: "http://\(server)\(phpFile)")
let url = NSURL(string: "?value=\(numberValue)", relativeToURL: baseURL)
let cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
var request = NSMutableURLRequest(URL: url!, cachePolicy: cachePolicy, timeoutInterval: 10.0)
request.HTTPMethod = "POST"
var dataString = ""
let requestBodyData = (dataString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPBody = requestBodyData
// etc...
This compiles fine but crashes on execution. It seems converting this to JSON would fix it, but is there cleaner way?
EDIT
stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) returns optional. It needed to be unwrapped.
stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) returns optional. It needed to be unwrapped.