11

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrew
  • 372
  • 2
  • 5
  • 25
  • 1
    Possible duplicate of https://stackoverflow.com/questions/4238979/how-programatically-restart-an-iphone-app-in-ios. – Martin R Sep 11 '18 at 12:14
  • 5
    You can't restart the app. However, you can kill it using `exit(0)` and hoping that the user will launch it again. – Adam Sep 11 '18 at 12:14
  • @Martin R it's for obj-c and i want for Swift – Andrew Sep 11 '18 at 12:15
  • @McNight no possibility to do this automatically? – Andrew Sep 11 '18 at 12:16
  • 1
    No, it is about iOS. It is not language specific. – Martin R Sep 11 '18 at 12:16
  • @Andrew, nope. Even if you kill the app using `exit(0)`, it's bound to be rejected. Unless that kill is user initiated. – avismara Sep 11 '18 at 12:17
  • just change the root viewcontroller – Anbu.Karthik Sep 11 '18 at 12:18
  • @Anbu.karthik can i get example? – Andrew Sep 11 '18 at 12:19
  • exit(0) can be used to kill the app but Apple might reject it later. Restart is not possible. – Arti Sep 11 '18 at 12:19
  • Besides, the method shown in the link is 8 years old, and requires you to opt out of multitasking. Not sure if this is what OP wants. – Adam Sep 11 '18 at 12:21
  • 6
    Why do you need restart application? Maybe there is a better solution for your actual problem. http://xyproblem.info/ – Marek R Sep 11 '18 at 12:22
  • @Marek R for my case, no because i have two localizabled file it's main story board string and localizable string – Andrew Sep 11 '18 at 12:23
  • @Andrew ?? can you rephrase that? – Marek R Sep 11 '18 at 12:25
  • @Marek R so i have two files, it's story board string which i translate, and error message and some system message which i translate at separate file Localizable.strings – Andrew Sep 11 '18 at 12:28
  • 3
    Is there any reason why you can't rely on the user's preferred language on the device? In other words; why are you implementing your own solution for switching languages instead of relying on the OS? – David Rönnqvist Sep 11 '18 at 12:31
  • @Andrew there is a way to change it on the fly with Swift by adding an extension function to String. This would resolve your problem. swapping the localizable strings wouldn't required app restart. check this link pls - https://stackoverflow.com/questions/29985614/how-can-i-change-locale-programmatically-with-swift – Sathish Sep 11 '18 at 13:15
  • 1
    if you are using `exit(0)` you app will be rejected by Apple. – holex Sep 11 '18 at 14:24

5 Answers5

25

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".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Arthur
  • 390
  • 1
  • 4
  • 13
17

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)
}

}

J. Edgell
  • 1,555
  • 3
  • 16
  • 24
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...
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Muhammad Ahmad
  • 388
  • 4
  • 9
  • using exit(0) will get your app removed from the App Store – Zun Aug 21 '19 at 09:25
  • 3
    Could you please put a reference to that? – Display Name Dec 07 '19 at 09:06
  • `Warning: Do not call the exit function.` [https://developer.apple.com/library/archive/qa/qa1561/_index.html#//apple_ref/doc/uid/DTS40007952](https://developer.apple.com/library/archive/qa/qa1561/_index.html#//apple_ref/doc/uid/DTS40007952) – Jamie Le Souëf Feb 16 '21 at 23:22
  • I have an app that has had updates every year for the past 9 years and has always had exit(0) and never been challenged. – ghr Nov 11 '21 at 05:58
-2

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()
ochs.tobi
  • 3,214
  • 7
  • 31
  • 52
  • 2
    This is a terrible way to do it, if you haven't pulled down the rest of the app. Otherwise you'll just end up with two instances of the app, running on top of each other, possibly corrupting each others data – Shayne Apr 01 '19 at 08:16
-2

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)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131