From 4722a73e59cd335c8e6e906d7779c213ac1da122 Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:39:55 +0200 Subject: [PATCH 1/4] Build works on Unity 6.5+, but app is crashing at runtime because this link fix is skipped --- Samples~/Meet/Assets/Editor/BuildPostProcessor.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs b/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs index ec5d37ba..3bbaeba4 100644 --- a/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs +++ b/Samples~/Meet/Assets/Editor/BuildPostProcessor.cs @@ -23,9 +23,13 @@ public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuil string guid = proj.TargetGuidByName("Unity-iPhone"); #endif proj.AddBuildProperty(guid, "OTHER_LDFLAGS", "-ObjC"); + // Unity 6000.5+ no longer exports Libraries/libiPhone-lib.a. string fileGuid = proj.FindFileGuidByProjectPath("Libraries/libiPhone-lib.a"); - proj.RemoveFileFromBuild(guid, fileGuid); - proj.AddFileToBuild(guid, fileGuid); + if (fileGuid != null) + { + proj.RemoveFileFromBuild(guid, fileGuid); + proj.AddFileToBuild(guid, fileGuid); + } ApplyAutomaticSigning(proj); From 62fd749d250865ae54d9f4549d5e7e9c9b89d3ae Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 24 Jul 2026 15:13:52 +0200 Subject: [PATCH 2/4] Apply iOS CELT link-order fix to the Unity 6000.5 UnityRuntime.framework export Co-Authored-By: Claude Fable 5 --- Editor/IosLinkOrderDiagnostics.cs | 128 +++++++++++++++++++----------- README.md | 8 +- 2 files changed, 85 insertions(+), 51 deletions(-) diff --git a/Editor/IosLinkOrderDiagnostics.cs b/Editor/IosLinkOrderDiagnostics.cs index edc8e8e0..29c3f267 100644 --- a/Editor/IosLinkOrderDiagnostics.cs +++ b/Editor/IosLinkOrderDiagnostics.cs @@ -13,8 +13,14 @@ public static class IosLinkOrderDiagnostics { private const string Prefix = "LiveKit"; private const string Libilivekit = "liblivekit_ffi.a in Frameworks"; - private const string Libiphone = "libiPhone-lib.a in Frameworks"; - private static readonly string[] ConflictingLibiPhoneMembers = + // Unity exports the engine as Libraries/libiPhone-lib.a up to 6000.3 and as a static + // Frameworks/UnityRuntime.framework from 6000.5; both bundle the old CELT objects. + private static readonly string[] EngineLibraryEntries = + { + "libiPhone-lib.a in Frameworks", + "UnityRuntime.framework in Frameworks" + }; + private static readonly string[] ConflictingEngineArchiveMembers = { "bands.o", "celt.o", @@ -50,12 +56,14 @@ public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProj } var projectText = File.ReadAllText(projectPath); - var fixedProjectText = EnsureSafeUnityFrameworkLinkOrder(projectText, out var wasModified); + var engineEntry = EngineLibraryEntries.FirstOrDefault(entry => projectText.Contains(entry)); + var engineName = engineEntry?.Replace(" in Frameworks", string.Empty); + var fixedProjectText = EnsureSafeUnityFrameworkLinkOrder(projectText, engineEntry, out var wasModified); if (wasModified) { File.WriteAllText(projectPath, fixedProjectText); projectText = fixedProjectText; - Debug.Log($"{Prefix}: iOS link-order fix applied. Moved libiPhone-lib.a after liblivekit_ffi.a in UnityFramework -> Frameworks and Libraries."); + Debug.Log($"{Prefix}: iOS link-order fix applied. Moved {engineName} after liblivekit_ffi.a in UnityFramework -> Frameworks and Libraries."); } StripConflictingCodecObjects(pathToBuiltProject); @@ -68,64 +76,67 @@ public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProj } var livekitIndex = frameworkSection.IndexOf(Libilivekit, StringComparison.Ordinal); - var iphoneIndex = frameworkSection.IndexOf(Libiphone, StringComparison.Ordinal); - if (livekitIndex < 0 || iphoneIndex < 0) + var engineIndex = engineEntry == null ? -1 : frameworkSection.IndexOf(engineEntry, StringComparison.Ordinal); + if (livekitIndex < 0 || engineIndex < 0) { - Debug.LogWarning($"{Prefix}: iOS link-order diagnostic could not locate both {Libilivekit} and {Libiphone} in UnityFramework -> Frameworks and Libraries."); + Debug.LogWarning($"{Prefix}: iOS link-order diagnostic could not locate both {Libilivekit} and a Unity engine library in UnityFramework -> Frameworks and Libraries."); return; } - if (livekitIndex < iphoneIndex) + if (livekitIndex < engineIndex) { - Debug.Log($"{Prefix}: iOS link-order diagnostic OK. UnityFramework links liblivekit_ffi.a before libiPhone-lib.a."); + Debug.Log($"{Prefix}: iOS link-order diagnostic OK. UnityFramework links liblivekit_ffi.a before {engineName}."); return; } Debug.LogWarning( $"{Prefix}: iOS link-order diagnostic found a risky order in UnityFramework -> Frameworks and Libraries. " + - "libiPhone-lib.a appears before liblivekit_ffi.a. This repo documents that this can crash Opus/CELT on iOS. " + + $"{engineName} appears before liblivekit_ffi.a. This repo documents that this can crash Opus/CELT on iOS. " + "The post-process fix could not rewrite the exported project automatically." ); } - private static string EnsureSafeUnityFrameworkLinkOrder(string projectText, out bool wasModified) + private static string EnsureSafeUnityFrameworkLinkOrder(string projectText, string engineEntry, out bool wasModified) { wasModified = false; + if (engineEntry == null) + return projectText; + if (!TryGetUnityFrameworkSectionBounds(projectText, out var sectionStart, out var sectionEnd)) return projectText; var frameworkSection = projectText.Substring(sectionStart, sectionEnd - sectionStart); var livekitIndex = frameworkSection.IndexOf(Libilivekit, StringComparison.Ordinal); - var iphoneIndex = frameworkSection.IndexOf(Libiphone, StringComparison.Ordinal); - if (livekitIndex < 0 || iphoneIndex < 0 || livekitIndex < iphoneIndex) + var engineIndex = frameworkSection.IndexOf(engineEntry, StringComparison.Ordinal); + if (livekitIndex < 0 || engineIndex < 0 || livekitIndex < engineIndex) return projectText; - var iphoneLineStart = frameworkSection.LastIndexOf('\n', iphoneIndex); - if (iphoneLineStart < 0) - iphoneLineStart = 0; + var engineLineStart = frameworkSection.LastIndexOf('\n', engineIndex); + if (engineLineStart < 0) + engineLineStart = 0; else - iphoneLineStart += 1; + engineLineStart += 1; - var iphoneLineEnd = frameworkSection.IndexOf('\n', iphoneIndex); - if (iphoneLineEnd < 0) - iphoneLineEnd = frameworkSection.Length; + var engineLineEnd = frameworkSection.IndexOf('\n', engineIndex); + if (engineLineEnd < 0) + engineLineEnd = frameworkSection.Length; else - iphoneLineEnd += 1; + engineLineEnd += 1; - var iphoneLine = frameworkSection.Substring(iphoneLineStart, iphoneLineEnd - iphoneLineStart); - var sectionWithoutIphone = frameworkSection.Remove(iphoneLineStart, iphoneLineEnd - iphoneLineStart); + var engineLine = frameworkSection.Substring(engineLineStart, engineLineEnd - engineLineStart); + var sectionWithoutEngine = frameworkSection.Remove(engineLineStart, engineLineEnd - engineLineStart); - var livekitIndexAfterRemoval = sectionWithoutIphone.IndexOf(Libilivekit, StringComparison.Ordinal); + var livekitIndexAfterRemoval = sectionWithoutEngine.IndexOf(Libilivekit, StringComparison.Ordinal); if (livekitIndexAfterRemoval < 0) return projectText; - var insertIndex = sectionWithoutIphone.IndexOf('\n', livekitIndexAfterRemoval); + var insertIndex = sectionWithoutEngine.IndexOf('\n', livekitIndexAfterRemoval); if (insertIndex < 0) return projectText; insertIndex += 1; - var reorderedSection = sectionWithoutIphone.Insert(insertIndex, iphoneLine); + var reorderedSection = sectionWithoutEngine.Insert(insertIndex, engineLine); wasModified = !string.Equals(frameworkSection, reorderedSection, StringComparison.Ordinal); if (!wasModified) return projectText; @@ -146,36 +157,49 @@ private static bool TryGetUnityFrameworkSectionBounds(string projectText, out in sectionStart = -1; sectionEnd = -1; - const string marker = "/* UnityFramework */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;"; - var markerIndex = projectText.IndexOf(marker, StringComparison.Ordinal); - if (markerIndex < 0) - return false; + // The frameworks build phase is not labeled consistently across Unity versions, + // so identify it as the one that links liblivekit_ffi.a. + const string marker = "isa = PBXFrameworksBuildPhase;"; + var searchIndex = 0; + while (true) + { + var markerIndex = projectText.IndexOf(marker, searchIndex, StringComparison.Ordinal); + if (markerIndex < 0) + return false; - var filesIndex = projectText.IndexOf("\t\t\tfiles = (", markerIndex, StringComparison.Ordinal); - if (filesIndex < 0) - return false; + var filesIndex = projectText.IndexOf("\t\t\tfiles = (", markerIndex, StringComparison.Ordinal); + if (filesIndex < 0) + return false; - var endIndex = projectText.IndexOf("\t\t\t);", filesIndex, StringComparison.Ordinal); - if (endIndex < 0) - return false; + var endIndex = projectText.IndexOf("\t\t\t);", filesIndex, StringComparison.Ordinal); + if (endIndex < 0) + return false; - sectionStart = filesIndex; - sectionEnd = endIndex; - return true; + var section = projectText.Substring(filesIndex, endIndex - filesIndex); + if (section.Contains(Libilivekit)) + { + sectionStart = filesIndex; + sectionEnd = endIndex; + return true; + } + + searchIndex = markerIndex + marker.Length; + } } private static void StripConflictingCodecObjects(string pathToBuiltProject) { - var archivePath = Path.Combine(pathToBuiltProject, "Libraries", "libiPhone-lib.a"); - if (!File.Exists(archivePath)) + var archivePath = FindEngineArchivePath(pathToBuiltProject); + if (archivePath == null) { - Debug.Log($"{Prefix}: iOS archive fix skipped because {archivePath} was not found."); + Debug.Log($"{Prefix}: iOS archive fix skipped because no Unity engine archive was found in the exported project."); return; } + var archiveName = Path.GetFileName(archivePath); if (!TryRunProcess("/usr/bin/ar", $" -t \"{archivePath}\"", out var memberOutput, out var listError)) { - Debug.LogWarning($"{Prefix}: iOS archive fix could not inspect libiPhone-lib.a members. {listError}"); + Debug.LogWarning($"{Prefix}: iOS archive fix could not inspect {archiveName} members. {listError}"); return; } @@ -186,17 +210,17 @@ private static void StripConflictingCodecObjects(string pathToBuiltProject) StringComparer.Ordinal ); - var membersToRemove = ConflictingLibiPhoneMembers.Where(members.Contains).ToArray(); + var membersToRemove = ConflictingEngineArchiveMembers.Where(members.Contains).ToArray(); if (membersToRemove.Length == 0) { - Debug.Log($"{Prefix}: iOS archive fix found no conflicting CELT objects in libiPhone-lib.a."); + Debug.Log($"{Prefix}: iOS archive fix found no conflicting CELT objects in {archiveName}."); return; } var deleteArguments = $" -d \"{archivePath}\" {string.Join(" ", membersToRemove.Select(QuoteArgument))}"; if (!TryRunProcess("/usr/bin/ar", deleteArguments, out _, out var deleteError)) { - Debug.LogWarning($"{Prefix}: iOS archive fix could not strip conflicting objects from libiPhone-lib.a. {deleteError}"); + Debug.LogWarning($"{Prefix}: iOS archive fix could not strip conflicting objects from {archiveName}. {deleteError}"); return; } @@ -206,7 +230,17 @@ private static void StripConflictingCodecObjects(string pathToBuiltProject) return; } - Debug.Log($"{Prefix}: iOS archive fix stripped {membersToRemove.Length} conflicting CELT objects from exported libiPhone-lib.a."); + Debug.Log($"{Prefix}: iOS archive fix stripped {membersToRemove.Length} conflicting CELT objects from exported {archiveName}."); + } + + private static string FindEngineArchivePath(string pathToBuiltProject) + { + var candidates = new[] + { + Path.Combine(pathToBuiltProject, "Libraries", "libiPhone-lib.a"), + Path.Combine(pathToBuiltProject, "Frameworks", "UnityRuntime.framework", "UnityRuntime") + }; + return candidates.FirstOrDefault(File.Exists); } private static bool TryRunProcess(string fileName, string arguments, out string stdout, out string error) diff --git a/README.md b/README.md index ab065a3c..81af3049 100644 --- a/README.md +++ b/README.md @@ -112,13 +112,13 @@ add other linker flags to `UnityFramework`: `-ObjC` -Since `libiPhone-lib.a` has built-in old versions of `celt` and `libvpx` (This will cause the opus and vp8/vp9 codecs to not be called correctly and cause a crash.), you need to ensure that `liblivekit_ffi.a` is linked before `libiPhone-lib.a`. +Unity's engine library — exported as `Libraries/libiPhone-lib.a` up to Unity 6000.4 and as the static `Frameworks/UnityRuntime.framework` from Unity 6000.5 — has built-in old versions of `celt` and `libvpx` (This will cause the opus and vp8/vp9 codecs to not be called correctly and cause a crash.), so you need to ensure that `liblivekit_ffi.a` is linked before the engine library. -The package now applies an iOS post-build fix that rewrites the exported Xcode project so `libiPhone-lib.a` is moved after `liblivekit_ffi.a` in `UnityFramework -> Frameworks and Libraries`. +The package now applies an iOS post-build fix that rewrites the exported Xcode project so the engine library is moved after `liblivekit_ffi.a` in `UnityFramework -> Frameworks and Libraries`. -It also strips the old CELT object cluster from the exported `Libraries/libiPhone-lib.a` so Xcode cannot resolve those codec symbols from Unity's archive. +It also strips the old CELT object cluster from the exported engine archive so Xcode cannot resolve those codec symbols from Unity's archive. -If your project disables package editor scripts or uses a custom Xcode export pipeline that overwrites `project.pbxproj` after LiveKit runs, you may still need to adjust the order manually by removing and re-adding `libiPhone-lib.a`. +If your project disables package editor scripts or uses a custom Xcode export pipeline that overwrites `project.pbxproj` after LiveKit runs, you may still need to adjust the order manually by removing and re-adding the engine library. ## Examples From 29b2f29bdfa4ea83f0800633178ae32eaaef403d Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 24 Jul 2026 15:27:16 +0200 Subject: [PATCH 3/4] Guard against missing libiPhone-lib.a in Agents sample iOS post-build Co-Authored-By: Claude Fable 5 --- Samples~/Agents/Assets/Editor/BuildPostProcessor.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Samples~/Agents/Assets/Editor/BuildPostProcessor.cs b/Samples~/Agents/Assets/Editor/BuildPostProcessor.cs index 5b899176..c952a8b5 100644 --- a/Samples~/Agents/Assets/Editor/BuildPostProcessor.cs +++ b/Samples~/Agents/Assets/Editor/BuildPostProcessor.cs @@ -23,9 +23,13 @@ public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuil string guid = proj.TargetGuidByName("Unity-iPhone"); #endif proj.AddBuildProperty(guid, "OTHER_LDFLAGS", "-ObjC"); + // Unity 6000.5+ no longer exports Libraries/libiPhone-lib.a. string fileGuid = proj.FindFileGuidByProjectPath("Libraries/libiPhone-lib.a"); - proj.RemoveFileFromBuild(guid, fileGuid); - proj.AddFileToBuild(guid, fileGuid); + if (fileGuid != null) + { + proj.RemoveFileFromBuild(guid, fileGuid); + proj.AddFileToBuild(guid, fileGuid); + } proj.WriteToFile(projPath); } } From 2bc2eecadc71947fe24db946edfd529cc2c4650a Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 24 Jul 2026 15:30:38 +0200 Subject: [PATCH 4/4] Correct engine library version boundary comment to 6000.4 Co-Authored-By: Claude Fable 5 --- Editor/IosLinkOrderDiagnostics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/IosLinkOrderDiagnostics.cs b/Editor/IosLinkOrderDiagnostics.cs index 29c3f267..4cf74b55 100644 --- a/Editor/IosLinkOrderDiagnostics.cs +++ b/Editor/IosLinkOrderDiagnostics.cs @@ -13,7 +13,7 @@ public static class IosLinkOrderDiagnostics { private const string Prefix = "LiveKit"; private const string Libilivekit = "liblivekit_ffi.a in Frameworks"; - // Unity exports the engine as Libraries/libiPhone-lib.a up to 6000.3 and as a static + // Unity exports the engine as Libraries/libiPhone-lib.a up to 6000.4 and as a static // Frameworks/UnityRuntime.framework from 6000.5; both bundle the old CELT objects. private static readonly string[] EngineLibraryEntries = {