Calling Methods from inside Methods in ContentModel

After completing the foundations course, I am attempting to build a small app that allows user to perform various functions associated with a website api.

One of those functions is to allow inputting of gift codes and having them be redeemed for each user (from JSON file).

Upon app start, the ContentModel gets the remote data (JSON) and puts it in a model. ==> This works GREAT!! No issues.

When the user goes to the correct tab and inputs a “gift code” and submits it, the code is sent into a ContentModel method (submitBoxCode).
This method should cycle through each person and call another method (submitToWebsite) to:

  1. provide that person’s data,
  2. decode JSON response (data) and provide appropriate feedback (success, fail, etc…)

Then it should return to the initial method (submitBoxCode) and go to the next person.

The following appears to be the sequence of events when I run through DEBUG mode:

  1. submitBoxCode calls first person and submits it to submitToWebsite.
  2. submitToWebiste creates URL and URLRequest, but DOES NOT enter the URLSession… it resumes and goes back to submitBoxCode.

This continues until all the names from the JSON have been run through, then it returns to submitToWebsite and proceeds to conduct all the URLSessions.

This is not what I expected. Why does this happen?
What am I not understanding about URLSession??

Thank you!

Code below: some specific items (like website) is scrubbed out.

ADDITIONALLY, the successfulPosting and failedtoPost dictionaries never update with the relevant information either (from the switch / case in the submitToWebsite method).

import Foundation

class ContentModel: ObservableObject {
    
    @Published var people = [Model]()
    var successfulPosting: [String:String] = [:]
    var failedtoPost: [String:String] = [:]
    
    init () {
        getRemoteData()
    }
    
    // MARK: Get json data from remote site
    func getRemoteData () {
        
        // String path
//        let urlString = Constants.jsonUrlLocation
        let urlString = Constants.jsonUrlLocationTesting
        
        guard let url = URL(string: urlString) else { return }
        
        let request = URLRequest(url: url)
        
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {
                do {
                    let decodedJSON = try JSONDecoder().decode([Model].self, from: data)
                    
                    DispatchQueue.main.async {
                        self.people += decodedJSON
                    }
                } catch {
                    print("error:  \(error)")
                    return
                }
            }

        }.resume()
    }

    
    // MARK: Submit new box code for all names in the json
    
    func submitBoxCode(_ boxcode: String) {
           
        for person in people {
            let state = String(person.state)
            for currentName in person.members {
                let name = currentName.name

                self.submitToWebsite(state, name, boxcode)
            }
        }
        print(successfulPosting)
        print(failedtoPost)
        return
    }
    
    func submitToWebsite(_ state: String, _ name: String, _ boxcode: String) {
        
        let urlString = // https website with queries allowing for name, state, and boxcode to be presented as variables

        guard let url = URL(string: urlString) else { return }

        let request = URLRequest(url: url)

        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {
                print(String(data: data, encoding: .utf8)!)
                if let decodedJSON = try? JSONDecoder().decode(responseData.self, from: data) {
                    print(decodedJSON.code!)
                    switch decodedJSON.code {
                    case -20001:
                        let invalidName = "Name (\(name)) does not exist"
                        print(invalidName)
                        self.failedtoPost[name]?.append(invalidName)
                    case -20003:
                        let invalidCode = "Redemption code \(boxcode) is invalid for \(name)"
                        print(invalidCode)
                        self.failedtoPost[name]?.append(invalidCode)
                    case -20002:
                        print("Fail")
                    case 1:
                        let successful = "\(name) successfully redeemed \(boxcode)"
                        self.successfulPosting[name]?.append(successful)
                        print(self.successfulPosting)
                    default:
                        print("Failed for unknown reason")
                    }
                }
                return
            }
            else {
                print(error!)
                return
            }
        }.resume()
        
    }
}

URLSession runs in the background so what you are seeing is all of the calls to the URLSessions being set up and then they are executing in the background. If you are expecting to see any feedback in your UI from those sessions executing then you have to do that on the main thread.

Is the website getting any data?

Hey Chris! Thanks for taking a look at this already. Yes, the website is getting info and sending back data. It just seemed weird after doing the same thing in python.

What about the 2 dictionaries not getting updated in the switch / case portion? What am I doing wrong there?

Are my “returns” in the wrong place (or not required)?

Thanks again! I’ve been fighting with this both in playgrounds and app all day!

@cajunlat

Are any of the switch cases on decodedJSON.code being triggered? If not then have you set a breakpoint at the line that says switch decodedJSON.code { so that you can interrogate what code is containing?

Yes. They print on the the console, but are not updating the dictionaries……

@cajunlat

Ah, I see the problem.

When you add an entry to a dictionary you don’t append an item to it like you do to an array. What you are doing with a dictionary is adding key: value pairs and to do that in each of those case statements you do this:

                    switch decodedJSON.code {
                    case -20001:
                        let invalidName = "Name (\(name)) does not exist"
                        print(invalidName)
                        self.failedtoPost[name] = invalidName
                    case -20003:
                        let invalidCode = "Redemption code \(boxcode) is invalid for \(name)"
                        print(invalidCode)
                        self.failedtoPost[name] = invalidCode
                    case -20002:
                        print("Fail")
                    case 1:
                        let successful = "\(name) successfully redeemed \(boxcode)"
                        self.successfulPosting[name] = successful
                        print(self.successfulPosting)
                    default:
                        print("Failed for unknown reason")
                    }

To remove an entry from a Dictionary you assign the key = nil
So for the failedtoPost dictionary you would say failedtoPost[name] = nil and that “key” with the value pair will be removed.

Does that make sense?

Yes! I think I confused some python code where you can append another element to an existing list of values in a dictionary…
Darn it!!! Work myself into circles sometimes!

Thank you