0

I have two classes in Swift, one is a ViewController.swift, another has some business logic, called Brain.swift. In Brain.swift I have a class which contains a function called convert() which executes an NSTask.

In ViewController.swift all of the UI updating occurs.

What I would like to accomplish is getting the output of the convert()'s NSTask into a TextView in the ViewController.

I have implemented the solution from this answer, but I'm a bit of a novice so I'm unsure how to return it as a class property in real time to be accessible by other classes.

Brain.swift

import Foundation


internal func convert(chosenFile: NSURL, addText: (newText: String) -> Void) {


    let bundle = NSBundle.mainBundle()

    let task = NSTask()

    let outputPipe = NSPipe()

    task.standardOutput = outputPipe
    let outHandle = outputPipe.fileHandleForReading

    outHandle.readabilityHandler = { outputPipe in
        if let line = String(data: outputPipe.availableData, encoding: NSUTF8StringEncoding) {
            addText(newText: line)
        } else {
            print("Error decoding data: \(outputPipe.availableData)")
        }
    }

    task.launch()
    task.waitUntilExit()

}

ViewController.swift

    @IBAction func Run(sender: AnyObject) {

    let qualityOfServiceClass = QOS_CLASS_USER_INITIATED
    let userInitiatedQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
    dispatch_async(userInitiatedQueue, {
        self.btnConvert.enabled = false
        self.btnSelect.enabled = false
        self.activitySpinner.hidden = false
        self.activitySpinner.startAnimation(self)


        convert(self.inputFile.chosenFile) { newText in
                self.statusText.stringValue = "\(newText)"
        }
    })

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        self.statusText.stringValue = "Done!"
        self.activitySpinner.hidden = true
        self.activitySpinner.stopAnimation(self)

        self.btnSelect.enabled = true

    })
}
user43633
  • 319
  • 3
  • 7

1 Answers1

0

This should work:

func convert(chosenFile: NSURL, addText: (newText: String) -> Void) {
    let task = NSTask()

    // Set up task

    let pipe = NSPipe()
    task.standardOutput = pipe
    let outHandle = pipe.fileHandleForReading

    outHandle.readabilityHandler = { pipe in
        if let line = String(data: pipe.availableData, encoding: NSUTF8StringEncoding) {
            addText(newText: line)
        } else {
            print("Error decoding data: \(pipe.availableData)")
        }
    }

    task.launch()
}

You can call it like this:

convert(myURL) { newText in
    print("New output available: \(newText)")
}

I made a function out of your class because there's no reason for a class when you're just gonna use it for a single function. The readabilityHandler approach also simplifies your code greatly.

I also added this way of getting updates to the mentioned question.

Kametrixom
  • 14,673
  • 7
  • 45
  • 62
  • Thank you, but I am not able to retrieve the text using this solution. I have turned the class into a function, but it seems that anything inside of the convert() { } function call is not being executed. i checked this with a debugger. Does it need to be multithreaded with GCD? Is readabilityHandler already multithreaded? – user43633 Jun 29 '16 at 01:46
  • @user43633 If it's not being executed, you're not calling it, check again or show the relevant code. Yes, `readabilityHandler` calls already on a separate thread. Note that this function doesn't wait until the task is done, rather it uses the provided closure to give you feedback when something happened. – Kametrixom Jun 29 '16 at 01:54
  • Thanks for confirming that about readabiltyHandler, i've updated with the relevant code. I think I might have an incorrect implementation of threads which is contributing to my problem, probably has nothing to do with your solution. – user43633 Jun 29 '16 at 02:04
  • @user43633 You should use the main thread to do updates to the UI (where you assign the labels text to newText). You probably want to add text too instead of overwriting the whole label. Make sure your function actually gets called as well. Also make sure to set up your `NSTask` with the task you want to perform. Initialising an `NSTask` and not telling it what to do and you end up with a task that does nothing. – Kametrixom Jun 29 '16 at 02:12