How to get progress of file upload without multipart? - ios

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
}

Related

How to Compress UIIMage Using TinyPNG Swift

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.

Display UIActivityIndicator while loading API data safely

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()
}
}

Getting an Authentication Failed error from server after hitting web service in Swift

The following below code is of hitting web service in swift by using NSURLSession .. I always get authentication failed message from server.Not able to get what is missing in request body.
let urlString:String = "\(BaseURL)"+"user_register.php?"
let deviceStr = "xyz"
let dictParam:NSDictionary = ["email":"\((emailTF?.text!)!)","username":"\((usernameTF?.text!)!)","password":"\((passwordTF?.text!)!)","device_key":deviceStr]
print(dictParam)
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
request.timeoutInterval = 60.0
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
// request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPMethod = "POST"
let dataToPost = try! NSJSONSerialization.dataWithJSONObject(dictParam, options: [])
request.HTTPBody = dataToPost
let task = session.dataTaskWithRequest(request) {
(
let data, let response, let error) in
guard let _:NSData = data, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let response:AnyObject! = try! NSJSONSerialization.JSONObjectWithData(data!, options: [])
print(data!)
print(response)
}
task.resume()

sendSynchronousRequest is deprecated in ios 9 [duplicate]

This question already has an answer here:
Fixing NSURLConnection Deprecation from Swift 1.2 to 2.0
(1 answer)
Closed 7 years ago.
Xcode says that sendSynchronousRequest is now deprecated.
How should I replace it?
let postData:NSData = post.dataUsingEncoding(NSASCIIStringEncoding)!
let postLength:NSString = String( postData.length )
let request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.HTTPBody = postData
request.setValue(postLength as String, forHTTPHeaderField: "Content-Length")
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
var response: NSURLResponse?
var urlData: NSData?
do {
urlData = try NSURLConnection.sendSynchronousRequest(request, returningResponse:&response)
} catch _ as NSError {
urlData = nil
} catch {
fatalError()
}
This is a working example,
You should use NSURLSession, with Request.
func testPost(sender: UIButton) {
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: "http://localhost:8080/iOSServer/ios/helloworld/swiftCalculator")!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let d = "4"
let data = "x=4&y=\(d)"
request.HTTPBody = data.dataUsingEncoding(NSASCIIStringEncoding)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let data = data{
print("data =\(data)")
}
if let response = response {
print("url = \(response.URL!)")
print("response = \(response)")
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
//if you response is json do the following
do{
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())
let arrayJSON = resultJSON as! NSArray
for value in arrayJSON{
let dicValue = value as! NSDictionary
for (key, value) in dicValue {
print("key = \(key)")
print("value = \(value)")
}
}
}catch _{
print("Received not-well-formatted JSON")
}
}
})
task.resume()
}
Notice it is not necessary to use the request. you can have a data task with URL, but I added the request because in your code, you have set some headers in the request.
Notice using the completionHandler which will be called when your server responses by http response.

sendAsynchronousRequest was deprecated in iOS 9, How to alter code to fix

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.

Resources