I would like to track the progress of videos uploaded through a stream request with a UIProgressView. Unfortunately, I am not using Alamofire, so I'm not sure if URLSession has this ability. Below is relevant code from my application.
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
let uploadProgress:Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend)
let uploadCell = contentTableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! NewContentCell
uploadCell.uploadProgressView.progress = uploadProgress
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
let uploadCell = contentTableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! NewContentCell
uploadCell.uploadProgressView.progress = 1.0
}
didCompleteWithError correctly sets the UIProgressView to indicate that the upload is complete, however, didSendBodyData is returning values greater than 1 through the uploadProgress calculation.
It's my first time utilizing a stream request, so I'm hoping I simply glossed over something that you could help point out. Here is the structure of my request as well for reference.
let request = NSMutableURLRequest(url: NSURL(string: "\(requestUrl)")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBodyStream = InputStream(data: body as Data)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
let dataTask = session.uploadTask(withStreamedRequest: request as URLRequest)
dataTask.resume()
Much thanks for your input and help.
Implementing
public func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)
is the correct way to track stream request progression.
But if you want to now the totalBytesExpectedToSend, you must tell it to the server. So don't forget to set the correct Content-Length header in your request!
Here's the way i'm creating the request:
var request = URLRequest(url: url)
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
request.addValue(String(dataToUpload.count), forHTTPHeaderField: "Content-Length") // <-- here!
request.httpBodyStream = InputStream(data: dataToUpload)
var task = session.uploadTask(withStreamedRequest: request)
task?.resume()
Reading documentation further, figured out that stream objects do not support totalBytesExpectedToSend. It may be a hack, but just using the file's NSData.length feature allows for correct progress tracking. So for stream requests using URLSession, progress can be tracked by using didSendBodyData, with let uploadProgress: Float = Float(totalBytesSent) / Float(mediaSize), where mediaSize is NSData.length.
Related
im trying to fetch some data from a Http Api and use the responded JSON to update the text in my SwiftUI view accordingly
The problem is, that I need to fetch the data two times per second and I don't really know how to refresh my view accordingly.
This is how I fetch the data
var timer = Timer()
func scheduleTimer()
timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(fetchData), userInfo: nil, repeats: true)
}
#objc func fetchData() {
let url:URL = URL(string: "http://XXX.XXX.XXX.23:8085/telemachus/datalink?altitude=v.altitude&longitude=v.long&latitude=v.lat&name=v.name")!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error { print(error) }
guard let data = data else {return}
do {
let jsonData = try JSONDecoder().decode(JsonData.self, from: data)
let altitude = jsonData.altitude
print(altitude)
} catch let error {
print(error)
}
}.resume()
}
I used a timer to repeat this action. I also have a Model for the JsonData.
#EnvironmentObject is not usable in this case because the function is not inside the view.
First of all, This will kill the battery very quick. Also it may kill the bandwidth and the user may be blocked for too many requests.
Second of all, you should use socket for these kind of requests instead of restful.
Third of all your answer:
Two times per second? change the timeInterval value to 0.5. This will cause the execution to perform each half of second.
To update UI, you need to perform the update code in the main thread using the GCD or Operation queue like this:
DispatchQueue.main.async {
/* UI Work Goes Here */
// for example: myLabel.text = jsonData.altitude
}
Please assist in figuring out how do I make REST API GET/POST/DELETE/PUT using swift 3 and in playground.
Examples I got from search don't work as expacted. Also I want to consume Laravel REST API first using GET method.
import Foundation
let headers = ["content-type": "application/json"]
let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:8088/api/person")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
To run asynchronous code in a Playground you have to add these two lines
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
You are mixing up GET and POST semantics.
In a GET request the parameters are included in the URL (http://example.com/api?key1=value1&key2=value2)
In a POST request the parameters are passed in the HTTP body
Note: In Swift 3+ don't use NSURL and NSMutableURLRequest. Use the native API
please can you explain how to pass the json-data
in "let result" to a new view e.g. a textfield?
Here is the related piece of code:
// make http POST request
let uploadTask = session.uploadTask(with: request as URLRequest, from: body?.data(using: String.Encoding.utf8)!){
(data, response, error) in
(response as? HTTPURLResponse)?.statusCode
// URL Object to String
let result = String(data: data!, encoding: String.Encoding.utf8)
}
uploadTask.resume()
I am working on swift 3.0 uisng backendless. I am new to this concept. I am uploading image which I am selecting from the phone gallery using UIImagePickerController. In back endless I am using Rest Api. I am uplaoding the image using the following code ..
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
let image = info[UIImagePickerControllerOriginalImage] as? UIImage
self.uploadButton.isHidden = true
myImageView.contentMode = .scaleAspectFit
myImageView.image = image
let imageUrl = info[UIImagePickerControllerReferenceURL] as! NSURL
let imageName = imageUrl.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
let localPath = photoURL.appendingPathComponent(imageName!)
print(localPath!)
let imageurl = info[UIImagePickerControllerReferenceURL] as? NSURL
let imagename = imageurl?.lastPathComponent
print(imagename!)
//https://api.backendless.com/<application id>/<version name>/files/<path>/<file name>
Alamofire.request(“https://api.backendless.com/66B90F83-A813-84CF-FF9D-4A01AC28E100/v1/files/ + "\(localPath!)/\(imagename!)", method: .post, parameters: nil, encoding: JSONEncoding.default, headers: HeadersClass.allHeaders.headers).responseJSON { response in
debugPrint(response)
}
imagePicker.dismiss(animated: true, completion: nil)
}
But I am getting the “Status Code error = 400”.
Can anyone please tell me what mistake I did here.
Thanks in Advance.
Your are totally wrong from first step.
The request url you made is the return url that the backendless service will return back to you when you uploaded your file successfully.
regarding to backendless service, https://backendless.com/documentation/files/ios/files_file_upload.htm, you need to implement the functions that listed down here:
// Upload data block identified as 'content' to the specified path.
// If the server returns an error, it is delivered through
// the 'responder' object
func upload(_ path: String,
content content: NSData,
responder responder: IResponder!) -> Void
// Upload data block identified as 'content' to the specified path.
// If the file already exists on the server, overwrite if the
// 'overwrite' argument is set to YES/TRUE.
// If the server returns an error, it is delivered through
// the 'responder' object
func upload(_ path: String,
content content: NSData,
overwrite overwrite: Bool,
responder responder: IResponder!) -> Void
// Upload data block identified as 'content' to the specified path.
// If the server returns an error, it is delivered through
// the 'error' block-based callback
func upload(_ path: String,
content content: NSData,
response responseBlock: ((BackendlessFile!) -> Void)!,
error errorBlock: ((Fault!) -> Void)!) -> Void
// Upload data block identified as 'content' to the specified path.
// If the file already exists on the server, overwrite if the
// 'overwrite' argument is set to YES/TRUE.
// If the server returns an error, it is delivered through
// the 'error' block-based callback
func upload(_ path: String,
content content: NSData,
overwrite overwrite: Bool,
response responseBlock: ((BackendlessFile!) -> Void)!,
error errorBlock: ((Fault!) -> Void)!) -> Void
More detail, you can read in their document.
I am building mobile applications to go with my django based backend. I make a post request in swift like this:
var request: NSMutableURLRequest = NSMutableURLRequest()
var url = "https://webapp.com/makepost/"
url += NSUserDefaults.standardUserDefaults().stringForKey("userPk")!
url += "/"
var err: NSError?
request.URL = NSURL(string: url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: NSJSONWritingOptions(rawValue:0))
} catch _ {
print ("error")
}
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) {(response, data, error) -> Void in
var query = String(data: data!, encoding: NSUTF8StringEncoding)
query = query!.stringByReplacingOccurrencesOfString("Optional(", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
query = query!.stringByReplacingOccurrencesOfString(")", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
print(query)
//update ui
}
As soon as I make this post it creates the necessary models in django by reading the jsonObject.
The response doesn't matter and could take long as I'm notifying other users via FCM.
This is what I'm trying to do:
Make a post request.
Ignore the response.
Update the UI immediately after I make the post request.
How should I achieve this?
like so
//set request variable here
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) {(response, data, error) -> Void in
var query = String(data: data!, encoding: NSUTF8StringEncoding)
}
sleep(2)
// wait two seconds for models to create in backend
dispatch_async(dispatch_get_main_queue()) {
self.refresh(self.refreshButton)
//refresh ui here with method that fires another get request
}
}