diff --git a/Source/Engine/Platform/File.h b/Source/Engine/Platform/File.h
index 12c772064..607356c8f 100644
--- a/Source/Engine/Platform/File.h
+++ b/Source/Engine/Platform/File.h
@@ -4,8 +4,10 @@
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32File.h"
-#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS
+#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC
#include "Unix/UnixFile.h"
+#elif PLATFORM_IOS
+#include "iOS/iOSFile.h"
#elif PLATFORM_ANDROID
#include "Android/AndroidFile.h"
#elif PLATFORM_SWITCH
diff --git a/Source/Engine/Platform/FileSystem.h b/Source/Engine/Platform/FileSystem.h
index 80874a05a..9398ff704 100644
--- a/Source/Engine/Platform/FileSystem.h
+++ b/Source/Engine/Platform/FileSystem.h
@@ -23,7 +23,7 @@
#elif PLATFORM_MAC
#include "Mac/MacFileSystem.h"
#elif PLATFORM_IOS
-#include "Apple/AppleFileSystem.h"
+#include "iOS/iOSFileSystem.h"
#else
#error Missing File System implementation!
#endif
diff --git a/Source/Engine/Platform/Types.h b/Source/Engine/Platform/Types.h
index 4eff07b18..76135dcaf 100644
--- a/Source/Engine/Platform/Types.h
+++ b/Source/Engine/Platform/Types.h
@@ -260,12 +260,12 @@ class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixConditionVariable;
typedef UnixConditionVariable ConditionVariable;
-class AppleFileSystem;
-typedef AppleFileSystem FileSystem;
+class iOSFileSystem;
+typedef iOSFileSystem FileSystem;
class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
-class UnixFile;
-typedef UnixFile File;
+class iOSFile;
+typedef iOSFile File;
class iOSPlatform;
typedef iOSPlatform Platform;
class AppleThread;
diff --git a/Source/Engine/Platform/iOS/iOSFile.h b/Source/Engine/Platform/iOS/iOSFile.h
new file mode 100644
index 000000000..7c7400835
--- /dev/null
+++ b/Source/Engine/Platform/iOS/iOSFile.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#if PLATFORM_IOS
+
+#include "Engine/Platform/Unix/UnixFile.h"
+
+///
+/// iOS platform file object implementation.
+///
+class FLAXENGINE_API iOSFile : public UnixFile
+{
+public:
+ iOSFile(int32 handle)
+ : UnixFile(handle)
+ {
+ }
+
+ static iOSFile* Open(const StringView& path, FileMode mode, FileAccess access = FileAccess::ReadWrite, FileShare share = FileShare::None);
+};
+
+#endif
diff --git a/Source/Engine/Platform/iOS/iOSFileSystem.h b/Source/Engine/Platform/iOS/iOSFileSystem.h
new file mode 100644
index 000000000..60b3ec43a
--- /dev/null
+++ b/Source/Engine/Platform/iOS/iOSFileSystem.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#if PLATFORM_IOS
+
+#include "Engine/Platform/Apple/AppleFileSystem.h"
+
+///
+/// iOS platform implementation of filesystem service.
+///
+class FLAXENGINE_API iOSFileSystem : public AppleFileSystem
+{
+public:
+ static bool FileExists(const StringView& path);
+ static uint64 GetFileSize(const StringView& path);
+ static bool IsReadOnly(const StringView& path);
+};
+
+#endif
diff --git a/Source/Engine/Platform/iOS/iOSPlatform.cpp b/Source/Engine/Platform/iOS/iOSPlatform.cpp
index 8960ff452..cda8235d0 100644
--- a/Source/Engine/Platform/iOS/iOSPlatform.cpp
+++ b/Source/Engine/Platform/iOS/iOSPlatform.cpp
@@ -4,6 +4,8 @@
#include "iOSPlatform.h"
#include "iOSWindow.h"
+#include "iOSFile.h"
+#include "iOSFileSystem.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Collections/Array.h"
@@ -13,12 +15,20 @@
#include "Engine/Platform/Window.h"
#include "Engine/Platform/WindowsManager.h"
#include "Engine/Threading/Threading.h"
+#include "Engine/Engine/Engine.h"
+#include "Engine/Engine/Globals.h"
#include
#include
int32 Dpi = 96;
Guid DeviceId;
+// Used by iOS project in XCode to run engine (see main.m)
+extern "C" FLAXENGINE_API int FlaxEngineMain()
+{
+ return Engine::Main(TEXT(""));
+}
+
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
NSString* title = (NSString*)AppleUtils::ToString(caption);
@@ -38,6 +48,40 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
return DialogResult::OK;
}
+#define IOS_FALLBACK_PATH(path) (Globals::ProjectFolder / StringUtils::GetFileName(path))
+
+iOSFile* iOSFile::Open(const StringView& path, FileMode mode, FileAccess access, FileShare share)
+{
+ iOSFile* file = (iOSFile*)UnixFile::Open(path, mode, access, share);
+ if (!file && mode == FileMode::OpenExisting)
+ {
+ // Fallback to file placed side-by-side with application
+ file = (iOSFile*)UnixFile::Open(IOS_FALLBACK_PATH(path), mode, access, share);
+ }
+ return file;
+}
+
+bool iOSFileSystem::FileExists(const StringView& path)
+{
+ return AppleFileSystem::FileExists(path) || AppleFileSystem::FileExists(IOS_FALLBACK_PATH(path));
+}
+
+uint64 iOSFileSystem::GetFileSize(const StringView& path)
+{
+ if (AppleFileSystem::FileExists(path))
+ return AppleFileSystem::GetFileSize(path);
+ return AppleFileSystem::GetFileSize(IOS_FALLBACK_PATH(path));
+}
+
+bool iOSFileSystem::IsReadOnly(const StringView& path)
+{
+ if (AppleFileSystem::FileExists(path))
+ return AppleFileSystem::IsReadOnly(path);
+ return AppleFileSystem::IsReadOnly(IOS_FALLBACK_PATH(path));
+}
+
+#undef IOS_FALLBACK_PATH
+
bool iOSPlatform::Init()
{
if (ApplePlatform::Init())
diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp
index 3ae1e02d5..80f9a801f 100644
--- a/Source/Engine/Scripting/Runtime/DotNet.cpp
+++ b/Source/Engine/Scripting/Runtime/DotNet.cpp
@@ -1782,6 +1782,7 @@ static MonoAssembly* OnMonoAssemblyPreloadHook(MonoAssemblyName* aname, char** a
}
#if 0
+
static unsigned char* OnMonoLoadAOT(MonoAssembly* assembly, int size, void* user_data, void** out_handle)
{
MonoAssemblyName* assemblyName = mono_assembly_get_name(assembly);
@@ -1800,6 +1801,57 @@ static void OnMonoFreeAOT(MonoAssembly* assembly, int size, void* user_data, voi
LOG(Info, "Free AOT data for C# assembly {0}", String(assemblyNameStr));
#endif
}
+
+#endif
+
+#if PLATFORM_IOS
+
+#include "Engine/Engine/Globals.h"
+#include
+#include
+
+static void* OnMonoDlFallbackLoad(const char* name, int flags, char** err, void* user_data)
+{
+ const String fileName = StringUtils::GetFileName(String(name));
+#if DOTNET_HOST_MONO_DEBUG
+ LOG(Info, "Loading dynamic library {0}", fileName);
+#endif
+ int dlFlags = 0;
+ if (flags & MONO_DL_GLOBAL && !(flags & MONO_DL_LOCAL))
+ dlFlags |= RTLD_GLOBAL;
+ else
+ dlFlags |= RTLD_LOCAL;
+ if (flags & MONO_DL_LAZY)
+ dlFlags |= RTLD_LAZY;
+ else
+ dlFlags |= RTLD_NOW;
+ void* result = dlopen(name, dlFlags);
+ if (!result)
+ {
+ // Try Frameworks location on iOS
+ String path = Globals::ProjectFolder / TEXT("Frameworks") / fileName;
+ if (!path.EndsWith(TEXT(".dylib")))
+ path += TEXT(".dylib");
+ result = dlopen(StringAsANSI<>(*path).Get(), dlFlags);
+ if (!result)
+ {
+ LOG(Error, "Failed to load dynamic libary {0}", String(name));
+ }
+ }
+ return result;
+}
+
+static void* OnMonoDlFallbackSymbol(void* handle, const char* name, char** err, void* user_data)
+{
+ return dlsym(handle, name);
+}
+
+static void* OnMonoDlFallbackClose(void* handle, void* user_data)
+{
+ dlclose(handle);
+ return 0;
+}
+
#endif
bool InitHostfxr()
@@ -1815,6 +1867,12 @@ bool InitHostfxr()
// Enable AOT mode (per-platform)
mono_jit_set_aot_mode(USE_MONO_AOT_MODE);
#endif
+
+ // Platform-specific setup
+#if PLATFORM_IOS
+ setenv("MONO_AOT_MODE", "aot", 1);
+ setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", 1);
+#endif
#ifdef USE_MONO_AOT_MODULE
// Load AOT module
@@ -1916,6 +1974,9 @@ bool InitHostfxr()
#if 0
mono_install_load_aot_data_hook(OnMonoLoadAOT, OnMonoFreeAOT, nullptr);
#endif
+#if PLATFORM_IOS
+ mono_dl_fallback_register(OnMonoDlFallbackLoad, OnMonoDlFallbackSymbol, OnMonoDlFallbackClose, nullptr);
+#endif
// Init managed runtime
#if PLATFORM_ANDROID || PLATFORM_IOS
diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp
index 8796c7587..1159ae823 100644
--- a/Source/Engine/Scripting/Scripting.cpp
+++ b/Source/Engine/Scripting/Scripting.cpp
@@ -351,6 +351,12 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde
{
nativePath = String(StringUtils::GetDirectoryName(Platform::GetExecutableFilePath())) / StringUtils::GetFileName(nativePath);
}
+#elif PLATFORM_IOS
+ // iOS uses Frameworks folder with native binaries
+ if (!FileSystem::FileExists(nativePath))
+ {
+ nativePath = Globals::ProjectFolder / TEXT("Frameworks") / StringUtils::GetFileName(nativePath);
+ }
#endif
auto library = Platform::LoadLibrary(nativePath.Get());
if (!library)