PlaidLink.Maui
1.0.9
dotnet add package PlaidLink.Maui --version 1.0.9
NuGet\Install-Package PlaidLink.Maui -Version 1.0.9
<PackageReference Include="PlaidLink.Maui" Version="1.0.9" />
<PackageVersion Include="PlaidLink.Maui" Version="1.0.9" />
<PackageReference Include="PlaidLink.Maui" />
paket add PlaidLink.Maui --version 1.0.9
#r "nuget: PlaidLink.Maui, 1.0.9"
#:package PlaidLink.Maui@1.0.9
#addin nuget:?package=PlaidLink.Maui&version=1.0.9
#tool nuget:?package=PlaidLink.Maui&version=1.0.9
PlaidLink.Maui
A modern, cross-platform .NET library for integrating Plaid Link on iOS and Android. Provides a clean, unified API for Plaid Link integration in .NET MAUI, Xamarin, and native .NET mobile apps.
Features
- Simple, intuitive API with fluent builder pattern
- Cross-platform support for iOS and Android
- Type-safe with full nullability annotations
- Zero configuration - auto-detects platform
- Modern .NET 10 targeting
- Comprehensive documentation with XML comments
- Async-friendly callback architecture
Installation
Install via NuGet Package Manager:
dotnet add package PlaidLink.Maui
Or via Package Manager Console:
Install-Package PlaidLink.Maui
Known Issues - Android
Duplicate Compose Runtime Classes: If you encounter the error Type androidx.compose.runtime.Stable is defined multiple times, add this to your project:
<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
<PackageReference Include="Xamarin.AndroidX.Compose.Runtime.Annotation.Jvm" Version="1.10.0.1"
ExcludeAssets="all" PrivateAssets="all" />
</ItemGroup>
This is a known issue with AndroidX binding dependencies and is not specific to PlaidLink.Maui.
Quick Start
Basic Usage
using PlaidLink.Maui;
// Simple API
PlaidLink.Open(
linkToken: "link-sandbox-xxx",
onSuccess: success =>
{
Console.WriteLine($"Connected to {success.InstitutionName}");
Console.WriteLine($"Public Token: {success.PublicToken}");
// Send publicToken to your server to exchange for access_token
await ExchangePublicToken(success.PublicToken);
},
onExit: exit =>
{
if (exit.ErrorMessage != null)
{
Console.WriteLine($"Error: {exit.ErrorMessage}");
}
else
{
Console.WriteLine("User exited Plaid Link");
}
}
);
Fluent Builder Pattern
using PlaidLink.Maui;
PlaidLink.CreateBuilder()
.WithLinkToken(linkToken)
.OnSuccess(success =>
{
// Handle successful connection
var accounts = success.Accounts;
foreach (var account in accounts)
{
Console.WriteLine($" - {account.Name} ({account.Mask})");
}
})
.OnExit(exit =>
{
// Handle exit or error
Console.WriteLine($"Exited: {exit.ErrorMessage ?? "User cancelled"}");
})
.OnEvent(evt =>
{
// Track user interactions (optional)
Console.WriteLine($"📊 Event: {evt.EventName}");
})
.Open();
Platform Setup
Android Setup
- Update your AndroidManifest.xml:
<manifest>
<uses-permission android:name="android.permission.INTERNET" />
<application>
</application>
</manifest>
- Initialize Platform in MainActivity:
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
// This is required for MAUI apps
Microsoft.Maui.ApplicationModel.Platform.Init(this, savedInstanceState);
}
}
iOS Setup
- Update your Info.plist:
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
</dict>
- No additional code required - the library automatically finds the root view controller.
Advanced Usage
Handling Success
PlaidLink.Open(
linkToken: linkToken,
onSuccess: success =>
{
// Success data includes:
var publicToken = success.PublicToken; // Exchange for access_token
var institutionId = success.InstitutionId; // Bank ID
var institutionName = success.InstitutionName; // Bank name
var linkSessionId = success.LinkSessionId; // Session identifier
// Iterate through connected accounts
foreach (var account in success.Accounts)
{
var accountId = account.Id; // Account ID
var accountName = account.Name; // e.g., "Checking"
var accountMask = account.Mask; // Last 4 digits
var accountType = account.Type; // e.g., "depository"
var accountSubtype = account.Subtype; // e.g., "checking"
}
// Exchange token on your backend
await YourApiClient.ExchangePlaidToken(publicToken);
},
onExit: exit => { /* ... */ }
);
Handling Exit/Errors
onExit: exit =>
{
if (exit.ErrorCode != null)
{
// An error occurred
Console.WriteLine($"Error Code: {exit.ErrorCode}");
Console.WriteLine($"Error Type: {exit.ErrorType}");
Console.WriteLine($"Error Message: {exit.ErrorMessage}");
// Log for debugging
Logger.LogError(
"Plaid Link error: {ErrorCode} - {ErrorMessage}",
exit.ErrorCode,
exit.ErrorMessage
);
}
else
{
// User closed the flow without completing
Console.WriteLine("User exited Plaid Link");
}
// Always available
Console.WriteLine($"Session ID: {exit.LinkSessionId}");
Console.WriteLine($"Request ID: {exit.RequestId}");
}
Tracking Events (Analytics)
PlaidLink.CreateBuilder()
.WithLinkToken(linkToken)
.OnSuccess(success => { /* ... */ })
.OnExit(exit => { /* ... */ })
.OnEvent(evt =>
{
// Track user interactions for analytics
Analytics.Track("PlaidLinkEvent", new
{
EventName = evt.EventName,
Institution = evt.InstitutionName,
SessionId = evt.LinkSessionId,
Timestamp = evt.Timestamp
});
// Common events:
// - OPEN
// - SEARCH_INSTITUTION
// - SELECT_INSTITUTION
// - SUBMIT_CREDENTIALS
// - HANDOFF
// - And many more...
})
.Open();
Architecture
Project Structure
PlaidLink.Maui/
├── PlaidLink.cs # Main entry point
├── PlaidLinkBuilder.cs # Fluent builder
├── IPlaidLinkService.cs # Core interface & models
├── Platforms/
│ ├── Android/
│ │ └── PlaidLinkService.cs # Android implementation
│ └── iOS/
│ └── PlaidLinkService.cs # iOS implementation
└── PlaidLink.Maui.csproj
Dependency Injection
For advanced scenarios, you can use dependency injection:
// In your DI setup (e.g., MauiProgram.cs)
builder.Services.AddSingleton<IPlaidLinkService>(sp =>
{
#if ANDROID
return new PlaidLink.Maui.Platforms.Android.PlaidLinkService();
#elif IOS
return new PlaidLink.Maui.Platforms.iOS.PlaidLinkService();
#else
throw new PlatformNotSupportedException();
#endif
});
// In your view model or service
public class BankingViewModel
{
private readonly IPlaidLinkService _plaidLink;
public BankingViewModel(IPlaidLinkService plaidLink)
{
_plaidLink = plaidLink;
}
public void ConnectBank(string linkToken)
{
_plaidLink.Open(linkToken, OnSuccess, OnExit);
}
}
Security Best Practices
- Never hardcode link tokens - always fetch from your secure backend
- Exchange tokens on your backend - never expose access_tokens in mobile apps
- Use HTTPS for all API communications
- Implement token expiration handling
- Log errors securely without exposing sensitive data
Troubleshooting
Android: "No current Android Activity found"
Solution: Make sure you call Platform.Init() in your MainActivity.OnCreate():
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
Microsoft.Maui.ApplicationModel.Platform.Init(this, savedInstanceState);
}
iOS: "No root view controller found"
Solution: Ensure your app has completed UI initialization before calling PlaidLink.Open(). Consider delaying the call until ViewDidAppear:
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
PlaidLink.Open(linkToken, OnSuccess, OnExit);
}
Platform Requirements
| Platform | Minimum Version | Target Framework |
|---|---|---|
| Android | API 23 (6.0) | net10.0-android |
| iOS | iOS 15.0 | net10.0-ios |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Related Links
Support
- Email: your-email@example.com
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made for the .NET community
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-android36.0 is compatible. net10.0-ios26.0 is compatible. |
-
net10.0-android36.0
- GoogleGson (>= 2.13.2.2)
- Microsoft.Maui.Essentials (>= 10.0.0)
- Square.OkHttp3 (>= 5.3.2.2)
- Square.Retrofit2 (>= 3.0.0.3)
- Square.Retrofit2.ConverterGson (>= 3.0.0.3)
- Xamarin.AndroidX.Browser (>= 1.9.0.1)
- Xamarin.AndroidX.Core.Core.Ktx (>= 1.17.0.1)
- Xamarin.AndroidX.Fragment.Ktx (>= 1.8.9.1)
- Xamarin.AndroidX.Lifecycle.Runtime (>= 2.10.0.1)
- Xamarin.AndroidX.Lifecycle.Runtime.Ktx (>= 2.10.0.1)
- Xamarin.AndroidX.Room.Room.Ktx (>= 2.8.4.1)
- Xamarin.AndroidX.Work.Work.Runtime.Ktx (>= 2.11.0.1)
- Xamarin.Google.Android.Material (>= 1.13.0.1)
- Xamarin.Google.Dagger (>= 2.57.2.1)
- Xamarin.GooglePlayServices.Auth.Api.Phone (>= 118.3.0.1)
- Xamarin.GooglePlayServices.Base (>= 118.9.0.1)
- Xamarin.GooglePlayServices.Basement (>= 118.9.0.1)
- Xamarin.GooglePlayServices.Tasks (>= 118.4.0.2)
- Xamarin.KotlinX.Serialization.Json (>= 1.9.0.2)
- Xamarin.Protobuf.JavaLite (>= 4.31.1.1)
-
net10.0-ios26.0
- Microsoft.Maui.Essentials (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.