skip to Main Content

I am pretty new to SwiftUI and with DispatchGroups and DispatchQueues.
I would like to create a Button which processes some server requests and then use the returned data with a CoreML model to predict some score. Once, the score is predicted, then the app can navigate to the next screen

Here is the sequence of actions which need to be done before moving to the next screen


// exemple of sequence of actions
let group = DispatchGroup()
group.enter()
DispatchQueue.main.async {
    self.name = self.names[self.selectedCompanyIndex]
    self.fetchTweets(company: self.arobases[self.selectedCompanyIndex])
    self.fetchTweets(company: self.hashes[self.selectedCompanyIndex])
    group.leave()
}
group.notify(queue: .main) {
     print("done")
}

//function for fetching tweets
func fetchTweets(company: String) {

        swifter.searchTweet(
            using: company,
            lang: "en",
            count: 100,
            tweetMode: .extended,
            success: { (results, metadata) in
                var tweets = [TextClassifier1Input]()
                for i in 0...99 {
                    if let tweet = results[i]["full_text"].string {
                        tweets.append(TextClassifier1Input(text: tweet))
                    }
                }
                let searchType = String(company.first!)
                self.makePrediction(with: tweets, type: searchType)
        }) { (error) in
            print("There was an error with the Twitter API: --> ", error)
        }
    }

//function for making predictions via the coreML model
func makePrediction(with tweets: [TextClassifier1Input], type: String) {
        do {
            let predictions = try self.sentimentClassifier.predictions(inputs: tweets)
            var sentimentScore = 0
            for pred in predictions {
                if pred.label == "pos" {
                    sentimentScore += 1
                } else if pred.label == "neg" {
                    sentimentScore -= 1
                } else {
                    print("something sent wrong: --> ", pred.label)
                }
            }
            if type == "@" {
                arobaseScore = sentimentScore
            } else if type == "#" {
                hashScore = sentimentScore
            }
        } catch {
            print("There was an error with the ML model: --> ", error)
        }
    }

The problem is the navigation is executed on the button click whereas I want the previous actions to be done before.
Can anyone let me know how should I use DispatchGroups and DispatchQueue to run my code in the correct sequence

Thanks in advance for your help

2

Answers


  1. welcome to Stack Overflow.

    You are not waiting for swifter.searchTweet to complete before doing the update.

    The first step is to add a completion handler to fetchTweets. The completion handler is a way of reporting when fetchTweets has finished the networking.

    //function for fetching tweets
    func fetchTweets(company: String, completion: @escaping () -> Void) {
                                   // ^^^^^^^^^^ Added completion handler
    
            swifter.searchTweet(
                using: company,
                lang: "en",
                count: 100,
                tweetMode: .extended,
                success: { (results, metadata) in
                    var tweets = [TextClassifier1Input]()
                    for i in 0...99 {
                        if let tweet = results[i]["full_text"].string {
                            tweets.append(TextClassifier1Input(text: tweet))
                        }
                    }
                    let searchType = String(company.first!)
                    self.makePrediction(with: tweets, type: searchType)
                    completion() // <-- Call completion on success
            }) { (error) in
                print("There was an error with the Twitter API: --> ", error)
                completion() // <-- Call completion on failure
            }
        }
    
    

    After that is done, then you can enter and leave the group when the networking is done. The code in notify will then be called. It’s in that code block where you need to update the UI.

    let group = DispatchGroup()
    
    self.name = self.names[self.selectedCompanyIndex]
    
    group.enter()
    self.fetchTweets(company: self.arobases[self.selectedCompanyIndex]) {
        group.leave() // <-- leave the group in the completion handler
    }
    
    group.enter()
    self.fetchTweets(company: self.hashes[self.selectedCompanyIndex]) {
        group.leave() // <-- leave the group in the completion handler
    }
    
    group.notify(queue: .main) {
         print("done")
         // BEGIN UPDATING THE UI
         // NOTE: Here is where you update the UI.
         // END UPDATING THE UI
    }
    
    Login or Signup to reply.
  2. for those using programmatic navigation, trying to navigate before a call to the backend is done will cause all sorts of problems where the view wants to still stay until the call is over and your navigation logic wants to continue

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search