I am fetching data using NSURLSession but I don't know how to get the data back once the download is complete my code :
class ViewController: UIViewController,NSURLSessionDelegate,NSURLSessionDataDelegate{
#IBOutlet var imageView: UIImageView!
#IBOutlet weak var progress: UIProgressView!
var buffer:NSMutableData = NSMutableData()
var session:NSURLSession?
var dataTask:NSURLSessionDataTask?
let url = NSURL(string:"https://initiate.alphacoders.com/download/wallpaper/566046/images7/jpg/37100/1353" )!
var expectedContentLength = 0
var downloadedImage = UIImage()
var downloadedData = NSData()
override func viewDidLoad() {
super.viewDidLoad()
progress.progress = 0.0
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let manqueue = NSOperationQueue.mainQueue()
session = NSURLSession(configuration: configuration, delegate:self, delegateQueue: manqueue)
dataTask = session?.dataTaskWithRequest(NSURLRequest(URL: url))
dataTask?.resume()
// Do any additional setup after loading the view, typically from a nib.
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
//here you can get full lenth of your content
expectedContentLength = Int(response.expectedContentLength)
print(expectedContentLength)
completionHandler(NSURLSessionResponseDisposition.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
if progress.progress > 0.99 {
downloadedImage = UIImage(data: data , scale: 1)!
}
buffer.appendData(data)
print("checking")
let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength)
progress.progress = percentageDownloaded
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
//use buffer here.Download is done
progress.progress = 1.0// download 100% complete
imageView.image = downloadedImage
}
}
I tried to get the data from func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData but at didReceiveData my file is not yet fully downloaded and i don't know how to get the data from didCompleteWithError function , am new in swift and still figuring out things here ,
If anybody knows here what I am missing or what should I do then please tell me, any guidance will be so appreciated and helpful.
- URLSession:downloadTask:didFinishDownloadingToURL:
Is something you are missing.
Called to inform the delegate that a download task has finished downloading.
This method gives you a file URL for the temporary file (The third parameter). Because the file is temporary, you must either open the file for reading or move it to a permanent location in your app’s sandbox container directory before returning from this delegate method.
Find more here.
EDIT 1: To check the progress override following method:
- URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
EDIT 2: Okay I see now. Make following changes and see if it works?:
Let your class conform NSURLSessionDownloadDelegate
Change dataTask = session?.dataTaskWithRequest(NSURLRequest(URL: url)) to dataTask = session?.downloadTaskWithURL(NSURLRequest(URL: url))
That should make you download the file.
Related
I'm currently connecting to a D-Link Camera successfully through iOS and receiving a stream of images. I need to do the same with audio stream, however I receive raw data and have been facing difficulties in playing the data stream. Below a sample code to show the idea briefly
import UIKit
import AVKit
import MediaPlayer
class ViewController: UIViewController, NSURLSessionDataDelegate{
var audioBuffer = AudioBuffer()
var audioData = NSMutableData()
var player:AVAudioPlayer?
var task:NSURLSessionDataTask?
override func viewDidLoad() {
super.viewDidLoad()
downloadImage()
}
func downloadImage()
{
let url:NSURL = NSURL(string: "http://197.162.158.4:1090/audio/ACAS.cgi")!
let config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("download_session")
let userPasswordString = "admin:admin"
let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions([])
let authString = "Basic \(base64EncodedCredential)"
config.HTTPAdditionalHeaders = ["Authorization" : authString]
let request:NSMutableURLRequest = NSMutableURLRequest(URL: url)
let session:NSURLSession = NSURLSession(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
task = session.dataTaskWithRequest(request)
task?.resume()
}
//MARK: NSURLSessionDownloadDelegate
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
audioData.appendData(data)
print(audioData.length)
if audioData.length > 700000
{
//Need to carry out something like this
/* player = try? AVAudioPlayer(data: audioData, fileTypeHint: AVFileTypeCoreAudioFormat)
player?.play()
audioData = NSMutableData()*/
}
}
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
}
func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) {
print(error?.localizedDescription)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am trying to download 30+ images using NSURLSessionDownloadTask, the problem is that it won't finish downloading everything. Sometimes, only 26 images are download but other times it is more or less. I've checked my url's and all of them exist but I don't understand why it's doing this. I have tried downloading using the json urls directly from the server and it crashes because of an empty optional which is caused I think by a connection timeout. I understand that happens because the internet here is not that stable. But when I tried to download the whole json file and saved it in my document directory and tried to download using the json that is saved locally, it doesn't crash anymore since I won't need to get the urls from the server. However, what happens is that my progress bar completes but not all images are downloaded. It's like the other urls are being ignored.
import UIKit
class ViewController: UIViewController, NSURLSessionDownloadDelegate {
var taskProgress = [NSURL: (written: Int64, expected: Int64)]()
#IBOutlet weak var progressBar: UIProgressView!
#IBOutlet weak var progressCount: UILabel!
var images = [String]()
var task : NSURLSessionTask!
var pics = ["http://pillar.foundationu.com/wp-content/uploads/2016/03/banner.jpg", "http://pillar.foundationu.com/wp-content/uploads/2016/03/Abad-Edmerl.jpg", "http://pillar.foundationu.com/wp-content/uploads/2016/03/Abellana-Deniz-Dawn-1.jpg", "http://pillar.foundationu.com/wp-content/uploads/2016/03/Abequibel-Arneth.jpg", "http://pillar.foundationu.com/wp-content/uploads/2016/03/Abilla-Harriette.jpg", "http://pillar.foundationu.com/wp-content/uploads/2016/03/Pasko-Maria-Katrina.jpg", "http://pillar.foundationu.com/wp-content/uploads/2016/03/Pastias-Grace.jpg"]
var counter:Float = 0.0 {
didSet {
let fractionalProgress = Float(counter) / 100.0
let animated = counter != 0
progressBar.setProgress(fractionalProgress, animated: animated)
progressCount.text = ("\(counter)%")
}
//The didSet is called immediately after the new value is stored. The fractionalProgress constant keeps track of the progress.
}
lazy var session : NSURLSession = {
let config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
config.allowsCellularAccess = false
let session = NSURLSession(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
return session
}()
override func viewDidLoad() {
progressBar.setProgress(0.0, animated: true) //set progressBar to 0 at
start
}
#IBAction func doElaborateHTTP (sender:AnyObject!) {
progressCount.text = "0%"
if self.task != nil {
return
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
let taskID = self.beginBackgroundUpdateTask()
let file = FileHelperClass()
var url = NSURL!()
var req = NSMutableURLRequest()
var task: NSURLSessionDownloadTask!
for s in file.parseJsonFromFile() /* fund returning images url */ {
let url = NSURL(string: s)
req = NSMutableURLRequest(URL:url!)
task = self.session.downloadTaskWithRequest(req)
self.task = task
task.resume()
}
// Do something with the result
self.endBackgroundUpdateTask(taskID)
})
}
func beginBackgroundUpdateTask() -> UIBackgroundTaskIdentifier {
return UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({})
}
func endBackgroundUpdateTask(taskID: UIBackgroundTaskIdentifier) {
UIApplication.sharedApplication().endBackgroundTask(taskID)
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten writ: Int64, totalBytesExpectedToWrite exp: Int64) {
dispatch_async(dispatch_get_main_queue()) {
self.taskProgress[downloadTask.originalRequest!.URL!] = (writ, exp)
// Update your views, you may separate it into a different function
let totalWritten = self.taskProgress.reduce(0) { $0 + $1.1.written }
let totalExpected = self.taskProgress.reduce(0) { $0 + $1.1.expected }
let progress = Float(totalWritten) / Float(totalExpected)
self.progressBar.setProgress(progress, animated: true)
self.progressCount.text = "\(progress * 100)%"
}
}
func URLSession(session: NSURLSession, downloadTask:
NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64,
expectedTotalBytes: Int64) {
// unused in this example
}
func URLSession(session: NSURLSession, task: NSURLSessionTask,
didCompleteWithError error: NSError?) {
// print("completed: error: \(error)")
}
// this is the only required NSURLSessionDownloadDelegate method
func URLSession(session: NSURLSession, downloadTask:
NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
// print(downloadTask.response!.suggestedFilename!)
images.append(downloadTask.response!.suggestedFilename!)
print("\n \(images)")
}
}
I am making a basic "hello world" iOS app. I have an ubuntu server in the cloud which I want to query from the iOS app. I understand that the server needs to be secure, ie, needs to be accessed via https request, and the certificate on the server needs to be "trusted".
Right now what I am trying to do is override this requirement. I have a self-signed certificate that is working for https on my server. When I make the request with the iOS app, it gives me some errors about NSURLErrorFailingURLPeerTrustErrorKey and even one line returned saying: NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?.
I know this is a common issue and there are many threads on this site about how to deal with it. I tried a piece of code from this post. I added this piece to my code:
func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust{
let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential);
}
}
My entire ViewController code is here:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate, NSURLSessionDelegate {
// MARK: Properties
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var mealNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Handle the text field’s user input through delegate callbacks.
nameTextField.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: UITextFieldDelegate
func textFieldShouldReturn(textField: UITextField) -> Bool {
// Hide the keyboard.
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(textField: UITextField) {
mealNameLabel.text = nameTextField.text
}
// MARK: Actions
#IBAction func setDefaultLabelText(sender: UIButton) {
mealNameLabel.text = "Default Text"
post_request()
}
func post_request(){
let request = NSMutableURLRequest(URL: NSURL(string: "https://54.164.XXX.XX/post_script.php")!)
request.HTTPMethod = "POST"
let postString = "id=13&name=Jack"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust{
let credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential,credential);
}
}
}
It looks like the idea is to pick up the challenge event from the connection, and tell it that "yes I want to proceed". But that piece of code does not seem to be getting called. The post request gets sent, but I receive the error messages about the untrusted certificate. Can someone help me fix my code so that I can accept this certificate from my server?
You need to make your ViewController NSURLSessionDelegate to receive the callbacks and set the session's delegate to be your controller, like this:
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
...
let task = session.dataTaskWithRequest(request) { data, response, error in
}
task.resume()
You cannot use the shared session to get those delegate methods. You must instantiate a new NSURLSession and declare yourself as the delegate.
// Delegate methods won't be callend
let task = NSURLSession.sharedSession()...
// Use this to get delegate calls
let task = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(),
delegate: self,
delegateQueue: /* make a queue */)...
With iOS 9, you will also need to look into NSAppTransportSecurity and adding those keys to your plist. These are needed in iOS 9 to allow SSL connections.
My App downloads multiple files at the same time. My scenario is as follows:
The user clicks a download button.
A Download UITableViewCell is initialized that implements NSURLSessionDelegate to download the specify file.
A download is started. The Download Cell shows progress as the file is being downloaded using the provided Delegates by NSURLSession.
The file is saved locally in the documents folder.
The user clicks a button the clears all the finished downloads. This button clears the array 'downloadQueue' that Download Cell uses. The button also calls tableView.reloadData to update the tableView
The problem is, when the user initiates multiple downloads, no problem occurs. All files download properly simultaneously. However when the user clicks the button described in step 5, and then tries to download another file, the download progress shows 100% completed immediately. And no file is saved locally. This is probably due to didFinishDownloadingToURL being called immediately.
Here is the complete code of my DownloadCell.swift
class DownloadCell: UITableViewCell, NSURLSessionDelegate {
#IBOutlet weak var lblSongName: UILabel!
#IBOutlet weak var progressBar: UIProgressView!
#IBOutlet weak var progressCount: UILabel!
var song: Song!
var task: NSURLSessionTask!
var percentageWritten: Float = 0.0
var taskTotalBytesWritten = 0
var taskTotalBytesExpectedToWrite = 0
lazy var session: NSURLSession = {
let config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
config.allowsCellularAccess = false
let session = NSURLSession(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
return session
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func startDownload() {
lblSongName.text = song.songName
if self.task != nil {
return
}
let url = NSURL(string: song.songDownloadLink)!
let req = NSMutableURLRequest(URL: url)
let task = self.session.downloadTaskWithRequest(req)
self.task = task
task.resume()
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten writ: Int64, totalBytesExpectedToWrite exp: Int64) {
taskTotalBytesWritten = Int(writ)
taskTotalBytesExpectedToWrite = Int(exp)
percentageWritten = Float(taskTotalBytesWritten) / Float(taskTotalBytesExpectedToWrite)
progressBar.progress = percentageWritten
progressCount.text = String(Int(percentageWritten*100)) + "%"
print("(\(taskTotalBytesWritten) / \(taskTotalBytesExpectedToWrite))")
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Completed with error: \(error)")
}
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
do {
let documentsDirectoryURL = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as NSURL!
progressCount.text = "Done!"
progressBar.progress = 100.0
try NSFileManager().moveItemAtURL(location, toURL: documentsDirectoryURL.URLByAppendingPathComponent(song.songName + ".mp3"))
//Handling Badge Values
let tabBar = UIApplication.sharedApplication().keyWindow?.rootViewController as! UITabBarController
let downloadVC = tabBar.viewControllers?[1] as! DownloadViewController
let badgeValue = Int(downloadVC.tabBarItem.badgeValue!)! - 1
downloadVC.tabBarItem.badgeValue = String(badgeValue)
if badgeValue == 0 {
downloadVC.tabBarItem.badgeValue = nil
}
} catch {
print("Error occurred during saving.")
progressCount.text = String(error)
}
}
}
Maybe you should implement the prepareForReuse func of uitableviewcell? If these cells are being reused, they still might have a task, so startDownload returns at the beginning. Also the progressbar's value is not resetted to 0.
I'm trying to use NSURLSession with a DataTask and a Delegate but I can't find a way to convert the data obtained by the didReceiveData method into a string.
This is the code that I'm using, the problem that I'm experiencing is that I get some data but when I try to convert this data into a string I get nil, while the expected result would be the source of the page that I've requested.
class ViewController: UIViewController, NSURLSessionDataDelegate {
var session:NSURLSession?
var data = NSMutableData()
override func viewDidLoad() {
super.viewDidLoad()
let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
session = NSURLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
session!.dataTaskWithRequest(NSURLRequest(URL: NSURL(string: "http://www.google.com")!)).resume()
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?){
var str = NSString(data: NSData(data: self.data), encoding: NSUTF8StringEncoding)
println(data) // IT PRINTS DATA CONTENT
println(str) // IT PRINTS nil :(
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData){
self.data.appendData(data)
}
Most probably the received string data is not UTF8 encoded. Try NSASCIIStringEncoding.