Function from another class ios swift not executed

Viewed 150

Please tell me, I have 2 classes. There is a load_shop() function that enables the display of the webView. If I call a function from the first class ViewController then the function is executed and the webView is displayed, and if from another class CheckUpdate the function is executed (print is output) but the webView is not displayed. What could be the problem? Thanks for any help. I'm new to this.

class ViewController: UIViewController {
   ...
   override func viewDidLoad() {
      super.viewDidLoad()
      self.load_shop()
   }
   ...
   func load_shop() {
      view = webView
      print("finish_update")
   }
}
class CheckUpdate: NSObject {
   if (...) {
      ViewController().load_shop()
   }
}

ViewController.swift

import UIKit
import WebKit
import UserNotifications
import Foundation

class ViewController: UIViewController, WKUIDelegate, WKScriptMessageHandler, WKNavigationDelegate {
    
    var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.scrollView.bounces = false;

        
        let myURL = URL(string:"https://google.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest);
        webView.navigationDelegate = self

    }
    
    var update = 0
    
    func load_shop() {
        view = webView
        print("finish_update")
        
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        CheckUpdate.shared.showUpdate(withConfirmation: false)
        
    }

    func close() {
        exit(0);
    }
    
    override var prefersHomeIndicatorAutoHidden: Bool {
        return true
    }
}

CheckUpdate.swift

import Foundation
import UIKit

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
    var trackViewUrl: String
}

class CheckUpdate: NSObject {


    let first_class  = ViewController()
    static let shared = CheckUpdate()
    
    
    
    
    func showUpdate(withConfirmation: Bool) {
        DispatchQueue.global().async {
            self.checkVersion(force : !withConfirmation)
        }
    }
  

    private  func checkVersion(force: Bool) {
        if let currentVersion = self.getBundle(key: "CFBundleShortVersionString") {
            _ = getAppInfo { (info, error) in
                if let appStoreAppVersion = info?.version {
                    if let error = error {
                        print("error getting app store version: ", error)
                    } else if appStoreAppVersion == currentVersion {
                        self.first_class.load_shop()
                        print("Already on the last app version: ",currentVersion)
                    } else {
                        print("Needs update: AppStore Version: \(appStoreAppVersion) > Current version: ",currentVersion)
                        DispatchQueue.main.async {
                            let topController: UIViewController = (UIApplication.shared.windows.first?.rootViewController)!
                            topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!)
                        }
                    }
                }
            }
        }
    }

    private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
    
      // You should pay attention on the country that your app is located, in my case I put Brazil */br/*
      // Você deve prestar atenção em que país o app está disponível, no meu caso eu coloquei Brasil */br/*
      
        guard let identifier = self.getBundle(key: "CFBundleIdentifier"),
              let url = URL(string: "http://itunes.apple.com/br/lookup?bundleId=\(identifier)") else {
                DispatchQueue.main.async {
                    completion(nil, VersionError.invalidBundleInfo)
                }
                return nil
        }
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
          
            
                do {
                    if let error = error { throw error }
                    guard let data = data else { throw VersionError.invalidResponse }
                    
                    let result = try JSONDecoder().decode(LookupResult.self, from: data)
                    print(result.results)
                    guard let info = result.results.first else {
                        throw VersionError.invalidResponse
                    }

                    completion(info, nil)
                } catch {
                    completion(nil, error)
                }
            }
        
        task.resume()
        return task

    }

    func getBundle(key: String) -> String? {

        guard let filePath = Bundle.main.path(forResource: "Info", ofType: "plist") else {
          fatalError("Couldn't find file 'Info.plist'.")
        }
        // 2 - Add the file to a dictionary
        let plist = NSDictionary(contentsOfFile: filePath)
        // Check if the variable on plist exists
        guard let value = plist?.object(forKey: key) as? String else {
          fatalError("Couldn't find key '\(key)' in 'Info.plist'.")
        }
        return value
    }
}

extension UIViewController {
    @objc fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) {
        guard let appName = CheckUpdate.shared.getBundle(key: "CFBundleName") else { return } //Bundle.appName()

        let alertTitle = "New version"
        let alertMessage = "A new version of \(appName) are available on AppStore. Update now!"

        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)

        if !Force {
            let notNowButton = UIAlertAction(title: "Not now", style: .default)
            alertController.addAction(notNowButton)
        }

        let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in
            guard let url = URL(string: AppURL) else {
                return
            }
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                UIApplication.shared.openURL(url)
            }
        }

        alertController.addAction(updateButton)
        self.present(alertController, animated: true, completion: nil)
    }
}
2 Answers

let first_class = ViewController() // stop doing this. It will not work. Think of it; you have a car (view controller), you want to turn on the radio of your car. What would you have to do? First turn your car on and then its radio right? But what you do in your code is; instead of turning on the radio of your car, you go and get a new car (like new instance of viewcontroller) and turn on the radio of the new car. But actually you need to turn on the radio of your own car not the new car. This how works instances in object oriented programming. You created an instance of a UIViewController (it is your car), then in another class you are creating another instance of UIViewController (that is not your car in the example). Then you try to modify the first one but actually you are modifying the another one. As soon as I am home I will modify your code from my laptop, because it is hard to edit it from the mobile phone.

Here is the modified code. NOTE THAT I just correct the parts where you want to call load_shop function from the CheckUpdate class. I mean I don't know all the logic behind your code so, if it contains more logical errors it's all up to you.

ViewController.swift

import UIKit
import WebKit
import UserNotifications
import Foundation // Not really necesary cause UIKit import already imports it

class ViewController: UIViewController, WKUIDelegate, WKScriptMessageHandler, WKNavigationDelegate {
    
    var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.scrollView.bounces = false;

        
        let myURL = URL(string:"https://google.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest);
        webView.navigationDelegate = self

    }
    
    var update = 0
    
    func load_shop() {
        view = webView
        print("finish_update")
        
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        CheckUpdate.shared.showUpdate(withConfirmation: false, caller: self) // Attention here!!!
        
    }

    func close() {
        exit(0);
    }
    
    override var prefersHomeIndicatorAutoHidden: Bool {
        return true
    }
}

CheckUpdate.swift

import UIKit

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
    var trackViewUrl: String
}

class CheckUpdate: NSObject {


    let first_class: ViewController? // Attention here!!!
    static let shared = CheckUpdate()
    
    
    func showUpdate(withConfirmation: Bool, viewController: ViewController) {
        first_class = viewController // Attention here!!!
        DispatchQueue.global().async {
            self.checkVersion(force : !withConfirmation)
        }
    }
  

    private  func checkVersion(force: Bool) {
        if let currentVersion = self.getBundle(key: "CFBundleShortVersionString") {
            _ = getAppInfo { (info, error) in
                if let appStoreAppVersion = info?.version {
                    if let error = error {
                        print("error getting app store version: ", error)
                    } else if appStoreAppVersion == currentVersion {
                        self.first_class?.load_shop() // Atention here!!!
                        print("Already on the last app version: ",currentVersion)
                    } else {
                        print("Needs update: AppStore Version: \(appStoreAppVersion) > Current version: ",currentVersion)
                        DispatchQueue.main.async {
                            let topController: UIViewController = (UIApplication.shared.windows.first?.rootViewController)!
                            topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!)
                        }
                    }
                }
            }
        }
    }

    private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
    
      // You should pay attention on the country that your app is located, in my case I put Brazil */br/*
      // Você deve prestar atenção em que país o app está disponível, no meu caso eu coloquei Brasil */br/*
      
        guard let identifier = self.getBundle(key: "CFBundleIdentifier"),
              let url = URL(string: "http://itunes.apple.com/br/lookup?bundleId=\(identifier)") else {
                DispatchQueue.main.async {
                    completion(nil, VersionError.invalidBundleInfo)
                }
                return nil
        }
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
          
            
                do {
                    if let error = error { throw error }
                    guard let data = data else { throw VersionError.invalidResponse }
                    
                    let result = try JSONDecoder().decode(LookupResult.self, from: data)
                    print(result.results)
                    guard let info = result.results.first else {
                        throw VersionError.invalidResponse
                    }

                    completion(info, nil)
                } catch {
                    completion(nil, error)
                }
            }
        
        task.resume()
        return task

    }

    func getBundle(key: String) -> String? {

        guard let filePath = Bundle.main.path(forResource: "Info", ofType: "plist") else {
          fatalError("Couldn't find file 'Info.plist'.")
        }
        // 2 - Add the file to a dictionary
        let plist = NSDictionary(contentsOfFile: filePath)
        // Check if the variable on plist exists
        guard let value = plist?.object(forKey: key) as? String else {
          fatalError("Couldn't find key '\(key)' in 'Info.plist'.")
        }
        return value
    }
}

extension UIViewController {
    @objc fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) {
        guard let appName = CheckUpdate.shared.getBundle(key: "CFBundleName") else { return } //Bundle.appName()

        let alertTitle = "New version"
        let alertMessage = "A new version of \(appName) are available on AppStore. Update now!"

        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)

        if !Force {
            let notNowButton = UIAlertAction(title: "Not now", style: .default)
            alertController.addAction(notNowButton)
        }

        let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in
            guard let url = URL(string: AppURL) else {
                return
            }
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                UIApplication.shared.openURL(url)
            }
        }

        alertController.addAction(updateButton)
        self.present(alertController, animated: true, completion: nil)
    }
}

If your CheckUpdate class resides in a file other than ViewController's file this is not to correct way to do it. You can define a closure or protocol in ViewController then implement it in CheckUpdate class. Something like:

class ViewController: UIViewController {
   ...
   typeAlias OnUpdate = () -> Void
   ...
   override func viewDidLoad() {
      super.viewDidLoad()
      self.load_shop()
   }
   ...
   let checkupdate = CheckUpdate(){
      view = webView
      print("finish_update")
   }
}
class CheckUpdate: NSObject {
   ...
   var onupdate:OnUpdate?
   ...
   init(_ onupdate: OnUpdate?){
       self.onupdate = onupdate
   }
   ...
   if (...) {
      //ViewController().load_shop()
      onupdate?()
   }
}
Related