{"id":13594461,"url":"https://github.com/reactiveui/splat","last_synced_at":"2025-12-16T15:52:46.203Z","repository":{"id":9252156,"uuid":"11075506","full_name":"reactiveui/splat","owner":"reactiveui","description":"Makes things cross-platform","archived":false,"fork":false,"pushed_at":"2025-04-20T14:44:17.000Z","size":7852,"stargazers_count":980,"open_issues_count":17,"forks_count":141,"subscribers_count":53,"default_branch":"main","last_synced_at":"2025-04-20T15:37:41.200Z","etag":null,"topics":["cross-platform","hacktoberfest","ioc","logging","nlog","splat-locators"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reactiveui.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["reactivemarbles"]}},"created_at":"2013-06-30T18:47:46.000Z","updated_at":"2025-04-20T14:43:45.000Z","dependencies_parsed_at":"2023-02-19T12:31:14.149Z","dependency_job_id":"d0d8744f-12f0-48f3-8f3b-ec2bc2247715","html_url":"https://github.com/reactiveui/splat","commit_stats":{"total_commits":1154,"total_committers":69,"mean_commits":16.72463768115942,"dds":0.7746967071057193,"last_synced_commit":"c27b84e7e9086af4a0bd7f9d73294df6ce05b422"},"previous_names":["paulcbetts/splat"],"tags_count":115,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiveui%2Fsplat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiveui%2Fsplat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiveui%2Fsplat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactiveui%2Fsplat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reactiveui","download_url":"https://codeload.github.com/reactiveui/splat/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249922420,"owners_count":21345905,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cross-platform","hacktoberfest","ioc","logging","nlog","splat-locators"],"created_at":"2024-08-01T16:01:33.792Z","updated_at":"2025-12-16T15:52:46.194Z","avatar_url":"https://github.com/reactiveui.png","language":"C#","funding_links":["https://github.com/sponsors/reactivemarbles"],"categories":["C# #","Media","C\\#","C#"],"sub_categories":[],"readme":"[![NuGet Stats](https://img.shields.io/nuget/v/splat.svg)](https://www.nuget.org/packages/splat) ![Build](https://github.com/reactiveui/splat/workflows/Build/badge.svg) [![Code Coverage](https://codecov.io/gh/reactiveui/splat/branch/main/graph/badge.svg)](https://codecov.io/gh/reactiveui/splat)\n\u003cbr\u003e\n\u003ca href=\"https://www.nuget.org/packages/splat\"\u003e\n        \u003cimg src=\"https://img.shields.io/nuget/dt/splat.svg\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://reactiveui.net/slack\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/chat-slack-blue.svg\"\u003e\n\u003c/a\u003e\n\n\u003cimg src=\"https://github.com/reactiveui/styleguide/blob/master/logo_splat/logo.png?raw=true\" width=\"200\" /\u003e\n\n# Splat\n\nCertain types of things are basically impossible to do in cross-platform\nmobile code today, yet there's no reason why. Writing a ViewModel that handles\nloading a gallery of pictures from disk will be completely riddled with\n`#ifdefs` and basically unreadable.\n\nSplat aims to fix that, by providing a usable leaky abstraction above platform\ncode. It is leaky, because it always provides an extension method `ToNative()`\nand `FromNative()`, which converts the abstraction to the platform-specific\nversion. Load the image in the cross-platform code, then call `ToNative()` in\nyour view to actually display it.\n\n### What does it do?\n\nSplat currently supports:\n\n* Cross-platform image loading/saving\n* A port of System.Drawing.Color for portable libraries\n* Cross-platform geometry primitives (PointF, SizeF, RectangleF), as well as a bunch of\n  additional extension methods to make using them easier.\n* A way to detect whether you're in a Unit Test runner / Design Mode\n* A cross-platform logging framework\n* Simple yet flexible Service Location\n\n### Core Team\n\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\"\u003e\n        \u003cimg width=\"100\" height=\"100\" src=\"https://github.com/glennawatson.png?s=150\"\u003e\n        \u003cbr\u003e\n        \u003ca href=\"https://github.com/glennawatson\"\u003eGlenn Watson\u003c/a\u003e\n        \u003cp\u003eMelbourne, Australia\u003c/p\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\"\u003e\n        \u003cimg width=\"100\" height=\"100\" src=\"https://github.com/rlittlesii.png?s=150\"\u003e\n        \u003cbr\u003e\n        \u003ca href=\"https://github.com/rlittlesii\"\u003eRodney Littles II\u003c/a\u003e\n        \u003cp\u003eTexas, USA\u003c/p\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\"\u003e\n        \u003cimg width=\"100\" height=\"100\" src=\"https://github.com/dpvreony.png?s=150\"\u003e\n        \u003cbr\u003e\n        \u003ca href=\"https://github.com/dpvreony\"\u003eDavid Vreony\u003c/a\u003e\n        \u003cp\u003eUK\u003c/p\u003e\n      \u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\"\u003e\n        \u003cimg width=\"100\" height=\"100\" src=\"https://github.com/chrispulman.png?s=150\"\u003e\n        \u003cbr\u003e\n        \u003ca href=\"https://github.com/chrispulman\"\u003eChris Pulman\u003c/a\u003e\n        \u003cp\u003eUK\u003c/p\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n### How do I install?\n\n[Always Be NuGetting](https://nuget.org/packages/Splat/). Package contains binaries for:\n\n* .NET Framework 4.6.2, .NET Framework 4.7.2, .NET Standard 2.0, .NET 6.0, and .NET 8.0\n- Works with:\n  * WPF\n  * Windows Forms\n  * WinUI 3\n  * Maui (WinUI, Android, iOS and Mac)\n  * Avalonia\n\n## Detecting whether you're in a unit test runner\n\n```cs\n// If true, we are running unit tests\nModeDetector.InUnitTestRunner();  \n```\n\n## Service Location\n\nSplat provides a simple service location implementation that is optimized for\nDesktop and Mobile applications, while still remaining reasonably flexible.\n\nThere are 2 parts to the locator design:\n\n* **AppLocator.Current** The property to use to **retrieve** services. AppLocator.Current is a static variable that can be set on startup, to adapt Splat to other DI/IoC frameworks. We're currently working from v7 onward to make it easier to use your DI/IoC framework of choice. (see below)\n* **AppLocator.CurrentMutable** The property to use to **register** services\n\nTo get a service:\n\n```cs\n// To get a single service registration\nvar toaster = AppLocator.Current.GetService\u003cIToaster\u003e();\n\n// To get all service registrations\nvar allToasterImpls = AppLocator.Current.GetServices\u003cIToaster\u003e();\n```\n\nLocator.Current is a static variable that can be set on startup, to adapt Splat\nto other DI/IoC frameworks. We're currently working from v7 onward to make it easier to use your DI/IoC framework of choice.\n\nThe default implementation of Service Location also allows new types to be\nregistered at runtime.\n\n```cs\n// Create a new Toaster any time someone asks\nLocator.CurrentMutable.Register(() =\u003e new Toaster(), typeof(IToaster));\n\n// Register a singleton instance\nLocator.CurrentMutable.RegisterConstant(new ExtraGoodToaster(), typeof(IToaster));\n\n// Register a singleton which won't get created until the first user accesses it\nLocator.CurrentMutable.RegisterLazySingleton(() =\u003e new LazyToaster(), typeof(IToaster));\n```\n\n### Dependency Injection Source Generator\nThere is a source generator that will inject constructor and properties. See [here](https://github.com/reactivemarbles/Splat.DI.SourceGenerator) for instructions.\n\n### Dependency Resolver Packages\nFor each of the provided dependency resolver adapters, there is a specific package that allows the service locator to be implemented by another ioc container.\n\n**Note:** When using ReactiveUI and overriding Splat's default behavior, you have to be sure to [initialize ReactiveUI](https://reactiveui.net/docs/handbook/dependency-inversion/custom-dependency-inversion#set-the-locator.current-to-your-implementation) before your container finalizes.\n\nPlease note: If you are adjusting behaviours of Splat by working with your custom container directly. Please read the relevant projects documentation on\nREPLACING the registration. If the container supports appending\\ multiple registrations you may get undesired behaviours, such as the wrong logger factory\nbeing used.\n\n| Container | NuGet | Read Me\n|---------|-------|-------|\n| [Splat.Autofac][SplatAutofacNuGet] | [![SplatAutofacBadge]][SplatAutofacNuGet] | [Setup Autofac][SplatAutofacReadme]\n| [Splat.DryIoc][SplatDryIocNuGet] | [![SplatDryIocBadge]][SplatDryIocNuGet] | [Setup DryIoc][SplatDryIocReadme]\n| [Splat.Microsoft.Extensions.DependencyInjection][SplatMicrosoftNuGet] | [![SplatMicrosoftBadge]][SplatMicrosoftNuGet] | [Setup Microsoft DI][SplatMicrosoftReadme]\n| [Splat.Ninject][SplatNinjectNuGet] | [![SplatNinjectBadge]][SplatNinjectNuGet] | [Setup Ninject][SplatNinjectReadme]\n| [Splat.SimpleInjector][SplatSimpleInjectorNuGet] | [![SplatSimpleInjectorBadge]][SplatSimpleInjectorNuGet] | [Setup Simple Injector][SplatSimpleInjectorReadme] |\n\n[SplatAutofacNuGet]: https://www.nuget.org/packages/Splat.Autofac/\n[SplatAutofacBadge]: https://img.shields.io/nuget/v/Splat.Autofac.svg\n[SplatAutofacReadme]: ./src/Splat.Autofac/README.md\n[SplatDryIocNuGet]: https://www.nuget.org/packages/Splat.DryIoc/\n[SplatDryIocBadge]: https://img.shields.io/nuget/v/Splat.DryIoc.svg\n[SplatDryIocReadme]: ./src/Splat.DryIoc/README.md\n[SplatMicrosoftNuGet]: https://www.nuget.org/packages/Splat.Microsoft.Extensions.DependencyInjection/\n[SplatMicrosoftBadge]: https://img.shields.io/nuget/v/Splat.Microsoft.Extensions.DependencyInjection.svg\n[SplatMicrosoftReadme]: ./src/Splat.Microsoft.Extensions.DependencyInjection/README.md\n[SplatNinjectNuGet]: https://www.nuget.org/packages/Splat.Ninject/\n[SplatNinjectBadge]: https://img.shields.io/nuget/v/Splat.Ninject.svg\n[SplatNinjectReadme]: ./src/Splat.Ninject/README.md\n[SplatSimpleInjectorNuGet]: https://www.nuget.org/packages/Splat.SimpleInjector/\n[SplatSimpleInjectorBadge]: https://img.shields.io/nuget/v/Splat.SimpleInjector.svg\n[SplatSimpleInjectorReadme]: ./src/Splat.SimpleInjector/README.md\n\n## AppBuilder and IModule (AOT-friendly configuration)\n\nWhen targeting AOT or trimming scenarios, you can configure Splat (and consumers such as ReactiveUI) without reflection using the AppBuilder and IModule pattern.\n\n- IModule defines a single Configure(IMutableDependencyResolver) method where you register services.\n- AppBuilder gathers modules and custom registrations, then applies them to a chosen resolver when Build() is called.\n- You can target the current AppLocator.CurrentMutable or an external container that implements IMutableDependencyResolver via UseCurrentSplatLocator().\n\n### Define modules\n\n```cs\nusing Splat;\nusing Splat.Builder;\n\n// App services\npublic sealed class CoreModule : IModule\n{\n    public void Configure(IMutableDependencyResolver resolver)\n    {\n        // register your services here\n        resolver.RegisterLazySingleton(() =\u003e new ApiClient(), typeof(IApiClient));\n        resolver.Register(() =\u003e new MainViewModel(resolver.GetService\u003cIApiClient\u003e()!), typeof(IMainViewModel));\n    }\n}\n\n// Logging (Serilog adapter) - requires Splat.Serilog package\nusing Splat.Serilog;\npublic sealed class SerilogModule : IModule\n{\n    public void Configure(IMutableDependencyResolver resolver)\n    {\n        // assumes Serilog.Log has been configured elsewhere\n        resolver.UseSerilogFullLogger();\n    }\n}\n\n// Cross-platform drawing (IBitmapLoader) - requires Splat.Drawing package\npublic sealed class DrawingModule : IModule\n{\n    public void Configure(IMutableDependencyResolver resolver)\n    {\n        resolver.RegisterPlatformBitmapLoader();\n    }\n}\n```\n\nTip: You can also apply a module immediately to the current locator without a builder:\n\n```cs\nnew CoreModule().Apply(); // calls module.Configure(Locator.CurrentMutable)\n```\n\n### Build your application registrations\n\n```cs\nusing Splat;\nusing Splat.Builder;\n\n// choose the resolver to configure (Locator.CurrentMutable by default)\nnew AppBuilder(Locator.CurrentMutable)\n    .UsingModule(new CoreModule())\n    .UsingModule(new SerilogModule())\n    .UsingModule(new DrawingModule())\n    // ad-hoc registrations can be added too\n    .WithCustomRegistration(r =\u003e r.RegisterConstant(new AppConfig(\"Prod\"), typeof(AppConfig)))\n    .Build();\n```\n\n### Using an external container as the Splat resolver\n\nIf you adapt an external container to Splat first, UseCurrentSplatLocator() ensures later module registrations are applied to that container instance.\n\nMicrosoft.Extensions.DependencyInjection example:\n\n```cs\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\nusing Splat;\nusing Splat.Builder;\nusing Splat.Microsoft.Extensions.DependencyInjection;\nusing Splat.Microsoft.Extensions.Logging;\n\nvar services = new ServiceCollection();\nservices.UseMicrosoftDependencyResolver(); // set AppLocator.Current to MS.DI resolver backed by IServiceCollection\n\n// register framework logging provider that forwards to Splat\nservices.AddLogging(b =\u003e b.AddSplat());\n\n// build container and rebind AppLocator.Current to the built provider\nvar provider = services.BuildServiceProvider();\nprovider.UseMicrosoftDependencyResolver();\n\nnew AppBuilder(AppLocator.CurrentMutable)\n    .UseCurrentSplatLocator() // target whatever AppLocator.CurrentMutable points to (MS.DI in this case)\n    .UsingModule(new CoreModule())\n    .WithCustomRegistration(r =\u003e\n    {\n        // forward Microsoft.Extensions.Logging to Splat\n        var factory = provider.GetRequiredService\u003cILoggerFactory\u003e();\n        r.UseMicrosoftExtensionsLoggingWithWrappingFullLogger(factory);\n    })\n    .Build();\n```\n\nThe same pattern works with other adapters (Autofac, DryIoc, SimpleInjector, Ninject) after setting AppLocator.Current to the adapter’s resolver.\n\n### Extending AppBuilder\n\nAppBuilder is extensible. Override WithCoreServices() to ensure common registrations are always applied before modules:\n\n```cs\nusing Splat;\nusing Splat.Builder;\n\npublic sealed class MyAppBuilder : AppBuilder\n{\n    public MyAppBuilder(IMutableDependencyResolver resolver) : base(resolver) { }\n\n    public override AppBuilder WithCoreServices()\n    {\n        // register core bits once for your app\n        // e.g., default logger, scheduler, or platform services\n        // resolver is obtained per registration in Build(),\n        // so prefer idempotent registrations guarded by HasRegistration\n        return this;\n    }\n}\n\n// usage\nnew MyAppBuilder(Locator.CurrentMutable)\n    .UsingModule(new CoreModule())\n    .Build();\n```\n\nNotes:\n- Build() is idempotent per process: subsequent calls no-op after the first successful build.\n- Prefer RegisterLazySingleton for singletons and check HasRegistration when composing multiple modules.\n\n## Logging\n\nSplat provides a simple logging proxy for libraries and applications to set up.\nBy default, this logging isn't configured (i.e. it logs to the Null Logger). To\nset up logging:\n\n1. Register an implementation of `ILogger` using Service Location.\n1. In the class in which you want to log stuff, \"implement\" the `IEnableLogger`\n   interface (this is a tag interface, no implementation actually needed).\n1. Call the `Log` method to write log entries:\n\n```cs\nthis.Log().Warn(\"Something bad happened: {0}\", errorMessage);\nthis.Log().ErrorException(\"Tried to do a thing and failed\", exception);\n```\n\nFor static methods, `LogHost.Default` can be used as the object to write a log\nentry for. The Static logger uses a different interface from the main logger to allow capture of additional\ncaller context as it doesn't have the details of the class instance etc. when compared to the normal logger.\nTo get the benefit of these you don't need to do much as they are optional parameters at the end of the methods\nthat are utilised by the compiler\\framework. Currently we only capture [CallerMemberName](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callermembernameattribute).\n\n### Available logging adapters\n\nSplat has support for the following logging frameworks\n\n| Target | Package | NuGet |\n|---------|-------|------|\n| Console | [Splat][SplatNuGet] | [![SplatBadge]][SplatNuGet] |\n| Debug | [Splat][SplatNuGet] | [![SplatBadge]][SplatNuGet] |\n| Log4Net | [Splat.Log4Net][SplatLog4NetNuGet] | [![SplatLog4NetBadge]][SplatLog4NetNuGet]  |\n| Microsoft Extensions Logging | [Splat.Microsoft.Extensions.Logging][SplatMicrosoftExtensionsLoggingNuGet] | [![SplatMicrosoftExtensionsLoggingBadge]][SplatMicrosoftExtensionsLoggingNuGet] |\n| NLog | [Splat.NLog][SplatNLogNuGet] | [![SplatNLogBadge]][SplatNLogNuGet] |\n| Serilog | [Splat.Serilog][SplatSerilogNuGet] | [![SplatSerilogBadge]][SplatSerilogNuGet] |\n\n[SplatNuGet]: https://www.nuget.org/packages/Splat/\n[SplatBadge]: https://img.shields.io/nuget/v/Splat.svg\n[SplatLog4NetNuGet]: https://www.nuget.org/packages/Splat.Log4Net/\n[SplatLog4NetBadge]: https://img.shields.io/nuget/v/Splat.Log4Net.svg\n[SplatMicrosoftExtensionsLoggingNuGet]: https://www.nuget.org/packages/Splat.Microsoft.Extensions.Logging/\n[SplatMicrosoftExtensionsLoggingBadge]: https://img.shields.io/nuget/v/Splat.Microsoft.Extensions.Logging.svg\n[SplatNLogNuGet]: https://www.nuget.org/packages/Splat.NLog/\n[SplatNLogBadge]: https://img.shields.io/nuget/v/Splat.NLog.svg\n[SplatSerilogNuGet]: https://www.nuget.org/packages/Splat.Serilog/\n[SplatSerilogBadge]: https://img.shields.io/nuget/v/Splat.Serilog.svg\n\n### Log4Net\n\nFirst configure Log4Net. For guidance see https://logging.apache.org/log4net/release/manual/configuration.html\n\n```cs\nusing Splat.Log4Net;\n\n// then in your service locator initialisation\nLocator.CurrentMutable.UseLog4NetWithWrappingFullLogger();\n```\n\nThanks to @dpvreony for first creating this logger.\n\n### Microsoft.Extensions.Logging\n\nFirst configure Microsoft.Extensions.Logging. For guidance see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/\n\n```cs\nusing Splat.Microsoft.Extensions.Logging;\n\n// note: this is different from the other adapter extension methods\n//       as it needs knowledge of the logger factory\n//       also the \"container\" is how you configured the Microsoft.Logging.Extensions\nvar loggerFactory = container.Resolve\u003cILoggerFactory\u003e();\n// in theory it could also be\n// var loggerFactory = new LoggerFactory();\n\n\n/// then in your service locator initialisation\nLocator.CurrentMutable.UseMicrosoftExtensionsLoggingWithWrappingFullLogger(loggerFactory);\n```\n\nThanks to @dpvreony for first creating this logger.\n\n### NLog\n\nFirst configure NLog. For guidance see https://github.com/nlog/nlog/wiki/Tutorial and https://github.com/nlog/nlog/wiki/Configuration-file\n\n```cs\nusing Splat.NLog;\n\n//  then in your service locator initialisation\nLocator.CurrentMutable.UseNLogWithWrappingFullLogger();\n```\n\nThanks to @dpvreony for first creating this logger.\n\n### Serilog\n\nFirst configure Serilog. For guidance see https://github.com/serilog/serilog/wiki/Configuration-Basics\n\n```cs\nusing Splat.Serilog;\n\n// Then in your service locator initialisation\nLocator.CurrentMutable.UseSerilogFullLogger()\n```\n\nThanks to @joelweiss for first creating this logger.\n\n## Cross platform drawing\n\n| Target | Package | NuGet |\n|---------|-------|------|\n| Splat.Drawing | [Splat.Drawing][SplatDrawingNuGet] | [![SplatDrawingBadge]][SplatDrawingNuGet] |\n\n[SplatDrawingNuGet]: https://www.nuget.org/packages/Splat.Drawing/\n[SplatDrawingBadge]: https://img.shields.io/nuget/v/Splat.Drawing.svg\n\n### Using Cross-Platform Colors and Geometry\n\n\n```cs\n// This System.Drawing class works, even on WinRT where it's not supposed to exist\n// Also, this works in a Portable Library, in your ViewModel\nProfileBackgroundAccentColor = Color.FromArgb(255, 255, 255, 255);\n```\n\nLater, in the view, we can use it:\n\n```\nImageView.Background = ViewModel.ProfileBackgroundAccentColor.ToNativeBrush();\n```\n\nIf targeting iOS or Mac in a cross-platform solution (e.g. iOS \u0026 Android), use\nthe SplatColor class to define colors in your netstandard library\n(since Cocoa doesn't include System.Drawing.Color).\n\n```cs\n// In a netstandard library\nSplatColor BackgroundColor = SplatColor.Red;\n```\n\n```\n// From an iOS project\nUIColor bgColor = ViewModel.BackgroundColor.ToNative();\n```\n\n```\n// From an Android project\nAndroid.Graphics.Color bgColor = ViewModel.BackgroundColor.ToNative();\n```\n### Cross-platform Image Loading\n\nYou can register with the Splat locators.\n\n```cs\nLocator.CurrentMutable.RegisterPlatformBitmapLoader();\n```\n\nYou can then load your images in a cross platform way:\n\n```cs\n//\n// Load an Image\n// This code even works in a Portable Library\n//\n\nvar wc = new WebClient();\nStream imageStream = wc.OpenRead(\"http://octodex.github.com/images/Professortocat_v2.png\");\n\n// IBitmap is a type that provides basic image information such as dimensions\nIBitmap profileImage = await BitmapLoader.Current.Load(imageStream, null /* Use original width */, null /* Use original height */);\n```\n\nThen later, in your View:\n\n```cs\n// ToNative always converts an IBitmap into the type that the platform\n// uses, such as UIBitmap on iOS or BitmapSource in WPF\nImageView.Source = ViewModel.ProfileImage.ToNative();\n```\n\nImages can also be loaded from a Resource. On Android, this can either be a\nResource ID casted to a string, or the name of the resource *as* as string\n(optionally including the extension).\n\n```cs\nvar profileImage = await BitmapLoader.Current.LoadFromResource(\"DefaultAvatar.png\", null, null);\n```\n\nBitmaps can also be created and saved - actually *drawing* on the image is\nbeyond the scope of this library, you should do this in your view-specific\ncode.\n\n```cs\nvar blankImage = BitmapLoader.Current.Create(512.0f, 512.0f);\nawait blankImage.Save(CompressedBitmapFormat.Png, 0.0, File.Open(\"ItsBlank.png\"));\n```\n### Detecting if you're in design mode\n\n```cs\n// If true, we are running inside Blend, so don't do anything\nPlatformModeDetector.InDesignMode();\n```\n\n### Application Performance Monitoring\n\nApplication Performance Monitoring is split into the follow sections\n\n* Error Reporting\n* Feature Usage Tracking\n* View Tracking\n\nThe table below shows the support across various APM packages\n\n| Product | Package | NuGet | Maturity Level | Error Reporting | Feature Usage Tracking | View Tracking |\n|----|----|----|----|----|----|----|\n| Appcenter | [Splat.AppCenter][SplatAppcenterNuGet] | [![SplatAppcenterBadge]][SplatAppcenterNuGet] | Alpha | TODO | Native | Native |\n| Application Insights | [Splat.ApplicationInsights][SplatApplicationInsightsNuGet] | [![SplatApplicationInsightsBadge]][SplatApplicationInsightsNuGet] | Alpha | TODO | Native | Native |\n| Exceptionless | [Splat.Exceptionless][SplatExceptionlessNuGet] | [![SplatExceptionlessBadge]][SplatExceptionlessNuGet] | Alpha | TODO | Native | By Convention |\n| New Relic | N\\A | N\\A | Not Started | TODO | TODO | TODO\n| OpenTrace | N\\A | N\\A | Not Started |TODO | TODO | TODO\n| Raygun | [Splat.Raygun][SplatRaygunNuGet] | [![SplatRaygunBadge]][SplatRaygunNuGet] | Prototype | TODO | By Convention | By Convention |\n\n[SplatAppcenterNuGet]: https://www.nuget.org/packages/Splat.Appcenter/\n[SplatAppcenterBadge]: https://img.shields.io/nuget/v/Splat.Appcenter.svg\n[SplatApplicationInsightsNuGet]: https://www.nuget.org/packages/Splat.ApplicationInsights/\n[SplatApplicationInsightsBadge]: https://img.shields.io/nuget/v/Splat.ApplicationInsights.svg\n[SplatExceptionlessNuGet]: https://www.nuget.org/packages/Splat.Exceptionless/\n[SplatExceptionlessBadge]: https://img.shields.io/nuget/v/Splat.Exceptionless.svg\n[SplatRaygunNuGet]: https://www.nuget.org/packages/Splat.Raygun/\n[SplatRaygunBadge]: https://img.shields.io/nuget/v/Splat.Raygun.svg\n\n#### Goals of the Splat APM feature\n\n* To sit on top of existing APM libaries using native features where possible, or by using a common convention that gives parity in behaviour.\n** Where there is a convention behaviour it will be detailed under the relevant frameworks documentation.\n* To define basic behaviours that are dropped into consuming libraries, for example with ReactiveUI\n** Commands\n** ViewModels\n** Views\n\n#### Getting started with APM with Splat\n\nSplat comes with a default implementation that pushes events into your active Splat logging framework. This allows for design and testing prior to hooking up a full APM offering.\n\n#### Error Reporting\n\nTODO\n\n#### Feature Usage Tracking\n\nThe most basic ability for feature usage tracking is to implement the Splat.ApplicationPerformanceMonitoring.IEnableFeatureUsageTracking interface. This has the same behaviour as the logging interface and allows Splat to inject whichever\nAPM platform is registered with the ServiceLocator at initialization.\n\n```cs\n        /// \u003csummary\u003e\n        /// Dummy object for testing IEnableFeatureUsageTracking.\n        /// \u003c/summary\u003e\n        public sealed class TestObjectThatSupportsFeatureUsageTracking : IEnableFeatureUsageTracking\n        {\n\t\t\tpublic async Task SomeFeatureIWantToTrack()\n\t\t\t{\n                using (var trackingSession = this.FeatureUsageTrackingSession(\"featureName\"))\n                {\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\t// do some work here.\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception exception)\n\t\t\t\t\t{\n\t\t\t\t\t\ttrackingSession.OnException(exception);\n\t\t\t\t\t}\n                }\n\t\t\t}\n        }\n```\n\nSplat also has the notion of subfeatures, some APM platforms support this natively, others have been done by convention, which will be explained in the relevant library.\nSplat itself does not dictate when these should be used. It's up to you. You may have a primary feature (such as a search view) and then track buttons, etc. on that view\nas subfeatures.\n\n```cs\n        /// \u003csummary\u003e\n        /// Dummy object for testing IEnableFeatureUsageTracking.\n        /// \u003c/summary\u003e\n        public sealed class TestObjectThatSupportsFeatureUsageTracking : IEnableFeatureUsageTracking\n        {\n\t\t\tpublic async Task SomeFeatureIWantToTrack()\n\t\t\t{\n                using (var mainFeature = this.FeatureUsageTrackingSession(\"featureName\"))\n                {\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tawait DoSubFeature(mainFeature).ConfigureAwait(false);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception exception)\n\t\t\t\t\t{\n\t\t\t\t\t\tmainFeature.OnException(exception);\n\t\t\t\t\t}\n                }\n\t\t\t}\n\n\t\t\tpublic async Task SomeFeatureIWantToTrack(IFeatureUsageTrackingSession parentFeature)\n\t\t\t{\n                using (var subFeature = parentFeature.SubFeature(\"subFeatureName\"))\n                {\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\t// do some work here.\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception exception)\n\t\t\t\t\t{\n\t\t\t\t\t\tsubFeature.OnException(exception);\n\t\t\t\t\t}\n                }\n\t\t\t}\n        }\n```\n\n#### View Tracking\n\nTODO\n\n#### Configuring Appcenter\n\nFirst configure Appcenter. For guidance see https://docs.microsoft.com/en-us/appcenter/diagnostics/enabling-diagnostics\n\n```cs\nusing Splat.AppCenter;\n\n// then in your service locator initialisation\nLocator.CurrentMutable.UseAppcenterApm();\n```\n\n#### Configuring Application Insights\n\nFirst configure Application Insights. For guidance see https://docs.microsoft.com/en-us/azure/azure-monitor/app/worker-service\n\n```cs\nusing Splat.ApplicationInsights;\n\n// then in your service locator initialisation\nLocator.CurrentMutable.UseApplicationInsightsApm();\n```\n\n#### Configuring Exceptionless\n\nFirst configure Exceptionless. For guidance see https://github.com/exceptionless/Exceptionless/wiki/Getting-Started\n\n```cs\nusing Splat.Exceptionless;\n\n// then in your service locator initialisation\nLocator.CurrentMutable.UseExceptionlessApm();\n```\n\n#### Configuring New Relic\n\nNew Relic support isn't currently available.\n\n#### Configuring OpenTrace\n\nOpenTrace support isn't currently available.\n\n#### Configuring Raygun\n\nFirst configure Raygun. For guidance see TODO\n\n```cs\nusing Splat.Raygun;\n\n// then in your service locator initialisation\nLocator.CurrentMutable.UseRaygunApm();\n```\n\n#### Testing and developing the APM functionality\n\nThe unit tests for this functionality do not generate activity to the relevant platform.\nThe integration tests DO SEND TEST DATA to the relevant platforms, so they need to have\nthe user-secrets configured. There is a script in the \\scripts\\inttestusersecrets.cmd\nthat shows how to set the relevant secrets up.\n\n## Contribute\n\nSplat is developed under an OSI-approved open source license, making it freely usable and distributable, even for commercial use. We ❤ the people who are involved in this project, and we’d love to have you on board, especially if you are just getting started or have never contributed to open-source before.\n\nSo here's to you, lovely person who wants to join us — this is how you can support us:\n\n* [Responding to questions on StackOverflow](https://stackoverflow.com/questions/tagged/splat)\n* [Passing on knowledge and teaching the next generation of developers](http://ericsink.com/entries/dont_use_rxui.html)\n* Submitting documentation updates where you see fit or lacking.\n* Making contributions to the code base.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactiveui%2Fsplat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freactiveui%2Fsplat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactiveui%2Fsplat/lists"}