iosjsonswiftyandex

Using singleton and parsing JSON in Swift


I create a translator app using yandex translate API. I create singleton class.

class TranslatorManager {
    static let shared = TranslatorManager()

    var translation: Translation?

    let key = "mykey here"
    let translateUrl = "https://translate.yandex.net/api/v1.5/tr.json/translate"

    func getTranslate(text: String, lang: String) {
        guard let url = URL(string: translateUrl + "?key=\(key)&text=\(text)&lang=\(lang)&format=plain&options=1") else { return }
        var request = URLRequest(url: url)
        request.httpMethod = "POST"

        URLSession.shared.dataTask(with: request) { (data, response, error) in
            if error != nil {
                print(error!.localizedDescription)
            }

            guard let data = data else { return }

            do {
                let translation = try JSONDecoder().decode(Translation.self, from: data)
                DispatchQueue.main.async {
                    self.translation = translation
                }
            } catch {
                print(error)
            }
         }.resume()
    }
}

In viewController I create some func:

func getTranslate(text: String, lang: String) {
    TranslatorManager.shared.getTranslate(text: text, lang: lang)
    translate = TranslatorManager.shared.translation
    self.translationLabel.text = self.translate?.text[0]
}

which I call when I click the button:

@IBAction func translateTapped(_ sender: UIButton) {
    guard let text = originalText.text else { return }
    getTranslate(text: text, lang: "en-ru")
}

In this case, the translation is displayed on the label only after the second pressing of the button. What am I doing wrong? Why does not it work correctly?


Solution

  • You are not properly dealing with the fact that the translation is being done asynchronously in the background. You need to refactor your TranslationManager.

    let key = "mykey here"
    let translateUrl = "https://translate.yandex.net/api/v1.5/tr.json/translate"
    
    class TranslatorManager {
        static let shared = TranslatorManager()
    
        func getTranslate(text: String, lang: String, completion: @escaping (Translation?) -> Void) {
            guard let url = URL(string: translateUrl + "?key=\(key)&text=\(text)&lang=\(lang)&format=plain&options=1") else { return }
            var request = URLRequest(url: url)
            request.httpMethod = "POST"
    
            URLSession.shared.dataTask(with: request) { (data, response, error) in
                if let error = error {
                    print(error.localizedDescription)
                    completion(nil)
                    return
                }
    
                guard let data = data else {
                    completion(nil)
                    return
                }
    
                do {
                    let translation = try JSONDecoder().decode(Translation.self, from: data)
                    completion(translation)
                } catch {
                    print(error)
                    completion(nil)
                }
            }.resume()
        }
    }
    

    Then update your getTranslate to deal with the new completion handler:

    func getTranslate(text: String, lang: String) {
        self.translationLabel.text = "Translating..."
        TranslatorManager.shared.getTranslate(text: text, lang: lang) { translation in
            DispatchQueue.main.async {
                if let translation = translation {
                    self.translationLabel.text = self.translate.text[0]
                } else {
                    // failed, act accordingly
                    self.translationLabel.text = ""
                }
            }
        }
    }