{"id":13710246,"url":"https://github.com/TinyStuff/TinyPubSub","last_synced_at":"2025-05-06T18:34:45.733Z","repository":{"id":8378839,"uuid":"58031192","full_name":"TinyStuff/TinyPubSub","owner":"TinyStuff","description":"Worlds smallest pub/sub thingy created mostly for Xamarin Forms but should also work else where...","archived":false,"fork":false,"pushed_at":"2022-11-27T19:50:17.000Z","size":1856,"stargazers_count":25,"open_issues_count":6,"forks_count":3,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-05-01T17:15:49.937Z","etag":null,"topics":["android","dotnet","dotnet-standard","duck","ios","xamarin","xamarin-forms"],"latest_commit_sha":null,"homepage":null,"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/TinyStuff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-05-04T07:18:06.000Z","updated_at":"2024-10-09T14:23:32.000Z","dependencies_parsed_at":"2023-01-11T18:46:21.715Z","dependency_job_id":null,"html_url":"https://github.com/TinyStuff/TinyPubSub","commit_stats":null,"previous_names":["johankson/tinypubsub"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TinyStuff%2FTinyPubSub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TinyStuff%2FTinyPubSub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TinyStuff%2FTinyPubSub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TinyStuff%2FTinyPubSub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TinyStuff","download_url":"https://codeload.github.com/TinyStuff/TinyPubSub/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252744998,"owners_count":21797714,"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":["android","dotnet","dotnet-standard","duck","ios","xamarin","xamarin-forms"],"created_at":"2024-08-02T23:00:53.521Z","updated_at":"2025-05-06T18:34:44.458Z","avatar_url":"https://github.com/TinyStuff.png","language":"C#","funding_links":[],"categories":["Plugins"],"sub_categories":[],"readme":"# TinyPubSub\n\nA really small pub/sub thingy created for .net!\nIn memory, in-process, tiny and shiny. Sync publish, fire-and-forget publish, async publish, non-generic and generic publish/subscribe.\n\n## Roadmap\n\n- V2 is under development - the aim is to modernize the lib to make use of new C# language features and runtimes.\n- TinyPubSub 2.0.0-preview4 is available as a nuget (\u003chttps://www.nuget.org/packages/TinyPubSub/2.0.0-preview4\u003e)\n\n## Build status\n\n[![CI](https://github.com/TinyStuff/TinyPubSub/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/TinyStuff/TinyPubSub/actions/workflows/main.yml)\n\n## TLDR\n\nWhat does it do? TinyPubSub lets you run code when an event happens.\n\nInit - if you are using forms, install the TinyPubSub.Forms package\n\n```csharp\n\n// If you are using Xamarin\npublic App ()\n{\n    // If you are using forms\n    TinyPubSubLib.TinyPubSubForms.Init(this);\n\n    // The root page of your application\n    var navPage = new NavigationPage(new MainView());\n\n    // If you don't use the TinyPubSubForms.Init(..) method you can register the events yourself like this\n    // navPage.Popped += (object sender, NavigationEventArgs e) =\u003e TinyPubSub.Unsubscribe(e.Page.BindingContext);\n    // navpage.PoppedToRoot += (s, args) =\u003e\n    //\t\t{\n    //\t\t\tvar poppedToRootEventArgs = args as PoppedToRootEventArgs;\n    //\t\t\tforeach (var poppedPage in poppedToRootEventArgs.PoppedPages)\n    //\t\t\t{\n    //\t\t\t\tTinyPubSub.Unsubscribe(poppedPage.BindingContext);\n    //\t\t\t}\n    //\t\t};\n    MainPage = navPage;\n}\n\n```\n\nSubscribe\n\n```csharp\n// The forms way (from ViewModel)\nTinyPubSub.Subscribe(this, \"new-duck-added\", () =\u003e RebindDuckGui());\n\n// Non-forms way\nTinyPubSub.Subscribe(\"new-duck-added\", () =\u003e RebindDuckGui());\n\n// The forms way (from ViewModel) with an argument\nTinyPubSub.Subscribe(this, \"new-duck-added\", (x) =\u003e RebindDuckGui(nameofduck: x));\n\n// Non-forms way\nTinyPubSub.Subscribe(\"new-duck-added\", (x) =\u003e RebindDuckGui(nameofduck: x));\n\n// The forms way with a typed argument, where MessageModel can be any class you'd like\nTinyPubSub.Subscribe\u003cMessageModel\u003e(this, \"new-duck-added\", (model) =\u003e RebindDuckGui(nameofduck: model.Name));\n\n// Subscription by attribute\npublic class MyClass\n{\n    public MyClass()\n    {\n        // Register it directly in the class or somewhere else\n        TinyPubSub.Register(this);\n    }\n\n    [TinySubscribe(\"new-duck-added\")]\n    public void DuckAdded(MessageModel duck)\n    {\n        // Do something with the duck\n    }\n}\n\n// Subscription with control to determine if we should prevent the next subscriber on the same channel to handle the event\n// NOTE: Must be triggered by any PublishControlled* method to work. The _ argument is because we need an argument even if it's null\nTinyPubSub.Subscribe(\"new-duck-added\", (_, args) =\u003e\n{\n    args.HaltExecution = true;\n});\n\n```\n\nPublish\n\n```csharp\n// Without argument\nTinyPubSub.Publish(\"new-duck-added\");\n\n// With argument\nTinyPubSub.Publish(\"new-duck-added\", \"Ducky McDuckface\");\n\n// As a task (that runs later on)\nTinyPubSub.PublishAsTask(\"new-duck-added\");\n\n// As a task (that runs later on) with argument\nTinyPubSub.PublishAsTask(\"new-duck-added\", \"Ducky McDuckface\");\n\n// Async\nawait TinyPubSub.PublishAsync(\"new-duck-added\");\n\n// Async with argument\nawait TinyPubSub.PublishAsync(\"new-duck-added\", \"Ducky McDuckface\");\n\n// Publish with a typed argument\nTinyPubSub.Publish(\"new-duck-added\", new MessageModel() { Name = \"Ducky\" });\n\n// Publish and wait for the result - can be used to make sure someone has handled it\nvar result = TinyPubSub.PublishControlled(\"new-duck-added\");\nvar successful = result.Handled;\n\n// Publish controlled with a typed argument\nvar result = TinyPubSub.PublishControlled(\"new-duck-added\", new MessageModel() { Name = \"Ducky\" });\nvar successful = result.Handled;\n\n```\n\n## WHY SHOULD I USE IT?\n\n\u003cimg align=\"right\" src=\"http://i.imgur.com/p0xJYYC.png\"\u003e\n\nThis lib should be used when you want to easily register to events within a small app. It's not meant for data transfer (at least not at this point), it's not thread safe and it's never going to be finished. :)\n\n### EXAMPLE\n\nI have a view that shows ducks. This is my main view. When I edit ducks on another view and the main page is covered I still want the main view to get new ducks before I return to it. I don't want the MainPage to start loading when it gets displayed.\n\nThe main view can listen for the \"ducks-added\" event and run an action when that happens. When I create a new function in the system I can trust that if I publish an event on the \"ducks-added\" channel, all my other views subscribing to that event will get notified.\n\nAnd by following some patterns regarding the NavigationPage(...) we can also make sure that the subscriptions are removed when the view go out of scope.\n\nIt's designed with MVVM in mind. Subscription to new events should be done in the ViewModel and the Unsubscription should be made automatically when pages are popped (see usage).\n\nIt's not meant to solve world problems so if you want a robust and mature pub/sub framework then there are plenty others out there to use. This is bare metal.\n\n## STATE\n\nRelease (1.2.x for TinyPubSub and 1.2.x for TinyPubSub.Forms)\n\n## NUGET\n\nPackage 1.0.x and 1.1.x are built for profile 259. Packaged 1.2.x are built using netstandard 1.0.\n\n## EXCEPTION HANDLING\n\nExceptions are sent back to you through TinyPubSub itself.\n\n```csharp\nTinyPubSub.Subscribe(this, TinyExceptionDefaultChannel, (TinyException ex) =\u003e { HandleException(ex) });\n```\n\nYou can also send an error handler directly into the publish call.\n\n```csharp\nTinyPubSub.Publish(this, \"new-duck-added\", () =\u003e HandleDuck(), onError: (ex, s) =\u003e\n    {\n        // ex is the Exception that was thrown\n        // s is the subscription that failed\n    });\n```\n\n### Forms\n\nIf you are using TinyPubSub from Xamarin forms, install this package and call the init method as described at the top. You don't have to install any other package.\n\n[https://www.nuget.org/packages/tinypubsub.forms](https://www.nuget.org/packages/tinypubsub.forms)\n\n### Vanilla\n\n[https://www.nuget.org/packages/tinypubsub](https://www.nuget.org/packages/tinypubsub)\n\n## USAGE\n\nTo subscribe, simply register what \"channel\" (we call them channels) you would like to subscribe to.\n\n```csharp\nTinyPubSub.Subscribe(\"new-duck-added\", () =\u003e { RebindDuckGui(); });\n```\n\nAnd in another part of you application, publish events to execute the actions that are registered for that channel.\n\n```csharp\n\n// Sync publish with or without argument\nTinyPubSub.Publish(\"new-duck-added\", \"optional argument\");\n\n// As a task (fire and forget)\nTinyPubSub.PublishAsTask(\"new-duck-added\", \"optional argument\");\n\n// As an async call\nawait TinyPubSub.PublishAsync(\"new-duck-added\", \"optional argument\");\n```\n\n### WHAT ABOUT MEMORY ISSUES?\n\nIf you are using the Xamarin Forms version (TinyPubSub.Forms) and call the Init(..) method as described at the top of this page, then you have no worries. The lib will take care of deregistration just in time given that you take two things into consideration.\n\n- You register your subscriptions from the ViewModel (whatever object you bind to BindingContext) or the page it self\n- You pass in `this` into the subscription registration like `TinyPubSub.Subscribe(this, \"new-duck-added\", () =\u003e { RebindDuckGui(); });`\n\nIf you use the vanilla version, continue reading.\n\n#### Plan A - tags\n\nWhen subscribing you get a tag.\n\n```c#\nvar tag = TinyPubSub.Subscribe(\"new-duck-added\", () =\u003e { RebindDuckGui(); });\n```\n\nAnd when you are done you unsubscribe with that tag.\n\n```c#\nTinyPubSub.Unsubscribe(tag);\n```\n\n#### Plan B - object refs\n\nThis is a more suitable option for Xamarin MVVM (which is really the reason for this projects existance). I don't like having to keep track of tags. So instead we pass a reference to an object that counts as the owner of the subscription. Usually this and most usually a ViewModel. This way we can subscribe to several channels.\n\n```c#\nTinyPubSub.Subscribe(this, \"new-duck-added\", () =\u003e { RebindDuckGui(); });\nTinyPubSub.Subscribe(this, \"old-duck-removed\", () =\u003e { RebindDuckGui(); });\n```\n\nAnd when the view is done (if we're talking MVVM) then the unsubscription could look like this.\n\n```c#\nTinyPubSub.Unsubscribe(this);\n```\n\nOr specifically in Xamarin Forms\n\n```c#\nTinyPubSub.Unsubscribe(this.BindingContext); // if this is a View and the Binding context the view model\n```\n\nThe tricky part is still knowing when the view is done. One way is to hook up to the navigation page Popped and PoppedToRoot (if Forms, but then just use TinyPubSub.Forms package instead).\n\n```c#\n// The root page of your application\nvar navPage = new NavigationPage(new MainView());\nnavPage.Popped += (object sender, NavigationEventArgs e) =\u003e TinyPubSub.Unsubscribe(e.Page.BindingContext);\nnavpage.PoppedToRoot += (s, args) =\u003e\n\t\t\t{\n\t\t\t\tvar poppedToRootEventArgs = args as PoppedToRootEventArgs;\n\t\t\t\tforeach (var poppedPage in poppedToRootEventArgs.PoppedPages)\n\t\t\t\t{\n\t\t\t\t\tTinyPubSub.Unsubscribe(poppedPage.BindingContext);\n\t\t\t\t}\n\t\t\t};\nMainPage = navPage;\n```\n\n\u003cdel\u003eThis works as long as PopToRoot isn't called and you are more than one level deep in the navigation stack. There is also a NavigationPage.PoppedToRoot event, but looking at the Xamarin Forms code it simply clears the children without calling popped for each page. I've started a thread about this at the xamarin forums. \u003c/del\u003e\n\nI got some new code into the Xamarin Forms Core Navigation stuff so now we can get information on what pages that are popped.\n\n# CI\n\nPipeline was inspired by this post: https://www.jamescroft.co.uk/how-to-build-publish-nuget-packages-with-github-actions/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTinyStuff%2FTinyPubSub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTinyStuff%2FTinyPubSub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTinyStuff%2FTinyPubSub/lists"}