-1

I am using a switch statement in Swift 3 (Xcode 8.3.2) to parse a range of JSON objects (using SwiftyJSON) for a macOS app. For each case I am trying to print an update to a textView that is declared in the same class (NSViewController) and bound in the relevant storyboard using an @IBOutlet.

I have declared a function within the class to update the textView and call that function (func addLogToConsoleWindow(newLogEntry: String) {}) with the relevant text that I want to print to the textView.

The relevant code is:

    @IBOutlet var textViewActivityLog: NSTextView!  // Create an outlet for the activity log view

.............

        for jsonObj in arrayOfJSONObjects {
        if jsonObj != JSON.null {


            // Use a switch statement to select the correct Class to use for storing the appropriate event

            switch jsonObj["event"].string! {
            case "Docked":
                arrayOfDockedEvents.append(jsonObj)
                self.addLogToConsoleWindow(newLogEntry: "Docked event being parsed")
            case "FSDJump":
                arrayOfFSDJumpEvents.append(jsonObj)
                self.addLogToConsoleWindow(newLogEntry: "FSD Jump event being parsed")
            case "Progress":
                arrayOfProgressEvents.append(jsonObj)
                self.addLogToConsoleWindow(newLogEntry: "Commander's progress being parsed")
            case "Rank":
                arrayOfRankEvents.append(jsonObj)
                self.addLogToConsoleWindow(newLogEntry: "Rank information being parsed")
            case "LoadGame":
                arrayOfLoadGameEvents.append(jsonObj)
                self.addLogToConsoleWindow(newLogEntry: "Game load details being parsed")
            case "StartJump":
                arrayOfStartFSDJumps.append(jsonObj)
                self.addLogToConsoleWindow(newLogEntry: "Start FSD Jump event being parsed")
            case "MiningRefined":
                arrayOfMiningRefined.append(jsonObj)
                self.addLogToConsoleWindow(newLogEntry: "Mining event being parsed")
            default:
                if !setOfEventType.contains(jsonObj["event"].string!) {
                    self.addLogToConsoleWindow(newLogEntry: "\((jsonObj["event"].string!)) event discovered but not parsed")
                }
            }                
        } else {
            print("Haven't been able to find a jsonObj")
        }
    }  //  END OF 'for jsonObj'

...........

    @objc func addLogToConsoleWindow(newLogEntry: String) {
         textViewActivityLog.string? = "\n" + newLogEntry + (textViewActivityLog.string)!
         textViewActivityLog.scrollRangeToVisible(NSMakeRange(0, 0))
} // END OF addLogToConsoleWindow()

The behaviour that I am looking for is to have the textView (textViewActivityLog.string?) update immediately when the particular case is accessed.

However, the textView does not update immediately. It only updates after the for jsonObj in arrayOfJSONObjects {} loop has completed which means upwards of several thousands of text lines appear at the same time rather than progressively as the for loop runs.

How can I get the textView to update at the point in the code that the func addLogToConsoleWindow(newLogEntry: String) {} function is called?

Any advice or guidance gratefully received. I have been unable to find any relevant previous questions.

Wolfstar
  • 111
  • 10

2 Answers2

1

As Willeke said, you can do something like that:

    switch jsonObj["event"].string! 
{
    case "Docked":
        arrayOfDockedEvents.append(jsonObj)
        self.addLogToConsoleWindow(newLogEntry: "Docked event being parsed")
    case "FSDJump":
        arrayOfFSDJumpEvents.append(jsonObj)
        self.addLogToConsoleWindow(newLogEntry: "FSD Jump event being parsed")

        //do on main thread...
        DispatchQueue.main.async
        {
            //updating text on label or other textview
        }

    case "Progress":
        arrayOfProgressEvents.append(jsonObj)
        self.addLogToConsoleWindow(newLogEntry: "Commander's progress being parsed")
    // ...
    default:
    if !setOfEventType.contains(jsonObj["event"].string!) 
    {
        self.addLogToConsoleWindow(newLogEntry: "\((jsonObj["event"].string!)) event discovered but not parsed")
    }
}
  • Hi Edmund - that does not seems to have worked. I encapsulated the self.addLogToConsoleWindow(newLogEntry: "Docked event being parsed") statement within the DispatchQueue.main.async and it hasn't made any difference to the behaviour – Wolfstar Jun 02 '17 at 09:47
  • 1
    I will keep working on it and see if I can solve it first. best way to learn. – Wolfstar Jun 02 '17 at 10:37
  • I have sorted out an answer - please see the marked answer below. – Wolfstar Jun 08 '17 at 03:17
0

For those interested, I solved the behaviour issue that I was having after reading this response (DispatchQueue.main.sync returning exc_bad_instruction Swift 3)

I made the following changes to the addLogToConsoleWindow(newLogEntry: String) {} function:

    func addLogToConsoleWindow(newLogEntry: String) {
    DispatchQueue.global().async (execute: {
        DispatchQueue.main.sync {
            self.textViewActivityLog.string? = "\n" + ":->  " + newLogEntry + (self.textViewActivityLog.string)!
            self.textViewActivityLog.scrollRangeToVisible(NSMakeRange(0, 0))
        }
    })
} // END OF addLogToConsoleWindow()

Which seems to have done the trick.

My thanks to those who have responded - your guidance towards multithreading was helpful.

Wolfstar
  • 111
  • 10