5 Replies
      Latest reply on May 19, 2020 4:55 PM by ben_singaccord
      ben_singaccord Level 1 Level 1 (0 points)

        I'm trying to implement scene-based state restoration in an iPad OS 13 / macCatalyst app.

         

        The docs say .stateRestorationActivity might be nil in scene(_:, willConnectTo:, options:) if data protection is turned on.

         

        I don't have data protection turned on in my macCatalyst app, but .stateRestorationActivity is always nil, despite my verifying that stateRestorationActivity(for scene: UIScene) -> NSUserActivity? is returning a non-nil activity with an identifier that's set up in my Info.plist and has the target content identifier:

        class DocumentViewContoller : UIViewController {
        
        ...
        
        override func viewDidAppear(_ animated: Bool) {
          super.viewDidAppear(animated)
          updateDocumentUrlInTitleBar()
          let activity = NSUserActivity.newShowActivity()     //extension creates this with correct activityType string
          activity.targetContentIdentifier = document?.fileURL.absoluteString
          activity.title = document?.fileURL.lastPathComponent
          view.window?.windowScene?.userActivity = activity
          guard isViewLoaded
          ,let activationConditions = view.window?.windowScene?.activationConditions
          else { return }
          guard let url = document?.fileURL else {
          activationConditions.canActivateForTargetContentIdentifierPredicate = NSPredicate(value: true)
          activationConditions.prefersToActivateForTargetContentIdentifierPredicate = NSPredicate(value: false)
          return }
        
          activationConditions.canActivateForTargetContentIdentifierPredicate = NSPredicate(format: "self == '\(url.absoluteString)'")
          activationConditions.prefersToActivateForTargetContentIdentifierPredicate = NSPredicate(format: "self == '\(url.absoluteString)'")
        // activity.becomeCurrent()
          }
        

        and I verified that the predicates were set on the scene in stateRestorationActivity(for scene:)

         

        What other conditions do I have to meet in order that my scenes are restored when the app launches again?

         

        If it will always be nil, why did the WWDC presenters, in 3 separate talks, tell us we should look for it in that method?

        If it will always be nil in this method, during which delegate callback method should I expect it to be non-nil?

        It it will always be nil in all delegate callbacks, then how do I get an app to re-open document scenes which were open when the app was last quit?

        • Re: Why is .stateRestorationActivity always nil in willConnectTo: ?
          ben_singaccord Level 1 Level 1 (0 points)

          I tried printing out the discarded session in

          func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)

           

          and I was able to verify that the scene session associated with my open document before I quit the app was discarded moments after the app re-launches, and that the new scene it creates when the app opens is not the one that was open before.

            • Re: Why is .stateRestorationActivity always nil in willConnectTo: ?
              gregm Level 1 Level 1 (10 points)

              I'm seeing the same issue. The UISceneDelegate based stateRestoration works correctly when run on iOS 13, but the same code run on macCatalyst behaves differently.

               

               

              I looked at the session.stateRestorationActivity in scene(_:willConnectTo:options:) and in the iOS 13 case the userActivity.userInfo contains the correct information. However in macCatalyst session.stateRestorationActivity is nil.

               

               

              On macCatalyst (but not iOS 13) the application(_:didDiscardSceneSessions:) is called shortly after the scene(_:willConnectTo:options:) is called and the set contain only one session and when I inspect the stateRestorationActivity it is the correct one that was saved in the previous run of the app.

               

               

              It is clear that the userActivity data is available to the new run of the app, but it is not being used for some reason. I've tried the Apple supplied sample code and I get the same behavior. It seems that some environmental condition is wrong, but I have no idea what it might be.

               

              As a side note, I've noticed that initial window sizing is not maintained if I have the "multiple windows" plist entry turned on but if I turn that off the initial window size is preserved (no effect on the stateRestorationActivity though).

               

               

              Hopefully someone has managed to get this to work and can shed some light on the subject.

                • Re: Why is .stateRestorationActivity always nil in willConnectTo: ?
                  gregm Level 1 Level 1 (10 points)

                  I enabled the restoration debugging keys:

                   

                  UserDefaults.standard.set(true, forKey: "UIStateRestorationDebugLogging")
                  UserDefaults.standard.set(true, forKey: "UIStateRestorationDeveloperMode")

                   

                  Then I ran the app under both macCatalyst and iPadOS 13. The process was the same up to when +[_UICanvasUserActivityManager _knownSceneSessionMap] returned. Then the iPad app proceeded to call -[_UICanvasUserActivityManager initWithScene:] with the discovered identfier.

                   

                  However, in the macCatalyst case -[_UICanvasUserActivityManager initWithScene:] is not called but instead calls:

                   

                         +[_UICanvasUserActivityManager _updatePersistedSceneSession:]_block_invoke: Adding SceneSession for persistent identifier

                   

                  with a completely different identifier to the one returned from _knownSceneSessionMap and ultimately calls -[_UICanvasUserActivityManager initWithScene:] with this new identifier. Prior to this point that identifer is not reported in the debug log.

                   

                  Hopefully that provides a clue for someone smarter than me :-)

                    • Re: Why is .stateRestorationActivity always nil in willConnectTo: ?
                      gregm Level 1 Level 1 (10 points)

                      While sorting through this issue, I realized that if you have the System Preference "General -> Close Windows when quitting app" set "On" that the system removes the previously saved sessions and creates a new session when starting the app. Of course, this makes sense for a document based app (and probably many others). However, it is a change from the older state restoration mechanism where the information about the view controller hierarchy was available regardless of this System Preference. It is easy enough to have your own mechanism for saving state that can be used in the case that the previous session is removed for apps that it makes sense for.

                       

                      Hopefully that helps anyone that comes across this thread.

                       

                      Cheers,

                       

                      Greg

                        • Re: Why is .stateRestorationActivity always nil in willConnectTo: ?
                          ben_singaccord Level 1 Level 1 (0 points)

                          "I was not familiar with this "Close windows when quitting an app" setting, but mine is unchecked.

                           

                          I noticed you said "if you have the System Preference "General -> Close Windows when quitting app" set "On" that the system removes the previously saved sessions and creates a new session when starting the app".

                           

                          But you did not say "if you have the System Preference "General -> Close Windows when quitting app" set "off" that the system correctly restores the scenes just like on ios when starting the app".   Does it work correctly for you when turn it off?  Perhaps I just need to turn mine one and off again.

                           

                          If apple wants to give the user control over whether the documents are automatically re-opened when the user restarts the app, I don't have a problem with that.  But I do have a problem with two things:  1) no option to restore documents when re opening an app, and 2) re-opening the blank windows from previously opened documents and never allowing my code to populate those windows with the document contents.

                           

                          I filed a tech support incident over this, giving up and asking dts to fix my app to handle this correctly, because I was tired of delaying features.  I sent them my entire code base.  They gave up pretty quickly and told me to file a bug against the OS.  I have not received any word about the bug.

                           

                          It looks like Catalina's MacCatalyst simply doesn't fully support document-based apps, and that fact just isn't documented anywhere.  Or else it's just another huge ios 13/ctaalina bug they haven't got around to fixing yet.  At this point, I've competely given up on their fixing it in catalina and I'm just trying to make enough noise for them to fix it in mac os 10.16, because we only have 5 more months to fix all the bugs in that before my app is delayed for yet a third year due to the os not supporting features they've announced.