When trying to set the progress from a URLSessionDownloadTask, I get a unexpectedly found nil while unwrapping an Optional value
error. What I'm trying to accomplish is have a separate class, a URLSessionDownloadDelegate
, handle the download and update the necessary UI elements, in this case, an NSProgressIndicator
. Here's my code:
AppDelegate.swift
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
@IBOutlet var button: NSButton!
@IBOutlet var progind: NSProgressIndicator!
@IBAction func update(_ sender:AnyObject){
button.isEnabled = false
updater().downloadupdate(arg1: "first argument")
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
progind.doubleValue = 50.0 //me trying to test if the progress indicator even works
}
func applicationWillTerminate(_ aNotification: Notification) {
}
func updateDownload(done: Double, expect: Double){
print(done)
print(expect)
progind.maxValue = expect //this line crashes from the unexpected nil error
progind.doubleValue = done //so does this one, if I delete the one above
}
}
updater.swift
import Foundation
class updater: NSObject, URLSessionDelegate, URLSessionDownloadDelegate {
func downloadupdate(arg1: String){
print(arg1)
let requestURL: URL = URL(string: "https://www.apple.com")!
let urlRequest: URLRequest = URLRequest(url: requestURL as URL)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
let downloads = session.downloadTask(with: urlRequest)
print("starting download...")
downloads.resume()
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){
print("download finished!")
print(location)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
let expectDouble = Double(totalBytesExpectedToWrite)
let doneDouble = Double(totalBytesWritten)
AppDelegate().updateDownload(done: doneDouble, expect: expectDouble)
}
}
I have tried replacing
AppDelegate().updateDownload(done: doneDouble, expect: expectDouble)
with
AppDelegate().progind.maxValue = expect
AppDelegate().progind.doubleValue = done
and had the same results.
I actually think I know what is causing this. My research has led me to believe that I'm actually declaring a NEW instance of AppDelegate
, in which progind
doesn't even exist! So how can I properly set the value of progind
, while keeping as much of the process as possible in updater.swift?
You're right, when you type AppDelegate()
, you are creating a new object. Since it's new, it doesn't have any of the outlets initialized for it the way one does when it comes from a storyboard or xib.
You need to get the shared singleton that represents your application (see NSApplication
docs), ask it for its delegate, cast that to an AppDelegate
, and set the property there.
Sample code (Swift 2.2):
if let delegate = NSApplication.sharedApplication().delegate as? AppDelegate {
delegate.progind.maxValue = expected
} else {
print("Unexpected delegate type: \(NSApplication.sharedApplication().delegate)")
}