There is a specific kind of dread reserved for the moment you update your mediation stack in Unity, generate the Xcode project, and hit Cmd+B, only to see the build fail instantly.
If you are integrating Smaato alongside other ad networks (specifically Verve, HyprMX, or InMobi) within the AppLovin MAX ecosystem, you have likely encountered the infamous Xcode build error:
error: Multiple commands produce '/Users/.../derivedData/Build/Products/Release-iphoneos/YourGame.app/OMIDSDK.bundle'
Or perhaps a variation involving PrivacyInfo.xcprivacy.
This is not a code logic error. It is a dependency collision that manual deletion will not permanently solve. This article details the root cause of this conflict and provides a production-grade, automated solution using Unity's IPostprocessBuildWithReport to patch your iOS build pipeline permanently.
The Root Cause: The Open Measurement SDK War
To understand the fix, you must understand the architecture. The error "Multiple commands produce" means that the Xcode build system has identified two distinct actions in the build phases that attempt to create a file at the exact same path in the application bundle.
The Shared Dependency
The culprit is almost always the IAB Open Measurement SDK (OM SDK). This is an industry-standard validation tool that allows third-party verification of ad viewability.
Both Smaato and Verve (among others) bundle the OM SDK to ensure their ads are measurable. However, they integrate it differently:
- Network A (e.g., Verve): Might include
OMIDSDK.bundleas a raw resource in their Pod. - Network B (e.g., Smaato): Might include
OMIDSDK.bundleembedded inside their.frameworkor as a resource spec in their Podspec.
When CocoaPods generates the Pods-Unity-iPhone project, it creates a "Copy Pods Resources" build phase. Because both SDKs claim ownership of OMIDSDK.bundle, CocoaPods (or the underlying Xcode build system) attempts to copy both files to the root of your app package. Xcode 14 and 15 strictly forbid this file overwriting.
The Manual Fix (For Verification Only)
Before implementing the automation, verify that this is indeed your issue by attempting a manual fix in Xcode.
- Open your generated Xcode project.
- Navigate to the Pods project (not your main Unity project).
- Select the Target corresponding to one of the conflicting networks (e.g.,
SmaatoSDKor the specific adapter pod). - Go to Build Phases -> Copy Bundle Resources.
- Locate
OMIDSDK.bundle. - Select it and hit Remove (-).
- Clean Build Folder (
Cmd+Shift+K) and Build (Cmd+B).
If the build succeeds, you have confirmed the collision. However, do not stop here. The next time you build from Unity, this change will be overwritten. You need an automated script.
The Permanent Fix: Automated Podfile Patching
The most robust way to solve this in a Unity/AppLovin environment is to intercept the CocoaPods generation process. We will create a Unity Editor script that injects a post_install hook into the Podfile.
This hook will iterate through the Pod targets and programmatically remove the resource reference from one of the SDKs before Xcode ever tries to build it.
Step 1: Create the Post-Processor
Create a file named iOSPodfilePatcher.cs inside your Unity project at Assets/Editor/iOSPodfilePatcher.cs.
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
public class iOSPodfilePatcher
{
// High priority to ensure this runs before the Podfile is executed by the resolver
[PostProcessBuild(45)]
public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath)
{
if (buildTarget != BuildTarget.iOS) return;
string podfilePath = Path.Combine(buildPath, "Podfile");
if (!File.Exists(podfilePath))
{
UnityEngine.Debug.LogWarning("[iOSPodfilePatcher] No Podfile found at " + podfilePath);
return;
}
string podfileContent = File.ReadAllText(podfilePath);
// Check if we've already patched it to avoid duplicate hooks
if (podfileContent.Contains("generated_duplicate_resource_fix"))
{
UnityEngine.Debug.Log("[iOSPodfilePatcher] Podfile already patched.");
return;
}
// The Ruby script to append to the Podfile
string rubyPatch = @"
# BEGIN: generated_duplicate_resource_fix
post_install do |installer|
installer.pods_project.targets.each do |target|
# Logic to handle OMIDSDK.bundle collision
if target.name == 'SmaatoSDK' || target.name.include?('Smaato')
puts ""[Fix] Processing #{target.name} for OMIDSDK conflicts...""
target.build_phases.each do |phase|
if phase.is_a?(Xcodeproj::Project::Object::PBXResourcesBuildPhase)
phase.files_references.each do |file_ref|
if file_ref.path.include? 'OMIDSDK.bundle'
puts ""[Fix] Removing OMIDSDK.bundle from #{target.name} to prevent 'Multiple commands produce' error.""
phase.remove_file_reference(file_ref)
end
end
end
end
end
end
end
# END: generated_duplicate_resource_fix
";
// If a post_install block already exists, this simplistic append might fail syntax.
// For standard AppLovin/Unity generated Podfiles, appending usually works
// because the MAX plugin often adds dependencies but leaves post_install open.
// Ideally, we append this at the very end.
File.AppendAllText(podfilePath, rubyPatch);
UnityEngine.Debug.Log("[iOSPodfilePatcher] Successfully patched Podfile to handle Smaato OMIDSDK collision.");
}
}
Step 2: Regenerate the Project
- In Unity, go to File > Build Settings.
- Build the iOS project (Append or Replace).
- Watch the Unity Console. You should see
[iOSPodfilePatcher] Successfully patched Podfile.... - Open the terminal in the build directory and run
pod install(or let Unity/External Dependency Manager do it automatically).
During the pod install phase in the terminal, you will now see the Ruby output: [Fix] Removing OMIDSDK.bundle from SmaatoSDK...
Deep Dive: Why This Code Works
The PostProcessBuild Attribute
We use [PostProcessBuild(45)]. The External Dependency Manager for Unity (EDM4U) typically runs its pod generation around priority 40-50. By timing this correctly, we modify the Podfile after Unity generates the skeleton but before the command line executes pod install.
The Ruby Script
CocoaPods is built on Ruby. The post_install hook gives us access to installer.pods_project. This is a direct reference to the .xcodeproj file that CocoaPods is generating.
- Iterate Targets: We loop through every pod target (Smaato, AppLovin, Verve, GoogleMobileAds, etc.).
- Identify Smaato: We specifically look for
target.name == 'SmaatoSDK'. We choose Smaato to strip because, in my experience, the Verve/HyprMX implementation is often more fragile regarding the OM SDK location, whereas Smaato continues to function correctly if it falls back to the bundle provided by the other network, provided the versions are compatible. - PBXResourcesBuildPhase: We find the build phase responsible for copying assets.
remove_file_reference: This is the key method from theXcodeprojruby gem. It cleanly detaches the file from the target without deleting the physical file from the disk, ensuring that checksums don't fail, but the copy command is removed from Xcode.
Edge Cases and Pitfalls
1. PrivacyInfo.xcprivacy Conflicts
Recently, Apple mandated privacy manifests. Similar to the OM SDK, you might get a "Multiple commands produce" error for PrivacyInfo.xcprivacy.
You can adapt the script above by changing the string check:
if file_ref.path.include? 'PrivacyInfo.xcprivacy'
# Logic to decide which one to keep
end
Note: Be careful removing privacy manifests. You must ensure the merged application still accurately reports privacy usage. Usually, CocoaPods attempts to merge these, but if it fails, manual intervention via this script logic is required.
2. Existing post_install Hooks
If you use other plugins that heavily modify the Podfile (like certain notifications plugins), simply appending post_install at the end might create a Podfile with two post_install blocks, which is invalid Ruby syntax.
If your Podfile is complex, you should modify the C# script to Regex replace the existing post_install block or insert your logic inside the existing block rather than appending a new one.
3. "Library not found"
If you remove the bundle and the build fails with a linker error (symbol not found) rather than a resource error, it means the adapter was linking against binary code inside that bundle. This is rare for OMIDSDK.bundle (which is usually assets/JS), but if it happens, you must keep the file reference and instead rename the input file in one of the pods—a much more complex operation.
Conclusion
The "Multiple commands produce" error is a rite of passage for mobile developers dealing with mediation. While it looks intimidating, it is simply Xcode protecting you from indeterminate behavior (which file should be used?).
By using the iOSPodfilePatcher script provided above, you move the fix from a manual, error-prone step into your automated build pipeline. This ensures that every build—whether on your local machine or a CI/CD server—compiles flawlessly, allowing you to focus on revenue optimization rather than build errors.