CarPlay parking app crashed when launching from Xcode 12 CarPlay simulator

532 views Asked by At

We are extending our Xamarin.iOS app (by Xamarin.Forms) for CarPlay. When open the CarPlay simulator, the app is showing on the screen of CarPlay, but crashed when launching from CarPlay simulator.

Below is the Info.plist Scene configuration:

    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UISceneConfigurations</key>
        <dict>
            <key>CPTemplateApplicationSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneClassName</key>
                    <string>CPTemplateApplicationScene</string>
                    <key>UISceneConfigurationName</key>
                    <string>ParkingPlus-Car</string>
                    <key>UISceneDelegateClassName</key>
                    <string>ParkingPlus.AppSceneDelegateImp</string>
                </dict>
            </array>
        </dict>
    </dict>

"ParkingPlus" is the app bundle name!

The class AppSceneDelegateImp (under the root folder of the iOS project

    public class AppSceneDelegateImp : UIResponder, ICPTemplateApplicationSceneDelegate
    {
        private CPInterfaceController _interfaceController;

        public async void DidConnect(CPTemplateApplicationScene templateApplicationScene, CPInterfaceController interfaceController)
        {
            _interfaceController = interfaceController;
           .....
        }

        public void DidDisconnect(CPTemplateApplicationScene templateApplicationScene, CPInterfaceController interfaceController)
        {
            _interfaceController.Dispose();
            _interfaceController = null;
        }
   }

When I override the AppDelegate.GetConfiguration as below

public override UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options)
{
...
}

When tapping the app icon on CarPlay, the method will be called. But when I inspected the connectingSceneSession, I found there are some exceptions inside the variable members. "CPTemplateApplicationSceneSessionRoleApplication has no associated enum value on this platform".

Screenshot for connectingSceneSession inspection

If continue, then the app will throw an exception which seems advise that the SceneDelegate is not being loaded properly: Exception

My envorinment: Visual studio for mac Version 8.7.8 Xamarin.iOS 14.0.0 Xcode 12.0

It seems like Xamarin.ios 14 missing something when binding the iOS library. Anyone has the similar issue. Did I do anything wrong or Is there any other way I can implement the CarPlay part feature by Xcode/swift while keeping the mobile app on Xamarin.Forms/Xamarin.iOS?

Appreciate any comments or help.

1

There are 1 answers

5
Kevin Hu On BEST ANSWER

With the help from Xamarin.ios team, below is the full solution to this issue: https://github.com/xamarin/xamarin-macios/issues/9749

  1. The AppDelegate to create the scene configuration for two cases (CarPlay and phone)

        [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
        public extern static IntPtr IntPtr_objc_msgSend_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);

        public static UISceneConfiguration Create(string? name, NSString sessionRole)
        {
            global::UIKit.UIApplication.EnsureUIThread();
            var nsname = NSString.CreateNative(name);

            UISceneConfiguration sceneConfig;
            sceneConfig = Runtime.GetNSObject<UISceneConfiguration>(IntPtr_objc_msgSend_IntPtr_IntPtr(Class.GetHandle("UISceneConfiguration"), Selector.GetHandle("configurationWithName:sessionRole:"), nsname, sessionRole.Handle));
            NSString.ReleaseNative(nsname);
            //Because only the CarPlay scene will be here to create a scene configuration
            //We need manually assign the CarPlay scene delegate here!
            sceneConfig.DelegateType = typeof(AppCarSceneDelegateImp);
            return sceneConfig!;
        }

        [Export("application:configurationForConnectingSceneSession:options:")]
        public UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options)
        {
            UIWindowSceneSessionRole sessionRole;
            bool isCarPlaySceneSession = false;
            try
            {
                //When the connecting scene is a CarPlay scene, an expected exception will be thrown
                //Under this moment from Xamarin.iOS.
                sessionRole = connectingSceneSession.Role;
            }
            catch (NotSupportedException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message) &&
                    ex.Message.Contains("CPTemplateApplicationSceneSessionRoleApplication"))
                {
                    isCarPlaySceneSession = true;
                }
            }

            if (isCarPlaySceneSession && UIDevice.CurrentDevice.CheckSystemVersion(14,0))
            {
                return Create("Car", CarPlay.CPTemplateApplicationScene.SessionRoleApplication);
            }
            else
            {
                //If it is phone scene, we need the regular UIWindow scene
                UISceneConfiguration phoneScene = new UISceneConfiguration("Phone", UIWindowSceneSessionRole.Application);
                //And assign the scene delegate here.
                phoneScene.DelegateType = typeof(AppWindowSceneDelegateImp);
                return phoneScene;
            }
        }
  1. Create a UIWindowScene delegate to handle the regular mobile scene window
    public class AppWindowSceneDelegateImp : UISceneDelegate
    {
        public override void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
        {
            var windowScene = scene as UIWindowScene;
            if (windowScene != null)
            {
                //Assign the Xamarin.iOS app window to this scene 
                UIApplication.SharedApplication.KeyWindow.WindowScene = windowScene;
                UIApplication.SharedApplication.KeyWindow.MakeKeyAndVisible();
            }
        }
    }