Type-safe event handling for Swift

Related tags

Event emitter-kit
Overview

emitter-kit v5.2.2

stable CocoaPods Compatible Carthage Compatible Platform

A replacement for NSNotificationCenter#addObserver and NSObject#addObserver that is type-safe and not verbose.

import EmitterKit

// A generic event emitter (but type-safe)!
var event = Event<T>()

// Any emitted data must be the correct type.
event.emit(data)

// This listener will only be called once.
// You are *not* required to retain it.
event.once { data: T in
  print(data)
}

// This listener won't stop listening;
// unless you stop it manually,
// or its Event<T> is deallocated.
// You *are* required to retain it.
var listener = event.on { data: T in
  print(data)
}

// Stop the listener manually.
listener.isListening = false

// Restart the listener (if it was stopped).
listener.isListening = true

 

Targeting

A target allows you to associate a specific AnyObject with an emit call. This is useful when emitting events associated with classes you can't add properties to (like UIView).

When calling emit with a target, you must also call on or once with the same target in order to receive the emitted event.

let myView = UIView()
let didTouch = Event<UITouch>()

didTouch.once(myView) { touch in
  print(touch)
}

didTouch.emit(myView, touch)

 

NSNotification

The Notifier class helps when you are forced to use NSNotificationCenter (for example, if you want to know when the keyboard has appeared).

// You are **not** required to retain this after creating your listener.
var event = Notifier(UIKeyboardWillShowNotification)

// Handle NSNotifications with style!
listener = event.on { (notif: Notification) in
  print(notif.userInfo)
}

 

Key-Value Observation (KVO)

// Any NSObject descendant will work.
var view = UIView()

// "Make KVO great again!" - Donald Trump
listener = view.on("bounds") { (change: Change<CGRect>) in
  print(change)
}

 

Thread Safety

⚠️ None of the classes in EmitterKit are thread-safe!

The following actions must be done on the same thread, or you need manual locking:

  • Emit an event
  • Add/remove a listener
  • Set the isListening property of a listener

 

v5.2.2 changelog

  • Fixed protocol casting (#60)

v5.2.1 changelog

  • Fix Carthage compatibility for non iOS platforms

v5.2.0 changelog

  • Added the Event.getListeners method
  • Listeners are now always called in the order they were added
  • Event.emit() can be called without an argument
  • Carthage support has been improved

v5.1.0 changelog

  • The NotificationListener class now takes a Notification instead of an NSDictionary.

  • A NotificationListener without a target will now receive every Notification with its name, regardless of the value of notif.object.

v5.0.0 changelog

  • Swift 3.0 + Xcode 8.0 beta 6 support

  • The Signal class was removed. (use Event<Void> instead)

  • The Emitter abstract class was removed.

  • The EmitterListener class was renamed EventListener<T>.

  • The Event<T> class no longer has a superclass.

  • The Notification class was renamed Notifier (to prevent collision with Foundation.Notification).

  • The on and once methods of Event<T> now return an EventListener<T> (instead of just a Listener)

  • The on and once methods of Notifier now return an NotificationListener (instead of just a Listener)

  • The on and once methods of NSObject now return an ChangeListener<T> (instead of just a Listener)

  • The keyPath, options, and object properties of ChangeListener<T> are now public.

  • A listenerCount: Int computed property was added to the Event<T> class.

  • An event: Event<T> property was added to the EventListener<T> class.

The changelog for older versions can be found here.

Comments
  • Call listeners in the order they were added

    Call listeners in the order they were added

    Listeners should be called in the order they were added. Currently they are stored in a Dictionary (with no defined order).

    Example:

    let didSave = Event<Void>()
            
    let listener1 = didSave.on {
       print("1")
    }
            
    let listener2 = didSave.on {
        print("2")
    }
            
    didSave.emit()
    

    The output may be:

    1
    2
    

    or

    2
    1
    

    This may be a pretty big problem when actions need to be done in the certain order. E.g. we may listen to an event in the same class where it's also located. The listener would be set in the constructor and I'd expect that this is the first listener that would get called. After that would be call other listeners (that were set let's say out of this class).

    bug help wanted 
    opened by Mattijah 10
  • Not receiving event when using 'on'

    Not receiving event when using 'on'

    Hi there,

    I'm trying to emit an event every time I update my array so I can reload a specific row in my table view. So far in other areas of my app I've been using 'once' without any problems but now I need to use 'on' instead of 'once' and for some reason it's not working...

    I'm probably just missing something small here but I just can figure it out.

    Hope you can help, Thanks

    opened by ashmore11 6
  • Setting SWIFT_INSTALL_OBJ_HEADER to YES

    Setting SWIFT_INSTALL_OBJ_HEADER to YES

    Setting SWIFT_INSTALL_OBJ_HEADER to YES helps carthage update use flag cache-build and project doesn’t rebuild after each update

    If SWIFT_INSTALL_OBJ_HEADER doesn't set to YES build process doesn't generate EmitterKit-Swift.h. If EmitterKit-Swift.h doesn't generate carthage cannot check swift's version parsed from that file so build cache is invalid and each time project is rebuilding

    opened by maercam 5
  • Prevent retain cycles

    Prevent retain cycles

    Working with the Event class I often have to make sure that I'm not creating a retain cycle. To make it a bit easier, I've created the following helper:

    func on<Owner: AnyObject>(_ owner: Owner, _ method: @escaping (Owner) -> (T) -> Void) -> EventListener<T> {
      return self.on { [weak owner] data in
        if let ownerRef = owner {
          method(ownerRef)(data)
        }
      }
    }
    

    You can use it like this:

    var listener = myEvent.on(self, MyClass.onMyEvent)
    
    private func onMyEvent(data: Data) {
      print(data)
    }
    

    Would you consider adding this to the codebase?

    Perhaps the method should have a different name than on to prevent confusion with the overloads that have the targetting arguments.

    enhancement 
    opened by yvbeek 4
  • `Unknown type name 'FOUNDATION_EXPORT'` when building EmitterKit.h

    `Unknown type name 'FOUNDATION_EXPORT'` when building EmitterKit.h

    Environment

    Xcode 6.3 (6D570) OS X 10.10.3 Building for iPhone 6 (8.1, 8.3)

    Setup

    Added emitter-kit as a submodule to the project. I am not using CocoaPods due to various problems experienced earlier.

    Error

    When compiling, receive the error Unknown type name 'FOUNDATION_EXPORT'.

    Error screenshots

    image

    image

    Workaround

    The project builds successfully if each occurrence of FOUNDATION_EXPORT is changed to extern.

    opened by danyalaytekin 4
  • Force unwrapping cause crashes

    Force unwrapping cause crashes

    Screen Shot 2019-03-29 at 11 35 29 am

    Please find the crash in the screenshot above. As mentioned in #57, force unwrapping _listeners[_targetID] is causing the crash because there's no value in _listeners.

    opened by wqz-leo 3
  • listenerCount returns wrong value

    listenerCount returns wrong value

    The listenerCount property of the Event class is currently computed with _listeners.count. That's not right, since the _listeners dictionary uses target identifiers for its keys; not listener identifiers.

    This will get fixed at the same time as #43.

    bug 
    opened by aleclarson 3
  • Added - CocoaPods podspec

    Added - CocoaPods podspec

    A simple CocoaPods podspec for your great framework. If you want to submit your project to CocoaPods trunk, you will need to create a tag (version "0.0.1").

    opened by kodlian 3
  • Question about .on vs .once

    Question about .on vs .once

    I've been playing around with the library, and after reading up on the README it looks like anytime you use .on vs .once for anything (in my case KVO), you have to push the listener into an array.

    I was trying out .on without pushing on an array and everything looked fine. Can you explain a little more how this works out, and why I shouldn't be using .on without pushing into an array? Also, is there any cleanup I have to do for .on instead of .once?

    Thanks

    opened by malkomalko 3
  • Fix Carthage compatibility for non iOS platforms

    Fix Carthage compatibility for non iOS platforms

    I kept running into this error when I tried to build for MacOS:

    Failed to write to /Users/jdf2/Xcode Projects/ProjectName/Carthage/Build/Mac/EmitterKit.framework: Error Domain=NSCocoaErrorDomain Code=260 "The file “EmitterKit.framework” couldn’t be opened because there is no such file."
    

    Found an issue over on the Carthage repo that seems to have fixed it for me: https://github.com/Carthage/Carthage/issues/1547#issuecomment-256169649

    Only thing changed in this pull request is the SDKROOT values from iphoneos to an empty string.

    Tested this pull request in the same project that produced the above error and it worked fine.

    opened by jdf221 2
  • Event Listners Count always return 0 despite of adding listners

    Event Listners Count always return 0 despite of adding listners

    
           signInEvent = Event<MasterViewController>()
           
           //button touchup inside event
           btnSignIn.onTap { [weak self] in
               if self == nil {
                   return
               }
               self?.signInEvent?.emit(SigninController()) //Emit data Here
               print(self?.signInEvent?.listenerCount) //Always return 0
           }
         
           //Listner
           let listner = signInEvent?.on({ (data) in
               print(data)
           })
    
    support 
    opened by ghost 2
  • Swift 4

    Swift 4

    I used to use this library a lot. Opening my new Xcode after more than a year, it reports that the library is not compatible with Swift 4. Do they already have some native event emitter I can use or should I try to port emitter-kit to make it work with Swift 4?

    opened by SamDecrock 0
  • This application’s bundle identifier does not match its code signing identifier.

    This application’s bundle identifier does not match its code signing identifier.

    I installed the Library with Carthage 0.26.2 X-Code 9.0.1 Swift 3.2

    Simulator: no errors Device: This application’s bundle identifier does not match its code signing identifier.

    If I remove the library all is working fine. I can install other libraries with Carthage with no problems.

    Any help would be greatly appreciated!

    help wanted question 
    opened by nm 4
  • Add method for stopping emit phase early

    Add method for stopping emit phase early

    This method would prevent listeners added after the current listener from receiving the event.

    Not certain on how this would be implemented or what the method name would be.

    It's also possible something like this calls for an abstraction layer on top of EmitterKit instead of being baked in.

    proposal 
    opened by aleclarson 0
  • Add `AssociatedListeners` class

    Add `AssociatedListeners` class

    Currently, targeted listeners must be manually retained by their target if you want the listeners to get released when/if their target gets released. If you don't do that, your code may be retaining listeners whose target has long been released. This means wasted memory, because those listeners will never be triggered.

    The AssociatedListeners class would use objc_setAssociatedObject to attach an array (of strong pointers to all associated listeners) to the appropriate target. You would no longer have to manually retain indefinite listeners, since they would be released when the target is released.

    Additionally, a helper method would be added to the Event class called getListeners(target) for retrieving the array of associated listeners. You could use this method to remove associated listeners any time before the target is released.

    I'm interested in finding out if anyone thinks this is a problem worth solving. Thanks.

    proposal 
    opened by aleclarson 1
  • Make isListening immutable

    Make isListening immutable

    I'm considering making isListening readonly. Right now, you can set it to false to temporarily remove the listener from its associated event, and set it to true to receive events again. Currently, doing that will make the listener lose its place in the call order (assuming #43 gets fixed).

    Instead, there could be a disable method for disabling the listener without losing its place, and an enable method for allowing events to be handled again. Also, a destroy method would be added for removing the listener from its event forever; which would be used for cancelling one-time listeners and automatically clearing out dereferenced listeners.

    I'm curious what anyone else thinks of this change. Would the breaking change be worth it? I'm also open to suggestions on the naming of the proposed methods.

    proposal 
    opened by aleclarson 0
  • Notifier: NSObject

    Notifier: NSObject

    I think it's a good idea to make Notifier subclass of NSObject. this way, developers can retain Notifier object with protocol extensions too. (using objc_setAssociatedObject) .

    I've described it here: http://stackoverflow.com/questions/38190702/how-to-use-objc-protocol-with-optional-and-extensions-at-the-same-time/42212943#42212943

    enhancement 
    opened by farzadshbfn 1
Releases(5.2.0)
  • 5.2.0(Mar 14, 2018)

    • feat: improved Carthage support
    • feat: add Event.getListeners method
    • deprecated: the Event.listenerCount property
    • fix: call listeners in the order they started listening
    • fix: Event<Void>.emit can be called without an argument again
    Source code(tar.gz)
    Source code(zip)
  • 5.1.0(Aug 4, 2017)

    • The NotificationListener class now takes a Notification instead of an NSDictionary.
    • A NotificationListener without a target will now receive every Notification with its name, regardless of the value of notif.object.
    Source code(tar.gz)
    Source code(zip)
Owner
Alec Larson
Alec Larson
Modern thread-safe and type-safe key-value observing for Swift and Objective-C

Now Archived and Forked PMKVObserver will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork o

Postmates Inc. 708 Jun 29, 2022
NoticeObserveKit is type-safe NotificationCenter wrapper.

NoticeObserveKit NoticeObserveKit is type-safe NotificationCenter wrapper. // .keyboardWillShow is a static property. Notice.Center.default.observe(na

Taiki Suzuki 147 Nov 24, 2022
TopicEventBus is Easy to use, type safe way of implementing Publish–subscribe design pattern.

TopicEventBus Publish–subscribe design pattern implementation framework, with ability to publish events by topic. (NotificationCenter extended alterna

Matan Abravanel 55 Nov 29, 2021
EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

Ali Samaiee 4 Oct 5, 2022
Bond is a Swift binding framework that takes binding concepts to a whole new level.

Bond, Swift Bond Update: Bond 7 has been released! Check out the migration guide to learn more about the update. Bond is a Swift binding framework tha

Declarative Hub 4.2k Jan 5, 2023
Write great asynchronous code in Swift using futures and promises

BrightFutures How do you leverage the power of Swift to write great asynchronous code? BrightFutures is our answer. BrightFutures implements proven fu

Thomas Visser 1.9k Dec 20, 2022
Easy Swift Futures & Promises.

❗️ Archived now ❗️ Since Apple released Combine framework, I decide to archive this repo. You still can use this repo as an example of Future/Promise

Dmytro Mishchenko 40 Sep 23, 2022
A Swift based Future/Promises Library for IOS and OS X.

FutureKit for Swift A Swift based Future/Promises Library for IOS and OS X. Note - The latest FutureKit is works 3.0 For Swift 2.x compatibility use v

null 759 Dec 2, 2022
Swift Apps in a Swoosh! A modern framework for creating iOS apps, inspired by Redux.

Katana is a modern Swift framework for writing iOS applications' business logic that are testable and easy to reason about. Katana is strongly inspire

Bending Spoons 2.2k Jan 1, 2023
📡 Helping you own NotificationCenter in Swift!

Notificationz ?? Helping you own NotificationCenter Highlights Keep Your Naming Conventions: This library gives you convenient access to NotificationC

Kitz 77 Feb 18, 2022
Observable is the easiest way to observe values in Swift.

Observable is the easiest way to observe values in Swift. How to Create an Observable and MutableObservable Using MutableObservable you can create and

Robert-Hein Hooijmans 368 Nov 9, 2022
A library for reactive and unidirectional Swift applications

ReactorKit is a framework for a reactive and unidirectional Swift application architecture. This repository introduces the basic concept of ReactorKit

ReactorKit 2.5k Dec 28, 2022
ReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift.

ReSwift is a Redux-like implementation of the unidirectional data flow architecture in Swift. ReSwift helps you to separate three important concerns of your app's components.

null 7.3k Jan 9, 2023
🐌 snail - An observables framework for Swift

?? snail A lightweight observables framework, also available in Kotlin Installation Carthage You can install Carthage with Homebrew using the followin

Compass 179 Nov 21, 2022
Lightweight Promises for Swift & Obj-C

Tomorrowland Tomorrowland is an implementation of Promises for Swift and Objective-C. A Promise is a wrapper around an asynchronous task that provides

Lily Ballard 115 Nov 23, 2022
VueFlux is the architecture to manage state with unidirectional data flow for Swift, inspired by Vuex and Flux.

Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux Introduction VueFlux is the architecture to manage state with unidi

Ryo Aoyama 324 Dec 17, 2022
When is a lightweight implementation of Promises in Swift

Description When is a lightweight implementation of Promises in Swift. It doesn't include any helper functions for iOS and OSX and it's intentional, t

Vadym Markov 259 Dec 29, 2022
A New, Modern Reactive State Management Library for Swift and SwiftUI (The iOS implementation of Recoil)

RecoilSwift RecoilSwift is a lightweight & reactive swift state management library. RecoilSwift is a SwiftUI implementation of recoil.js which powered

Holly Li 160 Dec 25, 2022
Type-safe networking abstraction layer that associates request type with response type.

APIKit APIKit is a type-safe networking abstraction layer that associates request type with response type. // SearchRepositoriesRequest conforms to Re

Yosuke Ishikawa 1.9k Dec 30, 2022
🚎 Simple type-safe event bus implementation in swift

?? RealEventsBus RealEventsBus is a small swift experiment package to implement a basic type-safe event bus mechanism. Some other implementations in G

Daniele Margutti 12 Jul 19, 2022