The easiest way is to implement a function that will send a request every four seconds to AppDelegate (or in a separate class). And when you receive a response from the server, send a notification for UIViewContollers. When displaying the screen (viewDidAppear), the controllers subscribe to this notification, and when they close it (viewDidDesappear) they unsubscribe from the notification. To disable the timer, you can also send a notification (already to your class) or simply set a variable, for example, allowUpdateTimer to false directly from UIViewContoller, and check its state (in your class) before accessing the server.
If the data is not actually updated every 4 seconds, for example, every few minutes, it makes sense to use the notification via Firebase Cloud Messaging or open and hold the WebSocket. The app will spend less battery.
UPDATE:
I made a simple example:
In AppDelegate, we create a timer, make it every 4 seconds call the function of receiving time from the server. The server response sends a notification named update_new_date.
AppDelegate.swift file
var timer: Timer? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { timer = Timer.scheduledTimer(timeInterval: 4, target:self, selector: #selector(AppDelegate.update_data), userInfo: nil, repeats: true) return true } func update_data() { Alamofire.request("https://time.is/Moscow").responseJSON { response in if let contentType = response.response?.allHeaderFields["Expires"] as? String { print("Date: \(contentType)") let dataDict:[String: String] = ["date": contentType] NotificationCenter.default.post(name: Notification.Name("update_new_date"), object: nil, userInfo: dataDict) } } }
We subscribe to this notification when displaying UIViewControllers:
UIViewController files:
override func viewDidAppear(_ animated: Bool) { NotificationCenter.default.addObserver(self, selector: #selector(update_new_date(notification:)), name: Notification.Name("update_new_date"), object: nil) }
The incoming notification calls the function of updating the data on the screen:
func update_new_date(notification: Notification){ if let userInfo = notification.userInfo as? [String: Any] { if let date = userInfo["date"] as? String { time_label.text = date appDelegate.animate_update(view: time_label) } } }
When the screen is no longer displayed, we unsubscribe from the notifications:
override func viewDidDisappear(_ animated: Bool) { NotificationCenter.default.removeObserver(self) }
You can stop the timer from anywhere by contacting AppDelegate:
let appDelegate = UIApplication.shared.delegate as! AppDelegate appDelegate.timer?.invalidate()
And run further:
appDelegate.timer?.fire()

Full version of the example: Github