Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 111 additions & 19 deletions BUILDING.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,135 @@
# Building Plugin.Firebase
# Building Plugin.Firebase (reproducible setup)

## Prerequisites
- .NET SDK version matching `global.json`
- Android workload (for `net9.0-android`)
- iOS workload + Xcode (for `net9.0-ios`, macOS only)
Goal: avoid “works on my machine” issues around .NET SDKs, workloads/packs, and Xcode.

Install workloads (if needed):
## Supported toolchain matrix
This repo targets `net9.0` + mobile TFMs (`net9.0-ios`, `net9.0-android`).

**Officially supported (reproducible):**
| .NET SDK | Workloads | Notes |
|---:|---|---|
| 9.0.306 | Workload-set mode + workloads aligned to 9.0.306 | Pinned by `global.json`. Used for CI guidance and docs. |

**Best-effort / observed OK (not CI-pinned):**
| .NET SDK | Workloads | Notes |
|---:|---|---|
| 10.0.100 | Matching workloads for that SDK | Works locally when `global.json` is not used; we don’t guarantee it stays working. |

If you want to officially support a new toolchain combination (ex: .NET 10), propose it in a PR with CI updates and a documented matrix change.

## Quick start (diagnose first, then fix)

### Step 0 — Run the verifier (no installs)
Run:
```bash
bash scripts/verify-env.sh
```
dotnet workload install android ios

If `scripts/verify-env.sh` ends with `STATUS=HEALTHY`, you can build iOS right away:
```bash
dotnet build src/Core/Core.csproj -c Release -f net9.0-ios
```

## Restore & build
If it prints `STATUS=UNHEALTHY`, it will also print `REASON_1..N` and `NEXT_1..N` with suggested fix commands.

### Step 1 — Fix only if unhealthy (workloads / packs)
When SDK and workloads are out of sync, the most reliable repair path is:
```bash
dotnet workload config --update-mode workload-set
dotnet workload update --from-previous-sdk --version "$(dotnet --version)"
dotnet workload install ios maccatalyst maui --version "$(dotnet --version)"
```

Notes:
- You do **not** need to run `dotnet workload update` on every machine every time. Use it when you changed SDK feature bands, when packs are missing, or when you hit workload-related errors.
- Minimal iOS requirement to build `src/Core/Core.csproj` is Apple workloads/packs (`ios`). `maui` is needed for `sample/Playground`.

Then clean and rebuild:
```bash
dotnet clean Plugin.Firebase.sln -c Release
dotnet build src/Core/Core.csproj -c Release -f net9.0-ios
```

## Restore & build
Restore:
```bash
dotnet restore Plugin.Firebase.sln
```

Build the full solution (includes sample + integration tests):
```bash
dotnet build Plugin.Firebase.sln -c Release
```

Note: building the full solution includes the sample and integration test apps.
Running those apps requires `GoogleService-Info.plist` and `google-services.json` files (not committed).
If you don’t have local Firebase configs, use the `net9.0` build below to validate the packages.
If you don’t have local Firebase configs, use these builds to validate packages:

### Build without mobile workloads
If you want to validate core code without Android/iOS toolchains:
```
Build without mobile workloads:
```bash
dotnet build src/Auth/Auth.csproj -c Release -f net9.0
```

## Tests (integration)
Tests live under `tests/Plugin.Firebase.IntegrationTests` and run on a real device.
You must supply your own Firebase config files (not committed):
Build iOS only:
```bash
dotnet build src/Core/Core.csproj -c Release -f net9.0-ios
```

## Tests

### Unit tests (run anywhere)
```bash
dotnet test tests/Plugin.Firebase.UnitTests/Plugin.Firebase.UnitTests.csproj -c Release
```

### Integration tests (device-only)
Integration tests require a real device and local Firebase config files (not committed):
- `GoogleService-Info.plist` (iOS)
- `google-services.json` (Android)

Run:
```
```bash
dotnet test tests/Plugin.Firebase.IntegrationTests/Plugin.Firebase.IntegrationTests.csproj --no-build
```

## Formatting
```
```bash
dotnet format Plugin.Firebase.sln
```

Formatting is required before every PR.

## Troubleshooting

### Workloads missing (`NETSDK1147`)
Symptoms:
- “To build this project, the following workloads must be installed: ios/android”

Fix:
```bash
dotnet workload install ios maccatalyst maui --version "$(dotnet --version)"
```

### Packs missing / mismatched (often `CS1705`)
Symptoms:
- `CS1705` mentioning `Microsoft.iOS, Version=...` mismatches

Fix (repair path):
```bash
dotnet workload config --update-mode workload-set
dotnet workload update --from-previous-sdk --version "$(dotnet --version)"
dotnet workload install ios maccatalyst maui --version "$(dotnet --version)"
```

### Xcode / CLT / xcode-select
Symptoms:
- `xcodebuild` not found
- `xcode-select -p` points to an unexpected path

Fix:
```bash
xcodebuild -version
xcode-select -p
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
```

## Glossary
If you need the deeper explanation of “SDK vs workloads vs manifests vs packs vs TFM”, see `docs/toolchain-glossary.md`.
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ Thanks for contributing! This repo is public OSS. Please keep contributions gene
3) Run formatting and relevant builds.
4) Open a PR with a clear description.

Build/toolchain details (macOS iOS builds): see `BUILDING.md`.

## Development guidelines
- Follow `.editorconfig` rules.
- Format code before PRs: `dotnet format Plugin.Firebase.sln`
- Formatting is required before every PR.
- Avoid app-specific or proprietary context in code/docs.
- Never commit secrets (Firebase configs, signing keys, tokens).
- Keep API changes backward-compatible when possible.
Expand Down Expand Up @@ -51,3 +54,6 @@ dotnet format Plugin.Firebase.sln --include $(git diff --name-only --cached)

## Documentation
If you change behavior or APIs, update the relevant docs in `docs/` and/or `README.md`.

## XML docs
When adding or changing public APIs, prefer adding/updating XML doc comments so the generated `.xml` documentation stays useful.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

This is a wrapper library around the native Android and iOS Firebase SDKs which includes cross-platform APIs for most of the Firebase features. Documentation and the included sample app are MAUI-centric, but the plugin should be usable in any cross-platform .NET9+ project.

## Build / toolchain setup
See `BUILDING.md` for a reproducible macOS setup (SDK/workloads/Xcode), verification commands, and troubleshooting.

## v4.0 Upgrade Notes
- The experience of building projects using this plugin has been improved on Windows / Visual Studio. Issues related to hanging builds caused by XamarinBuildDownload and long path issues affecting iOS NuGet packages have been mitigated, if not commpletely resolved.
- MAUI-specific dependencies have been removed. This makes the plugin friendlier to non-MAUI mobile .NET projects.
Expand Down
61 changes: 61 additions & 0 deletions docs/toolchain-glossary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Toolchain glossary (SDK vs workloads vs manifests vs packs vs TFM)

This document explains why “mobile builds” can break when .NET SDK versions, workloads/manifests, packs, and Xcode get out of sync.

## Glossary

### .NET SDK version (ex: `9.0.306`, `10.0.100`)
The SDK is the `dotnet` toolchain that runs MSBuild and selects workloads/packs.

Check:
```bash
dotnet --version
```

### Workload version (ex: `9.0.306`, `10.0.102`)
The workload “version” is the bundle of workload components used for a given SDK.

Check:
```bash
dotnet workload --version
dotnet workload --info
```

### Workload manifests (ex: `maui 9.0.111/9.0.100` or `10.0.1/10.0.100`)
A manifest is the metadata that defines what a workload installs (packs, templates, etc.).

You’ll see manifest versions in:
```bash
dotnet workload --info
```

### Packs (Apple packs: `Microsoft.iOS.*` under `<DOTNET_ROOT>/packs`)
Packs are the reference assemblies + runtimes + SDK bits used by the Apple toolchain.

Common location (Homebrew install):
```
/usr/local/share/dotnet/packs
```

Key Apple packs:
- `Microsoft.iOS.Sdk.*` (targets/toolchain pack)
- `Microsoft.iOS.Ref.*` (reference assemblies used at compile time)
- `Microsoft.iOS.Runtime.*` (runtime bits)

### TFM (Target Framework Moniker)
This repo targets (examples):
- `net9.0` (no mobile toolchain required)
- `net9.0-ios` (iOS toolchain + packs + Xcode required)
- `net9.0-android` (Android toolchain required)

TFM with an explicit platform version (example): `net9.0-ios18.0`
- Same base .NET version (`net9.0`) but **pins the iOS target platform version** (API surface / analyzers).
- It does **not** install workloads for you. It only changes compile-time targeting.

## Relationship (text diagram)
```
.NET SDK (dotnet) -> selects/uses -> workloads + manifests
workloads + manifests -> install -> packs under <DOTNET_ROOT>/packs
TFM (net9.0-ios...) -> consumes -> packs + Xcode toolchain to compile/link
```

6 changes: 3 additions & 3 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"sdk": {
"version": "9.0.0",
"version": "9.0.306",
"allowPrerelease": false,
"rollForward": "latestMinor"
"rollForward": "latestPatch"
}
}
}
6 changes: 5 additions & 1 deletion sample/Playground/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#nullable enable
namespace Playground;

public partial class App : Application
{
public App()
{
InitializeComponent();
}

MainPage = new AppShell();
protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(new AppShell());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Playground.Resources;
using System.Linq;

namespace Playground.Common.Services.UserInteraction;

Expand Down Expand Up @@ -148,7 +149,8 @@ protected static async Task SetResultToCancelledAfterDurationAsync(TaskCompletio
tcs.TrySetResult(DialogButtonIndex.Cancel);
}

private static Page CurrentPage => Application.Current.MainPage;
private static Page CurrentPage => Application.Current?.Windows.FirstOrDefault()?.Page
?? throw new InvalidOperationException("No active window is available.");
}

public static class DialogButtonIndex
Expand Down
18 changes: 14 additions & 4 deletions sample/Playground/Platforms/Android/MainActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using AndroidX.Core.App;
using AndroidX.Core.Content;
using Plugin.Firebase.CloudMessaging;
using System.Runtime.Versioning;

namespace Playground;

Expand All @@ -29,18 +30,27 @@ private static void HandleIntent(Intent intent)

private void RequestPushNotificationsPermission()
{
if(Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu && ContextCompat.CheckSelfPermission(this, Manifest.Permission.PostNotifications) != Permission.Granted) {
ActivityCompat.RequestPermissions(this, new[] { Manifest.Permission.PostNotifications }, 0); ;
if(OperatingSystem.IsAndroidVersionAtLeast(33)) {
RequestPostNotificationsPermission();
}
}

[SupportedOSPlatform("android33.0")]
private void RequestPostNotificationsPermission()
{
if(ContextCompat.CheckSelfPermission(this, Manifest.Permission.PostNotifications) != Permission.Granted) {
ActivityCompat.RequestPermissions(this, new[] { Manifest.Permission.PostNotifications }, 0);
}
}

private void CreateNotificationChannelIfNeeded()
{
if(Build.VERSION.SdkInt >= BuildVersionCodes.O) {
if(OperatingSystem.IsAndroidVersionAtLeast(26)) {
CreateNotificationChannel();
}
}

[SupportedOSPlatform("android26.0")]
private void CreateNotificationChannel()
{
var channelId = $"{PackageName}.general";
Expand All @@ -62,4 +72,4 @@ protected override void OnNewIntent(Intent intent)
base.OnNewIntent(intent);
HandleIntent(intent);
}
}
}
Loading