0

I'm working on an app that needs to open on the users last used view even if the app is completly killed by the user or ios.

As a result I'm holding last view used in UserDefaults and automatically moving the user through each view in the stack until they reach their destination.

The code on each view is as follows:

@Binding var redirectionID: Int

VStack() {
  List {
    NavigationLink(destination: testView(data: data, moc: moc), tag: data.id, selection: 
         $redirectionId) {
                        
                           DataRow(data: data)
                        
                         }
        }
}.onAppear() {
  redirectionID = userData.lastActiveView
}

Is there a better / standard way to achieve this? This works reasonably on iOS 14.* but doesn't work very well on iOS 13.* On iOS 13.* The redirection regularly doesnt reach its destination page and non of the preceeding views in the stack seem to be created. Pressing back etc results in a crash.

Any help / advice would be greatly appreciated.

swift--help
  • 637
  • 5
  • 15
  • 1
    Personally I would show directly the page needed. The code to go to the parent page should be quite static. If there is a notion of "history" (meaning a user can arrive to a page by multiple path) I would store the history in the usedDefault as well. – La pieuvre Jan 29 '21 at 01:10
  • If you went directly to the page needed how would you handle the user moving back down the stack? For example, if it opened on page three, how would the user navigate back to page 1? – swift--help Jan 29 '21 at 01:15

1 Answers1

1

This sounds like the perfect use of if SceneStorage

"You use SceneStorage when you need automatic state restoration of the value. SceneStorage works very similar to State, except its initial value is restored by the system if it was previously saved, and the value is· shared with other SceneStorage variables in the same scene."

@SceneStorage("ContentView.selectedProduct") private var selectedProduct: String?

@SceneStorage("DetailView.selectedTab") private var selectedTab = Tabs.detail

It is only available in iOS 14+ though so something manual would have to be implemented. Maybe something in CoreData. An object that would have variables for each important state variable. It would work like an ObservedObject ViewModel with persistence.

Also. you can try...

"An NSUserActivity object captures the app’s state at the current moment in time. For example, include information about the data the app is currently displaying. The system saves the provided object and returns it to the app the next time it launches. The sample creates a new NSUserActivity object when the user closes the app or the app enters the background."

Here is some sample code that summarizes how to bring it all together. It isn't a minimum reproducible example because it is a part of the larger project called "Restoring Your App's State with SwiftUI" from Apple. But it gives a pretty good picture on how to implement it.

struct ContentView: View {
    // The data model for storing all the products.
    @EnvironmentObject var productsModel: ProductsModel
    
    // Used for detecting when this scene is backgrounded and isn't currently visible.
    @Environment(\.scenePhase) private var scenePhase

    // The currently selected product, if any.
    @SceneStorage("ContentView.selectedProduct") private var selectedProduct: String?
    
    let columns = Array(repeating: GridItem(.adaptive(minimum: 94, maximum: 120)), count: 3)
    
    var body: some View {
        NavigationView {
            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(productsModel.products) { product in
                        NavigationLink(destination: DetailView(product: product, selectedProductID: $selectedProduct),
                                       tag: product.id.uuidString,
                                       selection: $selectedProduct) {
                            StackItemView(itemName: product.name, imageName: product.imageName)
                        }
                        .padding(8)
                        .buttonStyle(PlainButtonStyle())
                        
                        .onDrag {
                            /** Register the product user activity as part of the drag provider which
                                will  create a new scene when dropped to the left or right of the iPad screen.
                            */
                            let userActivity = NSUserActivity(activityType: DetailView.productUserActivityType)
                            
                            let localizedString = NSLocalizedString("DroppedProductTitle", comment: "Activity title with product name")
                            userActivity.title = String(format: localizedString, product.name)
                            
                            userActivity.targetContentIdentifier = product.id.uuidString
                            try? userActivity.setTypedPayload(product)
                            
                            return NSItemProvider(object: userActivity)
                        }
                    }
                }
                .padding()
            }
            .navigationTitle("ProductsTitle")
        }
        .navigationViewStyle(StackNavigationViewStyle())
        
        .onContinueUserActivity(DetailView.productUserActivityType) { userActivity in
            if let product = try? userActivity.typedPayload(Product.self) {
                selectedProduct = product.id.uuidString
            }
        }
        .onChange(of: scenePhase) { newScenePhase in
            if newScenePhase == .background {
                // Make sure to save any unsaved changes to the products model.
                productsModel.save()
            }
        }
    }
}
lorem ipsum
  • 21,175
  • 5
  • 24
  • 48
  • This looks very promising. I'll have a look into SceneStorage and post any progress. Thanks! – swift--help Jan 29 '21 at 13:23
  • 1
    I'm looking into multiple windows right now and there is a lot of information on scene restoration in the [WWDC19 - Introducing Multiple Windows on iPad](https://developer.apple.com/videos/play/wwdc2019/212) video that comes along with some sample core – lorem ipsum Jan 29 '21 at 14:07
  • Legend, I'll have a play with this tomorrow morning and let you know how it goes. – swift--help Jan 29 '21 at 16:49