swiftnulloption-typensprogressindicator

Why does trying to set the value of my NSProgressIndicator crash my application?


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?


Solution

  • 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)")
    }