Semaphore waits forever - ios

I'm trying to create a function that will execute a post request to an API and I want the function to wait before it returns but it does not seem to work properly. I'm not sure what I'm missing.
As per the suggestion of another answer here on SO I'm using semaphores to accomplish this however it seems that it will "wait forever" since the app just hangs. I know the request is executed successfully and the app gets a response(tried with with prints all over the place).
This is what I've done so far:
func sendAuthRequest(username: String, password: String) -> Int {
let sem = DispatchSemaphore(value: 0)
var authStatus: Int = 0
let params = ["username":username, "password":password] as Dictionary<String, String>
var request = URLRequest(url: URL(string: Constants.api_base_url + "/auth/validateuser")!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
if (httpResponse.statusCode == 200) {
authStatus = 200
} else if (httpResponse.statusCode == 403) {
authStatus = 403
} else {
authStatus = 500
}
}
})
task.resume()
sem.wait()
return authStatus
}
Thanks!

If I restructure it quickly, I would do it this way:
func sendAuthRequest(username: String, password: String, completion: #escaping (Result<Int, Error>) -> Void) {
let params = ["username":username, "password":password] as Dictionary<String, String>
var request = URLRequest(url: URL(string: Constants.api_base_url + "/auth/validateuser")!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in
if let error = error {
completion(.failure(error))
return
}
if let httpResponse = response as? HTTPURLResponse {
var authStatus: Int
print(httpResponse.statusCode)
if (httpResponse.statusCode == 200) {
authStatus = 200
} else if (httpResponse.statusCode == 403) {
authStatus = 403
} else {
authStatus = 500
}
completion(.success(authStatus))
} else {
let error = NSError(domain:"", code: 0, userInfo:[NSLocalizedDescriptionKey: "Failed to get response"])
completion(.failure(error))
}
})
task.resume()
}

Related

Swift 5, RxSwift: Network request with RxSwift

I am starting to use RxSwift to make the service call.
This was my old code:
class Service: GraphQLService {
func graphQL(body: [String: Any?], onSuccess: #escaping (Foundation.Data) throws -> (), onFailure: #escaping (Error) -> ()) {
guard let urlValue = Bundle.main.urlValue else { return }
guard let url = URL(string: urlValue) else { return
print("Error with info.plist")
}
var request = URLRequest(url: url)
let userKey = Bundle.main.userKeyValue
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(userKey, forHTTPHeaderField: "userid")
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
onFailure(error)
}
if let data = data {
do{
try onSuccess(data)
}
catch{
onFailure(error)
}
}
}.resume()
}
And here I do the function to get time deposits:
final class TimeDepositManager: Service, TimeDepositManagerProtocol {
let timeDepositQuery = Bundle.main.queryValue
func getTimeDeposits(onSuccess: #escaping ([TimeDeposits]) -> (), onFailure: #escaping (Error) -> ()) {
let body = ["query": timeDepositQuery]
Service().graphQL(body: body, onSuccess: { data in
let json = try? JSONDecoder().decode(GraphQLResponse.self, from: data)
onSuccess(json?.data?.account?.timeDeposits ?? [])
}, onFailure: onFailure)
}
And so far this is my code with RxSwift:
class Service: GraphQLService {
func graphQL(body: [String : Any?]) -> Observable<Foundation.Data> {
return Observable.create { observer in
let urlValue = Bundle.main.urlValue
let url = URL(string: urlValue ?? "")
let session = URLSession.shared
var request = URLRequest(url: url!)
let userKey = Bundle.main.userKeyValue
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(userKey, forHTTPHeaderField: "userid")
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
session.dataTask(with: request) { (data, response, error) in
if let error = error {
observer.onError(error)
}
if let data = data {
do{
try onSuccess(data)
observer.onNext(data)
}
catch{
//onFailure(error)
observer.onError(error)
print("Error: \(error.localizedDescription)")
}
}
}.resume()
return Disposables.create {
session.finishTasksAndInvalidate()
}
}
}
This is where I don't understand how in my getTimeDeposits () I can do the deserialization with try? JSONDecoder () ... with RxSwift without using onSuccess?
final class TimeDepositManager: Service, TimeDepositManagerProtocol {
let timeDepositQuery = Bundle.main.queryValue
func getTimeDeposits() -> Observable<[TimeDeposits]> {
let body = ["query": timeDepositQuery]
Service().graphQL(body: body)
}
You can have getTimeDeposits() return an Observable as well and handle the deserialization in a map closure. A couple of other things.
RxCocoa already has a method on URLSession so you don't need to write your own.
I suggest reducing the amount of code you have in a function that makes the network request. You want to be able to test your logic for making the request without actually making it.
Something like this:
final class TimeDepositManager: Service, TimeDepositManagerProtocol {
let timeDepositQuery = Bundle.main.queryValue
func getTimeDeposits() -> Observable<[TimeDeposits]> {
let body = ["query": timeDepositQuery]
return Service().graphQL(body: body)
.map { try JSONDecoder().decode(GraphQLResponse.self, from: $0).data?.account?.timeDeposits ?? [] }
}
}
class Service: GraphQLService {
func graphQL(body: [String: Any?]) -> Observable<Data> {
guard let urlValue = Bundle.main.urlValue else { fatalError("Error with info.plist") }
let request = urlRequest(urlValue: urlValue, body: body)
return URLSession.shared.rx.data(request: request) // this is in RxCocoa
}
func urlRequest(urlValue: String, body: [String: Any?]) -> URLRequest {
guard let url = URL(string: urlValue) else { fatalError("Error with urlValue") }
var request = URLRequest(url: url)
let userKey = Bundle.main.userKeyValue
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(userKey, forHTTPHeaderField: "userid")
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
return request
}
}
If you don't want to use RxCocoa for some reason, here is the correct way to wrap the URLSession.dataTask method:
extension URLSession {
func data(request: URLRequest) -> Observable<Data> {
Observable.create { observer in
let task = self.dataTask(with: request, completionHandler: { data, response, error in
guard let response = response as? HTTPURLResponse else {
observer.onError(URLError.notHTTPResponse(data: data, response: response))
return
}
guard 200 <= response.statusCode && response.statusCode < 300 else {
observer.onError(URLError.failedResponse(data: data, response: response))
return
}
guard let data = data else {
observer.onError(error ?? RxError.unknown)
return
}
observer.onNext(data)
observer.onCompleted() // be sure to call `onCompleted()` when you are done emitting values.
// make sure every possible path through the code calls some method on `observer`.
})
return Disposables.create { task.cancel() } // don't forget to handle cancelation properly. You don't want to kill *all* tasks, just this one.
}
}
}
enum URLError: Error {
case notHTTPResponse(data: Data?, response: URLResponse?)
case failedResponse(data: Data?, response: HTTPURLResponse)
}

How to get JSON response data from shared class to ViewController?

I'm not using Alamofire, so i want to use JSON post approach in SharedClass and i want to send my api name and all parameters to that function. Finally i want to get the response back. I tried but it's not working. If it's not correct please correct me or if any other options are available please suggest me.
My code in SharedClass
func postRequestFunction(apiName:String , parameters:String ) -> [String:Any] {
var localURL = "hostname/public/index.php/v/***?"
localURL = localURL.replacingOccurrences(of: "***", with: apiName)
var request = URLRequest(url: URL(string: localURL)!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
print("shared URL : \(request)")
request.httpBody = parameters.data(using: .utf8)
var returnRes:[String:Any] = [:]
let task = URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data, error == nil else { // check for fundamental networking error
print(error!)
// print("error=\(String(describing: error))")
print("localizedDescription : \(String(describing: error?.localizedDescription))")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
do {
returnRes = try JSONSerialization.jsonObject(with: data, options: []) as! [String : Any]
print(returnRes)
} catch let error as NSError {
print(error)
}
}
task.resume()
return returnRes
}
In my view controller class my code is. Here i'm calling function
func getProjectDetails() {
let response = SharedClass.sharedInstance.postRequestFunction(apiName: "API Name", parameters: parameters)
print(response)
let res = response["Response"] as! [String:Any]
let status = res["status"] as! String
if status == "SUCCESS" {
//I will handle response here
} else {
let message = res["message"] as! String
//Call alert function
SharedClass.sharedInstance.alert(view: self, title: "", message: message)
}
}
Here is my solution:
class APIManager {
private init () {}
static let shared = APIManager()
func postRequestFunction(apiName: String , parameters: String, onCompletion: #escaping (_ success: Bool, _ error: Error?, _ result: [String: Any]?)->()) {
var localURL = "hostname/public/index.php/v/***?"
localURL = localURL.replacingOccurrences(of: "***", with: apiName)
var request = URLRequest(url: URL(string: localURL)!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
print("shared URL : \(request)")
request.httpBody = parameters.data(using: .utf8)
var returnRes:[String:Any] = [:]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
onCompletion(false, error, nil)
} else {
guard let data = data else {
onCompletion(false, error, nil)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode == 200 {
do {
returnRes = try JSONSerialization.jsonObject(with: data, options: []) as! [String : Any]
onCompletion(true, nil, returnRes)
} catch let error as NSError {
onCompletion(false, error, nil)
}
} else {
onCompletion(false, error, nil)
}
}
}
task.resume()
}
}
func getProjectDetails() {
/* Notes:
** onCompletion Block Parameters:
success - This indicates whether the API called successfully or not.
error - This indicates errors from either API calling failed, JSON parsing, or httpStatus is not 200.
result - This indicates the JSON parsed result.
** APIManager:
I have renamed your SharedClass to APIManager for better readibility.
** sharedInstance:
I have renamed sharedInstance to shared for better readibility.
*/
APIManager.shared.postRequestFunction(apiName: "API Name", parameters: "parameters") { (success, error, result) in
if success {
if let res = result?["Response"] as? [String: Any] {
if let status = res["status"] as? String {
if status == "SUCCESS" {
//You can handle response here.
} else {
let message = res["message"] as! String
//Call alert function.
}
}
}
} else {
print(error?.localizedDescription)
}
}
}
You forgot the asynchronous paradigm of Service, You can return your API response in Closure, as like below
func postRequestFunction(apiName:String , parameters:String, returnRes: #escaping ([String: Any]) -> () ) {
var localURL = "hostname/public/index.php/v/***?"
localURL = localURL.replacingOccurrences(of: "***", with: apiName)
var request = URLRequest(url: URL(string: localURL)!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
print("shared URL : \(request)")
request.httpBody = parameters.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data, error == nil else {
// check for fundamental networking error
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
do {
if let response = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] {
returnRes(response)
}
} catch let error as NSError {
print(error)
}
}
task.resume()
}
And use like below
postRequestFunction(apiName: "yourUrl", parameters: "Param") { (response) in
print(response)
}

Send Nested JSON With POST Request iOS Swift 3

I want to send JSON to Server with POST request but i did not understand how i do this. I select friends from table view and show these friends in my Collection view. And the selected peoples shows in collection views and their email are sent in JSON to Create group. Here is my code written in Swift.
#IBAction func createGroupButton(_ sender: Any) {
let groupName = groupNameTextField.text
let adminEmail = UserDefaults.standard.value(forKey: "userEmail")
if groupName == "" {
alertMessage(msg: "Enter Group name")
}
else if base64String == nil {
alertMessage(msg: "Select Group Image")
}
else if emailArray.isEmpty {
alertMessage(msg: "Select atleast one person")
}
else {
if isNetAvailable {
var emailData: String?
print("email data \(emailArray)")
for i in emailArray {
emailData = "\"email\":\"\(i)\""
}
let postData = "\"name\":\"\(groupName!)\",\"adminemail\":\"\(adminEmail!)\",\"image\":\"\(base64String!)\",\"emailArray\":\"\(emailData!)\""
let postDat = "jsondata={\(postData)}"
print("Post Data \(postDat)")
let urlString = create_group
let url = URL(string: urlString)
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.httpBody = postDat.data(using: String.Encoding.utf8)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Something wrong with creating Group")
}
if data == nil {
print("Nil Data")
}
if response == nil {
print("Nil Response")
}
if response != nil {
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("Response String is \(responseString)")
}
}
task.resume()
}
else {
alertMessage(msg: "Internet Not Available")
}
}
}
Here is my API
<?php
if($_SERVER["REQUEST_METHOD"] !="POST"){ exit; }
require_once 'dbcon.php';
if (!empty($_POST['jsondata']))
{
$configjson = $_POST['jsondata'];
$config = json_decode($configjson, true);
$groupname = $config['name'];
$adminemail = $config['adminemail'];
$image = $config['image'];
$queryemailcheck = "SELECT * FROM groups_name WHERE admin = '$adminemail' AND groupname = '$groupname'";
$selectquery = mysqli_query($connect, $queryemailcheck);
$totalemails= mysqli_num_rows($selectquery);
if($totalemails>0)
{
echo "Already exist";
}
else {
$queryinsert= "INSERT INTO groups_name(admin , groupname , pic ) VALUES('$adminemail' ,'$groupname' , '$image')";
if(mysqli_query($connect, $queryinsert)){
echo "Successfully Saved";
}else{
echo "Error: " ;
}
$members = $config['emailArray'];
foreach($members as $row ){
$email = $row['email'];
$queryinsert2= "INSERT INTO group_members(groupname , member , status ) VALUES('$groupname' ,'$email' , '0')";
if(mysqli_query($connect, $queryinsert2)){
echo "Successfully Saved";
}else{
echo "Error: " ;
}
}
}
}
else echo "post data is empty";
?>
You can use Alamofire library for handling your webservices. like
Alamofire.request(logoutUrl, method: .post, parameters: dict, encoding: JSONEncoding.default, headers:["Authorization":"Bearer \(accessToken)", "Accept":"application/json", "Content-Type":"application/json; charset=UTF-8"]).responseJSON { (response) in
let json:JSON = JSON(response.result.value)
let status = json["status"].stringValue
let statusCode = response.response?.statusCode
Create network handler class where you can hit webservices from all of your app.
func makeHttpPostRequest(uri:String, postBody:Dictionary<String, Any> ,Completion:#escaping (_ response: AnyObject, _ success: Bool) -> Void) {
let networkCheck = self.getNetworkStatus()
if networkCheck == false {
Completion("NO internet connection." as AnyObject,false)
return
}//end network check block
var urlRequest = self.makeHttpRequestWithUrl(url: uri)
urlRequest.httpMethod = "POST"
urlRequest.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
//check and set authorization header
if (UserDefaults.standard.object(forKey: AppConstant.KAppAuthenticationToken) != nil) {
let authToken = UserDefaults.standard.object(forKey: AppConstant.KAppAuthenticationToken) as! String
urlRequest.setValue("Token token="+authToken, forHTTPHeaderField: "Authorization")
}
//let postData = [self.addDefultParameters(detailDict: postBody)]
let jsonData = try? JSONSerialization.data(withJSONObject: postBody)
urlRequest.httpBody = jsonData
//create connection
self.httpConnectionWithRequest(request: urlRequest, Completion: Completion)
}
func makeHttpRequestWithUrl(url: String) -> URLRequest {
let urlString = AppConstant.baseUrl+url
let networkUrlRequest = URLRequest.init(url: URL.init(string: urlString)!, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 50)
return networkUrlRequest
}
func httpConnectionWithRequest(request: URLRequest, Completion:#escaping (_ response: AnyObject, _ success: Bool) -> Void) {
let session = URLSession.shared
// make the request
let task = session.dataTask(with: request) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error is: " + String.init(format: "%#", error?.localizedDescription as! CVarArg))
Completion(error!.localizedDescription as String as AnyObject, false)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
Completion("didn't get response data" as String as AnyObject, false)
return
}
let responseString = String.init(data: responseData, encoding: .utf8)
// parse the result as JSON, since that's what the API provides
if let resp = response as? HTTPURLResponse{
if (UserDefaults.standard.object(forKey: AppConstant.KAppAuthenticationToken) != nil) {
if resp.statusCode == 401{
//auth token expired
self.makeRequestWithNewToken(request: request, Completion: Completion)
}
}
//check if response is Array or dictionary
var jsonArray:[Any]?
var jsonDict:[String:Any]?
// response is in array
do{
jsonArray = try JSONSerialization.jsonObject(with: responseData, options: [])as? [Any]
}catch{
print("response doesn't contain array")
}
//response is in dictionary
do{
jsonDict = try JSONSerialization.jsonObject(with: responseData, options: [])as? [String:Any]
}catch{
print("response doesn't contain dict")
}
//=====
// if resp.statusCode == 200{
if jsonArray != nil{
Completion(jsonArray! as AnyObject, true)
return
}
if jsonDict != nil{
Completion(jsonDict! as AnyObject, true)
return
}
Completion("didn't get response data" as String as AnyObject, false)
return
}
}
task.resume()
}
And through the completion handler you will get the fetched data.

How to make HTTP request in Swift?

I read The Programming Language Swift by Apple in iBooks, but cannot figure out how to make an HTTP request (something like cURL) in Swift. Do I need to import Obj-C classes or do I just need to import default libraries? Or is it not possible to make an HTTP request based on native Swift code?
You can use URL, URLRequest and URLSession or NSURLConnection as you'd normally do in Objective-C. Note that for iOS 7.0 and later, URLSession is preferred.
Using URLSession
Initialize a URL object and a URLSessionDataTask from URLSession. Then run the task with resume().
let url = URL(string: "http://www.stackoverflow.com")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
}
task.resume()
Using NSURLConnection
First, initialize a URL and a URLRequest:
let url = URL(string: "http://www.stackoverflow.com")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
Then, you can load the request asynchronously with:
NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.main) {(response, data, error) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
}
Or you can initialize an NSURLConnection:
let connection = NSURLConnection(request: request, delegate:nil, startImmediately: true)
Just make sure to set your delegate to something other than nil and use the delegate methods to work with the response and data received.
For more detail, check the documentation for the NSURLConnectionDataDelegate protocol
Testing on an Xcode playground
If you want to try this code on a Xcode playground, add import PlaygroundSupport to your playground, as well as the following call:
PlaygroundPage.current.needsIndefiniteExecution = true
This will allow you to use asynchronous code in playgrounds.
Check Below Codes :
1. SynchonousRequest
Swift 1.2
let urlPath: String = "YOUR_URL_HERE"
var url: NSURL = NSURL(string: urlPath)!
var request1: NSURLRequest = NSURLRequest(URL: url)
var response: AutoreleasingUnsafeMutablePointer<NSURLResponse?>=nil
var dataVal: NSData = NSURLConnection.sendSynchronousRequest(request1, returningResponse: response, error:nil)!
var err: NSError
println(response)
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataVal, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary
println("Synchronous\(jsonResult)")
Swift 2.0 +
let urlPath: String = "YOUR_URL_HERE"
let url: NSURL = NSURL(string: urlPath)!
let request1: NSURLRequest = NSURLRequest(URL: url)
let response: AutoreleasingUnsafeMutablePointer<NSURLResponse?>=nil
do{
let dataVal = try NSURLConnection.sendSynchronousRequest(request1, returningResponse: response)
print(response)
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(dataVal, options: []) as? NSDictionary {
print("Synchronous\(jsonResult)")
}
} catch let error as NSError {
print(error.localizedDescription)
}
}catch let error as NSError
{
print(error.localizedDescription)
}
2. AsynchonousRequest
Swift 1.2
let urlPath: String = "YOUR_URL_HERE"
var url: NSURL = NSURL(string: urlPath)!
var request1: NSURLRequest = NSURLRequest(URL: url)
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var err: NSError
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
println("Asynchronous\(jsonResult)")
})
Swift 2.0 +
let urlPath: String = "YOUR_URL_HERE"
let url: NSURL = NSURL(string: urlPath)!
let request1: NSURLRequest = NSURLRequest(URL: url)
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("ASynchronous\(jsonResult)")
}
} catch let error as NSError {
print(error.localizedDescription)
}
})
3. As usual URL connection
Swift 1.2
var dataVal = NSMutableData()
let urlPath: String = "YOUR URL HERE"
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: true)!
connection.start()
Then
func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
self.dataVal?.appendData(data)
}
func connectionDidFinishLoading(connection: NSURLConnection!)
{
var error: NSErrorPointer=nil
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataVal!, options: NSJSONReadingOptions.MutableContainers, error: error) as NSDictionary
println(jsonResult)
}
Swift 2.0 +
var dataVal = NSMutableData()
let urlPath: String = "YOUR URL HERE"
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: true)!
connection.start()
Then
func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
dataVal.appendData(data)
}
func connectionDidFinishLoading(connection: NSURLConnection!)
{
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(dataVal, options: []) as? NSDictionary {
print(jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
4. Asynchonous POST Request
Swift 1.2
let urlPath: String = "YOUR URL HERE"
var url: NSURL = NSURL(string: urlPath)!
var request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request1.HTTPMethod = "POST"
var stringPost="deviceToken=123456" // Key and Value
let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)
request1.timeoutInterval = 60
request1.HTTPBody=data
request1.HTTPShouldHandleCookies=false
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var err: NSError
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
println("AsSynchronous\(jsonResult)")
})
Swift 2.0 +
let urlPath: String = "YOUR URL HERE"
let url: NSURL = NSURL(string: urlPath)!
let request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request1.HTTPMethod = "POST"
let stringPost="deviceToken=123456" // Key and Value
let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)
request1.timeoutInterval = 60
request1.HTTPBody=data
request1.HTTPShouldHandleCookies=false
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("ASynchronous\(jsonResult)")
}
} catch let error as NSError {
print(error.localizedDescription)
}
})
5. Asynchonous GET Request
Swift 1.2
let urlPath: String = "YOUR URL HERE"
var url: NSURL = NSURL(string: urlPath)!
var request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request1.HTTPMethod = "GET"
request1.timeoutInterval = 60
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var err: NSError
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
println("AsSynchronous\(jsonResult)")
})
Swift 2.0 +
let urlPath: String = "YOUR URL HERE"
let url: NSURL = NSURL(string: urlPath)!
let request1: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request1.HTTPMethod = "GET"
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request1, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print("ASynchronous\(jsonResult)")
}
} catch let error as NSError {
print(error.localizedDescription)
}
})
6. Image(File) Upload
Swift 2.0 +
let mainURL = "YOUR_URL_HERE"
let url = NSURL(string: mainURL)
let request = NSMutableURLRequest(URL: url!)
let boundary = "78876565564454554547676"
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST" // POST OR PUT What you want
let session = NSURLSession(configuration:NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: nil, delegateQueue: nil)
let imageData = UIImageJPEGRepresentation(UIImage(named: "Test.jpeg")!, 1)
var body = NSMutableData()
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
// Append your parameters
body.appendData("Content-Disposition: form-data; name=\"name\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("PREMKUMAR\r\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"description\"\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("IOS_DEVELOPER\r\n".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!)
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
// Append your Image/File Data
var imageNameval = "HELLO.jpg"
body.appendData("--\(boundary)\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Disposition: form-data; name=\"profile_photo\"; filename=\"\(imageNameval)\"\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("Content-Type: image/jpeg\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData(imageData!)
body.appendData("\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
body.appendData("--\(boundary)--\r\n".dataUsingEncoding(NSUTF8StringEncoding)!)
request.HTTPBody = body
let dataTask = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
//handle error
}
else {
let outputString : NSString = NSString(data:data!, encoding:NSUTF8StringEncoding)!
print("Response:\(outputString)")
}
}
dataTask.resume()
Another option is the Alamofire lib that offers Chainable Request / Response methods.
https://github.com/Alamofire/Alamofire
Making a Request
import Alamofire
Alamofire.request(.GET, "http://httpbin.org/get")
Response Handling
Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
.response { request, response, data, error in
print(request)
print(response)
print(error)
}
Swift 4 and above : Data Request using URLSession API
// create the url
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")! //change the url
// create the session object
let session = URLSession.shared
// now create the URLRequest object using the url object
let request = URLRequest(url: url)
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
Swift 4 and above, Decodable and Result enum
// APIError enum which shows all possible Network errors
enum APIError: Error {
case networkError(Error)
case dataNotFound
case jsonParsingError(Error)
case invalidStatusCode(Int)
case badURL(String)
}
// Result enum to show success or failure
enum Result<T> {
case success(T)
case failure(AppError)
}
// dataRequest which sends request to given URL and convert to Decodable Object
func dataRequest<T: Decodable>(with url: String, objectType: T.Type, completion: #escaping (Result<T>) -> Void) {
// create the url with NSURL
guard let dataURL = URL(string: url) else {
completion(.failure(APIError.badURL(url))
return
}
// create the session object
let session = URLSession.shared
// now create the URLRequest object using the url object
let request = URLRequest(url: dataURL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60)
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request, completionHandler: { data, response, error in
guard error == nil else {
completion(Result.failure(APIError.networkError(error!)))
return
}
guard let data = data else {
completion(Result.failure(AppError.dataNotFound))
return
}
do {
// create decodable object from data
let decodedObject = try JSONDecoder().decode(objectType.self, from: data)
completion(Result.success(decodedObject))
} catch let error {
completion(Result.failure(APIError.jsonParsingError(error as! DecodingError)))
}
})
task.resume()
}
example:
//if we want to fetch todo from placeholder API, then we define the ToDo struct and call dataRequest and pass "https://jsonplaceholder.typicode.com/todos/1" string url.
struct ToDo: Decodable {
let id: Int
let userId: Int
let title: String
let completed: Bool
}
dataRequest(with: "https://jsonplaceholder.typicode.com/todos/1", objectType: ToDo.self) { (result: Result) in
switch result {
case .success(let object):
print(object)
case .failure(let error):
print(error)
}
}
// this prints the result:
ToDo(id: 1, userId: 1, title: "delectus aut autem", completed: false)
Basic Swift 3+ Solution
guard let url = URL(string: "http://www.stackoverflow.com") else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
print(NSString(data: data, encoding: String.Encoding.utf8.rawValue))
}
task.resume()
Using URLSession + Swift 5
Just adding to cezar's answer, if you want to make web request using Apple's URLSession class, there are multiple way to do the task
Simple GET Request with URL
Simple GET Request with URL and Parameters
Simple GET Request with URL with Error Handlings
Simple POST Request with URL, Parameters with Error Handlings
1. Simple GET Request with URL
func simpleGetUrlRequest()
{
let url = URL(string: "https://httpbin.org/get")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
print("The response is : ",String(data: data, encoding: .utf8)!)
//print(NSString(data: data, encoding: String.Encoding.utf8.rawValue) as Any)
}
task.resume()
}
Note : Make sure You must add "NSAppTransportSecurity" key in pList for http requests
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
2. Simple GET Request with URL and Parameters
func simpleGetUrlWithParamRequest()
{
let url = URL(string: "https://www.google.com/search?q=peace")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
if error != nil || data == nil {
print("Client error!")
return
}
guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
print("Server error!")
return
}
print("The Response is : ",response)
}
task.resume()
}
3. Simple GET Request with URL with Error Handlings
func simpleGetUrlRequestWithErrorHandling()
{
let session = URLSession.shared
let url = URL(string: "https://httpbin.org/get")!
let task = session.dataTask(with: url) { data, response, error in
if error != nil || data == nil {
print("Client error!")
return
}
guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
print("Server error!")
return
}
guard let mime = response.mimeType, mime == "application/json" else {
print("Wrong MIME type!")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print("The Response is : ",json)
} catch {
print("JSON error: \(error.localizedDescription)")
}
}
task.resume()
}
4. Simple POST Request with URL, Parameters with Error Handlings.
func simplePostRequestWithParamsAndErrorHandling(){
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
configuration.timeoutIntervalForResource = 30
let session = URLSession(configuration: configuration)
let url = URL(string: "https://httpbin.org/post")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let parameters = ["username": "foo", "password": "123456"]
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
if error != nil || data == nil {
print("Client error!")
return
}
guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
print("Oops!! there is server error!")
return
}
guard let mime = response.mimeType, mime == "application/json" else {
print("response is not json")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print("The Response is : ",json)
} catch {
print("JSON error: \(error.localizedDescription)")
}
})
task.resume()
}
Your suggestions are appreciated!!
Swift 3.0
Through a small abstraction
https://github.com/daltoniam/swiftHTTP
Example
do {
let opt = try HTTP.GET("https://google.com")
opt.start { response in
if let err = response.error {
print("error: \(err.localizedDescription)")
return //also notify app of failure as needed
}
print("opt finished: \(response.description)")
//print("data is: \(response.data)") access the response of the data with response.data
}
} catch let error {
print("got an error creating the request: \(error)")
}
Details
Xcode 9.2, Swift 4
Xcode 10.2.1 (10E1001), Swift 5
Info.plist
NSAppTransportSecurity
Add to the info plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Alamofire Sample
Alamofire
import Alamofire
class AlamofireDataManager {
fileprivate let queue: DispatchQueue
init(queue: DispatchQueue) { self.queue = queue }
private func createError(message: String, code: Int) -> Error {
return NSError(domain: "dataManager", code: code, userInfo: ["message": message ])
}
private func make(session: URLSession = URLSession.shared, request: URLRequest, closure: ((Result<[String: Any]>) -> Void)?) {
Alamofire.request(request).responseJSON { response in
let complete: (Result<[String: Any]>) ->() = { result in DispatchQueue.main.async { closure?(result) } }
switch response.result {
case .success(let value): complete(.success(value as! [String: Any]))
case .failure(let error): complete(.failure(error))
}
}
}
func searchRequest(term: String, closure: ((Result<[String: Any]>) -> Void)?) {
guard let url = URL(string: "https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))") else { return }
let request = URLRequest(url: url)
make(request: request) { response in closure?(response) }
}
}
Usage of Alamofire sample
private lazy var alamofireDataManager = AlamofireDataManager(queue: DispatchQueue(label: "DataManager.queue", qos: .utility))
//.........
alamofireDataManager.searchRequest(term: "jack johnson") { result in
print(result.value ?? "no data")
print(result.error ?? "no error")
}
URLSession Sample
import Foundation
class DataManager {
fileprivate let queue: DispatchQueue
init(queue: DispatchQueue) { self.queue = queue }
private func createError(message: String, code: Int) -> Error {
return NSError(domain: "dataManager", code: code, userInfo: ["message": message ])
}
private func make(session: URLSession = URLSession.shared, request: URLRequest, closure: ((_ json: [String: Any]?, _ error: Error?)->Void)?) {
let task = session.dataTask(with: request) { [weak self] data, response, error in
self?.queue.async {
let complete: (_ json: [String: Any]?, _ error: Error?) ->() = { json, error in DispatchQueue.main.async { closure?(json, error) } }
guard let self = self, error == nil else { complete(nil, error); return }
guard let data = data else { complete(nil, self.createError(message: "No data", code: 999)); return }
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
complete(json, nil)
}
} catch let error { complete(nil, error); return }
}
}
task.resume()
}
func searchRequest(term: String, closure: ((_ json: [String: Any]?, _ error: Error?)->Void)?) {
let url = URL(string: "https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))")
let request = URLRequest(url: url!)
make(request: request) { json, error in closure?(json, error) }
}
}
Usage of URLSession sample
private lazy var dataManager = DataManager(queue: DispatchQueue(label: "DataManager.queue", qos: .utility))
// .......
dataManager.searchRequest(term: "jack johnson") { json, error in
print(error ?? "nil")
print(json ?? "nil")
print("Update views")
}
Results
I have done HTTP Request Both methods GET & POST with JSON Parsing this way:
on viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
makeGetRequest()
makePostRequest()
}
func makePostRequest(){
let urlPath: String = "http://www.swiftdeveloperblog.com/http-post-example-script/"
var url: NSURL = NSURL(string: urlPath)!
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
var stringPost="firstName=James&lastName=Bond" // Key and Value
let data = stringPost.dataUsingEncoding(NSUTF8StringEncoding)
request.timeoutInterval = 60
request.HTTPBody=data
request.HTTPShouldHandleCookies=false
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
if (jsonResult != nil) {
// Success
println(jsonResult)
let message = jsonResult["Message"] as! NSString
println(message)
}else {
// Failed
println("Failed")
}
})
}
func makeGetRequest(){
var url : String = "http://api.androidhive.info/contacts/"
var request : NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: url)
request.HTTPMethod = "GET"
request.timeoutInterval = 60
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
if (jsonResult != nil) {
// Success
println(jsonResult)
let dataArray = jsonResult["contacts"] as! NSArray;
for item in dataArray { // loop through data items
let obj = item as! NSDictionary
for (key, value) in obj {
println("Key: \(key) - Value: \(value)")
let phone = obj["phone"] as! NSDictionary;
let mobile = phone["mobile"] as! NSString
println(mobile)
let home = phone["home"] as! NSString
println(home)
let office = phone["office"] as! NSString
println(office)
}
}
} else {
// Failed
println("Failed")
}
})
}
Done
You can use Just, a python-requests style HTTP library.
Some example of sending HTTP request with Just:
// synchronous GET request with URL query a=1
let r = Just.get("https://httpbin.org/get", params:["a":1])
// asynchronous POST request with form value and file uploads
Just.post(
"http://justiceleauge.org/member/register",
data: ["username": "barryallen", "password":"ReverseF1ashSucks"],
files: ["profile_photo": .URL(fileURLWithPath:"flash.jpeg", nil)]
) { (r)
if (r.ok) { /* success! */ }
}
In both cases, the result of a request r can be accessed in ways similar to python-request:
r.ok // is the response successful?
r.statusCode // status code of response
r.content // response body as NSData?
r.text // response body as text?
r.json // response body parsed by NSJSONSerielization
You can find more examples in this playground
Using this library in synchronous mode in a playground is the closest thing to cURL one can get in Swift.
I am calling the json on login button click
#IBAction func loginClicked(sender : AnyObject) {
var request = NSMutableURLRequest(URL: NSURL(string: kLoginURL)) // Here, kLogin contains the Login API.
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(self.criteriaDic(), options: nil, error: &err) // This Line fills the web service with required parameters.
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
var err1: NSError?
var json2 = NSJSONSerialization.JSONObjectWithData(strData.dataUsingEncoding(NSUTF8StringEncoding), options: .MutableLeaves, error:&err1 ) as NSDictionary
println("json2 :\(json2)")
if(err) {
println(err!.localizedDescription)
}
else {
var success = json2["success"] as? Int
println("Success: \(success)")
}
})
task.resume()
}
Here, I have made a seperate dictionary for the parameters.
var params = ["format":"json", "MobileType":"IOS","MIN":"f8d16d98ad12acdbbe1de647414495ec","UserName":emailTxtField.text,"PWD":passwordTxtField.text,"SigninVia":"SH"]as NSDictionary
return params
}
// You can add your own sets of parameter here.
KISS answer:
URLSession.shared.dataTask(with: URL(string: "https://google.com")!) {(data, response, error) in
print(String(data: data!, encoding: .utf8))
}.resume()
In Swift 4.1 and Xcode 9.4.1.
JSON POST approach example. To check internet connection add Reachability.h & .m files from https://developer.apple.com/library/archive/samplecode/Reachability/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007324-Intro-DontLinkElementID_2
func yourFunctionName {
//Check internet connection
let networkReachability = Reachability.forInternetConnection()
let networkStatus:Int = (networkReachability?.currentReachabilityStatus())!.rawValue
print(networkStatus)
if networkStatus == NotReachable.rawValue {
let msg = SharedClass.sharedInstance.noNetMsg//Message
//Call alert from shared class
SharedClass.sharedInstance.alert(view: self, title: "", message: msg)
} else {
//Call spinner from shared class
SharedClass.sharedInstance.activityIndicator(view: self.view)//Play spinner
let parameters = "Your parameters here"
var request = URLRequest(url: URL(string: url)!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
print("URL : \(request)")
request.httpBody = parameters.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data, error == nil else { // check for fundamental networking error
//Stop spinner
SharedClass.sharedInstance.stopActivityIndicator() //Stop spinner
//Print error in alert
SharedClass.sharedInstance.alert(view: self, title: "", message: "\(String(describing: error!.localizedDescription))")
return
}
SharedClass.sharedInstance.stopActivityIndicator() //Stop spinner
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
}
do {
let response = try JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject]
print(response!)
//Your code here
} catch let error as NSError {
print(error)
}
}
task.resume()
}
}
If you have interest to use this function in SharedClass
//My shared class
import UIKit
class SharedClass: NSObject {
static let sharedInstance = SharedClass()
func postRequestFunction(apiName: String , parameters: String, onCompletion: #escaping (_ success: Bool, _ error: Error?, _ result: [String: Any]?)->()) {
var URL = "your URL here/index.php/***?"
URL = URL.replacingOccurrences(of: "***", with: apiName)
var request = URLRequest(url: URL(string: URL)!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
print("shared URL : \(request)")
request.httpBody = parameters.data(using: .utf8)
var returnRes:[String:Any] = [:]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
onCompletion(false, error, nil)
} else {
guard let data = data else {
onCompletion(false, error, nil)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode == 200 {
do {
returnRes = try JSONSerialization.jsonObject(with: data, options: []) as! [String : Any]
onCompletion(true, nil, returnRes)
} catch let error as NSError {
onCompletion(false, error, nil)
}
} else {
onCompletion(false, error, nil)
}
}
}
task.resume()
}
private override init() {
}
And finally call this function like this....
SharedClass.sharedInstance.postRequestFunction(apiName: "Your API name", parameters: parameters) { (success, error, result) in
print(result!)
if success {
//Your code here
} else {
print(error?.localizedDescription ?? "")
}
}
Here are start to finish instructions to make an HTTP request with Swift on Linux.
First create a SwiftPM package
mkdir swift-http && cd swift-http && swift package init --type executable
Then replace the contents of ./Sources/swift-http/main.swift
with the code below:
import Foundation
import FoundationNetworking
let sema = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: URL(string: "http://numbersapi.com/42")!) {(data, response, error) in
print(String(data: data!, encoding: .utf8) ?? String(describing: error))
sema.signal()
}.resume()
sema.wait()
Then run the code
swift run
Output example:
[6/6] Build complete!
42 is the answer to the Ultimate Question of Life, the Universe, and Everything.
Note: the DispatchSemaphore is used so that our program doesn't exit before getting a response.
You could have also done something like this:
import Foundation
import FoundationNetworking
var done = false
URLSession.shared.dataTask(with: URL(string: "http://numbersapi.com/42")!) {(data, response, error) in
print(String(data: data!, encoding: .utf8) ?? String(describing: error))
done = true
}.resume()
while !done { Thread.sleep(forTimeInterval: 1) }
A simple Swift 2.0 approach to making a HTTP GET request
The HTTP request is asynchronous so you need a way to get the returned value from the HTTP Request. This approach uses Notifiers and is spread over two classes.
The example is to check the username and password for an identifier token using the website http://www.example.com/handler.php?do=CheckUserJson&json= That is the file is called handler.php and has a switch statement on the do parameter to get a RESTful approach.
In the viewDidLoad we setup the NotifierObserver, set up the json and make the call to the getHTTPRequest function. It will return to the function checkedUsernameAndPassword with the returned parameter from the http request.
override func viewDidLoad() {
super.viewDidLoad()
// setup the Notification observer to catch the result of check username and password
NSNotificationCenter.defaultCenter().addObserver(self, selector: "checkedUsernameAndPassword:", name: CHECK_USERNAME_AND_PASSWORD, object: nil)
let username = GlobalVariables.USER_NAME
let password = GlobalVariables.PASSWORD
// check username and password
if let jsonString = Utility.checkUsernameAndPasswordJson(username, password:password){
print("json string returned = \(jsonString)")
let url = CHECKUSERJSON+jsonString
// CHECKUSERJSON = http://www.example.com/handler.php?do=CheckUserJson&json=
// jsonString = {\"username\":\"demo\",\"password\":\"demo\"}"
// the php script handles a json request and returns a string identifier
Utility.getHTTPRequest(url,notifierId: CHECK_USERNAME_AND_PASSWORD)
// the returned identifier is sent to the checkedUsernaeAndPassword function when it becomes availabel.
}
}
There are two static functions in Utility.swift first to encode the json and then to do the HTTP call.
static func checkUsernameAndPasswordJson(username: String, password: String) -> String?{
let para:NSMutableDictionary = NSMutableDictionary()
para.setValue("demo", forKey: "username")
para.setValue("demo", forKey: "password")
let jsonData: NSData
do{
jsonData = try NSJSONSerialization.dataWithJSONObject(para, options: NSJSONWritingOptions())
let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding) as! String
return jsonString
} catch _ {
print ("UH OOO")
return nil
}
}
and the Http request
static func getHTTPRequest (url:String , notifierId: String) -> Void{
let urlString = url
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
let safeURL = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
if let url = NSURL(string: safeURL){
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.timeoutInterval = 60
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
if (data != nil) {
let result = NSString(data: data! , encoding: NSUTF8StringEncoding)
sendNotification (notifierId, message: String(result), num: 0)
}else{
sendNotification (notifierId, message: String(UTF8String: nil), num: -1) }
})
taskData.resume()
}else{
print("bad urlString = \(urlString)")
}
}
The sendNotification function completes the circle. Notice that in teh Observer there is a ":" at the end of the selector string. This allows the notification to carry a payload in userInfo. I give this a String and an Int.
static func sendNotification (key: String, message:String?, num: Int?){
NSNotificationCenter.defaultCenter().postNotificationName(
key,
object: nil,
userInfo: (["message": message!,
"num": "\(num!)"])
)
}
Note that using HTTP is oldFashioned, prefer HTTPS see How do I load an HTTP URL with App Transport Security enabled in iOS 9?
Update :- Xcode 13.0 & Swift 5+
GET HTTP REQUEST
let url = URL(string: "URL HERE")! //PUT Your URL
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let safeData = data,
let response = response as? HTTPURLResponse,
error == nil else { // check for fundamental networking error
print("error", error ?? "Unknown error")
delegate?.onError(error!)
return
}
guard (200 ... 299) ~= response.statusCode else { // check for http errors
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
let responseString = String(data: safeData, encoding: .utf8)
print("Response String = \(responseString)")
}
task.resume()
var post:NSString = "api=myposts&userid=\(uid)&page_no=0&limit_no=10"
NSLog("PostData: %#",post);
var url1:NSURL = NSURL(string: url)!
var postData:NSData = post.dataUsingEncoding(NSASCIIStringEncoding)!
var postLength:NSString = String( postData.length )
var request:NSMutableURLRequest = NSMutableURLRequest(URL: url1)
request.HTTPMethod = "POST"
request.HTTPBody = postData
request.setValue(postLength, forHTTPHeaderField: "Content-Length")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
var reponseError: NSError?
var response: NSURLResponse?
var urlData: NSData? = NSURLConnection.sendSynchronousRequest(request, returningResponse:&response, error:&reponseError)
if ( urlData != nil ) {
let res = response as NSHTTPURLResponse!;
NSLog("Response code: %ld", res.statusCode);
if (res.statusCode >= 200 && res.statusCode < 300)
{
var responseData:NSString = NSString(data:urlData!, encoding:NSUTF8StringEncoding)!
NSLog("Response ==> %#", responseData);
var error: NSError?
let jsonData:NSDictionary = NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers , error: &error) as NSDictionary
let success:NSInteger = jsonData.valueForKey("error") as NSInteger
//[jsonData[#"success"] integerValue];
NSLog("Success: %ld", success);
if(success == 0)
{
NSLog("Login SUCCESS");
self.dataArr = jsonData.valueForKey("data") as NSMutableArray
self.table.reloadData()
} else {
NSLog("Login failed1");
ZAActivityBar.showErrorWithStatus("error", forAction: "Action2")
}
} else {
NSLog("Login failed2");
ZAActivityBar.showErrorWithStatus("error", forAction: "Action2")
}
} else {
NSLog("Login failed3");
ZAActivityBar.showErrorWithStatus("error", forAction: "Action2")
}
it will help you surely
//Here is an example that worked for me
//Swift function that post a request to a server with key values
func insertRecords()
{
let usrID = txtID.text
let checkin = lblInOut.text
let comment = txtComment.text
// The address of the web service
let urlString = "http://your_url/checkInOut_post.php"
// These are the keys that your are sending as part of the post request
let keyValues = "id=\(usrID)&inout=\(checkin)&comment=\(comment)"
// 1 - Create the session by getting the configuration and then
// creating the session
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: nil)
// 2 - Create the URL Object
if let url = NSURL(string: urlString){
// 3 - Create the Request Object
var request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
// set the key values
request.HTTPBody = keyValues.dataUsingEncoding(NSUTF8StringEncoding);
// 4 - execute the request
let taskData = session.dataTaskWithRequest(request, completionHandler: {
(data:NSData!, response:NSURLResponse!, error:NSError!) -> Void in
println("\(data)")
// 5 - Do something with the Data back
if (data != nil) {
// we got some data back
println("\(data)")
let result = NSString(data: data , encoding: NSUTF8StringEncoding)
println("\(result)")
if result == "OK" {
let a = UIAlertView(title: "OK", message: "Attendece has been recorded", delegate: nil, cancelButtonTitle: "OK")
println("\(result)")
dispatch_async(dispatch_get_main_queue()) {
a.show()
}
} else {
// display error and do something else
}
} else
{ // we got an error
println("Error getting stores :\(error.localizedDescription)")
}
})
taskData.resume()
}
}
PHP Code to get the key values
$empID = $_POST['id'];
$inOut = $_POST['inout'];
$comment = $_POST['comment'];
Here's a very simple Swift 4 example in a playground:
import UIKit
// run asynchronously in a playground
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// create a url
let url = URL(string: "http://www.stackoverflow.com")
// create a data task
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("there's a problem")
}
print(String(data: data!, encoding: String.Encoding.utf8) ?? "")
}
//running the task w/ resume
task.resume()
For XCUITest to stop the test finishing before the async request completes use this (maybe reduce the 100 timeout):
func test_api() {
let url = URL(string: "https://jsonplaceholder.typicode.com/posts/42")!
let exp = expectation(description: "Waiting for data")
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
exp.fulfill()
}
task.resume()
XCTWaiter.wait(for: [exp], timeout: 100)
}
An example for a sample "GET" request is given below.
let urlString = "YOUR_GET_URL"
let yourURL = URL(string: urlstring)
let dataTask = URLSession.shared.dataTask(with: yourURL) { (data, response, error) in
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
print("json --- \(json)")
}catch let err {
print("err---\(err.localizedDescription)")
}
}
dataTask.resume()

How do I perform GET and POST requests in Swift?

I adapted this from Ray Wenderlich's iOS Apprentice tutorial part 4.
This code works as a GET request sent to my Strongloop API with a simple database model, however:
This works, but I don't know why it works, since it invokes no method that I can see to actually send the request.
I see no means to make it into a POST request.
My question is: How do I perform a POST request? Is it done in a completely different way?
Let me know if you need more information.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func getFromDB() {
let url = urlWithSearchText("")
println("URL: '\(url)'")
if let jsonString = performGetRequestWithURL(url) {
println("Received JSON string '\(jsonString)'")
}
}
func urlWithSearchText(searchText: String) -> NSURL {
let escapedSearchText = searchText.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let urlString = String(format: "http://localhost:3000/api/Tests", escapedSearchText)
let url = NSURL(string: urlString)
return url!
}
func performGetRequestWithURL(url: NSURL) -> String? {
var error: NSError?
if let resultString = String(contentsOfURL: url, encoding: NSUTF8StringEncoding, error: &error) {
return resultString
} else if let error = error {
println("Download Error: \(error)")
} else {
println("Unknown Download Error")
}
return nil
}
Here is a picture of this working:
https://dl.dropboxusercontent.com/u/14464971/Images/Messages%20Image%281477993527%29.png
Swift 3 & above
GET
func getRequest() {
// request url
let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")! // change the url
// create URLSession with default configuration
let session = URLSession.shared
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: url) { data, response, error in
if let error = error {
print("GET Request Error: \(error.localizedDescription)")
return
}
// ensure there is valid response code returned from this HTTP response
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("Invalid Response received from the server")
return
}
// ensure there is data returned
guard let responseData = data else {
print("nil Data received from the server")
return
}
do {
// serialise the data object into Dictionary [String : Any]
if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
print(jsonResponse)
} else {
print("data maybe corrupted or in wrong format")
throw URLError(.badServerResponse)
}
} catch let error {
print("JSON Parsing Error: \(error.localizedDescription)")
}
}
// resume the task
task.resume()
}
POST
func postRequest() {
// declare the parameter as a dictionary that contains string as key and value combination. considering inputs are valid
let parameters: [String: Any] = ["name": "abc", "password": "password#123"]
// create the url with URL
let url = URL(string: "http://myServerName.com/api")! //change the url
// create the session object
let session = URLSession.shared
// now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
// add headers for the request
request.addValue("application/json", forHTTPHeaderField: "Content-Type") // change as per server requirements
request.addValue("application/json", forHTTPHeaderField: "Accept")
do {
// convert parameters to Data and assign dictionary to httpBody of request
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
return
}
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request) { data, response, error in
if let error = error {
print("Post Request Error: \(error.localizedDescription)")
return
}
// ensure there is valid response code returned from this HTTP response
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode)
else {
print("Invalid Response received from the server")
return
}
// ensure there is data returned
guard let responseData = data else {
print("nil Data received from the server")
return
}
do {
// create json object from data or use JSONDecoder to convert to Model stuct
if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
print(jsonResponse)
// handle json response
} else {
print("data maybe corrupted or in wrong format")
throw URLError(.badServerResponse)
}
} catch let error {
print(error.localizedDescription)
}
}
task.resume()
}
Below are two POST methods. Depending on if you want it synchronous (everything else waits until the post method is completed) or asynchronous (POST method runs in background, other methods run in parallel).
Methods
// POST data to url
func postDataAsynchronous(url: String, bodyData: String, completionHandler: (responseString: String!, error: NSError!) -> ()) {
var URL: NSURL = NSURL(string: url)!
var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
request.HTTPMethod = "POST";
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
response, data, error in
var output: String!
if data != nil {
output = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
}
completionHandler(responseString: output, error: error)
}
}
// Obtain the data
func postDataSynchronous(url: String, bodyData: String, completionHandler: (responseString: String!, error: NSError!) -> ())
{
let URL: NSURL = NSURL(string: url)!
var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
request.HTTPMethod = "POST"
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
var response: NSURLResponse?
var error: NSError?
// Send data
let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
var output: String! // Default to nil
if data != nil{
output = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
}
completionHandler(responseString: output, error: error)
}
Using them
You can then call (use) them like so:
postDataSynchronous(url, bodyData: bodyData) {
responseString, error in
if error != nil {
println("Error during post: \(error)")
return
}
else{
//Success
println(responseString)
userType = responseString // Set usertype based on server response
}
}
SWIFT 2.0
func postData(url: String, params: Dictionary<String, String>, completionHandler: (data: NSData?, response: NSURLResponse?, error: NSError?) -> ()) {
// Indicate download
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
let url = NSURL(string: url)!
// print("URL: \(url)")
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// Verify downloading data is allowed
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
} catch let error as NSError {
print("Error in request post: \(error)")
request.HTTPBody = nil
} catch {
print("Catch all error: \(error)")
}
// Post the data
let task = session.dataTaskWithRequest(request) { data, response, error in
completionHandler(data: data, response: response, error: error)
// Stop download indication
UIApplication.sharedApplication().networkActivityIndicatorVisible = false // Stop download indication
}
task.resume()
}
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
}
It's a get method.
This method invokes the http request.
String(contentsOfURL: url, encoding: NSUTF8StringEncoding, error: &error)
Because Swift String has no init signature like this.
This method would be written somewhere in the project, as extension of String
It would be something like this
extension String{
init(contentsOfURL: NSURL, encoding: NSUTF8StringEncoding, inout error: NSError){
// load data from url
self = //parse data to string
}
}
The String(contentsOfUrl:encoding:error) initializer makes a GET request under the hood and returns the content as a string with the specified encoding.
One way to make a request would be to create an NSURLConnection and use NSMutablrURLRequest set the HTTP method the post. With the NSMutableURLRequest, you can create a NSURLConnection and start it immediately with a delegate or you can call NSURLConnection.sendSynchronousRequest or NSURLConnection.sendAsynchronousRequest to send the request.
let parameters = ["username": "#Bipin_kumar", "tweet": "HelloWorld"]
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else { return }
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
} catch {
print(error)
}
}
}.resume()
It's a post method.
GET Request
func getRequest(with url: URL, callback: #escaping (Any?) -> Swift.Void) -> Void {
let defaultConfigObject = URLSessionConfiguration.default
defaultConfigObject.timeoutIntervalForRequest = 30.0
defaultConfigObject.timeoutIntervalForResource = 60.0
let session = URLSession.init(configuration: defaultConfigObject, delegate: nil, delegateQueue: nil)
var urlRequest = URLRequest(url: url as URL)
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpMethod = "GET"
session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
guard let httpResponse: HTTPURLResponse = response as? HTTPURLResponse
else {
print("Error: did not receive data")
return
}
var response : (Any)? = nil
if httpResponse.statusCode == 200 {
print(httpResponse)
guard let responseData = data else {
print("Error: did not receive data")
return
}
do {
let responseData = try JSONSerialization.jsonObject(with: responseData, options: [JSONSerialization.ReadingOptions.allowFragments])
response = responseData
callback(response)
}
catch _ as NSError {
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
callback(responseString)
return
}
}
else {
print(httpResponse)
guard error == nil else {
print("error calling GET on /todos/1")
print(error ?? "error")
callback(response!)
return
}
}
}).resume()
}
POST REQUEST
//MARK: post request
func postRequest(with url:URL, postBody:String, callback: #escaping (Any?) -> Void) -> Void {
let defaultConfigObject = URLSessionConfiguration.default
defaultConfigObject.timeoutIntervalForRequest = 30.0
defaultConfigObject.timeoutIntervalForResource = 60.0
let session = URLSession.init(configuration: defaultConfigObject, delegate: nil, delegateQueue: nil)
let params: String! = postBody
var urlRequest = URLRequest(url: url as URL)
urlRequest.httpMethod = "POST"
let data = params.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
urlRequest.httpBody = data
session.dataTask(with: urlRequest, completionHandler: { (data, urlResponse, error) in
guard let httpResponse:HTTPURLResponse = urlResponse as? HTTPURLResponse
else{
print("did not get any data")
return
}
var response : (Any)? = nil
if httpResponse.statusCode == 200 {
guard let responseData = data else {
print("Error: did not receive data")
return
}
do {
guard let responseData = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: AnyObject] else {
print("error trying to convert data to JSON")
return
}
response = responseData
callback(response)
} catch _ as NSError {
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
callback(responseString)
return
}
}
else {
guard error == nil else {
print("error calling GET on /todos/1")
print(error ?? "error")
callback(nil)
return
}
}
}).resume()
}
Always try to check the HTTPURLResponse code

Resources