juicycleff / flutter-unity-view-widget

Embeddable unity game engine view for Flutter. Advance demo here https://github.com/juicycleff/flutter-unity-arkit-demo
BSD 3-Clause "New" or "Revised" License
2.09k stars 505 forks source link

iOS app instantly crashes when using Niantic ARDK. #869

Closed khangpt closed 3 months ago

khangpt commented 10 months ago

Describe the bug I followed the instruction on pub.dev page and get building on Android perfectly. But when I export release for iOS, and config follow the guiding, I run on iPhone 7+, iOS version 15+. It crash instantly after installing successfully. I captured a screenshot about the log on console.

Hope someone can rescue me from this trouble :) Thanks a lot!

Screenshots Screenshot 2023-08-23 at 14 48 49

Unity (please complete the following information):

Smartphone (please complete the following information):

AkhilRaja commented 9 months ago

@timbotimbo @juicycleff Tagging you both to bring this to your notice. I am facing the same problem.

AkhilRaja commented 9 months ago

Adding some more context on what I am trying: I have a Niantic ARDK 3.0 app on Unity which I am trying to integrate with FUW.

The lightship ardk is trying to load the plugin like this:

// Put this into LightshipArPlugin/Runtime/Plugins/iOS

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#import "IUnityInterface.h"

// UI loaded observer from https://blog.eppz.eu/override-app-delegate-unity-ios-macos-2/
@interface RegisterPlugin : NSObject
@end

__strong RegisterPlugin *_instance;

@implementation RegisterPlugin
+(void)load
{
    NSLog(@"[Override_iOS load]");
    _instance = [RegisterPlugin new];

    [[NSNotificationCenter defaultCenter] addObserver:_instance
                                             selector:@selector(applicationDidFinishLaunching:)
                                                 name:UIApplicationDidFinishLaunchingNotification
                                               object:nil];
}
-(void)applicationDidFinishLaunching:(NSNotification*) notification
{
    NSLog(@"[Override_iOS applicationDidFinishLaunching:%@]",
    UnityRegisterRenderingPluginV5(&UnityPluginLoad, &UnityPluginUnload);
}
@end

And, this line UnityRegisterRenderingPluginV5(&UnityPluginLoad, &UnityPluginUnload); throws a bad access exception. I was trying to find how ARKit plugin does it loading, such that it is UnityRegisterRenderingPluginV5(&UnityPluginLoad, NULL); is called only once Unity is loaded. However the lightship Register plugin is called as soon as the applicationDidFinishLaunching due to the UI Kit notification.

I do not have plugin building experience, so any pointers on this would be super helpful.

PS: Found this blog from the niantic register plugin https://blog.eppz.eu/override-app-delegate-unity-ios-macos-2/ This is exactly how they are registering their plugin (Framework, unlike the arkit .a library)

timbotimbo commented 9 months ago

Once an export is completed, ios/UnityLibrary/Classes/UnityAppController.mm contains a line that triggers a notification to signal unity is loaded.

 - (void)startUnity:(UIApplication*)application
{
    NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized");

    UnityInitApplicationGraphics();

    // we make sure that first level gets correct display list and orientation
    [[DisplayManager Instance] updateDisplayListCacheInUnity];

    UnityLoadApplication();
    Profiler_InitProfiler();

    [self showGameUI];
    [self createDisplayLink];

    UnitySetPlayerFocus(1);

    AVAudioSession* audioSession = [AVAudioSession sharedInstance];
    // If Unity audio is disabled, we set the category to ambient to make sure we don't mute other app's audio. We set the audio session
    // to active so we can get outputVolume callbacks. If Unity audio is enabled, FMOD should have already handled all of this AVAudioSession init.
    if (!UnityIsAudioManagerAvailableAndEnabled())
    {
        [audioSession setCategory: AVAudioSessionCategoryAmbient error: nil];
        [audioSession setActive: YES error: nil];
    }

    [audioSession addObserver: self forKeyPath: @"outputVolume" options: 0 context: nil];
    UnityUpdateMuteState([audioSession outputVolume] < 0.01f ? 1 : 0);

#if UNITY_REPLAY_KIT_AVAILABLE
    void InitUnityReplayKit();  // Classes/Unity/UnityReplayKit.mm

    InitUnityReplayKit();
#endif
    // Modified by https://github.com/juicycleff/flutter-unity-view-widget
+    [[NSNotificationCenter defaultCenter] postNotificationName: @"UnityReady" object:self];
}

Maybe you can observe this notification instead of applicationDidFinishLaunching, or add your own custom notification that is triggered in the same location.

AkhilRaja commented 9 months ago

Thank-you @timbotimbo You are awesome!!

timbotimbo commented 3 months ago

Just got confirmation on the Discord that the suggestion above can get it to work.

You need to edit this file in the export. ios/UnityLibrary/Libraries/com.nianticlabs.lightship/Runtime/Plugins/iOS/RegisterPlugin.mm

 +(void)load
{
    NSLog(@"[Override_iOS load]");
    _instance = [RegisterPlugin new];

    [[NSNotificationCenter defaultCenter] addObserver:_instance
                                             selector:@selector(applicationDidFinishLaunching:)
-                                                 name:UIApplicationDidFinishLaunchingNotification
+                                                 name:@"UnityReady"
                                               object:nil];
}

Now the ARDK plugin should register when the UnityWidget is loaded instead of when the Flutter app loads.