{"id":23543850,"url":"https://github.com/beatthat/notifications","last_synced_at":"2026-05-18T00:31:43.518Z","repository":{"id":144017152,"uuid":"101001014","full_name":"beatthat/notifications","owner":"beatthat","description":"Global system for pub/sub notification events by string 'type'.","archived":false,"fork":false,"pushed_at":"2020-05-06T06:50:11.000Z","size":90,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-13T21:31:53.788Z","etag":null,"topics":["loosely-coupled-design","message-api","messaging","notifications","observer","observer-pattern","pubsub","unity3d"],"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/beatthat.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-08-21T23:36:51.000Z","updated_at":"2020-08-03T07:08:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"840221eb-7efb-44d1-a039-2a8c21b5078b","html_url":"https://github.com/beatthat/notifications","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/beatthat/notifications","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatthat%2Fnotifications","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatthat%2Fnotifications/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatthat%2Fnotifications/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatthat%2Fnotifications/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beatthat","download_url":"https://codeload.github.com/beatthat/notifications/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beatthat%2Fnotifications/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33160455,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T22:39:12.733Z","status":"ssl_error","status_checked_at":"2026-05-17T22:39:10.741Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["loosely-coupled-design","message-api","messaging","notifications","observer","observer-pattern","pubsub","unity3d"],"created_at":"2024-12-26T07:11:50.517Z","updated_at":"2026-05-18T00:31:43.502Z","avatar_url":"https://github.com/beatthat.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003ca name=\"readme\"\u003e\u003c/a\u003enotifications\n\nNotifications are a flexible, global pub/sub message system.\n\n## Install\n\nFrom your unity project folder:\n\n```bash\nnpm init --force \u0026\u0026 \\\nnpm install beatthat/notifications\n```\n\nThe package and all its dependencies will be installed under Assets/Plugins/packages.\n\nIn case it helps, a quick video of the above: https://youtu.be/Uss_yOiLNw8\n\n## USAGE\n\nIf you're using this notifications package, you probably encountered it through other `beatthat` unity3d packages that leverage notifications and also simplify their use. Will give some examples of those packages further down, but for now if you were using Notifications without any other support it would look like this:\n\n```csharp\nusing BeatThat.Notifications;\n\npublic static class StatusNotifications\n{\n    /// Notifications are identified by string types\n    /// Usually, you want to define those types as constants somewhere\n    public const string STATUS_UPDATED = \"STATUS_UPDATED\";\n}\n\npublic class StatusObserver : MonoBehavior\n{\n    void Start()\n    {\n        NotificationBus.Add\u003cstring\u003e(StatusNotifications.STATUS_UPDATED, this.OnStatusUpdated);\n    }\n\n    void OnStatusUpdated(string newStatus)\n    {\n        Debug.Log(\"got new status: \" + newStatus);\n    }\n}\n\npublic class StatusPublisher : MonoBehavior\n{\n    public string status;\n\n    public void SetStatus(string newStatus) \n    {\n        this.status = d;\n        NotificationsBus.Send(StatusNotifications.STATUS_UPDATED, newStatus)\n    }\n}\n```\n\nThe example above is very contrived but the main idea is that `StatusObserver` can get status updates without needing to know anything about `StatusPublisher`. Instead they both depend on shared notification type.\n\n## A Practical Example: State Stores\n\nThe example above is really a simplication of State Stores, which are a common use case for notificitions. The basic is to have a global singleton that manages some state item and then observers of that state that can both access the state value and subscribe to on-update notifications.\n\nA more usable version of the status-update example can be built using the [state-stores](https://github.com/beatthat/state-stores) package (in concert with a few other packages I will detail below)\n\n```csharp\n/// pretend we have a slightly more complex \n/// StatusData struct for our state\npublic struct StatusData\n{\n    public string status;\n    public bolean isHappy;\n}\n\nusing BeatThat.Service;\nusing BeatThat.StateStores;\n// register this singleton as implementation \n// of interface HasState\u003cStatusData\u003e (see beatthat/services below)\n[RegisterService(HasState\u003cStatusData\u003e)]\npublic class StatusStore : StateStore\u003cStatusData\u003e \n{\n    // Will expose the StatusData state as property `stateData`\n    // Not shown how here, but just assume that state can be updated\n}\n\n\nusing BeatThat.Controllers;\nusing BeatThat.DependencyInjection;\nusing BeatThat.StateStores;\npublic class StatusObserver : Controller \n/// extending Controller here mainly gives support for dependency-injection \n/// and simplified notificaton binding\n{\n    override protected void GoController() // called after dependencies injected\n    {\n        /// Bind is a wrapper for NotificationBus.Add \n        /// that handles cleanup, i.e. it makes sure \n        /// the registered callback is removed when this controller goes away\n        Bind(State\u003cStatusData\u003e.UPDATED, this.OnStatusUpdated);\n    }\n\n    void OnStatusUpdated()\n    {\n        StatusData s = this.statusStore.stateData; // t\n        Debug.Log(\"got new status: \" + s.status + \" with isHappy \" + s.isHappy);\n    }\n\n    // dependency injection will set this property to our singleton service\n    // based upon matching the registered interface HasState\u003cStatusData\u003e\n    [Inject] HasState\u003cStatusData\u003e statusStore { get; set; }\n}\n\n```\n\nFor more details on the packages used above see:\n\n\n#### The [service](https://github.com/beatthat/service) package manages a container of global singleton services\n\nThe `[RegisterService]` attribute above triggers the creation of a global singleton registered for lookup by interface `HasState\u003cStatusData\u003e`\n\n\n#### The [dependency-injection](https://github.com/beatthat/dependency-injection) package assigns references to service singletons\n\nThe `[Inject]` causes that property to be assigned with the registered `HasState\u003cStatusData\u003e` singleton\n\n#### The [controllers](https://github.com/beatthat/controllers) package simplifies use of dependency injection and notifications\n\nExtending `Controller` gives out-of-box support for Dependency Injection and simplified notificaton binding. It isn't necessary to extend controller though. For example, you could alternatively enable dependency injection on a plain MonoBehavior like this:\n\n```csharp\nvoid Start()\n{\n    // Something needs to call DependencyInjection.InjectDependencies.\n    // The Controller base class would have done this for you\n    BeatThat.DependencyInjection.InjectDependencies.On(this);\n}\n```\n\n#### Making sure to cleanup/unregister notification listeners\n\nIf you're listening for notifications from a class that doesn't live forever0--e.g. a screen--it's important to always unregister any listeners attached to the `NotificationBus`. For example, if you have a screen that's listening for those `StatusData` notifications above, and then the user exits/destroys that screen, it will cause errors and memory leaks if the (now zombie/destroyed) screen continues receiving notifications. \n\nThis is another reason we use that `Controller` base class above. Because it has a `Bind(string notificationType, System.Action callback)` function that takes care of that cleanup for you.\n\nIf you were making a screen from raw MonoBehavior and wanted to implement register + cleanup of noticiations properly, it would look like this:\n\n```csharp\nusing BeatThat.Notifications;\nclass MyScreen : MonoBehavior\n{\n    private NotificationBinding binding;\n    void OnEnable() {\n        this.binding = NotificationBus.Add(\"somenotification\", this.OnNotification);\n    }\n\n    void OnNotification() {\n        // do whatever\n    }\n\n    void OnDisable() {\n        if(this.binding != null) {\n            this.binding.Unbind();\n            this.binding = null;\n        }\n    }\n}\n```\n\n...again for comparison, if we used the `Controller` base class the same thing would look like this:\n```csharp\nusing BeatThat.Controllers;\nusing BeatThat.Notifications;\nclass MyScreen : Controller\n{\n    override protected void OnGoController() {\n        // we don't need to worry about cleaning up,\n        // controller does that for us\n        Bind(\"somenotification\", this.OnNotification);\n    }\n\n    void OnNotification() {\n        // do whatever\n    }\n}\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeatthat%2Fnotifications","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeatthat%2Fnotifications","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeatthat%2Fnotifications/lists"}