Crash iOS Stability Unity

How To Fix Deep Link/Push Notifications Crashes On Unity iOS (and Why Software Design Matters)

Thread 0, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x35f)
0 UnityFramework`core::StringStorageDefault<char>::assign(char const*, unsigned long) + 28
1 UnityFramework`PlayerSettings::SetAbsoluteURL(core::basic_string<char, core::StringStorageDefault<char> > const&) + 44
2 UnityFramework`UnitySetAbsoluteURL + 100
3 UnityFramework`-[UnityAppController application:openURL:options:] + 192
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    ::printf("-> applicationDidFinishLaunching()\n");

    // send notfications
#if !PLATFORM_TVOS && !PLATFORM_VISIONOS
    if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO)
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
#endif

    UnityInitApplicationNoGraphics(UnityDataBundleDir());

    [self selectRenderingAPI];
    [UnityRenderingView InitializeForAPI: self.renderingAPI];

#if !PLATFORM_VISIONOS
    if (@available(iOS 13, tvOS 13, *))
        _window = [[UIWindow alloc] initWithWindowScene: [self pickStartupWindowScene: application.connectedScenes]];
    else
        _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
#else
    _window = [[UIWindow alloc] init]; 
#endif

    _unityView = [self createUnityView];


    [DisplayManager Initialize];
    _mainDisplay = [DisplayManager Instance].mainDisplay;
    [_mainDisplay createWithWindow: _window andView: _unityView];

    [self createUI];
    [self preStartUnity];

    // if you wont use keyboard you may comment it out at save some memory
    [KeyboardDelegate Initialize];

    // delay is needed so that the attach managed debugger window would be properly created when OS view is prepared to show it,
    //  otherwise debug window will not appear and will cause application to be in frozen state. "startUnity" method after delay will be called on applicationDidBecomeActive
    // also this might introduce one black frame between launch screen and unity splash screen, but in most scenarios it will be not visible since the splash screen has black background itself
    [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0];
    
    return YES;
}
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    ::printf("-> applicationDidFinishLaunching()\n");

    // send notfications
#if !PLATFORM_TVOS && !PLATFORM_VISIONOS
    if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO)
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
#endif

    if ([self isBackgroundLaunchOptions: launchOptions])
        return YES;

    [self initUnityWithApplication: application];
    return YES;
}

- (BOOL)isBackgroundLaunchOptions:(NSDictionary*)launchOptions
{
    if (launchOptions.count == 0)
        return NO;

    // launch due to location event, the app likely will stay in background
    BOOL locationLaunch = [[launchOptions valueForKey: UIApplicationLaunchOptionsLocationKey] boolValue];
    if (locationLaunch)
        return YES;
    return NO;
}
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    ::printf("-> applicationDidFinishLaunching()\n");

    // send notfications
#if !PLATFORM_TVOS && !PLATFORM_VISIONOS
    if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO)
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
#endif

    if ([self isBackgroundLaunchOptions: launchOptions])
        return YES;

    [self initUnityWithApplication: application];
    return YES;
}

- (BOOL)isBackgroundLaunchOptions:(NSDictionary*)launchOptions
{
    if (launchOptions.count == 0)
        return NO;

    // launch due to location event, the app likely will stay in background
    BOOL locationLaunch = [[launchOptions valueForKey: UIApplicationLaunchOptionsLocationKey] boolValue];
    if (locationLaunch)
        return YES;
    return YES;
}
  
  [PostProcessBuild(10000)]
  public static void IOSBuildPostProcess(BuildTarget target, string pathToBuiltProject)
  {
	  ...
      FixUniversalLinksColdStartBugInFacebookSDK(pathToBuiltProject);  // Call this function from your IOSBuildPostProcess 
	  ...
  }

  private static void FixUniversalLinksColdStartBugInFacebookSDK(string path)
  {         

      string isBackgroundLaunchOptions = @"(?x)(isBackgroundLaunchOptions:\(NSDictionary\*\)launchOptions(?:.*\n)+?\s*return\ )YES(\;\n\})# }";
      string fullPath = Path.Combine(path, Path.Combine("Classes", "UnityAppController.mm"));
      string data = Load(fullPath);
      data = Regex.Replace(
          data,
          isBackgroundLaunchOptions,
          "$1NO$2");

      Save(fullPath, data);
      
      static string Load(string fullPath)
      {
          string data;
          FileInfo projectFileInfo = new FileInfo(fullPath);
          StreamReader fs = projectFileInfo.OpenText();
          data = fs.ReadToEnd();
          fs.Close();

          return data;
      }
      
      static void Save(string fullPath, string data)
      {
          System.IO.StreamWriter writer = new System.IO.StreamWriter(fullPath, false);
          writer.Write(data);
          writer.Close();
      }
  }

Unknown unknowns: The third symptom of complexity is that it is not obvious which pieces of code must be modified to complete a task, or what information a developer must have to carry out the task successfully.

An unknown unknown means that there is something you need to know, but there is no way for you to find out what it is, or even whether there is an issue. You won’t find out about it until bugs appear after you make a change.

“A Philosophy of Software Design” by John Ousterhout
AlexeyMerzlikin-TechLead-Gamedeveloper-Unity


Alexey Merzlikin

Experienced game developer and tech lead with a passion for writing educational content about game development and programming. I have over 10 years of industry experience focused on performance optimization, clean code practices, and robust game architecture. I share regular posts on my Telegram channel about applying software engineering best practices to build high-quality games. My goal is to help other game developers level up their skills.