Migrate your existing Unity iOS project from the Objective-C project type to the new Swift project type.
Consider the following before migrating to a Swift Xcode project type:
Use the following steps to create a Swift Xcode project type:
If you have post-process scripts that modify Xcode project files, you must update them to support changes to the project structure.
The Xcode project file now uses a sanitized version of your Unity project’s name, rather than the static name Unity-iPhone.xcodeproj. To return the correct project path, use the PBXProject.GetPBXProjectPath method.
For example, update your code as follows:
// Before (Objective-C only):
string projPath = Path.Combine(pathToBuiltProject, "Unity-iPhone.xcodeproj/project.pbxproj");
// After (works for both Objective-C and Swift project types):
string projPath = PBXProject.GetPBXProjectPath(pathToBuiltProject);
Review your existing modifications to the .xcodeproj file to ensure they’re still required. For more information, refer to Structure of a Unity Xcode Swift project type.
In the Swift Xcode project type, the Info.plist file for the main application target is located at MainApp/Info.plist, rather than at the project root as in the Objective-C project type. If your post-process scripts modify Info.plist using a hardcoded path, read the INFOPLIST_FILE build setting from the project instead.
For example, update your code as follows:
// Before (hardcoded path):
string plistPath = Path.Combine(pathToBuiltProject, "Info.plist");
// After (reads the path from the Xcode project build settings):
PBXProject project = new PBXProject();
project.ReadFromFile(PBXProject.GetPBXProjectPath(pathToBuiltProject));
string mainTargetGuid = project.GetUnityMainTargetGuid();
string plistPath = Path.Combine(pathToBuiltProject,
project.GetBuildPropertyForAnyConfig(mainTargetGuid, "INFOPLIST_FILE"));
To identify project types in your post-process scripts, use the UnityEditor.PlayerSettings.xcodeProjectType property. For example:
if (UnityEditor.PlayerSettings.xcodeProjectType == UnityEditor.XcodeProjectType.Swift) {
// Swift project type specific modifications
} else {
// Objective-C project type specific modifications
}
With the introduction of the Swift project type, you must adapt your native plug-ins to work with the new UnityFramework and the updated Xcode project structure. This migration involves updating API calls, handling lifecycle events differently, and ensuring your plug-ins are correctly configured in the Unity Editor.
Ensure your plug-in is set to be included in the correct Xcode project type. By default, plug-ins are included in both the Objective-C and Swift project types. In the Unity Editor, open the Plugin Inspector for your native plug-in and review its platform settings to confirm the correct compatibility. For more information, refer to Configure a plug-in for iOS with the Inspector window.
The contents of UnityFramework are different when using a Swift Xcode project type. Review your plug-in’s dependencies and update any code that relies on functions or classes that are no longer available.
To access the replacement APIs, import the UnityAPI framework at the top of each source file.
// Swift
import UnityAPI
// Objective-C
#import <UnityAPI/UnityAPI-Swift.h>
The UnityPlayer class now provides most of the functionality previously available through UnityAppController.
Note: UnityPlayer isn’t a direct substitute, so it’s recommended to review the Swift project type API reference for details on its specific methods and properties.
// Before:
UnityAppController* appController = GetAppController();
// After (Swift):
let player = UnityPlayer.shared
// After (Objective-C):
UnityPlayer *player = [UnityPlayer shared];
To access the main rendering view and root view controller, use the properties on the UnityPlayer instance.
// Before:
UIView* unityView = GetAppController().unityView;
UIView* unityView = UnityGetUnityView();
UIViewController* rootVC = GetAppController().rootViewController;
UIViewController* rootVC = UnityGetGLViewController();
// After (Swift):
let unityView = UnityPlayer.shared.renderingView
let rootVC = UnityPlayer.shared.rootViewController
// After (Objective-C):
UIView *unityView = [[UnityPlayer shared] renderingView];
UIViewController *rootVC = [[UnityPlayer shared] rootViewController];
Pausing and resuming the application is now handled through methods on the UnityPlayer instance. For example:
// Before:
if (!UnityIsPaused()) {
UnityPause(1); // pause
}
UnityPause(0); // resume
// After (Swift):
if (!UnityPlayer.shared.isPaused()) {
UnityPlayer.shared.pause()
}
UnityPlayer.shared.resume()
// After (Objective-C):
if (![[UnityPlayer shared] isPaused]) {
[[UnityPlayer shared] pause];
}
[[UnityPlayer shared] resume];
If your plug-in previously depended on LifeCycleListener.h or AppDelegateListener, you must migrate to using NotificationCenter to handle lifecycle events. The event definitions are now located in UnityAPI/UnityPluginInterface/UnityNotifications.swift.
// Before (using LifeCycleListener protocol):
@interface MyPlugin : NSObject<AppDelegateListener>
@end
- (void)applicationWillFinishLaunchingWithOptions:(NSNotification*)notification { ... }
// After (Swift):
NotificationCenter.default.addObserver(
forName: UnityNotifications.applicationWillFinishLaunching,
object: nil, queue: nil
) { _ in /* handle */ }
// After (Objective-C):
[[NSNotificationCenter defaultCenter]
addObserverForName:UnityNotifications.applicationWillFinishLaunching
object:nil queue:nil usingBlock:^(NSNotification *note) { /* handle */ }];
Available notifications include:
applicationWillFinishLaunchingapplicationDidRegisterForRemoteNotificationsapplicationDidFailToRegisterForRemoteNotificationsapplicationDidReceiveRemoteNotificationunityDidQuitunityDidUnloadapplicationHandleEventsForBackgroundURLSessionsceneOpenURLContextsUIApplication.didBecomeActiveNotificationUIApplication.didFinishLaunchingNotificationNote: To prevent duplicate notifications, Unity filters out certain lifecycle and app delegate events that iOS already provides, such as didFinishLaunchingNotification.
For the Swift project type, the recommended way to call into C# script is by using the UnityPlayer.sendMessage() method. The UnitySendMessage() C function is still available but now requires you to import UnityPluginInterface.h.
// Before (C/C++/Objective-C):
UnitySendMessage("GameObjectName", "MethodName", "argument");
// After (Swift):
UnityPlayer.shared.sendMessage(toGameObject: "GameObjectName", method: "MethodName", argument: "argument")
UnitySendMessage("GameObjectName", "MethodName", "argument")
// After (Objective-C):
#import "UnityPluginInterface.h"
#import <UnityFramework/UnityFramework-Swift.h>
[[UnityPlayer shared] sendMessageToGameObject:@"GameObjectName" method:@"MethodName" argument:@"argument"];
UnitySendMessage("GameObjectName", "MethodName", "argument");
// C/C++ still works (requires #import "UnityPluginInterface.h"):
UnitySendMessage("GameObjectName", "MethodName", "argument");
If you need to support both Xcode project types within the same plug-in source file, use the UNITY_XCODE_PROJECT_TYPE_SWIFT preprocessor macro to create different code paths.
#ifdef UNITY_XCODE_PROJECT_TYPE_SWIFT
UnityPlayer *player = [UnityPlayer shared];
#else
UnityAppController* appController = GetAppController();
#endif
The low-level native plug-in API remains the same, but the location of the header files has changed. The headers are now located in the UnityAPI/UnityPluginInterface/ directory.
However, you can continue to import them in your code using the same syntax as before.