Learn Courses My Dashboard

iOS Foundations M5L14 Threads Runtime Warning

Update: Chris discusses this in-depth here: CodeWithChris

Greetings!

After completing the Lesson app with the networking component, while running the app this warning displays on Xcode 13 iOS 15:

Full warning message:
LearningApp[14356:673748] [SwiftUI] Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

I heard that the latest Swift/ Xcode now supports asnyc/ await threads. Does this warning relate to that? Should we be concerned with it?

Perhaps, it doesn’t matter, because that thread will not publish anymore changes. I’m not sure…

Here’s a link to my code base.

While here is my method, which fetches the JSON from a remote server. Note that this code gets called after the method, which parses the JSON locally in the init method:

func getRemoteData() {
        
        // String path for content
        let urlString = "https://raw.githubusercontent.com/agholson/Learning-App/master/LessonConfigData/module2.json"
        
        // Create a URL object that returns an optional URL
        let url = URL(string: urlString)
        
        // Check that the URL exists
        guard url != nil else {
            // Could not create the URL
            return
        }
        
        // Create a URLRequest object - where we force unwrap our URL object, because we already checked it exists
        let request = URLRequest(url: url!)
        
        // Create a session based on the single session object for each app
        let session = URLSession.shared
        
        // Raw data fetched in data, response contains more details, while error contains any errors
        let dataTask = session.dataTask(with: request) { data, response, error in
            
            // Check if it hit any errors
            guard error == nil else {
                return
            }
            
            // Handle the response
            // Create a JSON decoder
            do {
                let decoder = JSONDecoder()
                
                // Decode - can safely force unrwapp, because checked for errors above
                let modules = try decoder.decode([Module].self, from: data!)
                
                // Append parsed modules into modules property
                self.modules += modules
                
            }
            catch {
                // Could not parse the JSON
            }
            
        }
        
        // Issue GET request, then parses it in the handler above
        dataTask.resume()
        
    }

The problem is here:

You are updating modules, which is a Published property, meaning it can update the UI, which means it has to take place on the main thread.

You should wrap it in a call to DispatchQueue.main.async to ensure that modules gets updated on the main thread.

The new async/await features could certainly be used here, but I haven’t explored them enough to know what changes would need to be made to your code.

1 Like

Hey @roosterboy wonderful job! Nicely, done. Thank you for taking the time to read through this code as well as provide this great answer :slight_smile: How did you know this would be the solution?

It’s interesting too that DispatchQueue.main.async runs this task asynchronously on the main thread. If it’s this easy to use asynchronous code, then Apple did a great job with this release.
dispatch_queue

@roosterboy you should apply to be a teaching assistant (TA) for CWC+! From the newsletter this Saturday:
"* I’ve been working on a job description for hiring additional iOS trainers because that’s the only way we’ll be able to keep up with the demand!

  • If you feel like you could fill the role or maybe you could contribute in some capacity, please hit reply and let me know."
    care@codewithchris.com