// Start a new project // Add a UILabel and UIButton to the default view controller // connect the label to countLabel @IBOutlet // connect the button to doFetch @IBAction // // each tap of the button will call fetch() in LoginManager.shared // and use a 1-second delay to simulate fetching data from an API class ViewController: UIViewController { @IBOutlet var countLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // set the "callback" closure in LoginManager LoginManager.shared.callback = { [weak self] str in // safely unwrap self guard let self = self else { return } // we have to update UI elements on the main thread DispatchQueue.main.async { // set the returned str to countLabel's text self.countLabel.text = str } } } @IBAction func doFetch(_ sender: Any) { countLabel.text = "Calling API..." LoginManager.shared.fetch() } } class LoginManager { static let shared = LoginManager() // counter var var myCounter: Int = 0 // closure for communication var callback: ((String) -> ())? func fetch() -> Void { // increment the counter myCounter += 1 // 1-second delay to simulate getting data from API DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 1, execute: { // get a random number from 1 to 9999 let n = Int.random(in: 1...9999) // use the closure to send it back as a string self.callback?("API Count: \(self.myCounter) Random: \(n)") }) } }