How can I use a Storyboard other than one named 'Main' in the AppClip target

I was trying to share functionality between the main app and wanted initially to embed some storyboard from main app.

On changing my plist entry of
UIMainStoryboardFile, from Main to a newly added storyboard in the same target membership, I get a wicked crash. I'm guessing this is a bug?

2ndly, is this possible (re-using storyboard from main app, obviously after adding to target etc)?

Alex.
Answered by Engineer in 615551022
I think your question is largely about how to bundle multiple distinct UIs into a single app clip? There isn't one answer to how to accomplish this, you're welcome to use any technology in the SDK. However, if you'd like to bundle multiple storyboard files (ex: Store.storyboard, Restaurant.storyboard, MobileOrder.storyboard) for distinct experiences it will probably be easiest to declare none of these in your Info.plist, or to declare a fallback UI there. Instead you can use something like UIStoryboard.instantiateInitialViewController() to create the view controller you desire at runtime, by inspecting your activation context.

As an example, let's say that in your app clip target you have all three of those storyboard files mentioned above. You could remove the main storyboard key from your Info.plist and perform all setup in code (see example below) or you could leave the Info.plist key and just make sure that it points to a storyboard that could be launched in a generic case.

Code Block class AppClipSceneDelegate : UIWindowSceneDelegate {		var window: UIWindow?		var currentURL: URL?		func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options: UIScene.ConnectionOptions) {				guard let windowScene = scene as? UIWindowScene else { return }				window = UIWindow(windowScene: windowScene)				window?.rootViewController = SomeDefaultViewController()				window?.makeKeyAndVisible()				if let userActivity = options.userActivity {						scene(scene, continue: userActivity)				}		}		func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {				guard let url = userActivity.webpageURL, url != currentURL else { return }				currentURL = url				window.rootViewController = makeViewController()		}		private func makeViewController() -> UIViewController {				let storyboardName: String				if currentURL.path.hasPrefix("store/") {						storyboardName = "Store"				} else if currentURL.path.hasPrefix("restaurant/") {						storyboardName = "Restaurant"				} else {						storyboardName = "MobileOrder"				}				return UIStoryboard(name: storyboardName).instantiateInitialViewController()!		}}


How you determine what controller/storyboard maps to what URL is specific to your use case and something you get to decide, but hopefully this provides some illustration. I also want to call out the additional state of currentURL. If your app clip is handling multiple experiences, its important to not always reset the state of your UI without checking that the experience actually changed. Say someone used an iMessage link to start and order, went away, and then came back to that order by using the iMessage link again. In that scenario we'll deliver a new NSUserActivity to signal the re-activation, but you probably shouldn't throw away the current order.
Hi Alex,

if you replace the main storyboard with a new storyboard, please make sure the view controller in the new storyboard has the same class as in the old main storyboard.
So I've got 3 storyboards (because I was playing around with this).. the original Main.storyboard for Clip, along with another one called ClipMain.storyboard.

Admittedly yes, the new storyboard whilst having a view controller didn't have the Class defined, but I've now added it, and it still renders the 'Main' one despite the plist asking for the ClipMain one.

Are you saying we're able to have the App Clip target honour the storyboard mentioned in the plist?

Code Block languagecode-block2020-06-25 15:45:58.041660+1000 AppClip[30797:1442010] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Could not find a storyboard named 'Main' in bundle NSBundle


plist has:
for UIMainStoryboardFile key, I have ClipMain

For sake of testing, I've removed membership to the original storyboard and I get that. crash above

Accepted Answer
I think your question is largely about how to bundle multiple distinct UIs into a single app clip? There isn't one answer to how to accomplish this, you're welcome to use any technology in the SDK. However, if you'd like to bundle multiple storyboard files (ex: Store.storyboard, Restaurant.storyboard, MobileOrder.storyboard) for distinct experiences it will probably be easiest to declare none of these in your Info.plist, or to declare a fallback UI there. Instead you can use something like UIStoryboard.instantiateInitialViewController() to create the view controller you desire at runtime, by inspecting your activation context.

As an example, let's say that in your app clip target you have all three of those storyboard files mentioned above. You could remove the main storyboard key from your Info.plist and perform all setup in code (see example below) or you could leave the Info.plist key and just make sure that it points to a storyboard that could be launched in a generic case.

Code Block class AppClipSceneDelegate : UIWindowSceneDelegate {		var window: UIWindow?		var currentURL: URL?		func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options: UIScene.ConnectionOptions) {				guard let windowScene = scene as? UIWindowScene else { return }				window = UIWindow(windowScene: windowScene)				window?.rootViewController = SomeDefaultViewController()				window?.makeKeyAndVisible()				if let userActivity = options.userActivity {						scene(scene, continue: userActivity)				}		}		func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {				guard let url = userActivity.webpageURL, url != currentURL else { return }				currentURL = url				window.rootViewController = makeViewController()		}		private func makeViewController() -> UIViewController {				let storyboardName: String				if currentURL.path.hasPrefix("store/") {						storyboardName = "Store"				} else if currentURL.path.hasPrefix("restaurant/") {						storyboardName = "Restaurant"				} else {						storyboardName = "MobileOrder"				}				return UIStoryboard(name: storyboardName).instantiateInitialViewController()!		}}


How you determine what controller/storyboard maps to what URL is specific to your use case and something you get to decide, but hopefully this provides some illustration. I also want to call out the additional state of currentURL. If your app clip is handling multiple experiences, its important to not always reset the state of your UI without checking that the experience actually changed. Say someone used an iMessage link to start and order, went away, and then came back to that order by using the iMessage link again. In that scenario we'll deliver a new NSUserActivity to signal the re-activation, but you probably shouldn't throw away the current order.
The storyboard is referenced twice in the default Info.plist Xcode creates when a new clip target is created. Please ensure both references are changed to your new/renamed storyboard file.

The first reference is UIMainStoryboardFile
The second reference is under UIApplicationSceneManifest->UISceneConfigurations->UIWindowSceneSessionRoleApplication->Item 0
I noticed that too this morning however did couple it with using your sample and approach which seems to work again. I'm a bit old school and still adjusting to the Scene concept.

I'll try changing it there too and see if it gives me the expected behaviour and if so I'll mark it as approved/resolved..

Thanks.
After coming out of my rabbit hole in working on the features for my clip, I was able to test this out and confirmed that once I changed the second reference to Storyboard via UIApplicationSceneManifest, it would honour that storyboard reference.

I had since removed the UIMainStoryboardFile key in plist to work with the solution above but can confirm this was user error in first instance which given it's iOS14 it'll be taking the scene configuration and not that definition of the older style UIMainStoryboardFile.

I haven't tested that, but I'm guessing/expecting that it'll just ignore the UIMainStoryboardFile key will be ignored.
Marked as resolved..
How can I use a Storyboard other than one named 'Main' in the AppClip target
 
 
Q