How can I restart an application programmatically in Swift 4 on iOS?

Viewed 35107

I have issues. After language changing, I want to restart my application.

So I want to get an alert message with the text "Do you want to restart app to change language?" "Yes" "No"

And if the user presses YES, how can I restart the app?

My solution:

let alertController = UIAlertController(title: "Language".localized(), message: "To changing language you need to restart application, do you want to restart?".localized(), preferredStyle: .alert)
let okAction = UIAlertAction(title: "Yes".localized(), style: UIAlertActionStyle.default) {
    UIAlertAction in
    NSLog("OK Pressed")
    exit(0)
}

let cancelAction = UIAlertAction(title: "Restart later".localized(), style: UIAlertActionStyle.cancel) {
    UIAlertAction in
    NSLog("Cancel Pressed")
}

alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)

After the app will close, the user will manually start the app.

5 Answers

You cannot restart an iOS app. One thing you could do is to pop to your rootViewController.

func restartApplication () {
    let viewController = LaunchScreenViewController()
    let navCtrl = UINavigationController(rootViewController: viewController)

    guard
        let window = UIApplication.shared.keyWindow,
        let rootViewController = window.rootViewController

    else {
        return
    }

    navCtrl.view.frame = rootViewController.view.frame
    navCtrl.view.layoutIfNeeded()

    UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: {
        window.rootViewController = navCtrl
    })
}

In one of my apps, I needed to restart. I wrapped all of the loading logic into a LaunchScreenViewController. Above is the piece of code for "restarting the app".

It's not perfect, but I solved this by sending a timed local notification just before exiting the application. Then the user only needs to click on the notification to restart the app so they don't need to look for the app to relaunch it. I also suggest displaying an alert informing the user of the restart:

enter image description here

enter image description here

import UserNotifications
import Darwin // needed for exit(0)

struct RestartAppView: View {
@State private var showConfirm = false

var body: some View {
    VStack {
        Button(action: {
            self.showConfirm = true
        }) {
            Text("Update Configuration")
        }
    }.alert(isPresented: $showConfirm, content: { confirmChange })
}

var confirmChange: Alert {
    Alert(title: Text("Change Configuration?"), message: Text("This application needs to restart to update the configuration.\n\nDo you want to restart the application?"),
        primaryButton: .default (Text("Yes")) {
            restartApplication()
        },
        secondaryButton: .cancel(Text("No"))
    )
}

func restartApplication(){
    var localUserInfo: [AnyHashable : Any] = [:]
    localUserInfo["pushType"] = "restart"
    
    let content = UNMutableNotificationContent()
    content.title = "Configuration Update Complete"
    content.body = "Tap to reopen the application"
    content.sound = UNNotificationSound.default
    content.userInfo = localUserInfo
    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.5, repeats: false)

    let identifier = "com.domain.restart"
    let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)
    let center = UNUserNotificationCenter.current()
    
    center.add(request)
    exit(0)
}

}

You can change your root controller and don't need to restart it. Just change the root view controller or update or refresh or recall the root view controller:

let alertController = UIAlertController(title: "Language".localized(), message: "To change language you need to restart the application. Do you want to restart?".localized(), preferredStyle: .alert)

let okAction = UIAlertAction(title: "Yes".localized(), style: UIAlertActionStyle.default) {
    UIAlertAction in
    // Change update / refresh rootview controller here...
}

You can add to AppDelegate

func resetApp() {   
    UIApplication.shared.windows[0].rootViewController = UIStoryboard(
        name: "Main",
        bundle: nil
        ).instantiateInitialViewController()
}

Call this function where you want

let appDelegate = AppDelegate()
appDelegate.startWith()

Add this to your viewDidLoad for starting the viewcontroller (VC):

override func viewDidLoad() {
    super.viewDidLoad()

    // Make dismiss for all VC that was presented from this start VC
    self.children.forEach({vc in
        print("Dismiss \(vc.description)")
        vc.dismiss(animated: false, completion: nil)
    })

    //  ....
}

And in the restart initiator:

// ...
let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewController(withIdentifier: "startVC")
    self.present(vc, animated: false, completion: nil)
Related