diff --git a/.github/workflows/dev-packages.yml b/.github/workflows/dev-packages.yml
index 424d0e8a..47a8b951 100644
--- a/.github/workflows/dev-packages.yml
+++ b/.github/workflows/dev-packages.yml
@@ -50,20 +50,6 @@ jobs:
- name: Run Pack For Common
run: dotnet pack PowerSync/PowerSync.Common -c Release -o ${{ github.workspace }}/output
- - name: Extract MAUI Package Version from CHANGELOG.md
- shell: bash
- run: |
- MAUI_VERSION=$(awk '/^## [0-9]+\.[0-9]+\.[0-9]+-dev(\.[0-9]+)?$/ {print $2; exit}' PowerSync/PowerSync.Maui/CHANGELOG.md)
- if [[ -z "$MAUI_VERSION" ]]; then
- echo "Error: Invalid dev version found in PowerSync.Maui/CHANGELOG.md. Expected format: x.x.x-dev.x"
- exit 1
- fi
- echo "Detected Version: $MAUI_VERSION"
- echo "VERSION=$MAUI_VERSION" >> $GITHUB_ENV
-
- - name: Run Pack For MAUI
- run: dotnet pack PowerSync/PowerSync.Maui -c Release -o ${{ github.workspace }}/output
-
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
@@ -158,9 +144,3 @@ jobs:
dotnet nuget push "${{ github.workspace }}\SignedArtifacts\PowerSync.Common*.nupkg"
--api-key "${{ steps.login.outputs.NUGET_API_KEY }}"
--source https://api.nuget.org/v3/index.json
-
- - name: Run Push For Maui
- run: >
- dotnet nuget push "${{ github.workspace }}\SignedArtifacts\PowerSync.Maui*.nupkg"
- --api-key "${{ steps.login.outputs.NUGET_API_KEY }}"
- --source https://api.nuget.org/v3/index.json
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 8847ae65..5cb2ea42 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -15,7 +15,6 @@ jobs:
outputs:
common_version: ${{ steps.extract_version.outputs.common_version }}
- maui_version: ${{ steps.extract_maui_version.outputs.maui_version }}
steps:
- name: Checkout Repository
@@ -56,22 +55,6 @@ jobs:
- name: Run Pack For Common
run: dotnet pack PowerSync/PowerSync.Common -c Release -o ${{ github.workspace }}/output
- - name: Extract MAUI Package Version from CHANGELOG.md
- id: extract_maui_version
- shell: bash
- run: |
- MAUI_VERSION=$(awk '/^## [0-9]+\.[0-9]+\.[0-9]+/ {print $2; exit}' PowerSync/PowerSync.Maui/CHANGELOG.md)
- if [[ -z "$MAUI_VERSION" ]]; then
- echo "Error: Invalid version found in PowerSync.Maui/CHANGELOG.md. Expected format: '## x.x.x'"
- exit 1
- fi
- echo "Detected Version: $MAUI_VERSION"
- echo "VERSION=$MAUI_VERSION" >> $GITHUB_ENV
- echo "maui_version=$MAUI_VERSION" >> $GITHUB_OUTPUT
-
- - name: Run Pack For MAUI
- run: dotnet pack PowerSync/PowerSync.Maui -c Release -o ${{ github.workspace }}/output
-
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
@@ -182,23 +165,3 @@ jobs:
--generate-notes \
SignedArtifacts/PowerSync.Common*.nupkg
- - name: Run Push For Maui
- run: >
- dotnet nuget push "${{ github.workspace }}\SignedArtifacts\PowerSync.Maui*.nupkg"
- --api-key "${{ steps.login.outputs.NUGET_API_KEY }}"
- --source https://api.nuget.org/v3/index.json
-
- - name: Create GitHub Release For Maui
- continue-on-error: true
- shell: bash
- env:
- GH_TOKEN: ${{ github.token }}
- GH_REPO: ${{ github.repository }}
- MAUI_VERSION: ${{ needs.build.outputs.maui_version }}
- run: |
- gh release create "PowerSync.Maui@${MAUI_VERSION}" \
- --draft \
- --title "PowerSync.Maui@${MAUI_VERSION}" \
- --target main \
- --generate-notes \
- SignedArtifacts/PowerSync.Maui*.nupkg
diff --git a/PowerSync/PowerSync.Common/CHANGELOG.md b/PowerSync/PowerSync.Common/CHANGELOG.md
index 60a52328..4474ba64 100644
--- a/PowerSync/PowerSync.Common/CHANGELOG.md
+++ b/PowerSync/PowerSync.Common/CHANGELOG.md
@@ -1,5 +1,11 @@
# PowerSync.Common Changelog
+## Unreleased
+
+- **Breaking:** `PowerSync.Maui` has been folded into `PowerSync.Common` and is no longer published. Mobile and desktop targets are now served by a single package.
+ - Remove the `PowerSync.Maui` package reference; `PowerSync.Common` alone is sufficient on all platforms.
+ - Replace `MAUISQLiteDBOpenFactory` with `MDSQLiteDBOpenFactory` (from `PowerSync.Common.MDSQLite`) and drop the `using PowerSync.Maui.SQLite;` import. `MAUISQLiteAdapter` is likewise replaced by `MDSQLiteAdapter`, which now selects the correct native extension per platform.
+
## 0.1.4
- Update the PowerSync SQLite core extension to 0.5.2.
diff --git a/PowerSync/PowerSync.Common/MDSQLite/MDSQLiteAdapter.cs b/PowerSync/PowerSync.Common/MDSQLite/MDSQLiteAdapter.cs
index cdb4ec9e..8f4e0755 100644
--- a/PowerSync/PowerSync.Common/MDSQLite/MDSQLiteAdapter.cs
+++ b/PowerSync/PowerSync.Common/MDSQLite/MDSQLiteAdapter.cs
@@ -153,16 +153,41 @@ protected virtual void LoadExtensions(SqliteConnection db)
}
///
- /// Loads the bundled PowerSync core SQLite extension. Override on
- /// platform-specific adapters (e.g. MAUI iOS/Android) where the native library
- /// lives outside the desktop runtime path.
+ /// Loads the bundled PowerSync core SQLite extension. The extension ships in a
+ /// different form per platform: an xcframework on iOS/MacCatalyst, a jniLib on
+ /// Android, and a plain native library under runtimes/ on desktop.
///
protected virtual void LoadDefaultPowerSyncExtension(SqliteConnection db)
{
+#if IOS || MACCATALYST
+ LoadAppleFrameworkExtension(db);
+#elif ANDROID
+ db.LoadExtension("libpowersync");
+#else
var path = PowerSyncPathResolver.GetNativeLibraryPath(AppContext.BaseDirectory);
db.LoadExtension(path, "sqlite3_powersync_init");
+#endif
}
+#if IOS || MACCATALYST
+ private static void LoadAppleFrameworkExtension(SqliteConnection db)
+ {
+ var bundlePath = Foundation.NSBundle.FromIdentifier("co.powersync.sqlitecore")?.BundlePath;
+ if (bundlePath == null)
+ {
+ throw new Exception("Could not find PowerSync SQLite extension bundle path");
+ }
+
+ var filePath = Path.Combine(bundlePath, "powersync-sqlite-core");
+
+ using var loadExtension = db.CreateCommand();
+ loadExtension.CommandText = "SELECT load_extension(@path, @entryPoint)";
+ loadExtension.Parameters.AddWithValue("@path", filePath);
+ loadExtension.Parameters.AddWithValue("@entryPoint", "sqlite3_powersync_init");
+ loadExtension.ExecuteNonQuery();
+ }
+#endif
+
public async Task Close()
{
tablesUpdatedCts?.Cancel();
diff --git a/PowerSync/PowerSync.Maui/build/ApiDefinition.cs b/PowerSync/PowerSync.Common/Platforms/ApiDefinition.cs
similarity index 71%
rename from PowerSync/PowerSync.Maui/build/ApiDefinition.cs
rename to PowerSync/PowerSync.Common/Platforms/ApiDefinition.cs
index 5fe8ef43..75d0dc6f 100644
--- a/PowerSync/PowerSync.Maui/build/ApiDefinition.cs
+++ b/PowerSync/PowerSync.Common/Platforms/ApiDefinition.cs
@@ -1,4 +1,4 @@
-namespace PowerSync.Maui.build
+namespace PowerSync.Common.Platforms
{
// Empty API definition - allows xcframework to be included without managed bindings
}
diff --git a/PowerSync/PowerSync.Common/PowerSync.Common.csproj b/PowerSync/PowerSync.Common/PowerSync.Common.csproj
index 6e45bb7d..ac52a4d4 100644
--- a/PowerSync/PowerSync.Common/PowerSync.Common.csproj
+++ b/PowerSync/PowerSync.Common/PowerSync.Common.csproj
@@ -20,6 +20,10 @@
NU5100
README.md
$(DefaultItemExcludes);runtimes/**/*.*;
+
+ true
+ true
@@ -42,21 +46,45 @@
-
-
+
-
+
PreserveNewest
-
-
-
-
-
+
+
+
+
+
+
+ Framework
+ False
+
+
+
+
+
+
+
+
+
+
diff --git a/PowerSync/PowerSync.Common/PowerSync.Common.targets b/PowerSync/PowerSync.Common/PowerSync.Common.targets
index 0c5bcba2..219c2224 100644
--- a/PowerSync/PowerSync.Common/PowerSync.Common.targets
+++ b/PowerSync/PowerSync.Common/PowerSync.Common.targets
@@ -1,7 +1,22 @@
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/PowerSync/PowerSync.Common/README.md b/PowerSync/PowerSync.Common/README.md
index 9526ecdf..4871acd7 100644
--- a/PowerSync/PowerSync.Common/README.md
+++ b/PowerSync/PowerSync.Common/README.md
@@ -2,6 +2,8 @@
This package contains a .NET implementation of a PowerSync database connector and streaming sync bucket implementation.
+It bundles the native PowerSync SQLite extension for every supported target and loads the right one automatically, so no additional package is required on mobile.
+
## ⚠️ Project Status & Release Note
This package is in beta and is considered ready for production use for tested use cases. See our feature status definitions [here](https://docs.powersync.com/resources/feature-status).
@@ -36,6 +38,26 @@ static async Task Main() {
```
+### MAUI / mobile
+
+Initialization is the same on mobile, except that the database file must be placed in a
+platform-appropriate location. Supply an `MDSQLiteDBOpenFactory` built from that path:
+
+```csharp
+var dbPath = Path.Combine(FileSystem.AppDataDirectory, "maui-example.db");
+var factory = new MDSQLiteDBOpenFactory(new MDSQLiteOpenFactoryOptions
+{
+ DbFilename = dbPath
+});
+
+var db = new PowerSyncDatabase(new PowerSyncDatabaseOptions
+{
+ Database = factory,
+ Schema = AppSchema.PowerSyncSchema,
+});
+await db.Init();
+```
+
### Watched queries
Watched queries will automatically update when a dependant table is updated.
diff --git a/PowerSync/PowerSync.Maui/CHANGELOG.md b/PowerSync/PowerSync.Maui/CHANGELOG.md
deleted file mode 100644
index a5a5ca77..00000000
--- a/PowerSync/PowerSync.Maui/CHANGELOG.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# PowerSync.Maui Changelog
-
-## 0.1.4
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.1.4 for more information)
-
-## 0.1.3
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.1.3 for more information)
-
-## 0.1.2
-
-- Add support for MacCatalyst.
-- Add support for .NET 9.0. Supported targets now also include `net9.0`, `net9.0-android`, `net9.0-ios`, and `net9.0-maccatalyst`.
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.1.2 for more information)
-
-## 0.1.1
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.1.1 for more information)
-
-## 0.1.0
-
-- Beta release.
-
-## 0.0.9-alpha.1
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.0.11-alpha.1 for more information)
-
-## 0.0.8-alpha.1
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.0.10-alpha.1 for more information)
-
-## 0.0.7-alpha.1
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.0.9-alpha.1 for more information)
- - _Breaking:_ Updates to how the application schema is defined.
-
-## 0.0.6-alpha.1
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.0.8-alpha.1 for more information)
-
-## 0.0.5-alpha.1
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.0.7-alpha.1 for more information)
-
-## 0.0.4-alpha.1
-
-- Upstream PowerSync.Common version bump (See Powersync.Common changelog 0.0.6-alpha.1 for more information)
-- Added ability to specify `AppMetadata` sync/stream requests (see Common changelog).
-
-## 0.0.3-alpha.1
-
-- Upstream PowerSync.Common version bump (See PowerSync.Common change 0.0.5-alpha.1 for more information)
-- Using the latest (0.4.9) version of the core extension, it introduces support for the Rust Sync implementation and also makes it the default - users can still opt out and use the legacy C# sync implementation as option when calling `connect()`.
-
-## 0.0.2-alpha.1
-
-- Fixed issues related to extension loading when installing package outside of the monorepo.
-
-## 0.0.1-alpha.1
-
-- Introduce package. Support for iOS/Android use cases.
-
-### Platform Runtime Support Added
-
-- MAUI iOS
-- MAUI Android
diff --git a/PowerSync/PowerSync.Maui/PowerSync.Maui.csproj b/PowerSync/PowerSync.Maui/PowerSync.Maui.csproj
deleted file mode 100644
index 74e9a948..00000000
--- a/PowerSync/PowerSync.Maui/PowerSync.Maui.csproj
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
- netstandard2.0;net6.0;net8.0;net9.0;net8.0-ios;net8.0-android;net8.0-maccatalyst;net9.0-ios;net9.0-android;net9.0-maccatalyst
- 12
- enable
- enable
- PowerSync.Maui
- PowerSync.Maui
- PowerSync.Maui is a package that enables MAUI usage for PowerSync
- PowerSync
- powersync
- Apache-2.0
- https://github.com/powersync-ja/powersync-dotnet
- https://powersync.com
- true
- https://github.com/powersync-ja/powersync-dotnet/PowerSync/PowerSync.Maui/CHANGELOG.md
- powersync local-first local-storage state-management offline sql db persistence sqlite sync
- icon.png
- NU5100
- README.md
- true
- true
-
-
-
-
-
-
-
-
-
-
- Designer
-
-
-
-
-
-
-
-
- Framework
- False
-
-
-
-
-
-
-
- Framework
- False
-
-
-
-
-
-
-
-
-
-
-
diff --git a/PowerSync/PowerSync.Maui/README.md b/PowerSync/PowerSync.Maui/README.md
deleted file mode 100644
index bc75882c..00000000
--- a/PowerSync/PowerSync.Maui/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# PowerSync SDK .NET MAUI
-
-This package provides .NET Multi-platform App UI (MAUI) integration for PowerSync, designed to work with the PowerSync.Common package for cross-platform mobile and desktop applications.
-
-## ⚠️ Project Status & Release Note
-
-This package is in beta and is considered ready for production use for tested use cases. See our feature status definitions [here](https://docs.powersync.com/resources/feature-status).
-
-## Installation
-
-This package is published on [NuGet](https://www.nuget.org/packages/PowerSync.Maui) and requires PowerSync.Common to also be installed.
-
-```bash
-dotnet add package PowerSync.Maui
-dotnet add package PowerSync.Common
-```
-
-## Usage
-
-Initialization differs slightly from our Common SDK when using MAUI.
-
-```csharp
-
-private record ListResult(string id, string name, string owner_id, string created_at);
-
-static async Task Main() {
-
- // Ensures the DB file is stored in a platform appropriate location
- var dbPath = Path.Combine(FileSystem.AppDataDirectory, "maui-example.db");
- var factory = new MAUISQLiteDBOpenFactory(new MDSQLiteOpenFactoryOptions()
- {
- DbFilename = dbPath
- });
-
- var Db = new PowerSyncDatabase(new PowerSyncDatabaseOptions()
- {
- Database = factory, // Supply a factory
- Schema = AppSchema.PowerSyncSchema,
- });
-
- await db.Init();
-
- var lists = await db.GetAll("select * from lists");
-}
-```
diff --git a/PowerSync/PowerSync.Maui/SQLite/MAUISQLiteAdapter.cs b/PowerSync/PowerSync.Maui/SQLite/MAUISQLiteAdapter.cs
deleted file mode 100644
index 0d6e3993..00000000
--- a/PowerSync/PowerSync.Maui/SQLite/MAUISQLiteAdapter.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-namespace PowerSync.Maui.SQLite;
-
-using Microsoft.Data.Sqlite;
-
-using PowerSync.Common.MDSQLite;
-
-// iOS/MacCatalyst specific imports
-#if IOS || MACCATALYST
-using Foundation;
-#endif
-
-public class MAUISQLiteAdapter : MDSQLiteAdapter
-{
- public MAUISQLiteAdapter(MDSQLiteAdapterOptions options) : base(options)
- {
- }
-
- // The bundled PowerSync extension lives in a platform-specific location on
- // iOS/MacCatalyst/Android — the desktop runtime path used by the base class
- // does not resolve to it. Override only the PowerSync-extension load hook;
- // user-supplied custom extensions still flow through MDSQLiteAdapter.LoadExtensions
- // unchanged, so consumers can freely combine the bundled extension (via the
- // LoadPowerSyncExtension flag) with their own.
- protected override void LoadDefaultPowerSyncExtension(SqliteConnection db)
- {
-#if IOS || MACCATALYST
- LoadExtensionApple(db);
-#elif ANDROID
- db.LoadExtension("libpowersync");
-#else
- base.LoadDefaultPowerSyncExtension(db);
-#endif
- }
-
- private static void LoadExtensionApple(SqliteConnection db)
- {
-#if IOS || MACCATALYST
- var bundlePath = Foundation.NSBundle.FromIdentifier("co.powersync.sqlitecore")?.BundlePath;
- if (bundlePath == null)
- {
- throw new Exception("Could not find PowerSync SQLite extension bundle path");
- }
-
- var filePath =
- Path.Combine(bundlePath, "powersync-sqlite-core");
-
- using var loadExtension = db.CreateCommand();
- loadExtension.CommandText = "SELECT load_extension(@path, @entryPoint)";
- loadExtension.Parameters.AddWithValue("@path", filePath);
- loadExtension.Parameters.AddWithValue("@entryPoint", "sqlite3_powersync_init");
- loadExtension.ExecuteNonQuery();
-#endif
- }
-}
-
diff --git a/PowerSync/PowerSync.Maui/SQLite/MAUISQLiteDBOpenFactory.cs b/PowerSync/PowerSync.Maui/SQLite/MAUISQLiteDBOpenFactory.cs
deleted file mode 100644
index c8d35088..00000000
--- a/PowerSync/PowerSync.Maui/SQLite/MAUISQLiteDBOpenFactory.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using PowerSync.Common.DB;
-
-namespace PowerSync.Maui.SQLite;
-
-using PowerSync.Common.Client;
-using PowerSync.Common.MDSQLite;
-
-
-public class MAUISQLiteDBOpenFactory : ISQLOpenFactory
-{
- private readonly MDSQLiteOpenFactoryOptions options;
-
- public MAUISQLiteDBOpenFactory(MDSQLiteOpenFactoryOptions options)
- {
- this.options = options;
- }
-
- public IDBAdapter OpenDatabase()
- {
- return new MAUISQLiteAdapter(new MDSQLiteAdapterOptions
- {
- Name = options.DbFilename,
- SqliteOptions = options.SqliteOptions
- });
- }
-}
diff --git a/README.md b/README.md
index f71ef329..0832d5ce 100644
--- a/README.md
+++ b/README.md
@@ -13,9 +13,7 @@ _[PowerSync](https://www.powersync.com) is a sync engine for building local-firs
Packages are published to [NuGet](https://www.nuget.org/profiles/PowerSync).
- [PowerSync.Common](./PowerSync/PowerSync.Common/README.md)
- - Core package: .NET implementation of a PowerSync database connector and streaming sync bucket implementation. Packages meant for specific platforms will extend functionality of `Common`.
-- [PowerSync.Maui](./PowerSync/PowerSync.Maui/README.md)
- - Extends the PowerSync.Common package to provide the .NET Multi-platform App UI (MAUI) integration for PowerSync for cross-platform mobile and desktop applications.
+ - The only package you need: .NET implementation of a PowerSync database connector and streaming sync bucket implementation. It bundles the native PowerSync SQLite extension for every supported target — desktop (Windows, macOS, Linux), iOS, Android and MacCatalyst — and selects the right one automatically.
## Demo Apps / Example Projects
diff --git a/RELEASE.md b/RELEASE.md
index 6beed425..5845dfe2 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -4,14 +4,13 @@
1. Ensure all changes you want to release are merged into `main`.
-2. Ensure the changelog files for both [Common](./PowerSync/PowerSync.Common/CHANGELOG.md) and [Maui](./PowerSync/PowerSync.Common/CHANGELOG.md) have been updated with _new, well-formatted version numbers._
- - The [release workflow](./.github/workflows/release.yml) obtains the version number by searching for the top-most version line (prefixed with `## `), stripping the prefix, and taking the remaining text as the version number. It does this for both changelogs.
- - If a release only includes changes to `PowerSync.Common`, don't forget to also update `PowerSync.Maui`'s changelog so that a new release is created that uses the updated version of `PowerSync.Common`.
+2. Ensure the [Common changelog](./PowerSync/PowerSync.Common/CHANGELOG.md) has been updated with a _new, well-formatted version number._
+ - The [release workflow](./.github/workflows/release.yml) obtains the version number by searching for the top-most version line (prefixed with `## `), stripping the prefix, and taking the remaining text as the version number.
- By convention, we generally update the changelog within the PRs that add the changes. This sometimes leads to the version number in the changelog being a version ahead of the released version (eg. latest released version is v0.0.3, but changelog has v0.0.4.)
3. Run the `Release` workflow on Github. This will:
- - Extract version numbers from the changelogs.
- - Create a release for both packages on Nuget.
+ - Extract the version number from the changelog.
+ - Create a release for the package on Nuget.
- Create a Github release containing the changelog contents.
## Dev Releases
diff --git a/Tools/Setup/Setup.cs b/Tools/Setup/Setup.cs
index c91d2ecd..795f86ee 100644
--- a/Tools/Setup/Setup.cs
+++ b/Tools/Setup/Setup.cs
@@ -1,9 +1,13 @@
using System;
using System.IO;
+using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO.Compression;
+using System.Text;
+using System.Xml;
+using System.Xml.Linq;
///
/// Execute with `dotnet run --project Tools/Setup`
@@ -14,6 +18,17 @@ public class PowerSyncSetup
private const string GITHUB_BASE_URL = $"https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v{VERSION}";
+ ///
+ /// xcframework slices reachable from our target frameworks. Everything else is removed
+ /// by .
+ ///
+ private static readonly string[] REQUIRED_APPLE_SLICES =
+ [
+ "ios-arm64", // device
+ "ios-arm64_x86_64-simulator", // simulator
+ "ios-arm64_x86_64-maccatalyst" // MacCatalyst
+ ];
+
private readonly HttpClient _httpClient;
private readonly string _basePath;
@@ -28,9 +43,8 @@ public async Task RunSetup()
try
{
await SetupDesktop();
- await SetupMauiIos();
- await SetupMauiAndroid();
- await SetupMauiMacCatalyst();
+ await SetupApple();
+ await SetupAndroid();
}
finally
{
@@ -89,24 +103,130 @@ private async Task ProcessDesktopRuntime(string basePath, KeyValuePair
+ /// Downloads the xcframework used by the iOS and MacCatalyst targets. The archive is
+ /// self-describing and contains slices for both, so a single copy serves both targets.
+ ///
+ public async Task SetupApple()
{
- Console.WriteLine("Setting up MAUI iOS libraries...");
+ Console.WriteLine("Setting up Apple libraries...");
- var nativeDir = Path.Combine(_basePath, "PowerSync.Maui", "Platforms", "iOS", "NativeLibs");
+ var nativeDir = Path.Combine(_basePath, "PowerSync.Common", "Platforms", "Apple", "NativeLibs");
var config = new ArchiveConfig(
"powersync-sqlite-core.xcframework.zip",
"powersync-sqlite-core.xcframework"
);
await ProcessArchiveDownload(nativeDir, config, GITHUB_BASE_URL);
+
+ var xcframeworkPath = Path.Combine(nativeDir, config.ExtractedName);
+ if (Directory.Exists(xcframeworkPath))
+ {
+ TrimXcframework(xcframeworkPath);
+ }
}
- public async Task SetupMauiAndroid()
+ ///
+ /// Removes xcframework slices and debug symbols that none of our target frameworks can
+ /// use. The upstream archive also ships tvOS, watchOS and native macOS slices plus dSYMs
+ /// for every slice; together these are ~97% of its size, and they would otherwise be
+ /// embedded into the NuGet package four times over (net8/net9 x ios/maccatalyst).
+ ///
+ private static void TrimXcframework(string xcframeworkPath)
{
- Console.WriteLine("Setting up MAUI Android libraries...");
+ var infoPlistPath = Path.Combine(xcframeworkPath, "Info.plist");
+
+ // Parse the DOCTYPE (so Save writes it back) without fetching the external DTD.
+ var settings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Parse, XmlResolver = null };
+ XDocument plist;
+ using (var reader = XmlReader.Create(infoPlistPath, settings))
+ {
+ plist = XDocument.Load(reader);
+ }
- var nativeDir = Path.Combine(_basePath, "PowerSync.Maui", "Platforms", "Android", "jniLibs");
+ var availableLibraries = plist.Descendants("key")
+ .First(key => key.Value == "AvailableLibraries")
+ .ElementsAfterSelf("array")
+ .First();
+
+ var keptSlices = new List();
+
+ foreach (var slice in availableLibraries.Elements("dict").ToList())
+ {
+ var identifier = PlistValue(slice, "LibraryIdentifier")
+ ?? throw new Exception("xcframework slice has no LibraryIdentifier");
+ var sliceDir = Path.Combine(xcframeworkPath, identifier);
+
+ if (!REQUIRED_APPLE_SLICES.Contains(identifier))
+ {
+ DeleteDirectoryIfExists(sliceDir);
+ slice.Remove();
+ continue;
+ }
+
+ var debugSymbolsPath = PlistValue(slice, "DebugSymbolsPath");
+ if (debugSymbolsPath != null)
+ {
+ DeleteDirectoryIfExists(Path.Combine(sliceDir, debugSymbolsPath));
+ RemovePlistEntry(slice, "DebugSymbolsPath");
+ }
+
+ keptSlices.Add(identifier);
+ }
+
+ // Fail loudly rather than shipping a package that silently lost a platform, in case
+ // upstream renames a slice.
+ var missing = REQUIRED_APPLE_SLICES.Except(keptSlices).ToList();
+ if (missing.Count > 0)
+ {
+ throw new Exception($"xcframework is missing required slice(s): {string.Join(", ", missing)}");
+ }
+
+ // XDocument otherwise round-trips the empty internal DTD subset as "[]", and prepends
+ // a BOM. plutil rejects both.
+ if (plist.DocumentType != null)
+ {
+ plist.DocumentType.InternalSubset = null;
+ }
+
+ var writerSettings = new XmlWriterSettings
+ {
+ Indent = true,
+ Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
+ };
+ using (var writer = XmlWriter.Create(infoPlistPath, writerSettings))
+ {
+ plist.Save(writer);
+ }
+
+ Console.WriteLine($"✓ Trimmed xcframework to: {string.Join(", ", keptSlices)}");
+ }
+
+ private static string? PlistValue(XElement dict, string key) =>
+ dict.Elements("key")
+ .FirstOrDefault(element => element.Value == key)
+ ?.ElementsAfterSelf().FirstOrDefault()?.Value;
+
+ private static void RemovePlistEntry(XElement dict, string key)
+ {
+ var keyElement = dict.Elements("key").First(element => element.Value == key);
+ keyElement.ElementsAfterSelf().First().Remove();
+ keyElement.Remove();
+ }
+
+ private static void DeleteDirectoryIfExists(string path)
+ {
+ if (Directory.Exists(path))
+ {
+ Directory.Delete(path, recursive: true);
+ }
+ }
+
+ public async Task SetupAndroid()
+ {
+ Console.WriteLine("Setting up Android libraries...");
+
+ var nativeDir = Path.Combine(_basePath, "PowerSync.Common", "Platforms", "Android", "jniLibs");
try
{
@@ -135,19 +255,6 @@ private async Task DownloadAndroidLibrary(string filename, string jniLibsDir, st
await DownloadFile($"{GITHUB_BASE_URL}/{filename}", targetFile);
}
- public async Task SetupMauiMacCatalyst()
- {
- Console.WriteLine("Setting up MAUI MacCatalyst libraries...");
-
- var nativeDir = Path.Combine(_basePath, "PowerSync.Maui", "Platforms", "MacCatalyst", "NativeLibs");
- var config = new ArchiveConfig(
- "powersync-sqlite-core.xcframework.zip",
- "powersync-sqlite-core.xcframework"
- );
-
- await ProcessArchiveDownload(nativeDir, config, GITHUB_BASE_URL);
- }
-
private async Task ProcessArchiveDownload(string nativeDir, ArchiveConfig config, string baseUrl)
{
try
diff --git a/demos/MAUITodo/Data/PowerSyncData.cs b/demos/MAUITodo/Data/PowerSyncData.cs
index 676781f8..818e20c6 100644
--- a/demos/MAUITodo/Data/PowerSyncData.cs
+++ b/demos/MAUITodo/Data/PowerSyncData.cs
@@ -8,7 +8,6 @@
using PowerSync.Common.Attachments;
using PowerSync.Common.Client;
using PowerSync.Common.MDSQLite;
-using PowerSync.Maui.SQLite;
namespace MAUITodo.Data;
@@ -29,7 +28,7 @@ public PowerSyncData()
var logger = loggerFactory.CreateLogger("PowerSyncLogger");
var dbPath = Path.Combine(FileSystem.AppDataDirectory, "example.db");
- var factory = new MAUISQLiteDBOpenFactory(new MDSQLiteOpenFactoryOptions()
+ var factory = new MDSQLiteDBOpenFactory(new MDSQLiteOpenFactoryOptions()
{
DbFilename = dbPath
});
diff --git a/demos/MAUITodo/MAUITodo.csproj b/demos/MAUITodo/MAUITodo.csproj
index 56d224b4..21684022 100644
--- a/demos/MAUITodo/MAUITodo.csproj
+++ b/demos/MAUITodo/MAUITodo.csproj
@@ -72,6 +72,5 @@
-
diff --git a/root.sln b/root.sln
index 072d04eb..1519e87c 100644
--- a/root.sln
+++ b/root.sln
@@ -21,8 +21,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MAUITodo", "demos\MAUITodo\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPF", "demos\WPF\WPF.csproj", "{AF297026-0BEA-4B8E-97C9-6540C6D52B36}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerSync.Maui", "PowerSync\PowerSync.Maui\PowerSync.Maui.csproj", "{A4A91B9F-0C86-41CB-BEF0-C002819C43BE}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerSync.Common.IntegrationTests", "Tests\PowerSync\PowerSync.Common.IntegrationTests\PowerSync.Common.IntegrationTests.csproj", "{EB81D453-777D-40B5-A504-4144906ADBF4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerSync.Common.PerformanceTests", "Tests\PowerSync\PowerSync.Common.PerformanceTests\PowerSync.Common.PerformanceTests.csproj", "{B2BA37D6-0549-455D-9520-B3D21C7876D0}"
@@ -97,18 +95,6 @@ Global
{AF297026-0BEA-4B8E-97C9-6540C6D52B36}.Release|x64.Build.0 = Release|Any CPU
{AF297026-0BEA-4B8E-97C9-6540C6D52B36}.Release|x86.ActiveCfg = Release|Any CPU
{AF297026-0BEA-4B8E-97C9-6540C6D52B36}.Release|x86.Build.0 = Release|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Debug|x64.ActiveCfg = Debug|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Debug|x64.Build.0 = Debug|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Debug|x86.Build.0 = Debug|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Release|Any CPU.Build.0 = Release|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Release|x64.ActiveCfg = Release|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Release|x64.Build.0 = Release|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Release|x86.ActiveCfg = Release|Any CPU
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE}.Release|x86.Build.0 = Release|Any CPU
{EB81D453-777D-40B5-A504-4144906ADBF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB81D453-777D-40B5-A504-4144906ADBF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB81D453-777D-40B5-A504-4144906ADBF4}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -144,7 +130,6 @@ Global
{D7FDA714-D29F-4D85-B3F2-74C6810A36F9} = {9144195A-C68F-4B1E-A574-474EDD424D6C}
{B8B2A9E2-FEC9-495B-B03E-23078E7B651D} = {9144195A-C68F-4B1E-A574-474EDD424D6C}
{AF297026-0BEA-4B8E-97C9-6540C6D52B36} = {9144195A-C68F-4B1E-A574-474EDD424D6C}
- {A4A91B9F-0C86-41CB-BEF0-C002819C43BE} = {B1D87BA9-8812-4EFA-BBBE-1FF1EEEB5433}
{EB81D453-777D-40B5-A504-4144906ADBF4} = {C784FBE4-CC1E-4A0A-AE8E-6B818DD3724D}
{B2BA37D6-0549-455D-9520-B3D21C7876D0} = {C784FBE4-CC1E-4A0A-AE8E-6B818DD3724D}
EndGlobalSection