ComponentKit/Core/Trigger/CKTrigger.h (81 lines of code) (raw):
/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import <ComponentKit/CKDefines.h>
#if CK_NOT_SWIFT
#import <functional>
#import <vector>
#import <memory>
#import <RenderCore/RCAssert.h>
#import <ComponentKit/CKComponentScopeHandle.h>
struct CKTriggerScopedResponderAndKey {
CKScopedResponder *responder;
CKScopedResponderKey key;
CKTriggerScopedResponderAndKey(CKScopedResponder *responder, CKScopedResponderKey key);
CKTriggerScopedResponderAndKey(id<CKComponentProtocol> component, NSString *context = @"");
auto operator== (const CKTriggerScopedResponderAndKey& rhs) const {
return responder == rhs.responder;
}
};
template<typename... T>
class CKTriggerObservable {
using ScopedResponderAndKey = CKTriggerScopedResponderAndKey;
typedef void(*SpecBinderCallback)(id<CKComponentProtocol>, T...);
struct ObserverComponentSpec {
ScopedResponderAndKey scope;
SpecBinderCallback specCallback;
auto operator== (const ObserverComponentSpec& rhs) const {
return scope == rhs.scope;
}
};
protected:
std::shared_ptr<std::vector<ObserverComponentSpec>> _observers;
public:
CKTriggerObservable() : _observers(std::make_shared<std::vector<ObserverComponentSpec>>()) {};
void addObserver(const ScopedResponderAndKey& scope, const SpecBinderCallback& specCallback) const
{
RCCAssert(
[NSThread isMainThread],
@"Triggers are expected to be bound/unbound on the main thread (e.g., from didInit/willDispose lifecycle events)"
);
if (findObserver(scope) != _observers->end()) {
RCCFailAssert(@"Attempting to add duplicate observer!");
return;
}
_observers->push_back(ObserverComponentSpec{scope, specCallback});
};
void removeObserver(const ScopedResponderAndKey& scope) const
{
RCCAssert(
[NSThread isMainThread],
@"Triggers are expected to be bound/unbound on the main thread (e.g., from didInit/willDispose lifecycle events)"
);
if (findObserver(scope) == _observers->end()) {
RCCFailAssert(@"Observer not present!");
return;
}
_observers->erase(findObserver(scope));
};
auto operator==(const CKTriggerObservable<T...>& rhs) const -> bool {
return _observers == rhs._observers;
}
private:
auto findObserver(const ScopedResponderAndKey& scope) const {
return std::find_if(_observers->begin(), _observers->end(), [&](const auto& t){ return t.scope == scope; });
}
};
template<typename... T>
class CKTrigger : public CKTriggerObservable<T...> {
public:
void operator()(T... args) const
{
RCCAssert(
[NSThread isMainThread],
@"Triggers are expected to be invoked on the main thread"
);
for (const auto &observer : *this->_observers) {
// Fetch the earliest component that is still alive (same as CKAction).
id<CKComponentProtocol> updatedComponent = [observer.scope.responder responderForKey:observer.scope.key];
if (updatedComponent != nil) {
observer.specCallback(updatedComponent, args...);
}
}
};
};
#endif