I'm working on this app and have developed a full API for it. However, now that I've come to the front end part (the IOS app), I'm not sure how to load that data the right way.
I have made this class to make requests a little bit easier, but I get confused when it comes to threading and that sort of stuff...
Basically, I have a simple UITableView set up, and I want it to display data that I fetch from my API. During fetching, I need it to display a UIActivityIndicator which has to hide automatically as well.
How the hell do I do this? (Especially the automatic UIActivityIndicator)
Here is my request struct:
//
// Request.swift
// Voots
//
// Created by Carlo on 16/10/2017.
// Copyright © 2017 Carlo. All rights reserved.
//
import Foundation
struct Request {
// Post request with specific url, parameters and token
func post(params: [String: String], url: String, token: String?,
completion: ((Data, URLResponse) -> ())?) {
let nsUrl = NSURL(string: url)
var request = URLRequest(url: nsUrl! as URL)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// If a token was provided, add it.
if token != nil {
request.addValue("Bearer \(String(describing: token!))", forHTTPHeaderField: "Authorization")
print(request.value(forHTTPHeaderField: "Authorization")!)
}
request.httpMethod = "POST"
guard let httpBody = try? JSONSerialization.data(withJSONObject: params, options: JSONSerialization.WritingOptions.prettyPrinted) else {
return
}
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let data = data {
if let response = response {
if completion != nil {
completion!(data, response)
}
}
}
}.resume()
}
func get(url: String, token: String?, completion: ((Data, URLResponse) -> ())?) {
let nsUrl = NSURL(string: url)
var request = URLRequest(url: nsUrl! as URL)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// If a token was provided, add it.
if token != nil {
request.addValue("Bearer \(String(describing: token!))", forHTTPHeaderField: "Authorization")
print(request.value(forHTTPHeaderField: "Authorization")!)
}
request.httpMethod = "GET"
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let data = data {
if let response = response {
if completion != nil {
completion!(data, response)
}
}
}
}.resume()
}
}
Here is list of readymade (available) libraries, if you don't want to add manual effort to manage this:
iOS Libraries - ActivityIndicatorView
MBProgressHUD
SVProgressHUD
Here is sample for you, how you can manage it.
class ViewController: UIViewController {
// Create an IBOutlet of indicator or you can create it programatically also.
#IBOutlet weak var activitIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
activitIndicator.isHidden = false
Request.post(params: <#T##[String : String]#>, url: <#T##String#>, token: <#T##String?#>) { (<#Data#>, <#URLResponse#>) in
DispatchQueue.main.async(execute: {
self.activitIndicator.isHidden = true
})
}
}
}
struct Request {
// Post request with specific url, parameters and token
// Note: use static function
static func post(params: [String: String], url: String, token: String?,
completion: ((Data, URLResponse) -> ())?) {
let nsUrl = NSURL(string: url)
var request = URLRequest(url: nsUrl! as URL)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// If a token was provided, add it.
if token != nil {
request.addValue("Bearer \(String(describing: token!))", forHTTPHeaderField: "Authorization")
print(request.value(forHTTPHeaderField: "Authorization")!)
}
request.httpMethod = "POST"
guard let httpBody = try? JSONSerialization.data(withJSONObject: params, options: JSONSerialization.WritingOptions.prettyPrinted) else {
return
}
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let data = data {
if let response = response {
if completion != nil {
completion!(data, response)
}
}
}
}.resume()
}
// Note: use static function
static func get(url: String, token: String?, completion: ((Data, URLResponse) -> ())?) {
let nsUrl = NSURL(string: url)
var request = URLRequest(url: nsUrl! as URL)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// If a token was provided, add it.
if token != nil {
request.addValue("Bearer \(String(describing: token!))", forHTTPHeaderField: "Authorization")
print(request.value(forHTTPHeaderField: "Authorization")!)
}
request.httpMethod = "GET"
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let data = data {
if let response = response {
if completion != nil {
completion!(data, response)
}
}
}
}.resume()
}
}
Related
I have a request in Swift 5 that is trying to make a call to tinify.com. It is supposed to give me back a URL to a compressed image. I am currently getting this error in the print field:
{"error":"Not found","message":"This endpoint does not exist."}
TinyPNG.com API Reference
Code:
let string = "https://api.tinify.com/shrink"
let url = NSURL(string: string)
let request = NSMutableURLRequest(url: url! as URL)
request.httpMethod = "GET"
request.addValue("fakeAPIKey1234", forHTTPHeaderField: "user api")
request.addValue("/dev/stdout", forHTTPHeaderField: "dump-header")
request.addValue("\(String(describing: self.jpegData(compressionQuality: 0.8)!))", forHTTPHeaderField: "data-binary")
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
guard error == nil else { print(error!.localizedDescription); return }
guard let data = data else { print("Empty data"); return }
if let str = String(data: data, encoding: .utf8) {
print("Grab Image from this url: \(str)")
}
}
Note: fakeAPIKey1234 is not actually the string I'm using. Also I'm sure there is more wrong with my code than this little issue.
I am uploading file in swift with the help of URLSession. But issue is I don't get progress for the upload. I am not using any multipart request. I am just sending data of video in body of request.
let urlStr = UserDefaults.standard.value(forKey: "Resumable") as? String ?? ""
let url = URL(string: urlStr)
do{
var request = try URLRequest(url: url!, method: .put)
// request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
// request.setValue("300000", forHTTPHeaderField: "X-Upload-Content-Length")
request.setValue("video/*", forHTTPHeaderField: "Content-Type")
request.setValue("278", forHTTPHeaderField: "Content-Length")
request.timeoutInterval = 60.0
let path = Bundle.main.path(forResource: "video", ofType: "mov")
let videodata: NSData = NSData.dataWithContentsOfMappedFile(path!)! as! NSData
request.httpBody = videodata as Data
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.allHeaderFields)
if httpResponse.statusCode != 200 {
return
}else{
if let url = httpResponse.allHeaderFields["Location"] as? String{
}
}
}
})
task.resume()
}catch{
}
Please tell me how can I get the progress of how many bytes have been uploaded?
You need to implement the urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:) delegate method. And to do this, you need to create your own session and set its delegate.
You should also use an upload task. This avoids the need to load the file into memory.
Here's the updated code inside your do block:
var request = try URLRequest(url: url!)
// request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
// request.setValue("300000", forHTTPHeaderField: "X-Upload-Content-Length")
request.setValue("video/*", forHTTPHeaderField: "Content-Type")
request.setValue("278", forHTTPHeaderField: "Content-Length")
request.timeoutInterval = 60.0
let videoURL = Bundle.main.url(forResource: "video", withExtension: "mov")!
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
let task = session.uploadTask(with: request, fromFile: videoURL) { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.allHeaderFields)
if httpResponse.statusCode != 200 {
return
}else{
if let url = httpResponse.allHeaderFields["Location"] as? String{
}
}
}
}
task.resume()
Then add:
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
// update progress as needed
}
I am trying to receive and process a POST request being sent from my iOS app to my Node.js web server. The server responds with HTTP Error 502 whenever I try to send this POST request. Could you please look at my code below and see what is wrong with it? Thank you!
Node.js Code
app.post('/applogin', function(req, res) {
var parsedBody = JSON.parse(req.body);
console.log(parsedBody)
});
Swift Code (POST function)
func httpPost(jsonData: Data) {
if !jsonData.isEmpty {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
URLSession.shared.getAllTasks { (openTasks: [URLSessionTask]) in
NSLog("open tasks: \(openTasks)")
}
let task = URLSession.shared.dataTask(with: request, completionHandler: { (responseData: Data?, response: URLResponse?, error: Error?) in
NSLog("\(response)")
})
task.resume()
}
}
Swift Code (sending of the POST request)
#IBAction func onClick(_ sender: Any) {
let username = Username.text
let password = Password.text
var dataString = "username: \(username), password: \(password)"
let data = dataString.data(using: .utf8)
httpPost(jsonData: data!)
}
Thanks in advance!
You have to send a json instead dataString, and you have to set the "Content Type" header with value "application/json"
Swift 2
let request = NSMutableURLRequest(URL: requestUrl)
request.HTTPMethod = "POST"
let params = ["username" : username, "password" : password] as Dictionary<String, AnyObject>
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options:NSJSONWritingOptions.PrettyPrinted)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
Many answers they don't mention that we need to set header for the request from Swift side before sending to the backend otherwise it'll be a string in a wrong format that we can't use JSON.parse, here's what I firgured out (NOTE the IMPORTANT line):
let json = [
"email": emailTextField.text
]
let jsonData = try! JSONSerialization.data(withJSONObject: json)
let url = URL(string: BASE_URL + "/auth/register")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// insert json data to the request
request.httpBody = jsonData
//IMPORTANT
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume()
And in your NodeJS with Express just call req.body and you're done
Try this:
app.post('/applogin', function(req, res) {
var parsedBody = JSON.parse(req.body);
console.log(parsedBody)
res.send("Request received")
});
I have this API http://my-api.mydoctorfinder.com/
that will return a bool value depending on the email and password you have entered.
My problem is it will always return false despite using the correct email and password.
I was thinking that I might have not sent the right parameter since I created a dictionary containing the email and password. Then passed it on NSJSONSerialization.dataWithJSONObject method
By the way, I was using SwiftyJson.
This is my code
//creates a dictionary and calls the PostRequest method
func attemptLogIn( email: String, password: String) {
let route = loggerURL
let body: [String:String] = ["email":email, "password":password]
makeHTTPPostRequest(route, body: body)
}
//performs post request
private func makeHTTPPostRequest(path: String, body: [String: AnyObject]) {
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
// Set the method to POST
request.HTTPMethod = "POST"
do {
// Set the POST body for the request
let jsonBody = try NSJSONSerialization.dataWithJSONObject(body, options: .PrettyPrinted)
request.HTTPBody = jsonBody
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
//onCompletion(json, nil)
print("The Response: ")
print(json)
} else {
//onCompletion(nil, error)
print("The Response: ")
print("Hello")
}
})
task.resume()
} catch {
// Create your personal error
//onCompletion(nil, nil)
}
}
The response is simply a true or false i.e. its not a json object.
So i would suggest don't use Swifty son instead use Alamofire.
Following code should work for you:-
let myParameters = [
"email": "your email id",
"password": "your password"]
Alamofire.request(.POST, "http://my-api.mydoctorfinder.com/ ", parameters: myParameters)
.response { request, response, data, error in
print(request)
print(response)
if(response == true)
{
// do your thing
}
print(error)
}
Note: There might be a need to typecast response to bool
Also following is the screenshot of the link you gave, it returns true(and not a json object) [After registration, i tried to login with same credentials]
Try to create JSON object using NSJSONSerialization in this way:
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(body, options: [])
I assume that problem is in .PrettyPrinted constant.
Edit:
Also try adding correct content-type:
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
Here is the swift post request to get data :
func retriveTextDataByPost(requestURL:String, params: NSDictionary, handler:((dict:NSDictionary?) -> Void)) {
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let url = NSURL(string: requestURL)
let request = NSMutableURLRequest(URL: url!, cachePolicy: NSURLRequestCachePolicy.UseProtocolCachePolicy, timeoutInterval: 60)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPMethod = "POST"
do {
let postData = try NSJSONSerialization.dataWithJSONObject(params, options:NSJSONWritingOptions.PrettyPrinted)
request.HTTPBody = postData
let postDataTask = session.dataTaskWithRequest(request) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
if data != nil {
do {
let dictResult:NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
handler(dict: dictResult)
} catch { }
}
}
postDataTask.resume()
} catch { }
}
Check your keys for email and password with required input for APIs
Check your login URL
Below is my code I am getting the issue with:
func parseFeedForRequest(request: NSURLRequest, callback: (feed: RSSFeed?, error: NSError?) -> Void)
{
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response, data, error) -> Void in
if ((error) != nil)
{
callback(feed: nil, error: error)
}
else
{
self.callbackClosure = callback
let parser : NSXMLParser = NSXMLParser(data: data!)
parser.delegate = self
parser.shouldResolveExternalEntities = false
parser.parse()
}
}
}
This is now deprecated as of iOS 9, and is telling me to use dataTaskWithRequest instead. Can someone help me change sendAsync with dataTask, I don't know how to.
Use NSURLSession instead like below,
For Objective-C
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:"YOUR URL"]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// handle response
}] resume];
For Swift,
var request = NSMutableURLRequest(URL: NSURL(string: "YOUR URL")!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.HTTPBody = try? NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")})
task.resume()
For asynchronously query, from Apple docs
Like most networking APIs, the NSURLSession API is highly
asynchronous. It returns data in one of two ways, depending on the
methods you call:
To a completion handler block that returns data to your app when a
transfer finishes successfully or with an error.
By calling methods on your custom delegate as the data is received.
By calling methods on your custom delegate when download to a file is
complete.
Swift implementation
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
}
Swift 3.0
var request = URLRequest(url: URL(string: "http://example.com")!)
request.httpMethod = "POST"
let session = URLSession.shared
session.dataTask(with: request) {data, response, err in
print("Entered the completionHandler")
}.resume()
This is the swift 2.1 version:
let request = NSMutableURLRequest(URL: NSURL(string: "YOUR URL")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
let params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
print("Response: \(response)")})
task.resume()
Swift 2.0:
Old (replace with New below):
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) { (response, data, error) -> Void in
// Code
}
New:
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
// Code
}
task.resume()
Swift 4
let params = ["email":"email#email.com", "password":"123456"] as Dictionary<String, String>
var request = URLRequest(url: URL(string: "http://localhost:8080/api/1/login")!)
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
do {
let json = try JSONSerialization.jsonObject(with: data!) as! Dictionary<String, AnyObject>
print(json)
} catch {
print("error")
}
})
task.resume()
with swift 3.1
let request = NSMutableURLRequest(url: NSURL(string: image_url_string)! as URL)
let session = URLSession.shared
request.httpMethod = "POST"
let params = ["username":"username", "password":"password"] as Dictionary<String, String>
request.httpBody = try? JSONSerialization.data(withJSONObject: params, options: [])
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
print("Response: \(String(describing: response))")})
task.resume()
Illustrating with an example, the alternative code to the deprecation of:
sendAsynchronousRequest(_:queue:completionHandler:)' was deprecated in iOS 9.0: Use [NSURLSession dataTaskWithRequest:completionHandler:]
Tested and works in Swift 2.1 onwards.
import UIKit
class ViewController: UIViewController {
#IBOutlet var theImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "https://upload.wikimedia.org/wikipedia/commons/6/6a/Johann_Sebastian_Bach.jpg")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) -> Void in
if error != nil {
print("thers an error in the log")
} else {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data: data!)
self.theImage.image = image
}
}
}
task.resume()
}
}
//Displays an image on the ViewControllers ImageView. Connect an outlet of the ImageView
Here is the SWIFT3.0 Version of Nilesh Patel's Answer with JSONSerialised data
let url = URL(string: "<HERE GOES SERVER API>")!
var request = URLRequest(url: url)
request.httpMethod = "POST" //GET OR DELETE etc....
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("<ValueforAuthorization>", forHTTPHeaderField: "Authorization")
let parameter = [String:Any]() //This is your parameters [String:Any]
do {
let jsonData = try JSONSerialization.data(withJSONObject: parameter, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
request.httpBody = jsonData
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request, completionHandler: { (incomingData, response, error) in
if let error = error {
print(error.localizedDescription)
print(request)
}else if let response = response {
print(response)
}else if let incomingData = incomingData {
print(incomingData)
}
})
task.resume()
} catch {
print(error.localizedDescription)
}
Swift 4.2
This worked for me:
func loadImageFromURL(URL: NSURL) {
let request = URLRequest(url: URL as URL)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let imageData = data {
DispatchQueue.main.async {
self.imageView.image = UIImage(data: imageData)
}
}
}
task.resume()
}
I had to add "DispatchQueue.main.async { }" because I had a runtime warning, since only the main thread is supposed to modify UI elements.