Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a361954
Fix #60 (#74)
ryzngard Dec 11, 2025
1fd5b43
fix: UnityPickers.AssetPickerDrawer throws exception when fieldInfo i…
ilterbilguven Feb 5, 2025
9477dd7
fix: UnityPickers.AssetPickerDrawer throws exception when assetType i…
ilterbilguven Feb 5, 2025
4a7481e
Accessibility: Audio Descriptions support
Feb 2, 2026
e7c5a78
Accessibility: Audio Descriptions support (#89)
Feb 18, 2026
f883ce2
rdar://172239633 (Plug-In Builds Fail When Source Root Has Space Char…
Mar 11, 2026
199e6dc
rdar://172239633 (Plug-In Builds Fail When Source Root Has Space Char…
ryzngard Mar 11, 2026
97d9489
rdar://166326954 ([Unity Plug-ins] Add unity API for triggerForArcade…
ryzngard Mar 19, 2026
ceb79cc
fix: move @available attribute to extension in GKGameActivityDelegate…
ryzngard Apr 1, 2026
10569f5
rdar://170262914 add [MarshalAs(UnmanagedType.I1)] to bool DllImport …
ryzngard Apr 23, 2026
0dd7790
Update gitignore for AI
ryzngard May 28, 2026
26a62d3
Merge branch 'main' of https://github.com/apple/unityplugins into mai…
ryzngard May 28, 2026
ce6a8bb
Prepare for Xcode 27
ryzngard Jun 5, 2026
1c0cedb
Add a StoreKit plug-in (#105)
ryzngard Jun 8, 2026
7d4b1eb
Add Background Assets plug-in (#104)
Jun 8, 2026
ab84011
Consolidate markdown files and remove unneeded Scenes
ryzngard Jun 9, 2026
7890606
Update documentation to use correct code and make sure to callout how…
ryzngard Jun 9, 2026
c931d89
Fix -o flag not being respected when packing plug-in packages (#96)
ryzngard Jun 10, 2026
e6a5eec
Apple spatial controller - Stylus Support & Renaming (#84)
JScanlin-Apple Jun 10, 2026
b12a58a
Update README and QuickStart. BackgroundAssets will be added to quick…
ryzngard Jun 10, 2026
f9e24bd
Merge branch 'main_internal' of https://github.pie.apple.com/Apple-Un…
ryzngard Jun 10, 2026
81759e3
Add Background Assets plug-in to README file (#106)
Jun 11, 2026
d7f55e9
Remove unused command infrastructure
ryzngard Jun 12, 2026
6afefa5
- Update PHASEWrapper for engine rendering mode switch, world transfo…
cheesymoo Jun 22, 2026
b2e8857
Apple.StoreKit: add missing StoreKit API surface (#112)
ryzngard Aug 12, 2026
58cba0c
Fix Transaction.BeginRefundRequest native/managed callback mismatch (…
ryzngard Aug 12, 2026
5cd1c11
Remove GameKit GKLocalPlayer_RegisterLocalPlayerListener
antja0 Aug 20, 2026
4254fc0
Merge pull request #106 from antja0/remove-gamekit-register-local-pla…
ryzngard Sep 2, 2026
c464d8e
Escalate to error when both Debug and Release native libraries are mi…
ryzngard Sep 9, 2026
92b6536
Merge gh main
ryzngard Sep 9, 2026
c8daa5a
Merge branch '27-beta' into main-27-beta
ryzngard Sep 10, 2026
493cfc7
Merge pull request #112 from apple/main-27-beta
ryzngard Sep 10, 2026
88b7785
Merge branch '27-beta' into main_internal
ryzngard Sep 10, 2026
8e83441
Update for Unity 6 deprecations (#113)
ryzngard Sep 14, 2026
a8c2e67
Bump plug-in versions and update changelogs for the 27.0 release (#109)
ryzngard Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 17 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
# AI
.claude
.etabli

# Build Output
Build/
TestBuilds/
scripts/python/__pycache__/
**/.DS_Store
**/.idea/*
**/Temp/*
plug-ins/**/TestPlayers
**/Apple Plug-In Support/
**/Apple Plug-In Support.meta
**/NativeLibraries~/

# VS Code/Xcode
**/.vsconfig
**/xcuserdata
*.vsconfig
**/.vscode/*
**/Apple Plug-In Support/
**/Apple Plug-In Support.meta
**/NativeLibraries~/

# IDE-generated solution/project files (regenerated by Unity's IDE integration)
*.sln
*.slnx
*.csproj


2 changes: 2 additions & 0 deletions Documentation/Quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ The first step is to download or clone the [Apple-Unity Plug-ins repository](htt
| Apple.GameController | [Apple.GameController Documentation](../plug-ins/Apple.GameController/Apple.GameController_Unity/Assets/Apple.GameController/Documentation~/Apple.GameController.md) |
| Apple.GameKit | [Apple.GameKit Documentation](../plug-ins/Apple.GameKit/Apple.GameKit_Unity/Assets/Apple.GameKit/Documentation~/Apple.GameKit.md) |
| Apple.PHASE | [Apple.PHASE Documentation](../plug-ins/Apple.PHASE/Apple.PHASE_Unity/Assets/Documentation~/Apple.PHASE.md) |
| Apple.SpatialController | [Apple.SpatialController Documentation](../plug-ins/Apple.SpatialController/README.md) |
| Apple.StoreKit | [Apple.StoreKit Documentation](../plug-ins/Apple.StoreKit/Apple.StoreKit_Unity/Assets/Apple.StoreKit/Documentation~/Apple.StoreKit.md) |

## 2. Build
After downloading the plug-in(s), you will need to build the native libraries. If you are familiar with Unity plug-ins, these are compiled assets found in a Unity project's `Assets/.../Plugins/{iOS, macOS, tvOS}/` directories. Along with some C# bridging scripts, they enable communication with Apple devices' native technologies.
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# Apple Unity Plug-Ins

## 27 Beta

27 beta is now live! Watch the [WWDC26 video](https://developer.apple.com/videos/play/wwdc2026/378/) for information and check out the `27-beta` branch to test it out.

## Overview
The Apple Unity Plug-Ins expose a selection of Apple platform frameworks to Unity developers.
Expand All @@ -16,17 +13,19 @@ To get started with integration of these plug-ins into your Unity projects, run
| :------ | :---------- |
| Apple.Core | Provides integrated build post process management and Editor UI.<br/>**Note:** Apple.Core is a dependency of all Apple Unity plug-ins.|
| Apple.Accessibility | Provides Apple's accessibility to Unity developers allowing adding supports to Apple's built-in assistive technologies such as VoiceOver.|
| Apple.BackgroundAssets | Exposes Apple’s Background Assets framework to Unity developers, enabling out-of-band delivery of asset packs from Apple or third-party servers. The plug-in also automatically configures generated Xcode projects with a downloader extension and everything else that’s necessary to use Background Assets. |
| Apple.CoreHaptics | Brings Apple's Core Haptics framework to Unity developers, enabling for customizable haptic patterns and in-depth playback control on supported devices. This Plug-In also includes UIKit's UIFeedbackGenerator API.|
| Apple.GameController | Exposes Apple's GameController framework to Unity developers allowing for rich controller features in macOS, iOS, and tvOS apps. |
| Apple.GameKit | Allows Unity developers to easily integrate GameKit features such as leaderboards, achievements, and match making. |
| Apple.PHASE | The PHASE plug-in allows Unity developers to take full advantage of Apple's new geometry and material aware spatial audio system. |
| Apple.SpatialController | Exposes Apple's AccessoryTracking and GameController frameworks to Unity developers on visionOS allowing for spatial controller features in visionOS apps. Requires visionOS 26.0 |
| Apple.StoreKit | Exposes Apple's StoreKit 2 framework to Unity developers, enabling in-app purchases, subscriptions, and transaction management with modern async/await patterns. |

## Minimum Supported OS Versions
| OS | Version |
| :- | :------ |
| iOS | 15.6 |
| macOS | 11.5 |
| macOS | 12.0 |
| tvOS | 15.6 |
| visionOS | 1.3 |
| watchOS | not supported |
Expand Down
24 changes: 13 additions & 11 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# Handle command line args

argument_parser = argparse.ArgumentParser(description="Builds all native libraries, packages plug-ins, and moves packages to build folder.")
argument_parser.add_argument("-p", "--plugin-list", dest="plugin_list", nargs='*', default=[PluginID.ALL], help=f"Selects the plug-ins to process. Possible values are: {PluginID.ACCESSIBILITY}, {PluginID.CORE}, {PluginID.CORE_HAPTICS}, {PluginID.GAME_CONTROLLER}, {PluginID.SPATIAL_CONTROLLER}, {PluginID.GAME_KIT}, {PluginID.PHASE}, or {PluginID.ALL}. Default is: {PluginID.ALL}")
argument_parser.add_argument("-p", "--plugin-list", dest="plugin_list", nargs='*', default=[PluginID.ALL], help=f"Selects the plug-ins to process. Possible values are: {PluginID.ACCESSIBILITY}, {PluginID.CORE}, {PluginID.CORE_HAPTICS}, {PluginID.GAME_CONTROLLER}, {PluginID.SPATIAL_CONTROLLER}, {PluginID.GAME_KIT}, {PluginID.PHASE}, {PluginID.STORE_KIT}, {PluginID.BACKGROUND_ASSETS}, or {PluginID.ALL}. Default is: {PluginID.ALL}")
argument_parser.add_argument("-m", "--platforms", dest="platform_list", nargs='*', default=[PlatformID.ALL], help=f"Selects the desired platforms to target when building native libraries. Possible values are: {PlatformID.IOS}, {PlatformID.IOS_SIMULATOR}, {PlatformID.MACOS}, {PlatformID.TVOS}, {PlatformID.TVOS_SIMULATOR}, {PlatformID.VISIONOS}, {PlatformID.VISIONOS_SIMULATOR}, {PlatformID.SIMULATORS}, {PlatformID.DEVICES} or {PlatformID.ALL}. Default is: {PlatformID.ALL}")
argument_parser.add_argument("-b", "--build-action", dest="build_actions", nargs='*', default=[BuildActionID.BUILD, BuildActionID.PACK], help=f"Sets the build actions for the selected plug-ins. Possible values are: {BuildActionID.BUILD}, {BuildActionID.PACK}, {BuildActionID.NONE} or {BuildActionID.ALL}. Defaults are: {BuildActionID.BUILD}, {BuildActionID.PACK}")
argument_parser.add_argument("-bc","--build-config", dest="build_config", default=ConfigID.ALL, help=f"Sets the build configuration to compile. Possible values are: {ConfigID.RELEASE}, {ConfigID.DEBUG}, or {ConfigID.ALL} which builds all other configs. Default is: {ConfigID.ALL}")
Expand Down Expand Up @@ -186,7 +186,7 @@ def Main():
break
elif platform_id in CTX.platforms:
if platform_id in supported_platforms:
valid_platform_found = True
valid_platform_found = True
CTX.platforms[platform_id] = True
else:
CTX.printer.WarningMessage(f"Valid platform '{platform_id}' selected, but no {platform_id} SDK is installed.\nPlease add the SDK:\n {Printer.Bold('1.')} Open {Printer.Bold('Xcode')}\n {Printer.Bold('2.')} Open {Printer.Bold('Settings')} (Xcode > Settings...) or (⌘ + ,) \n {Printer.Bold('3.')} Go to the {Printer.Bold('Platforms')} tab\n {Printer.Bold('4.')} Install the {platform_id} SDK.")
Expand Down Expand Up @@ -235,7 +235,9 @@ def Main():
PluginID.GAME_CONTROLLER: False,
PluginID.SPATIAL_CONTROLLER: False,
PluginID.GAME_KIT: False,
PluginID.PHASE: False
PluginID.PHASE: False,
PluginID.STORE_KIT: False,
PluginID.BACKGROUND_ASSETS: False
}

valid_plugin_found = False
Expand All @@ -249,7 +251,7 @@ def Main():
valid_plugin_found = True
break
else:
utility.WarningMessage(f"Ignoring unknown plug-in '{plugin_id}'. Valid options are {PluginID.ACCESSIBILITY}, {PluginID.CORE}, {PluginID.CORE_HAPTICS}, {PluginID.GAME_CONTROLLER}, {PluginID.SPATIAL_CONTROLLER}, {PluginID.GAME_KIT}, {PluginID.PHASE}, or {PluginID.ALL} (Default)")
CTX.printer.WarningMessage(f"Ignoring unknown plug-in '{plugin_id}'. Valid options are {PluginID.ACCESSIBILITY}, {PluginID.CORE}, {PluginID.CORE_HAPTICS}, {PluginID.GAME_CONTROLLER}, {PluginID.SPATIAL_CONTROLLER}, {PluginID.GAME_KIT}, {PluginID.PHASE}, {PluginID.STORE_KIT}, {PluginID.BACKGROUND_ASSETS}, or {PluginID.ALL} (Default)")

# -------------------------------------------------------------------------

Expand Down Expand Up @@ -312,18 +314,18 @@ def Main():
CTX.printer.SectionHeading("Configure Build Paths")

# Configure build paths for packages
CTX.build_path = pathlib.Path(build_args.output_path)
CTX.build_output_path = pathlib.Path(build_args.output_path)

if CTX.clean_actions[CleanActionID.PACKAGES] and CTX.build_path.exists():
if CTX.clean_actions[CleanActionID.PACKAGES] and CTX.build_output_path.exists():
CTX.printer.StatusMessage("Cleaning packages.", "\n")
CTX.printer.StatusMessageWithContext("Removing folder at path:", f"{CTX.build_path}")
utility.RemoveFolder(CTX.build_path, prompt= not build_args.force_clean, printer= CTX.printer)
CTX.printer.StatusMessageWithContext("Removing folder at path:", f"{CTX.build_output_path}")
utility.RemoveFolder(CTX.build_output_path, prompt= not build_args.force_clean, printer= CTX.printer)

if CTX.build_actions[BuildActionID.BUILD] or CTX.build_actions[BuildActionID.PACK]:
if not CTX.build_path.exists():
if not CTX.build_output_path.exists():
CTX.printer.Message(f"Build output path not found.", "\n")
CTX.printer.StatusMessageWithContext("Creating: ", f"{CTX.build_path}")
CTX.build_path.mkdir()
CTX.printer.StatusMessageWithContext("Creating: ", f"{CTX.build_output_path}")
CTX.build_output_path.mkdir()

# Configure and optionally clean paths for test builds
test_build_root_path = pathlib.Path(build_args.test_output_path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Apple.Core;
using UnityEngine;

namespace Apple.Accessibility
Expand Down Expand Up @@ -388,60 +389,36 @@ private void OnDestroy()
UnregisterAXElement(this);
}

private static Dictionary<ulong, AccessibilityNode> axElements = new Dictionary<ulong, AccessibilityNode>();
private static Dictionary<ulong, AccessibilityNode> axElements = new();

static internal void RegisterAXElement(AccessibilityNode obj)
{
#if UNITY_6000_4_OR_NEWER
if (axElements.ContainsKey(EntityId.ToULong(obj.gameObject.GetEntityId())))
#else
if (axElements.ContainsKey((ulong)obj.gameObject.GetInstanceID()))
#endif
if (axElements.ContainsKey(obj.gameObject.GetLongId()))
{
return;
}

#if UNITY_6000_4_OR_NEWER
axElements.Add(EntityId.ToULong(obj.gameObject.GetEntityId()), obj);
AccessibilityNode parent = obj._accessibilityParent();
ulong parentId = EntityId.ToULong(EntityId.None);
#else
axElements.Add((ulong)obj.gameObject.GetInstanceID(), obj);
axElements.Add(obj.gameObject.GetLongId(), obj);
AccessibilityNode parent = obj._accessibilityParent();
ulong parentId = 0;
#endif

// Use Unity's lifetime-aware null check, not C#'s: a destroyed-but-not-collected
// parent is non-null to `?.` but null to `parent != null` below, which would leave
// us reporting a live parentId alongside hasParent: false.
ulong parentId = 0;
if (parent)
{
#if UNITY_6000_4_OR_NEWER
parentId = EntityId.ToULong(parent.gameObject.GetEntityId());
#else
parentId = (ulong)parent.gameObject.GetInstanceID();
#endif
parentId = parent.gameObject.GetLongId();
}

#if (UNITY_IOS || UNITY_TVOS) && !UNITY_EDITOR
#if UNITY_6000_4_OR_NEWER
_UnityAX_RegisterElementWithIdentifier(EntityId.ToULong(obj.gameObject.GetEntityId()), parentId, parent != null);
#else
_UnityAX_RegisterElementWithIdentifier((ulong)obj.gameObject.GetInstanceID(), parentId, parent != null);
#endif
_UnityAX_RegisterElementWithIdentifier(obj.gameObject.GetLongId(), parentId, parent != null);
#endif
}

static internal void UnregisterAXElement(AccessibilityNode obj)
{
#if UNITY_6000_4_OR_NEWER
axElements.Remove(EntityId.ToULong(obj.gameObject.GetEntityId()));
#else
axElements.Remove((ulong)obj.gameObject.GetInstanceID());
#endif
axElements.Remove(obj.gameObject.GetLongId());
#if (UNITY_IOS || UNITY_TVOS) && !UNITY_EDITOR
#if UNITY_6000_4_OR_NEWER
_UnityAX_UnregisterElementWithIdentifier(EntityId.ToULong(obj.gameObject.GetEntityId()));
#else
_UnityAX_UnregisterElementWithIdentifier((ulong)obj.gameObject.GetInstanceID());
#endif
_UnityAX_UnregisterElementWithIdentifier(obj.gameObject.GetLongId());
#endif
}

Expand Down Expand Up @@ -897,7 +874,7 @@ private static void RegisterCallbacks()

#if (UNITY_IOS || UNITY_TVOS) && !UNITY_EDITOR
[DllImport("__Internal")] private static extern void _UnityAX_InitializeAXRuntime();
[DllImport("__Internal")] private static extern void _UnityAX_RegisterElementWithIdentifier(ulong identifier, ulong parentIdentifier, bool hasParent);
[DllImport("__Internal")] private static extern void _UnityAX_RegisterElementWithIdentifier(ulong identifier, ulong parentIdentifier, [MarshalAs(UnmanagedType.I1)] bool hasParent);
[DllImport("__Internal")] private static extern void _UnityAX_UnregisterElementWithIdentifier(ulong identifier);

private delegate string AccessibilityFrameDelegate(ulong identifier);
Expand Down
Loading