Swift OSX- View Loading Late (viewDidLoad) (applicationDidFinishLaunching) - swift

Thanks for the help!
Whenever I enter any code after viewDidLoad or applicationDidFinishLaunching, my view is delayed until after that code has finished. Also tried viewDidAppear.
*Not Using SwiftUI
*Running Big Sur Xcode 13.2.1
Let's say I've entered
override func viewDidLoad() {
super.viewDidLoad()
sleep(5) }
The view will load after the sleep... I know sleep isn't a great example, but I've tried it with many functions.
Also if I run any code in AppDelegate - in applicationDidFinishLaunching
Same Result.
Any Tips/ Help/ Updates are greatly appreciated!

You can use DispatchQueue to resolve that issue.
Run your code in another thread using your own queue or predetermined:
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.global(qos: .utility).async {
//your code here
sleep(5)
}
}

Related

TableView not loading when tapped with Tab Bar Swift

When transitioning from the details tab to the tags tab, the table view is not loading. I know because in the view did load I had it print a comment but the comment and the rest of the code is running or loading. Even though the table view itself is appearing. Sorry if this is too vague , I'm not sure what I need to provide so please let me know if you need more info to help me
When you click on a tab it's loaded and maintained in memory. It doesn't require to be reloaded. If you want to load the data again move the data loading code to viewDidAppear like this:
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadData()
}
func loadData() {
// Code to load data
}
}
Where you get data from Api reload your tableView tableview.reloadData() there on Main thread using DisaptchQueue.main.async {}
// got response from Api
DisaptchQueue.main.async {
tableView.reloadData()
}

Swift - stopAnimating() - must be used from main thread only

I have a succession of tasks that have to be performed in sequence, so they are corralled by DispatchQueue. To inform the user that they are chugging away I start the activity icon. But how do I stop if off the main thread?
Logically what I am trying to achieve is:
override func viewDidLoad() {
super.viewDidLoad()
self.activityIcon.startAnimating()
DispatchQueue.main.async {
self.bigTask1()
}
DispatchQueue.main.async {
self.bigTask2()
self.activityIcon.stopAnimating()
}
}
This obviously generates the runtime error: " UIActivityIndicatorView.stopAnimating() must be used from main thread only".
Putting the stopAnimating() in the main queue turns it on and off so fast no one will ever see it.
What is the approved way call functions like this off the main queue?
Many thanks. P.s. I have read answers of similar questions on SO but don't quite get them.
You can do big task in default background queue, and when the big task completes then simply get the main queue and perfom any UI Updates.
override func viewDidLoad() {
super.viewDidLoad()
self.activityIcon.startAnimating()
DispatchQueue.global().async {
self.someBigTask()
DispatchQueue.main.async {
self.activityIcon.stopAnimating()
}
}
}
Hope you will find this helpful.

Swift Threading: When to use DispatchQueue.main.async?

I believe I understand what the dispatch queue is doing when I call it, but I'm not sure when exactly I should use it and what it's advantages are when I do use it.
If my understanding is correct, DispatchQueue.main.async { // code } will schedule the code contained within the closure to run on the main dispatch queue in an asynchronous manner. The main queue has the highest priority, and is typically reserved for updating UI to maximize App responsiveness.
Where I'm confused is: What exactly is the difference in updating UI elements within a dispatch queue closure versus just writing the code outside the closure in the same spot? Is it faster to execute the code in the body of a view did load method rather than sending it to the dispatch queue? If not, why?
Code Example:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
}
Versus:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.async {
updateUI()
}
}
}
Which one is will update the UI faster?
The primary use of DispatchQueue.main.async is when you have code running on a background queue and you need a specific block of code to be executed on the main queue.
In your code, viewDidLoad is already running on the main queue so there is little reason to use DispatchQueue.main.async.
But isn't necessarily wrong to use it. But it does change the order of execution.
Example without:
class MyViewController: UIViewController {
func updateUI() {
print("update")
}
override func viewDidLoad() {
super.viewDidLoad()
print("before")
updateUI()
print("after")
}
}
As one might expect, the output will be:
before
update
after
Now add DispatchQueue.main.async:
class MyViewController: UIViewController {
func updateUI() {
print("update")
}
override func viewDidLoad() {
super.viewDidLoad()
print("before")
DispatchQueue.main.async {
updateUI()
}
print("after")
}
}
And the output changes:
before
after
update
This is because the async closure is queued up to run after the current runloop completes.
I just ran into the exact situation discribed in your Question: viewDidLoad() calling DispatchQueue.main.async.
In my case I was wanting to modify Storyboard defaults prior to displaying a view.
But when I ran the app, the default Storyboard items were momentarily displayed. The animated segue would finish. And only THEN would the UI components be modified via the code in viewDidLoad(). So there was this annoying flash of all of the default storyboard values before the real values were edited in.
This was because I was modifying those controls via a helper function that always first dispatched to the main thread. That dispatch was too late to modify the controls prior to their first display.
So: modify Storyboard UI in viewDidLoad() without dispatching to the Main Thread. If you're already on the main thread, do the work there. Otherwise your eventual async dispatch may be too late.

ViewController + Storyboard setting up validation with controlTextDidChange

Trying to setup validation for a few text fields in a new (and very small) Swift Mac app. Following various other topics here on SO and a few other examples, I can still not get controlTextDidChange to propagate (to my ViewController).
E.g: How to live check a NSTextField - Swift OS X
I have read at least a dozen variations of basically that same concept. Since none of the accepted answers seem to work I am just getting more and more confused by something which is generally a fairly simple task on most platforms.
I have controlTextDidChange implemented to just call NSLog to let me know if I get anything.
AppDelegate should be part of the responder chain and should eventually handle controlTextDidChange but I see nothing there either.
Using the current Xcode I start a new project. Cocoa app, Swift, Storyboard and nothing else.
From what I can gather the below isolated example should work. In my actual app I have tried some ways of inserting the ViewController into the responder chain. Some answers I found suggested it was not always there. I also tried manually adding the ViewController as the delegate in code theTextField.delegate = self
Nothing I have done seems to get text changed to trigger any events.
Any ideas why I have so much trouble setting up this delegation?
My single textfield example app
Storyboard is about as simple as it gets:
AppDelegate
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSTextFieldDelegate, NSTextDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func controlTextDidChange(notification: NSNotification) {
let object = notification.object as! NSTextField
NSLog("AppDelegate::controlTextDidChange")
NSLog("field contains: \(object.stringValue)")
}
}
ViewController
import Cocoa
class ViewController: NSViewController, NSTextFieldDelegate, NSTextDelegate {
#IBOutlet var theTextField: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func controlTextDidChange(notification: NSNotification) {
let object = notification.object as! NSTextField
NSLog("ViewController::controlTextDidChange")
NSLog("field contains: \(object.stringValue)")
}
}
I think the samples you're following are a bit out-of-date.
Try...
override func controlTextDidChange(_ notification: Notification) {
...as the function definition for your method in your NSTextFieldDelegate.

How to Complete Async call before app loads?

I couldn't find the answer to this probably because I'm not really sure what I'm looking for since i just started programing a few weeks ago.
My storyboard entry point requires data that I get from an asynchronous session and JSON parse. Then once it gets the data it stores it to NSUserDefaults so it doesn't have to make the async call again and the app can access that data anytime.
I put my async call in the viewdidload of the storyboard entry point because as far as I know thats where the app starts. The issue is that the data isn't showing up until the app is started for a second time.
The data I'm getting from the async call only changes once every month so its not necessarily time sensitive.
How can I delay the app from getting to the storyboard entry point until the async call is finished?
Is that even the right way to go about it?
Should I switch to a synchronous call?
What if I changed the storyboard entry point to a view controller that looked like the app was loading and then when the async call finished, use a completion handler to perform segue to the view controller that requires the asynchronous call to finish?
Thanks Leo that worked.
Storing a variable when the app is first installed
I did two things here. First I created a new view controller that would execute the async call and segue to my main view controller when it finished. Then I detected if it was the first launch or not by using the above linked solution. Both of those together worked.
import Foundation
import UIKit
class FirstLoad: UIViewController {
var installedDate: NSDate? {
get {
return NSUserDefaults().objectForKey("installedDateKey") as? NSDate
}
set {
NSUserDefaults().setObject(newValue, forKey: "installedDateKey")
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(animated: Bool) {
firstLoad()
}
func firstLoad() {
if installedDate == nil {
installedDate = NSDate()
parseData(heroesDataProject) { heroesArrayFromParse in //this function gets my json and the following code is executed after completion
saveToDefaults("heroesOriginal")
print("First Run")
self.performSegueWithIdentifier("firstLoadToHomeMenu", sender: nil)
}
} else {
print("Not first run, installd on \(installedDate!)")
loadFromDefaults(userProfile)
performSegueWithIdentifier("firstLoadToHomeMenu", sender: nil)
}
}
}