iOSActivityKit 1.0.0

dotnet add package iOSActivityKit --version 1.0.0
                    
NuGet\Install-Package iOSActivityKit -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="iOSActivityKit" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="iOSActivityKit" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="iOSActivityKit" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add iOSActivityKit --version 1.0.0
                    
#r "nuget: iOSActivityKit, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package iOSActivityKit@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=iOSActivityKit&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=iOSActivityKit&version=1.0.0
                    
Install as a Cake Tool

iOSActivityKit

Use iOS ActivityKit Live Activities and the Dynamic Island from a .NET MAUI / .NET for iOS app — something the MAUI/.NET-iOS SDK doesn't support out of the box.

It works by pairing a tiny @objc Swift control bridge (shipped prebuilt as an .xcframework) with a thin C# layer that calls it over objc_msgSend. State is a generic stringstring dictionary, so one prebuilt bridge works for any app — you only write your activity's SwiftUI.

var id = liveActivity.Start("sync", new Dictionary<string, string>
{
    ["progress"] = "0.0",
    ["phase"]    = "Preparing",
});

liveActivity.Update("sync", new Dictionary<string, string>
{
    ["progress"] = "0.42",
    ["phase"]    = "Downloading",
    ["message"]  = "12 MB of 30 MB",
});

liveActivity.End("sync",
    finalState: new Dictionary<string, string> { ["progress"] = "1.0", ["phase"] = "Done" },
    dismissAfter: TimeSpan.FromSeconds(5));

On Android / Windows / Mac Catalyst every call is a safe no-op, so you never need to wrap calls in platform checks.


How it fits together

  ┌──────────────── your MAUI app (C#) ─────────────────┐
  │  ILiveActivityService  ──►  LiveActivityServiceiOS   │
  │                              (objc_msgSend)          │
  └───────────────────────────────┬─────────────────────┘
                                   │  calls @objc LiveActivityBridge.shared
            ┌──────────────────────▼───────────────────────┐
            │  iOSActivityKitBridge.xcframework (prebuilt)  │  ← <NativeReference>
            │  start / update / end  on  ActivityKit        │
            └──────────────────────┬───────────────────────┘
                                   │  ActivityKit matches by type name
            ┌──────────────────────▼───────────────────────┐
            │  YOUR widget extension (.appex, SwiftUI)      │  ← <AdditionalAppExtensions>
            │  renders Lock Screen + Dynamic Island         │
            └───────────────────────────────────────────────┘

Both the bridge and your widget compile the same GenericActivityAttributes type (name "GenericActivityAttributes", a ContentState holding data: [String: String]). ActivityKit pairs a running activity with its UI by that type, which is why the prebuilt bridge and your hand-written widget find each other without you editing any Swift in the bridge.

Repository layout

Path Contents
src/iOSActivityKit/ The C# library → NuGet package iOSActivityKit.
native/shared/GenericActivityAttributes.swift The shared state contract (compiled into both the bridge and your widget).
native/bridge/ The @objc control bridge + build.sh that produces iOSActivityKitBridge.xcframework. A prebuilt copy lives in native/bridge/output/.
native/widget-template/ A copy-in widget extension you customize (your SwiftUI lives here).

Setup

1. Add the NuGet C# layer

<PackageReference Include="iOSActivityKit" Version="1.0.0" />

Register it (MAUI DI):

using iOSActivityKit;

builder.Services.AddLiveActivities();   // singleton ILiveActivityService

…then inject ILiveActivityService. Without DI, use LiveActivity.Current.

2. Reference the native bridge

Build it once (or use the prebuilt native/bridge/output/iOSActivityKitBridge.xcframework):

./native/bridge/build.sh

Then in your iOS .csproj:

<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">
  <NativeReference Include="path/to/iOSActivityKitBridge.xcframework">
    <Kind>Framework</Kind>
    <SmartLink>true</SmartLink>
  </NativeReference>
</ItemGroup>

3. Add your widget extension

The Live Activity UI must live in a widget extension. Copy native/widget-template/, customize SampleLiveActivity.swift, and embed it — full step-by-step in native/widget-template/README.md. Don't forget NSSupportsLiveActivities = true in your app's iOS Info.plist.

API

public interface ILiveActivityService
{
    bool    IsSupported { get; }
    string? Start (string name, IReadOnlyDictionary<string,string> state);
    void    Update(string name, IReadOnlyDictionary<string,string> state);
    void    End   (string name, IReadOnlyDictionary<string,string>? finalState = null,
                   TimeSpan dismissAfter = default);
    void    EndAll();
}
  • name groups updates/ends and lets your SwiftUI switch layouts via context.attributes.name. Run several distinct activities by using different names.
  • state values are plain strings — format numbers/dates yourself and parse them in SwiftUI (Double(context.state.data["progress"] ?? "")).

Requirements

  • iOS 16.2+ at runtime (Dynamic Island 16.1, update/end 16.2). Older devices and other platforms degrade to no-ops.
  • .NET 9 (net9.0-ios). The package also exposes a net9.0 no-op target so it restores cleanly for Android/Windows heads of a MAUI app.
  • Xcode (to build the widget extension and, if you rebuild it, the bridge).

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-ios18.0 is compatible.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 111 5/29/2026