Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
fastlane/README.md
fastlane/report.xml
fastlane/test_output/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.xcscmblueprint
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
.DS_Store
*.dSYM
*.dSYM.zip
*.ipa
*/xcuserdata/*
SSignalKit.xcodeproj/*
View File
+27
View File
@@ -0,0 +1,27 @@
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SSignalKit",
platforms: [.macOS(.v10_13)],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "SwiftSignalKit",
targets: ["SwiftSignalKit"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "SwiftSignalKit",
dependencies: [],
path: "SwiftSignalKit/Source"),
]
)
+26
View File
@@ -0,0 +1,26 @@
objc_library(
name = "SSignalKit",
enable_modules = True,
module_name = "SSignalKit",
srcs = glob([
"Source/SSignalKit/*.m",
]),
copts = [
"-Werror",
],
hdrs = glob([
"Source/SSignalKit/*.h",
]),
includes = [
"Source",
],
deps = [
],
sdk_frameworks = [
"Foundation",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@interface SAtomic : NSObject
- (instancetype _Nonnull)initWithValue:(id _Nullable)value;
- (instancetype _Nonnull)initWithValue:(id _Nullable)value recursive:(bool)recursive;
- (id _Nullable)swap:(id _Nullable)newValue;
- (id _Nullable)value;
- (id _Nullable)modify:(id _Nullable (^ _Nonnull)(id _Nullable))f;
- (id _Nullable)with:(id _Nullable (^ _Nonnull)(id _Nullable))f;
@end
@@ -0,0 +1,93 @@
#import "SAtomic.h"
#import <pthread.h>
@interface SAtomic ()
{
pthread_mutex_t _lock;
pthread_mutexattr_t _attr;
bool _isRecursive;
id _value;
}
@end
@implementation SAtomic
- (instancetype)initWithValue:(id)value
{
self = [super init];
if (self != nil)
{
pthread_mutex_init(&_lock, NULL);
_value = value;
}
return self;
}
- (instancetype)initWithValue:(id)value recursive:(bool)recursive {
self = [super init];
if (self != nil)
{
_isRecursive = recursive;
if (recursive) {
pthread_mutexattr_init(&_attr);
pthread_mutexattr_settype(&_attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&_lock, &_attr);
} else {
pthread_mutex_init(&_lock, NULL);
}
_value = value;
}
return self;
}
- (void)dealloc {
if (_isRecursive) {
pthread_mutexattr_destroy(&_attr);
}
pthread_mutex_destroy(&_lock);
}
- (id)swap:(id)newValue
{
id previousValue = nil;
pthread_mutex_lock(&_lock);
previousValue = _value;
_value = newValue;
pthread_mutex_unlock(&_lock);
return previousValue;
}
- (id)value
{
id previousValue = nil;
pthread_mutex_lock(&_lock);
previousValue = _value;
pthread_mutex_unlock(&_lock);
return previousValue;
}
- (id)modify:(id (^)(id))f
{
id newValue = nil;
pthread_mutex_lock(&_lock);
newValue = f(_value);
_value = newValue;
pthread_mutex_unlock(&_lock);
return newValue;
}
- (id)with:(id (^)(id))f
{
id result = nil;
pthread_mutex_lock(&_lock);
result = f(_value);
pthread_mutex_unlock(&_lock);
return result;
}
@end
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface SBag : NSObject
- (NSInteger)addItem:(id _Nonnull)item;
- (void)enumerateItems:(void (^ _Nonnull)(id _Nonnull))block;
- (void)removeItem:(NSInteger)key;
- (bool)isEmpty;
- (NSArray * _Nonnull)copyItems;
@end
@@ -0,0 +1,74 @@
#import "SBag.h"
@interface SBag ()
{
NSInteger _nextKey;
NSMutableArray *_items;
NSMutableArray *_itemKeys;
}
@end
@implementation SBag
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_items = [[NSMutableArray alloc] init];
_itemKeys = [[NSMutableArray alloc] init];
}
return self;
}
- (NSInteger)addItem:(id)item
{
if (item == nil)
return -1;
NSInteger key = _nextKey;
[_items addObject:item];
[_itemKeys addObject:@(key)];
_nextKey++;
return key;
}
- (void)enumerateItems:(void (^)(id))block
{
if (block)
{
for (id item in _items)
{
block(item);
}
}
}
- (void)removeItem:(NSInteger)key
{
NSUInteger index = 0;
for (NSNumber *itemKey in _itemKeys)
{
if ([itemKey integerValue] == key)
{
[_items removeObjectAtIndex:index];
[_itemKeys removeObjectAtIndex:index];
break;
}
index++;
}
}
- (bool)isEmpty
{
return _items.count == 0;
}
- (NSArray *)copyItems
{
return [[NSArray alloc] initWithArray:_items];
}
@end
@@ -0,0 +1,7 @@
#import <SSignalKit/SDisposable.h>
@interface SBlockDisposable : NSObject <SDisposable>
- (instancetype _Nonnull)initWithBlock:(void (^ _Nullable)())block;
@end
@@ -0,0 +1,53 @@
#import "SBlockDisposable.h"
#import <os/lock.h>
#import <objc/runtime.h>
#import <pthread/pthread.h>
@interface SBlockDisposable () {
void (^_action)();
pthread_mutex_t _lock;
}
@end
@implementation SBlockDisposable
- (instancetype)initWithBlock:(void (^)())block
{
self = [super init];
if (self != nil)
{
_action = [block copy];
pthread_mutex_init(&_lock, nil);
}
return self;
}
- (void)dealloc {
void (^freeAction)() = nil;
pthread_mutex_lock(&_lock);
freeAction = _action;
_action = nil;
pthread_mutex_unlock(&_lock);
if (freeAction) {
}
pthread_mutex_destroy(&_lock);
}
- (void)dispose {
void (^disposeAction)() = nil;
pthread_mutex_lock(&_lock);
disposeAction = _action;
_action = nil;
pthread_mutex_unlock(&_lock);
if (disposeAction) {
disposeAction();
}
}
@end
@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
@protocol SDisposable <NSObject>
- (void)dispose;
@end
@@ -0,0 +1,10 @@
#import <SSignalKit/SDisposable.h>
@class SSignal;
@interface SDisposableSet : NSObject <SDisposable>
- (void)add:(id<SDisposable> _Nonnull)disposable;
- (void)remove:(id<SDisposable> _Nonnull)disposable;
@end
@@ -0,0 +1,83 @@
#import "SDisposableSet.h"
#import "SSignal.h"
#import <pthread/pthread.h>
@interface SDisposableSet ()
{
pthread_mutex_t _lock;
bool _disposed;
NSMutableArray<id<SDisposable>> *_disposables;
}
@end
@implementation SDisposableSet
- (instancetype)init {
self = [super init];
if (self != nil) {
pthread_mutex_init(&_lock, nil);
_disposables = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
NSArray<id<SDisposable>> *disposables = nil;
pthread_mutex_lock(&_lock);
disposables = _disposables;
_disposables = nil;
pthread_mutex_unlock(&_lock);
if (disposables) {
}
pthread_mutex_destroy(&_lock);
}
- (void)add:(id<SDisposable>)disposable {
bool disposeImmediately = false;
pthread_mutex_lock(&_lock);
if (_disposed) {
disposeImmediately = true;
} else {
[_disposables addObject:disposable];
}
pthread_mutex_unlock(&_lock);
if (disposeImmediately) {
[disposable dispose];
}
}
- (void)remove:(id<SDisposable>)disposable {
pthread_mutex_lock(&_lock);
for (NSInteger i = 0; i < _disposables.count; i++) {
if (_disposables[i] == disposable) {
[_disposables removeObjectAtIndex:i];
break;
}
}
pthread_mutex_unlock(&_lock);
}
- (void)dispose {
NSArray<id<SDisposable>> *disposables = nil;
pthread_mutex_lock(&_lock);
if (!_disposed) {
_disposed = true;
disposables = _disposables;
_disposables = nil;
}
pthread_mutex_unlock(&_lock);
if (disposables) {
for (id<SDisposable> disposable in disposables) {
[disposable dispose];
}
}
}
@end
@@ -0,0 +1,7 @@
#import <SSignalKit/SDisposable.h>
@interface SMetaDisposable : NSObject <SDisposable>
- (void)setDisposable:(id<SDisposable> _Nullable)disposable;
@end
@@ -0,0 +1,76 @@
#import "SMetaDisposable.h"
#import <pthread/pthread.h>
@interface SMetaDisposable ()
{
pthread_mutex_t _lock;
bool _disposed;
id<SDisposable> _disposable;
}
@end
@implementation SMetaDisposable
- (instancetype)init {
self = [super init];
if (self != nil) {
pthread_mutex_init(&_lock, nil);
}
return self;
}
- (void)dealloc {
id<SDisposable> freeDisposable = nil;
pthread_mutex_lock(&_lock);
if (_disposable) {
freeDisposable = _disposable;
_disposable = nil;
}
pthread_mutex_unlock(&_lock);
if (freeDisposable) {
}
pthread_mutex_destroy(&_lock);
}
- (void)setDisposable:(id<SDisposable>)disposable {
id<SDisposable> previousDisposable = nil;
bool disposeImmediately = false;
pthread_mutex_lock(&_lock);
disposeImmediately = _disposed;
if (!disposeImmediately) {
previousDisposable = _disposable;
_disposable = disposable;
}
pthread_mutex_unlock(&_lock);
if (previousDisposable) {
[previousDisposable dispose];
}
if (disposeImmediately) {
[disposable dispose];
}
}
- (void)dispose {
id<SDisposable> disposable = nil;
pthread_mutex_lock(&_lock);
if (!_disposed) {
_disposed = true;
disposable = _disposable;
_disposable = nil;
}
pthread_mutex_unlock(&_lock);
if (disposable) {
[disposable dispose];
}
}
@end
@@ -0,0 +1,19 @@
#import <Foundation/Foundation.h>
@interface SQueue : NSObject
+ (SQueue * _Nonnull)mainQueue;
+ (SQueue * _Nonnull)concurrentDefaultQueue;
+ (SQueue * _Nonnull)concurrentBackgroundQueue;
+ (SQueue * _Nonnull)wrapConcurrentNativeQueue:(dispatch_queue_t _Nonnull)nativeQueue;
- (void)dispatch:(dispatch_block_t _Nonnull)block;
- (void)dispatchSync:(dispatch_block_t _Nonnull)block;
- (void)dispatch:(dispatch_block_t _Nonnull)block synchronous:(bool)synchronous;
- (dispatch_queue_t _Nonnull)_dispatch_queue;
- (bool)isCurrentQueue;
@end
@@ -0,0 +1,124 @@
#import "SQueue.h"
static const void *SQueueSpecificKey = &SQueueSpecificKey;
@interface SQueue ()
{
dispatch_queue_t _queue;
void *_queueSpecific;
bool _specialIsMainQueue;
}
@end
@implementation SQueue
+ (SQueue *)mainQueue
{
static SQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_main_queue() queueSpecific:NULL];
queue->_specialIsMainQueue = true;
});
return queue;
}
+ (SQueue *)concurrentDefaultQueue
{
static SQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) queueSpecific:NULL];
});
return queue;
}
+ (SQueue *)concurrentBackgroundQueue
{
static SQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) queueSpecific:NULL];
});
return queue;
}
+ (SQueue *)wrapConcurrentNativeQueue:(dispatch_queue_t)nativeQueue
{
return [[SQueue alloc] initWithNativeQueue:nativeQueue queueSpecific:NULL];
}
- (instancetype)init
{
dispatch_queue_t queue = dispatch_queue_create(NULL, NULL);
dispatch_queue_set_specific(queue, SQueueSpecificKey, (__bridge void *)self, NULL);
return [self initWithNativeQueue:queue queueSpecific:(__bridge void *)self];
}
- (instancetype)initWithNativeQueue:(dispatch_queue_t)queue queueSpecific:(void *)queueSpecific
{
self = [super init];
if (self != nil)
{
_queue = queue;
_queueSpecific = queueSpecific;
}
return self;
}
- (dispatch_queue_t)_dispatch_queue
{
return _queue;
}
- (void)dispatch:(dispatch_block_t)block
{
if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
block();
else if (_specialIsMainQueue && [NSThread isMainThread])
block();
else
dispatch_async(_queue, block);
}
- (void)dispatchSync:(dispatch_block_t)block
{
if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
block();
else if (_specialIsMainQueue && [NSThread isMainThread])
block();
else
dispatch_sync(_queue, block);
}
- (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous {
if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
block();
else if (_specialIsMainQueue && [NSThread isMainThread])
block();
else {
if (synchronous) {
dispatch_sync(_queue, block);
} else {
dispatch_async(_queue, block);
}
}
}
- (bool)isCurrentQueue
{
if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
return true;
else if (_specialIsMainQueue && [NSThread isMainThread])
return true;
return false;
}
@end
@@ -0,0 +1,19 @@
//
// SQueueLocalObject.h
// SSignalKit
//
// Created by Mikhail Filimonov on 13.01.2021.
// Copyright © 2021 Telegram. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <SSignalKit/SQueue.h>
NS_ASSUME_NONNULL_BEGIN
@interface SQueueLocalObject : NSObject
-(id)initWithQueue:(SQueue *)queue generate:(id (^)(void))next;
-(void)with:(void (^)(id object))f;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,40 @@
//
// SQueueLocalObject.m
// SSignalKit
//
// Created by Mikhail Filimonov on 13.01.2021.
// Copyright © 2021 Telegram. All rights reserved.
//
#import "SQueueLocalObject.h"
@implementation SQueueLocalObject {
SQueue *_queue;
id valueRef;
}
-(id)initWithQueue:(SQueue *)queue generate:(id _Nonnull (^)(void))next {
if (self = [super init]) {
self->_queue = queue;
[queue dispatch:^{
self->valueRef = next();
}];
}
return self;
}
-(void)with:(void (^)(id object))f {
[self->_queue dispatch:^{
f(self->valueRef);
}];
}
-(void)dealloc {
__unused __block id value = self->valueRef;
self->valueRef = nil;
[_queue dispatch:^{
value = nil;
}];
}
@end
@@ -0,0 +1,9 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Catch)
- (SSignal * _Nonnull)catch:(SSignal * _Nonnull (^ _Nonnull )(id _Nullable error))f;
- (SSignal * _Nonnull)restart;
- (SSignal * _Nonnull)retryIf:(bool (^ _Nonnull)(id _Nullable error))predicate;
@end
@@ -0,0 +1,147 @@
#import "SSignal+Catch.h"
#import "SMetaDisposable.h"
#import "SDisposableSet.h"
#import "SBlockDisposable.h"
#import "SAtomic.h"
@implementation SSignal (Catch)
- (SSignal *)catch:(SSignal *(^)(id error))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SDisposableSet *disposable = [[SDisposableSet alloc] init];
[disposable add:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
SSignal *signal = f(error);
[disposable add:[signal startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
} completed:^
{
[subscriber putCompletion];
}]];
return disposable;
}];
}
static dispatch_block_t recursiveBlock(void (^block)(dispatch_block_t recurse))
{
return ^
{
block(recursiveBlock(block));
};
}
- (SSignal *)restart
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SAtomic *shouldRestart = [[SAtomic alloc] initWithValue:@true];
SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
void (^start)() = recursiveBlock(^(dispatch_block_t recurse)
{
NSNumber *currentShouldRestart = [shouldRestart with:^id(NSNumber *current)
{
return current;
}];
if ([currentShouldRestart boolValue])
{
id<SDisposable> disposable = [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
recurse();
}];
[currentDisposable setDisposable:disposable];
}
});
start();
return [[SBlockDisposable alloc] initWithBlock:^
{
[currentDisposable dispose];
[shouldRestart modify:^id(__unused id current)
{
return @false;
}];
}];
}];
}
- (SSignal *)retryIf:(bool (^)(id error))predicate {
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SAtomic *shouldRestart = [[SAtomic alloc] initWithValue:@true];
SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
void (^start)() = recursiveBlock(^(dispatch_block_t recurse)
{
NSNumber *currentShouldRestart = [shouldRestart with:^id(NSNumber *current)
{
return current;
}];
if ([currentShouldRestart boolValue])
{
id<SDisposable> disposable = [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
if (predicate(error)) {
recurse();
} else {
[subscriber putError:error];
}
} completed:^
{
[shouldRestart modify:^id(__unused id current) {
return @false;
}];
[subscriber putCompletion];
}];
[currentDisposable setDisposable:disposable];
} else {
[subscriber putCompletion];
}
});
start();
return [[SBlockDisposable alloc] initWithBlock:^
{
[currentDisposable dispose];
[shouldRestart modify:^id(__unused id current)
{
return @false;
}];
}];
}];
}
@end
@@ -0,0 +1,10 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Combine)
+ (SSignal * _Nonnull)combineSignals:(NSArray * _Nonnull)signals;
+ (SSignal * _Nonnull)combineSignals:(NSArray * _Nonnull)signals withInitialStates:(NSArray * _Nullable)initialStates;
+ (SSignal * _Nonnull)mergeSignals:(NSArray * _Nonnull)signals;
@end
@@ -0,0 +1,177 @@
#import "SSignal+Combine.h"
#import "SAtomic.h"
#import "SDisposableSet.h"
#import "SSignal+Single.h"
@interface SSignalCombineState : NSObject
@property (nonatomic, strong, readonly) NSDictionary *latestValues;
@property (nonatomic, strong, readonly) NSArray *completedStatuses;
@property (nonatomic) bool error;
@end
@implementation SSignalCombineState
- (instancetype)initWithLatestValues:(NSDictionary *)latestValues completedStatuses:(NSArray *)completedStatuses error:(bool)error
{
self = [super init];
if (self != nil)
{
_latestValues = latestValues;
_completedStatuses = completedStatuses;
_error = error;
}
return self;
}
@end
@implementation SSignal (Combine)
+ (SSignal *)combineSignals:(NSArray *)signals
{
if (signals.count == 0)
return [SSignal single:@[]];
else
return [self combineSignals:signals withInitialStates:nil];
}
+ (SSignal *)combineSignals:(NSArray *)signals withInitialStates:(NSArray *)initialStates
{
return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
{
NSMutableArray *completedStatuses = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < signals.count; i++)
{
[completedStatuses addObject:@false];
}
NSMutableDictionary *initialLatestValues = [[NSMutableDictionary alloc] init];
for (NSUInteger i = 0; i < initialStates.count; i++)
{
initialLatestValues[@(i)] = initialStates[i];
}
SAtomic *combineState = [[SAtomic alloc] initWithValue:[[SSignalCombineState alloc] initWithLatestValues:initialLatestValues completedStatuses:completedStatuses error:false]];
SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
NSUInteger index = 0;
NSUInteger count = signals.count;
for (SSignal *signal in signals)
{
id<SDisposable> disposable = [signal startWithNext:^(id next)
{
SSignalCombineState *currentState = [combineState modify:^id(SSignalCombineState *state)
{
NSMutableDictionary *latestValues = [[NSMutableDictionary alloc] initWithDictionary:state.latestValues];
latestValues[@(index)] = next;
return [[SSignalCombineState alloc] initWithLatestValues:latestValues completedStatuses:state.completedStatuses error:state.error];
}];
NSMutableArray *latestValues = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < count; i++)
{
id value = currentState.latestValues[@(i)];
if (value == nil)
{
latestValues = nil;
break;
}
latestValues[i] = value;
}
if (latestValues != nil)
[subscriber putNext:latestValues];
}
error:^(id error)
{
__block bool hadError = false;
[combineState modify:^id(SSignalCombineState *state)
{
hadError = state.error;
return [[SSignalCombineState alloc] initWithLatestValues:state.latestValues completedStatuses:state.completedStatuses error:true];
}];
if (!hadError)
[subscriber putError:error];
} completed:^
{
__block bool wasCompleted = false;
__block bool isCompleted = false;
[combineState modify:^id(SSignalCombineState *state)
{
NSMutableArray *completedStatuses = [[NSMutableArray alloc] initWithArray:state.completedStatuses];
bool everyStatusWasCompleted = true;
for (NSNumber *nStatus in completedStatuses)
{
if (![nStatus boolValue])
{
everyStatusWasCompleted = false;
break;
}
}
completedStatuses[index] = @true;
bool everyStatusIsCompleted = true;
for (NSNumber *nStatus in completedStatuses)
{
if (![nStatus boolValue])
{
everyStatusIsCompleted = false;
break;
}
}
wasCompleted = everyStatusWasCompleted;
isCompleted = everyStatusIsCompleted;
return [[SSignalCombineState alloc] initWithLatestValues:state.latestValues completedStatuses:completedStatuses error:state.error];
}];
if (!wasCompleted && isCompleted)
[subscriber putCompletion];
}];
[compositeDisposable add:disposable];
index++;
}
return compositeDisposable;
}];
}
+ (SSignal *)mergeSignals:(NSArray *)signals
{
if (signals.count == 0)
return [SSignal complete];
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
SDisposableSet *disposables = [[SDisposableSet alloc] init];
SAtomic *completedStates = [[SAtomic alloc] initWithValue:[[NSSet alloc] init]];
NSInteger index = -1;
NSUInteger count = signals.count;
for (SSignal *signal in signals)
{
index++;
id<SDisposable> disposable = [signal startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
NSSet *set = [completedStates modify:^id(NSSet *set)
{
return [set setByAddingObject:@(index)];
}];
if (set.count == count)
[subscriber putCompletion];
}];
[disposables add:disposable];
}
return disposables;
}];
}
@end
@@ -0,0 +1,14 @@
#import <SSignalKit/SSignal.h>
#import <SSignalKit/SQueue.h>
#import <SSignalKit/SThreadPool.h>
@interface SSignal (Dispatch)
- (SSignal * _Nonnull)deliverOn:(SQueue * _Nonnull)queue;
- (SSignal * _Nonnull)deliverOnThreadPool:(SThreadPool * _Nonnull)threadPool;
- (SSignal * _Nonnull)startOn:(SQueue * _Nonnull)queue;
- (SSignal * _Nonnull)startOnThreadPool:(SThreadPool * _Nonnull)threadPool;
- (SSignal * _Nonnull)throttleOn:(SQueue * _Nonnull)queue delay:(NSTimeInterval)delay;
@end
@@ -0,0 +1,212 @@
#import "SSignal+Dispatch.h"
#import "SAtomic.h"
#import "STimer.h"
#import "SBlockDisposable.h"
#import "SMetaDisposable.h"
@interface SSignal_ThrottleContainer : NSObject
@property (nonatomic, strong, readonly) id value;
@property (nonatomic, readonly) bool committed;
@property (nonatomic, readonly) bool last;
@end
@implementation SSignal_ThrottleContainer
- (instancetype)initWithValue:(id)value committed:(bool)committed last:(bool)last {
self = [super init];
if (self != nil) {
_value = value;
_committed = committed;
_last = last;
}
return self;
}
@end
@implementation SSignal (Dispatch)
- (SSignal *)deliverOn:(SQueue *)queue
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[queue dispatch:^
{
[subscriber putNext:next];
}];
} error:^(id error)
{
[queue dispatch:^
{
[subscriber putError:error];
}];
} completed:^
{
[queue dispatch:^
{
[subscriber putCompletion];
}];
}];
}];
}
- (SSignal *)deliverOnThreadPool:(SThreadPool *)threadPool
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SThreadPoolQueue *queue = [threadPool nextQueue];
return [self startWithNext:^(id next)
{
SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
{
if (!cancelled())
[subscriber putNext:next];
}];
[queue addTask:task];
} error:^(id error)
{
SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
{
if (!cancelled())
[subscriber putError:error];
}];
[queue addTask:task];
} completed:^
{
SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
{
if (!cancelled())
[subscriber putCompletion];
}];
[queue addTask:task];
}];
}];
}
- (SSignal *)startOn:(SQueue *)queue
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
__block bool isCancelled = false;
SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
[disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
{
isCancelled = true;
}]];
[queue dispatch:^
{
if (!isCancelled)
{
[disposable setDisposable:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
}
}];
return disposable;
}];
}
- (SSignal *)startOnThreadPool:(SThreadPool *)threadPool
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
{
if (cancelled && cancelled())
return;
[disposable setDisposable:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
}];
[disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
{
[task cancel];
}]];
[threadPool addTask:task];
return disposable;
}];
}
- (SSignal *)throttleOn:(SQueue *)queue delay:(NSTimeInterval)delay
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SAtomic *value = [[SAtomic alloc] initWithValue:nil];
STimer *timer = [[STimer alloc] initWithTimeout:delay repeat:false completion:^(__unused STimer *timer) {
[value modify:^id(SSignal_ThrottleContainer *container) {
if (container != nil) {
if (!container.committed) {
[subscriber putNext:container.value];
container = [[SSignal_ThrottleContainer alloc] initWithValue:container.value committed:true last:container.last];
}
if (container.last) {
[subscriber putCompletion];
}
}
return container;
}];
} queue:queue];
return [[self deliverOn:queue] startWithNext:^(id next) {
[value modify:^id(SSignal_ThrottleContainer *container) {
if (container == nil) {
container = [[SSignal_ThrottleContainer alloc] initWithValue:next committed:false last:false];
}
return container;
}];
[timer invalidate];
[timer start];
} error:^(id error) {
[timer invalidate];
[subscriber putError:error];
} completed:^{
[timer invalidate];
__block bool start = false;
[value modify:^id(SSignal_ThrottleContainer *container) {
bool wasCommitted = false;
if (container == nil) {
wasCommitted = true;
container = [[SSignal_ThrottleContainer alloc] initWithValue:nil committed:true last:true];
} else {
wasCommitted = container.committed;
container = [[SSignal_ThrottleContainer alloc] initWithValue:container.value committed:container.committed last:true];
}
start = wasCommitted;
return container;
}];
if (start) {
[timer start];
} else {
[timer fireAndInvalidate];
}
}];
}];
}
@end
@@ -0,0 +1,9 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Mapping)
- (SSignal * _Nonnull)map:(id _Nullable (^ _Nonnull)(id _Nullable))f;
- (SSignal * _Nonnull)filter:(bool (^ _Nonnull)(id _Nullable))f;
- (SSignal * _Nonnull)ignoreRepeated;
@end
@@ -0,0 +1,83 @@
#import "SSignal+Mapping.h"
#import "SAtomic.h"
@interface SSignalIgnoreRepeatedState: NSObject
@property (nonatomic, strong) id value;
@property (nonatomic) bool hasValue;
@end
@implementation SSignalIgnoreRepeatedState
@end
@implementation SSignal (Mapping)
- (SSignal *)map:(id (^)(id))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:f(next)];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)filter:(bool (^)(id))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
if (f(next))
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)ignoreRepeated {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SAtomic *state = [[SAtomic alloc] initWithValue:[[SSignalIgnoreRepeatedState alloc] init]];
return [self startWithNext:^(id next) {
bool shouldPassthrough = [[state with:^id(SSignalIgnoreRepeatedState *state) {
if (!state.hasValue) {
state.hasValue = true;
state.value = next;
return @true;
} else if ((state.value == nil && next == nil) || [(id<NSObject>)state.value isEqual:next]) {
return @false;
}
state.value = next;
return @true;
}] boolValue];
if (shouldPassthrough) {
[subscriber putNext:next];
}
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
@end
@@ -0,0 +1,22 @@
#import <SSignalKit/SSignal.h>
@class SQueue;
@interface SSignal (Meta)
- (SSignal * _Nonnull)switchToLatest;
- (SSignal * _Nonnull)mapToSignal:(SSignal * _Nonnull (^ _Nonnull)(id _Nullable))f;
- (SSignal * _Nonnull)mapToQueue:(SSignal * _Nonnull (^ _Nonnull)(id _Nullable))f;
- (SSignal * _Nonnull)mapToThrottled:(SSignal * _Nonnull (^ _Nonnull)(id _Nullable))f;
- (SSignal * _Nonnull)then:(SSignal * _Nonnull)signal;
- (SSignal * _Nonnull)queue;
- (SSignal * _Nonnull)throttled;
+ (SSignal * _Nonnull)defer:(SSignal * _Nonnull(^ _Nonnull)())generator;
@end
@interface SSignalQueue : NSObject
- (SSignal * _Nonnull)enqueue:(SSignal * _Nonnull)signal;
@end
@@ -0,0 +1,325 @@
#import "SSignal+Meta.h"
#import "SDisposableSet.h"
#import "SMetaDisposable.h"
#import "SSignal+Mapping.h"
#import "SAtomic.h"
#import "SSignal+Pipe.h"
#import <os/lock.h>
@interface SSignalQueueState : NSObject <SDisposable>
{
os_unfair_lock _lock;
bool _executingSignal;
bool _terminated;
id<SDisposable> _disposable;
SMetaDisposable *_currentDisposable;
SSubscriber *_subscriber;
NSMutableArray *_queuedSignals;
bool _queueMode;
bool _throttleMode;
}
@end
@implementation SSignalQueueState
- (instancetype)initWithSubscriber:(SSubscriber *)subscriber queueMode:(bool)queueMode throttleMode:(bool)throttleMode
{
self = [super init];
if (self != nil)
{
_subscriber = subscriber;
_currentDisposable = [[SMetaDisposable alloc] init];
_queuedSignals = queueMode ? [[NSMutableArray alloc] init] : nil;
_queueMode = queueMode;
_throttleMode = throttleMode;
}
return self;
}
- (void)beginWithDisposable:(id<SDisposable>)disposable
{
_disposable = disposable;
}
- (void)enqueueSignal:(SSignal *)signal
{
bool startSignal = false;
os_unfair_lock_lock(&_lock);
if (_queueMode && _executingSignal) {
if (_throttleMode) {
[_queuedSignals removeAllObjects];
}
[_queuedSignals addObject:signal];
}
else
{
_executingSignal = true;
startSignal = true;
}
os_unfair_lock_unlock(&_lock);
if (startSignal)
{
__weak SSignalQueueState *weakSelf = self;
id<SDisposable> disposable = [signal startWithNext:^(id next)
{
[_subscriber putNext:next];
} error:^(id error)
{
[_subscriber putError:error];
} completed:^
{
__strong SSignalQueueState *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf headCompleted];
}
}];
[_currentDisposable setDisposable:disposable];
}
}
- (void)headCompleted
{
SSignal *nextSignal = nil;
bool terminated = false;
os_unfair_lock_lock(&_lock);
_executingSignal = false;
if (_queueMode)
{
if (_queuedSignals.count != 0)
{
nextSignal = _queuedSignals[0];
[_queuedSignals removeObjectAtIndex:0];
_executingSignal = true;
}
else
terminated = _terminated;
}
else
terminated = _terminated;
os_unfair_lock_unlock(&_lock);
if (terminated)
[_subscriber putCompletion];
else if (nextSignal != nil)
{
__weak SSignalQueueState *weakSelf = self;
id<SDisposable> disposable = [nextSignal startWithNext:^(id next)
{
[_subscriber putNext:next];
} error:^(id error)
{
[_subscriber putError:error];
} completed:^
{
__strong SSignalQueueState *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf headCompleted];
}
}];
[_currentDisposable setDisposable:disposable];
}
}
- (void)beginCompletion
{
bool executingSignal = false;
os_unfair_lock_lock(&_lock);
executingSignal = _executingSignal;
_terminated = true;
os_unfair_lock_unlock(&_lock);
if (!executingSignal)
[_subscriber putCompletion];
}
- (void)dispose
{
[_currentDisposable dispose];
[_disposable dispose];
}
@end
@implementation SSignal (Meta)
- (SSignal *)switchToLatest
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:false throttleMode:false];
[state beginWithDisposable:[self startWithNext:^(id next)
{
[state enqueueSignal:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[state beginCompletion];
}]];
return state;
}];
}
- (SSignal *)mapToSignal:(SSignal *(^)(id))f
{
return [[self map:f] switchToLatest];
}
- (SSignal *)mapToQueue:(SSignal *(^)(id))f
{
return [[self map:f] queue];
}
- (SSignal *)mapToThrottled:(SSignal *(^)(id))f {
return [[self map:f] throttled];
}
- (SSignal *)then:(SSignal *)signal
{
return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
{
SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
[compositeDisposable add:currentDisposable];
[currentDisposable setDisposable:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[compositeDisposable add:[signal startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
}]];
return compositeDisposable;
}];
}
- (SSignal *)queue
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:true throttleMode:false];
[state beginWithDisposable:[self startWithNext:^(id next)
{
[state enqueueSignal:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[state beginCompletion];
}]];
return state;
}];
}
- (SSignal *)throttled {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:true throttleMode:true];
[state beginWithDisposable:[self startWithNext:^(id next)
{
[state enqueueSignal:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[state beginCompletion];
}]];
return state;
}];
}
+ (SSignal *)defer:(SSignal *(^)())generator
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
return [generator() startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
@end
@interface SSignalQueue () {
SPipe *_pipe;
id<SDisposable> _disposable;
}
@end
@implementation SSignalQueue
- (instancetype)init {
self = [super init];
if (self != nil) {
_pipe = [[SPipe alloc] init];
_disposable = [[_pipe.signalProducer() queue] startWithNext:nil];
}
return self;
}
- (void)dealloc {
[_disposable dispose];
}
- (SSignal *)enqueue:(SSignal *)signal {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SPipe *disposePipe = [[SPipe alloc] init];
SSignal *proxy = [[[[signal onNext:^(id next) {
[subscriber putNext:next];
}] onError:^(id error) {
[subscriber putError:error];
}] onCompletion:^{
[subscriber putCompletion];
}] catch:^SSignal *(__unused id error) {
return [SSignal complete];
}];
_pipe.sink([proxy takeUntilReplacement:disposePipe.signalProducer()]);
return [[SBlockDisposable alloc] initWithBlock:^{
disposePipe.sink([SSignal complete]);
}];
}];
}
@end
@@ -0,0 +1,11 @@
#import <SSignalKit/SSignalKit.h>
@interface SPipe : NSObject
@property (nonatomic, copy, readonly) SSignal * _Nonnull (^ _Nonnull signalProducer)();
@property (nonatomic, copy, readonly) void (^ _Nonnull sink)(id _Nullable);
- (instancetype _Nonnull)initWithReplay:(bool)replay;
@end
@@ -0,0 +1,103 @@
#import "SSignal+Pipe.h"
#import "SBlockDisposable.h"
#import "SAtomic.h"
#import "SBag.h"
@interface SPipeReplayState : NSObject
@property (nonatomic, readonly) bool hasReceivedValue;
@property (nonatomic, strong, readonly) id recentValue;
@end
@implementation SPipeReplayState
- (instancetype)initWithReceivedValue:(bool)receivedValue recentValue:(id)recentValue
{
self = [super init];
if (self != nil)
{
_hasReceivedValue = receivedValue;
_recentValue = recentValue;
}
return self;
}
@end
@implementation SPipe
- (instancetype)init
{
return [self initWithReplay:false];
}
- (instancetype)initWithReplay:(bool)replay
{
self = [super init];
if (self != nil)
{
SAtomic *subscribers = [[SAtomic alloc] initWithValue:[[SBag alloc] init]];
SAtomic *replayState = replay ? [[SAtomic alloc] initWithValue:[[SPipeReplayState alloc] initWithReceivedValue:false recentValue:nil]] : nil;
_signalProducer = [^SSignal *
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
__block NSUInteger index = 0;
[subscribers with:^id(SBag *bag)
{
index = [bag addItem:[^(id next)
{
[subscriber putNext:next];
} copy]];
return nil;
}];
if (replay)
{
[replayState with:^id(SPipeReplayState *state)
{
if (state.hasReceivedValue)
[subscriber putNext:state.recentValue];
return nil;
}];
}
return [[SBlockDisposable alloc] initWithBlock:^
{
[subscribers with:^id(SBag *bag)
{
[bag removeItem:index];
return nil;
}];
}];
}];
} copy];
_sink = [^(id next)
{
NSArray *items = [subscribers with:^id(SBag *bag)
{
return [bag copyItems];
}];
for (void (^item)(id) in items)
{
item(next);
}
if (replay)
{
[replayState modify:^id(__unused SPipeReplayState *state)
{
return [[SPipeReplayState alloc] initWithReceivedValue:true recentValue:next];
}];
}
} copy];
}
return self;
}
@end
@@ -0,0 +1,13 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (SideEffects)
- (SSignal * _Nonnull)onStart:(void (^ _Nonnull)())f;
- (SSignal * _Nonnull)onNext:(void (^ _Nonnull)(id _Nullable next))f;
- (SSignal * _Nonnull)afterNext:(void (^ _Nonnull)(id _Nullable next))f;
- (SSignal * _Nonnull)onError:(void (^ _Nonnull)(id _Nullable error))f;
- (SSignal * _Nonnull)onCompletion:(void (^ _Nonnull)())f;
- (SSignal * _Nonnull)afterCompletion:(void (^ _Nonnull)())f;
- (SSignal * _Nonnull)onDispose:(void (^ _Nonnull)())f;
@end
@@ -0,0 +1,141 @@
#import "SSignal+SideEffects.h"
#import "SBlockDisposable.h"
#import "SDisposableSet.h"
@implementation SSignal (SideEffects)
- (SSignal *)onStart:(void (^)())f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
f();
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)onNext:(void (^)(id next))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
f(next);
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)afterNext:(void (^)(id next))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
f(next);
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)onError:(void (^)(id error))f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
f(error);
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)onCompletion:(void (^)())f
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
f();
[subscriber putCompletion];
}];
}];
}
- (SSignal *)afterCompletion:(void (^)())f {
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
return [self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
f();
}];
}];
}
- (SSignal *)onDispose:(void (^)())f
{
return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
{
SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
[compositeDisposable add:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
[compositeDisposable add:[[SBlockDisposable alloc] initWithBlock:^
{
f();
}]];
return compositeDisposable;
}];
}
@end
@@ -0,0 +1,10 @@
#import <SSignalKit/SSignal.h>
@interface SSignal (Single)
+ (SSignal * _Nonnull)single:(id _Nullable)next;
+ (SSignal * _Nonnull)fail:(id _Nullable)error;
+ (SSignal * _Nonnull)never;
+ (SSignal * _Nonnull)complete;
@end
@@ -0,0 +1,41 @@
#import "SSignal+Single.h"
@implementation SSignal (Single)
+ (SSignal *)single:(id)next
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
[subscriber putNext:next];
[subscriber putCompletion];
return nil;
}];
}
+ (SSignal *)fail:(id)error
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
[subscriber putError:error];
return nil;
}];
}
+ (SSignal *)never
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (__unused SSubscriber *subscriber)
{
return nil;
}];
}
+ (SSignal *)complete
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
[subscriber putCompletion];
return nil;
}];
}
@end
@@ -0,0 +1,9 @@
#import <SSignalKit/SSignalKit.h>
@interface SSignal (Take)
- (SSignal * _Nonnull)take:(NSUInteger)count;
- (SSignal * _Nonnull)takeLast;
- (SSignal * _Nonnull)takeUntilReplacement:(SSignal * _Nonnull)replacement;
@end
@@ -0,0 +1,122 @@
#import "SSignal+Take.h"
#import "SAtomic.h"
@interface SSignal_ValueContainer : NSObject
@property (nonatomic, strong, readonly) id value;
@end
@implementation SSignal_ValueContainer
- (instancetype)initWithValue:(id)value {
self = [super init];
if (self != nil) {
_value = value;
}
return self;
}
@end
@implementation SSignal (Take)
- (SSignal *)take:(NSUInteger)count
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
SAtomic *counter = [[SAtomic alloc] initWithValue:@(0)];
return [self startWithNext:^(id next)
{
__block bool passthrough = false;
__block bool complete = false;
[counter modify:^id(NSNumber *currentCount)
{
NSUInteger updatedCount = [currentCount unsignedIntegerValue] + 1;
if (updatedCount <= count)
passthrough = true;
if (updatedCount == count)
complete = true;
return @(updatedCount);
}];
if (passthrough)
[subscriber putNext:next];
if (complete)
[subscriber putCompletion];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}];
}];
}
- (SSignal *)takeLast
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
SAtomic *last = [[SAtomic alloc] initWithValue:nil];
return [self startWithNext:^(id next)
{
[last swap:[[SSignal_ValueContainer alloc] initWithValue:next]];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
SSignal_ValueContainer *value = [last with:^id(id value) {
return value;
}];
if (value != nil)
{
[subscriber putNext:value.value];
}
[subscriber putCompletion];
}];
}];
}
- (SSignal *)takeUntilReplacement:(SSignal *)replacement {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
SDisposableSet *disposable = [[SDisposableSet alloc] init];
SMetaDisposable *selfDisposable = [[SMetaDisposable alloc] init];
SMetaDisposable *replacementDisposable = [[SMetaDisposable alloc] init];
[disposable add:selfDisposable];
[disposable add:replacementDisposable];
[disposable add:[replacement startWithNext:^(SSignal *next) {
[selfDisposable dispose];
[replacementDisposable setDisposable:[next startWithNext:^(id next) {
[subscriber putNext:next];
} error:^(id error) {
[subscriber putError:error];
} completed:^{
[subscriber putCompletion];
}]];
} error:^(id error) {
[subscriber putError:error];
} completed:^{
}]];
[selfDisposable setDisposable:[self startWithNext:^(id next) {
[subscriber putNext:next];
} error:^(id error) {
[replacementDisposable dispose];
[subscriber putError:error];
} completed:^{
[replacementDisposable dispose];
[subscriber putCompletion];
}]];
return disposable;
}];
}
@end
@@ -0,0 +1,11 @@
#import <SSignalKit/SSignal.h>
#import <SSignalKit/SQueue.h>
@interface SSignal (Timing)
- (SSignal * _Nonnull)delay:(NSTimeInterval)seconds onQueue:(SQueue * _Nonnull)queue;
- (SSignal * _Nonnull)timeout:(NSTimeInterval)seconds onQueue:(SQueue * _Nonnull)queue orSignal:(SSignal * _Nonnull)signal;
- (SSignal * _Nonnull)wait:(NSTimeInterval)seconds;
@end
@@ -0,0 +1,116 @@
#import "SSignal+Timing.h"
#import "SMetaDisposable.h"
#import "SDisposableSet.h"
#import "SBlockDisposable.h"
#import "SSignal+Dispatch.h"
#import "STimer.h"
@implementation SSignal (Timing)
- (SSignal *)delay:(NSTimeInterval)seconds onQueue:(SQueue *)queue
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SMetaDisposable *startDisposable = [[SMetaDisposable alloc] init];
SMetaDisposable *timerDisposable = [[SMetaDisposable alloc] init];
STimer *timer = [[STimer alloc] initWithTimeout:seconds repeat:false completion:^(__unused STimer *timer) {
[startDisposable setDisposable:[self startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
} queue:queue];
[timer start];
[timerDisposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
{
[timer invalidate];
}]];
return [[SBlockDisposable alloc] initWithBlock:^{
[startDisposable dispose];
[timerDisposable dispose];
}];
}];
}
- (SSignal *)timeout:(NSTimeInterval)seconds onQueue:(SQueue *)queue orSignal:(SSignal *)signal
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable> (SSubscriber *subscriber)
{
SMetaDisposable *startDisposable = [[SMetaDisposable alloc] init];
SMetaDisposable *timerDisposable = [[SMetaDisposable alloc] init];
STimer *timer = [[STimer alloc] initWithTimeout:seconds repeat:false completion:^(__unused STimer *timer)
{
[startDisposable setDisposable:[signal startWithNext:^(id next)
{
[subscriber putNext:next];
} error:^(id error)
{
[subscriber putError:error];
} completed:^
{
[subscriber putCompletion];
}]];
} queue:queue];
[timer start];
[timerDisposable setDisposable:[self startWithNext:^(id next)
{
[timer invalidate];
[subscriber putNext:next];
} error:^(id error)
{
[timer invalidate];
[subscriber putError:error];
} completed:^
{
[timer invalidate];
[subscriber putCompletion];
}]];
return [[SBlockDisposable alloc] initWithBlock:^{
[startDisposable dispose];
[timerDisposable dispose];
}];
}];
}
- (SSignal *)wait:(NSTimeInterval)seconds
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
id<SDisposable> disposable = [self startWithNext:^(id next)
{
dispatch_semaphore_signal(semaphore);
[subscriber putNext:next];
} error:^(id error)
{
dispatch_semaphore_signal(semaphore);
[subscriber putError:error];
} completed:^
{
dispatch_semaphore_signal(semaphore);
[subscriber putCompletion];
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)));
return disposable;
}];
}
@end
@@ -0,0 +1,22 @@
#import <Foundation/Foundation.h>
#import <SSignalKit/SSubscriber.h>
@interface SSignal : NSObject
{
@public
id<SDisposable> _Nullable (^ _Nonnull _generator)(SSubscriber * _Nonnull);
}
- (instancetype _Nonnull)initWithGenerator:(id<SDisposable> _Nullable (^ _Nonnull)(SSubscriber * _Nonnull))generator;
- (id<SDisposable> _Nullable)startWithNext:(void (^ _Nullable)(id _Nullable next))next error:(void (^ _Nullable)(id _Nullable error))error completed:(void (^ _Nullable)())completed;
- (id<SDisposable> _Nullable)startStrictWithNext:(void (^ _Nullable)(id _Nullable next))next error:(void (^ _Nullable)(id _Nullable error))error completed:(void (^ _Nullable)())completed file:(const char * _Nonnull)file line:(int)line;
- (id<SDisposable> _Nullable)startWithNext:(void (^ _Nullable)(id _Nullable next))next;
- (id<SDisposable> _Nullable)startStrictWithNext:(void (^ _Nullable)(id _Nullable next))next file:(const char * _Nonnull)file line:(int)line;
- (id<SDisposable> _Nullable)startWithNext:(void (^ _Nullable)(id _Nullable next))next completed:(void (^ _Nullable)())completed;
- (id<SDisposable> _Nullable)startStrictWithNext:(void (^ _Nullable)(id _Nullable next))next completed:(void (^ _Nullable)())completed file:(const char * _Nonnull)file line:(int)line;
@end
@@ -0,0 +1,163 @@
#import "SSignal.h"
#import "SBlockDisposable.h"
#import <pthread/pthread.h>
@interface SSubscriberDisposable : NSObject <SDisposable>
{
__weak SSubscriber *_subscriber;
id<SDisposable> _disposable;
pthread_mutex_t _lock;
}
@end
@implementation SSubscriberDisposable
- (instancetype)initWithSubscriber:(SSubscriber *)subscriber disposable:(id<SDisposable>)disposable {
self = [super init];
if (self != nil) {
_subscriber = subscriber;
_disposable = disposable;
pthread_mutex_init(&_lock, nil);
}
return self;
}
- (void)dealloc {
pthread_mutex_destroy(&_lock);
}
- (void)dispose {
SSubscriber *subscriber = nil;
id<SDisposable> disposeItem = nil;
pthread_mutex_lock(&_lock);
disposeItem = _disposable;
_disposable = nil;
subscriber = _subscriber;
_subscriber = nil;
pthread_mutex_unlock(&_lock);
[disposeItem dispose];
[subscriber _markTerminatedWithoutDisposal];
}
@end
@interface SStrictDisposable : NSObject<SDisposable> {
id<SDisposable> _disposable;
const char *_file;
int _line;
#if DEBUG
pthread_mutex_t _lock;
bool _isDisposed;
#endif
}
- (instancetype)initWithDisposable:(id<SDisposable>)disposable file:(const char *)file line:(int)line;
- (void)dispose;
@end
@implementation SStrictDisposable
- (instancetype)initWithDisposable:(id<SDisposable>)disposable file:(const char *)file line:(int)line {
self = [super init];
if (self != nil) {
_disposable = disposable;
_file = file;
_line = line;
#if DEBUG
pthread_mutex_init(&_lock, nil);
#endif
}
return self;
}
- (void)dealloc {
#if DEBUG
pthread_mutex_lock(&_lock);
if (!_isDisposed) {
NSLog(@"Leaked disposable from %s:%d", _file, _line);
assert(false);
}
pthread_mutex_unlock(&_lock);
pthread_mutex_destroy(&_lock);
#endif
}
- (void)dispose {
#if DEBUG
pthread_mutex_lock(&_lock);
_isDisposed = true;
pthread_mutex_unlock(&_lock);
#endif
[_disposable dispose];
}
@end
@interface SSignal ()
{
}
@end
@implementation SSignal
- (instancetype)initWithGenerator:(id<SDisposable> (^)(SSubscriber *))generator {
self = [super init];
if (self != nil) {
_generator = [generator copy];
}
return self;
}
- (id<SDisposable>)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed {
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:error completed:completed];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
}
- (id<SDisposable>)startStrictWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed file:(const char * _Nonnull)file line:(int)line {
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:error completed:completed];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SStrictDisposable alloc] initWithDisposable:[[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable] file:file line:line];
}
- (id<SDisposable>)startWithNext:(void (^)(id next))next {
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:nil];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
}
- (id<SDisposable>)startStrictWithNext:(void (^)(id next))next file:(const char * _Nonnull)file line:(int)line {
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:nil];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SStrictDisposable alloc] initWithDisposable:[[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable] file:file line:line];
}
- (id<SDisposable>)startWithNext:(void (^)(id next))next completed:(void (^)())completed {
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:completed];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
}
- (id<SDisposable>)startStrictWithNext:(void (^)(id next))next completed:(void (^)())completed file:(const char * _Nonnull)file line:(int)line {
SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:completed];
id<SDisposable> disposable = _generator(subscriber);
[subscriber _assignDisposable:disposable];
return [[SStrictDisposable alloc] initWithDisposable:[[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable] file:file line:line];
}
@end
@@ -0,0 +1,35 @@
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <UIKit/UIKit.h>
#else
#import <Foundation/Foundation.h>
#endif
//! Project version number for SSignalKit.
FOUNDATION_EXPORT double SSignalKitVersionNumber;
//! Project version string for SSignalKit.
FOUNDATION_EXPORT const unsigned char SSignalKitVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <SSignalKit/PublicHeader.h>
#import <SSignalKit/SAtomic.h>
#import <SSignalKit/SBag.h>
#import <SSignalKit/SSignal.h>
#import <SSignalKit/SSubscriber.h>
#import <SSignalKit/SDisposable.h>
#import <SSignalKit/SDisposableSet.h>
#import <SSignalKit/SBlockDisposable.h>
#import <SSignalKit/SMetaDisposable.h>
#import <SSignalKit/SSignal+Single.h>
#import <SSignalKit/SSignal+Mapping.h>
#import <SSignalKit/SSignal+Meta.h>
#import <SSignalKit/SSignal+Dispatch.h>
#import <SSignalKit/SSignal+Catch.h>
#import <SSignalKit/SSignal+SideEffects.h>
#import <SSignalKit/SSignal+Combine.h>
#import <SSignalKit/SSignal+Timing.h>
#import <SSignalKit/SSignal+Take.h>
#import <SSignalKit/SSignal+Pipe.h>
#import <SSignalKit/STimer.h>
#import <SSignalKit/SVariable.h>
#import <SSignalKit/SQueueLocalObject.h>
@@ -0,0 +1,23 @@
#import <Foundation/Foundation.h>
#import <SSignalKit/SDisposable.h>
@interface SSubscriber : NSObject <SDisposable>
{
}
- (instancetype _Nonnull)initWithNext:(void (^ _Nullable)(id _Nullable))next error:(void (^ _Nullable)(id _Nullable))error completed:(void (^ _Nullable)())completed;
- (void)_assignDisposable:(id<SDisposable> _Nullable)disposable;
- (void)_markTerminatedWithoutDisposal;
- (void)putNext:(id _Nullable)next;
- (void)putError:(id _Nullable)error;
- (void)putCompletion;
@end
@interface STracingSubscriber : SSubscriber
- (instancetype _Nonnull)initWithName:(NSString * _Nonnull)name next:(void (^ _Nullable)(id _Nullable))next error:(void (^ _Nullable)(id _Nullable))error completed:(void (^ _Nullable)())completed;
@end
@@ -0,0 +1,287 @@
#import "SSubscriber.h"
#import <os/lock.h>
@interface SSubscriberBlocks : NSObject {
@public
void (^_next)(id);
void (^_error)(id);
void (^_completed)();
}
@end
@implementation SSubscriberBlocks
- (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed {
self = [super init];
if (self != nil) {
_next = [next copy];
_error = [error copy];
_completed = [completed copy];
}
return self;
}
@end
@interface SSubscriber ()
{
@protected
os_unfair_lock _lock;
bool _terminated;
id<SDisposable> _disposable;
SSubscriberBlocks *_blocks;
}
@end
@implementation SSubscriber
- (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed
{
self = [super init];
if (self != nil)
{
_blocks = [[SSubscriberBlocks alloc] initWithNext:next error:error completed:completed];
}
return self;
}
- (void)_assignDisposable:(id<SDisposable>)disposable
{
bool dispose = false;
os_unfair_lock_lock(&_lock);
if (_terminated) {
dispose = true;
} else {
_disposable = disposable;
}
os_unfair_lock_unlock(&_lock);
if (dispose) {
[disposable dispose];
}
}
- (void)_markTerminatedWithoutDisposal
{
id<SDisposable> disposable = nil;
os_unfair_lock_lock(&_lock);
SSubscriberBlocks *blocks = nil;
if (!_terminated)
{
blocks = _blocks;
_blocks = nil;
_terminated = true;
}
disposable = _disposable;
_disposable = nil;
os_unfair_lock_unlock(&_lock);
if (blocks) {
blocks = nil;
}
if (disposable) {
disposable = nil;
}
}
- (void)putNext:(id)next
{
SSubscriberBlocks *blocks = nil;
os_unfair_lock_lock(&_lock);
if (!_terminated) {
blocks = _blocks;
}
os_unfair_lock_unlock(&_lock);
if (blocks && blocks->_next) {
blocks->_next(next);
}
}
- (void)putError:(id)error
{
bool shouldDispose = false;
SSubscriberBlocks *blocks = nil;
os_unfair_lock_lock(&_lock);
if (!_terminated)
{
blocks = _blocks;
_blocks = nil;
shouldDispose = true;
_terminated = true;
}
os_unfair_lock_unlock(&_lock);
if (blocks && blocks->_error) {
blocks->_error(error);
}
if (shouldDispose) {
[self->_disposable dispose];
self->_disposable = nil;
}
}
- (void)putCompletion
{
bool shouldDispose = false;
SSubscriberBlocks *blocks = nil;
os_unfair_lock_lock(&_lock);
if (!_terminated)
{
blocks = _blocks;
_blocks = nil;
shouldDispose = true;
_terminated = true;
}
os_unfair_lock_unlock(&_lock);
if (blocks && blocks->_completed)
blocks->_completed();
if (shouldDispose) {
[self->_disposable dispose];
self->_disposable = nil;
}
}
- (void)dispose
{
[self->_disposable dispose];
self->_disposable = nil;
}
@end
@interface STracingSubscriber ()
{
NSString *_name;
}
@end
@implementation STracingSubscriber
- (instancetype)initWithName:(NSString *)name next:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed
{
self = [super initWithNext:next error:error completed:completed];
if (self != nil)
{
_name = name;
}
return self;
}
/*- (void)_assignDisposable:(id<SDisposable>)disposable
{
if (_terminated)
[disposable dispose];
else
_disposable = disposable;
}
- (void)_markTerminatedWithoutDisposal
{
os_unfair_lock_lock(&_lock);
if (!_terminated)
{
NSLog(@"trace(%@ terminated)", _name);
_terminated = true;
_next = nil;
_error = nil;
_completed = nil;
}
os_unfair_lock_unlock(&_lock);
}
- (void)putNext:(id)next
{
void (^fnext)(id) = nil;
os_unfair_lock_lock(&_lock);
if (!_terminated)
fnext = self->_next;
os_unfair_lock_unlock(&_lock);
if (fnext)
{
NSLog(@"trace(%@ next: %@)", _name, next);
fnext(next);
}
else
NSLog(@"trace(%@ next: %@, not accepted)", _name, next);
}
- (void)putError:(id)error
{
bool shouldDispose = false;
void (^ferror)(id) = nil;
os_unfair_lock_lock(&_lock);
if (!_terminated)
{
ferror = self->_error;
shouldDispose = true;
self->_next = nil;
self->_error = nil;
self->_completed = nil;
_terminated = true;
}
os_unfair_lock_unlock(&_lock);
if (ferror)
{
NSLog(@"trace(%@ error: %@)", _name, error);
ferror(error);
}
else
NSLog(@"trace(%@ error: %@, not accepted)", _name, error);
if (shouldDispose)
[self->_disposable dispose];
}
- (void)putCompletion
{
bool shouldDispose = false;
void (^completed)() = nil;
os_unfair_lock_lock(&_lock);
if (!_terminated)
{
completed = self->_completed;
shouldDispose = true;
self->_next = nil;
self->_error = nil;
self->_completed = nil;
_terminated = true;
}
os_unfair_lock_unlock(&_lock);
if (completed)
{
NSLog(@"trace(%@ completed)", _name);
completed();
}
else
NSLog(@"trace(%@ completed, not accepted)", _name);
if (shouldDispose)
[self->_disposable dispose];
}
- (void)dispose
{
NSLog(@"trace(%@ dispose)", _name);
[self->_disposable dispose];
}*/
@end
@@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>
#import <SSignalKit/SThreadPoolTask.h>
#import <SSignalKit/SThreadPoolQueue.h>
@interface SThreadPool : NSObject
- (instancetype _Nonnull)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority;
- (void)addTask:(SThreadPoolTask * _Nonnull)task;
- (SThreadPoolQueue * _Nonnull)nextQueue;
- (void)_workOnQueue:(SThreadPoolQueue * _Nonnull)queue block:(void (^ _Nonnull)())block;
@end
@@ -0,0 +1,128 @@
#import "SThreadPool.h"
#import <os/lock.h>
#import <pthread.h>
#import "SQueue.h"
@interface SThreadPool ()
{
SQueue *_managementQueue;
NSMutableArray *_threads;
NSMutableArray *_queues;
NSMutableArray *_takenQueues;
pthread_mutex_t _mutex;
pthread_cond_t _cond;
}
@end
@implementation SThreadPool
+ (void)threadEntryPoint:(SThreadPool *)threadPool
{
SThreadPoolQueue *queue = nil;
while (true)
{
SThreadPoolTask *task = nil;
pthread_mutex_lock(&threadPool->_mutex);
if (queue != nil)
{
[threadPool->_takenQueues removeObject:queue];
if ([queue _hasTasks])
[threadPool->_queues addObject:queue];
}
while (true)
{
while (threadPool->_queues.count == 0)
pthread_cond_wait(&threadPool->_cond, &threadPool->_mutex);
queue = threadPool->_queues.firstObject;
task = [queue _popFirstTask];
if (queue != nil)
{
[threadPool->_takenQueues addObject:queue];
[threadPool->_queues removeObjectAtIndex:0];
break;
}
}
pthread_mutex_unlock(&threadPool->_mutex);
@autoreleasepool
{
[task execute];
}
}
}
- (instancetype)init
{
return [self initWithThreadCount:2 threadPriority:0.5];
}
- (instancetype)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority
{
self = [super init];
if (self != nil)
{
pthread_mutex_init(&_mutex, 0);
pthread_cond_init(&_cond, 0);
_managementQueue = [[SQueue alloc] init];
[_managementQueue dispatch:^
{
_threads = [[NSMutableArray alloc] init];
_queues = [[NSMutableArray alloc] init];
_takenQueues = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < threadCount; i++)
{
NSThread *thread = [[NSThread alloc] initWithTarget:[SThreadPool class] selector:@selector(threadEntryPoint:) object:self];
thread.name = [[NSString alloc] initWithFormat:@"SThreadPool-%p-%d", self, (int)i];
[thread setThreadPriority:threadPriority];
[_threads addObject:thread];
[thread start];
}
}];
}
return self;
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
- (void)addTask:(SThreadPoolTask *)task
{
SThreadPoolQueue *tempQueue = [self nextQueue];
[tempQueue addTask:task];
}
- (SThreadPoolQueue *)nextQueue
{
return [[SThreadPoolQueue alloc] initWithThreadPool:self];
}
- (void)_workOnQueue:(SThreadPoolQueue *)queue block:(void (^)())block
{
[_managementQueue dispatch:^
{
pthread_mutex_lock(&_mutex);
block();
if (![_queues containsObject:queue] && ![_takenQueues containsObject:queue])
[_queues addObject:queue];
pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}];
}
@end
@@ -0,0 +1,13 @@
#import <Foundation/Foundation.h>
@class SThreadPool;
@class SThreadPoolTask;
@interface SThreadPoolQueue : NSObject
- (instancetype _Nonnull)initWithThreadPool:(SThreadPool * _Nonnull)threadPool;
- (void)addTask:(SThreadPoolTask * _Nonnull)task;
- (SThreadPoolTask * _Nullable)_popFirstTask;
- (bool)_hasTasks;
@end
@@ -0,0 +1,51 @@
#import "SThreadPoolQueue.h"
#import "SThreadPool.h"
@interface SThreadPoolQueue ()
{
__weak SThreadPool *_threadPool;
NSMutableArray *_tasks;
}
@end
@implementation SThreadPoolQueue
- (instancetype)initWithThreadPool:(SThreadPool *)threadPool
{
self = [super init];
if (self != nil)
{
_threadPool = threadPool;
_tasks = [[NSMutableArray alloc] init];
}
return self;
}
- (void)addTask:(SThreadPoolTask *)task
{
SThreadPool *threadPool = _threadPool;
[threadPool _workOnQueue:self block:^
{
[_tasks addObject:task];
}];
}
- (SThreadPoolTask *)_popFirstTask
{
if (_tasks.count != 0)
{
SThreadPoolTask *task = _tasks[0];
[_tasks removeObjectAtIndex:0];
return task;
}
return nil;
}
- (bool)_hasTasks
{
return _tasks.count != 0;
}
@end
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface SThreadPoolTask : NSObject
- (instancetype _Nonnull)initWithBlock:(void (^ _Nonnull)(bool (^ _Nonnull)()))block;
- (void)execute;
- (void)cancel;
@end
@@ -0,0 +1,53 @@
#import "SThreadPoolTask.h"
@interface SThreadPoolTaskState : NSObject
{
@public
bool _cancelled;
}
@end
@implementation SThreadPoolTaskState
@end
@interface SThreadPoolTask ()
{
void (^_block)(bool (^)());
SThreadPoolTaskState *_state;
}
@end
@implementation SThreadPoolTask
- (instancetype)initWithBlock:(void (^)(bool (^)()))block
{
self = [super init];
if (self != nil)
{
_block = [block copy];
_state = [[SThreadPoolTaskState alloc] init];
}
return self;
}
- (void)execute
{
if (_state->_cancelled)
return;
SThreadPoolTaskState *state = _state;
_block(^bool
{
return state->_cancelled;
});
}
- (void)cancel
{
_state->_cancelled = true;
}
@end
@@ -0,0 +1,14 @@
#import <Foundation/Foundation.h>
@class SQueue;
@interface STimer : NSObject
- (instancetype _Nonnull)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(void (^ _Nonnull)(STimer * _Nonnull))completion queue:(SQueue * _Nonnull)queue;
- (instancetype _Nonnull)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(void (^ _Nonnull)(STimer * _Nonnull))completion nativeQueue:(dispatch_queue_t _Nonnull)nativeQueue;
- (void)start;
- (void)invalidate;
- (void)fireAndInvalidate;
@end
@@ -0,0 +1,83 @@
#import "STimer.h"
#import "SQueue.h"
@interface STimer ()
{
dispatch_source_t _timer;
NSTimeInterval _timeout;
NSTimeInterval _timeoutDate;
bool _repeat;
void (^_completion)(STimer * _Nonnull);
dispatch_queue_t _nativeQueue;
}
@end
@implementation STimer
- (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(void (^ _Nonnull)(STimer * _Nonnull))completion queue:(SQueue *)queue {
return [self initWithTimeout:timeout repeat:repeat completion:completion nativeQueue:queue._dispatch_queue];
}
- (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(void (^ _Nonnull)(STimer * _Nonnull))completion nativeQueue:(dispatch_queue_t)nativeQueue
{
self = [super init];
if (self != nil)
{
_timeoutDate = INT_MAX;
_timeout = timeout;
_repeat = repeat;
_completion = [completion copy];
_nativeQueue = nativeQueue;
}
return self;
}
- (void)dealloc
{
if (_timer != nil)
{
dispatch_source_cancel(_timer);
_timer = nil;
}
}
- (void)start
{
_timeoutDate = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970 + _timeout;
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _nativeQueue);
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeout * NSEC_PER_SEC)), _repeat ? (int64_t)(_timeout * NSEC_PER_SEC) : DISPATCH_TIME_FOREVER, 0);
dispatch_source_set_event_handler(_timer, ^
{
if (_completion)
_completion(self);
if (!_repeat)
[self invalidate];
});
dispatch_resume(_timer);
}
- (void)fireAndInvalidate
{
if (_completion)
_completion(self);
[self invalidate];
}
- (void)invalidate
{
_timeoutDate = 0;
if (_timer != nil)
{
dispatch_source_cancel(_timer);
_timer = nil;
}
}
@end
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>
@class SSignal;
@interface SVariable : NSObject
- (instancetype _Nonnull)init;
- (void)set:(SSignal * _Nonnull)signal;
- (SSignal * _Nonnull)signal;
@end
@@ -0,0 +1,93 @@
#import "SVariable.h"
#import <os/lock.h>
#import "SSignal.h"
#import "SBag.h"
#import "SBlockDisposable.h"
#import "SMetaDisposable.h"
@interface SVariable ()
{
os_unfair_lock _lock;
id _value;
bool _hasValue;
SBag *_subscribers;
SMetaDisposable *_disposable;
}
@end
@implementation SVariable
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_subscribers = [[SBag alloc] init];
_disposable = [[SMetaDisposable alloc] init];
}
return self;
}
- (void)dealloc
{
[_disposable dispose];
}
- (SSignal *)signal
{
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
{
os_unfair_lock_lock(&self->_lock);
id currentValue = _value;
bool hasValue = _hasValue;
NSInteger index = [self->_subscribers addItem:[^(id value)
{
[subscriber putNext:value];
} copy]];
os_unfair_lock_unlock(&self->_lock);
if (hasValue)
{
[subscriber putNext:currentValue];
}
return [[SBlockDisposable alloc] initWithBlock:^
{
os_unfair_lock_lock(&self->_lock);
[self->_subscribers removeItem:index];
os_unfair_lock_unlock(&self->_lock);
}];
}];
}
- (void)set:(SSignal *)signal
{
os_unfair_lock_lock(&_lock);
_hasValue = false;
os_unfair_lock_unlock(&_lock);
__weak SVariable *weakSelf = self;
[_disposable setDisposable:[signal startWithNext:^(id next)
{
__strong SVariable *strongSelf = weakSelf;
if (strongSelf != nil)
{
NSArray *subscribers = nil;
os_unfair_lock_lock(&strongSelf->_lock);
strongSelf->_value = next;
strongSelf->_hasValue = true;
subscribers = [strongSelf->_subscribers copyItems];
os_unfair_lock_unlock(&strongSelf->_lock);
for (void (^subscriber)(id) in subscribers)
{
subscriber(next);
}
}
}]];
}
@end
@@ -0,0 +1,15 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "SwiftSignalKit",
module_name = "SwiftSignalKit",
srcs = glob([
"Source/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
visibility = [
"//visibility:public",
],
)
@@ -0,0 +1,57 @@
import Foundation
public enum AtomicLockError: Error {
case isLocked
}
public final class Atomic<T> {
private var lock: pthread_mutex_t
private var value: T
public init(value: T) {
self.lock = pthread_mutex_t()
self.value = value
pthread_mutex_init(&self.lock, nil)
}
deinit {
pthread_mutex_destroy(&self.lock)
}
public func with<R>(_ f: (T) -> R) -> R {
pthread_mutex_lock(&self.lock)
let result = f(self.value)
pthread_mutex_unlock(&self.lock)
return result
}
public func tryWith<R>(_ f: (T) -> R) throws -> R {
if pthread_mutex_trylock(&self.lock) == 0 {
let result = f(self.value)
pthread_mutex_unlock(&self.lock)
return result
} else {
throw AtomicLockError.isLocked
}
}
public func modify(_ f: (T) -> T) -> T {
pthread_mutex_lock(&self.lock)
let result = f(self.value)
self.value = result
pthread_mutex_unlock(&self.lock)
return result
}
public func swap(_ value: T) -> T {
pthread_mutex_lock(&self.lock)
let previous = self.value
self.value = value
pthread_mutex_unlock(&self.lock)
return previous
}
}
@@ -0,0 +1,149 @@
import Foundation
public final class Weak<T: AnyObject> {
private weak var _value: T?
public var value: T? {
return self._value
}
public init(_ value: T) {
self._value = value
}
}
public final class Bag<T> {
public typealias Index = Int
private var nextIndex: Index = 0
private var items: [T] = []
private var itemKeys: [Index] = []
public init() {
}
public func add(_ item: T) -> Index {
let key = self.nextIndex
self.nextIndex += 1
self.items.append(item)
self.itemKeys.append(key)
return key
}
public func get(_ index: Index) -> T? {
var i = 0
for key in self.itemKeys {
if key == index {
return self.items[i]
}
i += 1
}
return nil
}
public func remove(_ index: Index) {
var i = 0
for key in self.itemKeys {
if key == index {
self.items.remove(at: i)
self.itemKeys.remove(at: i)
break
}
i += 1
}
}
public func removeAll() {
self.items.removeAll()
self.itemKeys.removeAll()
}
public func copyItems() -> [T] {
return self.items
}
public func copyItemsWithIndices() -> [(Index, T)] {
var result: [(Index, T)] = []
var i = 0
for key in self.itemKeys {
result.append((key, self.items[i]))
i += 1
}
return result
}
public var isEmpty: Bool {
return self.items.isEmpty
}
public var first: (Index, T)? {
if !self.items.isEmpty {
return (self.itemKeys[0], self.items[0])
} else {
return nil
}
}
}
public final class SparseBag<T>: Sequence {
public typealias Index = Int
private var nextIndex: Index = 0
private var items: [Index: T] = [:]
public init() {
}
public func add(_ item: T) -> Index {
let key = self.nextIndex
self.nextIndex += 1
self.items[key] = item
return key
}
public func get(_ index: Index) -> T? {
return self.items[index]
}
public func remove(_ index: Index) {
self.items.removeValue(forKey: index)
}
public func removeAll() {
self.items.removeAll()
}
public var isEmpty: Bool {
return self.items.isEmpty
}
public func makeIterator() -> AnyIterator<T> {
var iterator = self.items.makeIterator()
return AnyIterator { () -> T? in
return iterator.next()?.value
}
}
}
public final class CounterBag {
private var nextIndex: Int = 1
private var items = Set<Int>()
public init() {
}
public func add() -> Int {
let index = self.nextIndex
self.nextIndex += 1
self.items.insert(index)
return index
}
public func remove(_ index: Int) {
self.items.remove(index)
}
public var isEmpty: Bool {
return self.items.isEmpty
}
}
@@ -0,0 +1,271 @@
import Foundation
public protocol Disposable: AnyObject {
func dispose()
}
public final class StrictDisposable: Disposable {
private let disposable: Disposable
private let file: String
private let line: Int
private let isDisposed = Atomic<Bool>(value: false)
public init(_ disposable: Disposable, file: String, line: Int) {
self.disposable = disposable
self.file = file
self.line = line
}
deinit {
#if DEBUG
if !self.isDisposed.with({ $0 }) {
assertionFailure("Leaked disposable \(self.disposable) from \(self.file):\(self.line)")
}
#endif
}
public func dispose() {
let _ = self.isDisposed.swap(true)
self.disposable.dispose()
}
}
public extension Disposable {
func strict(file: String = #file, line: Int = #line) -> Disposable {
return StrictDisposable(self, file: file, line: line)
}
}
final class _EmptyDisposable: Disposable {
func dispose() {
}
}
public let EmptyDisposable: Disposable = _EmptyDisposable()
public final class ActionDisposable : Disposable {
private var lock = pthread_mutex_t()
private var action: (() -> Void)?
public init(action: @escaping() -> Void) {
self.action = action
pthread_mutex_init(&self.lock, nil)
}
deinit {
var freeAction: (() -> Void)?
pthread_mutex_lock(&self.lock)
freeAction = self.action
self.action = nil
pthread_mutex_unlock(&self.lock)
if let freeAction = freeAction {
withExtendedLifetime(freeAction, {})
}
pthread_mutex_destroy(&self.lock)
}
public func dispose() {
let disposeAction: (() -> Void)?
pthread_mutex_lock(&self.lock)
disposeAction = self.action
self.action = nil
pthread_mutex_unlock(&self.lock)
disposeAction?()
}
}
public final class MetaDisposable : Disposable {
private var lock = pthread_mutex_t()
private var disposed = false
private var disposable: Disposable! = nil
public init() {
pthread_mutex_init(&self.lock, nil)
}
deinit {
var freeDisposable: Disposable?
pthread_mutex_lock(&self.lock)
if let disposable = self.disposable {
freeDisposable = disposable
self.disposable = nil
}
pthread_mutex_unlock(&self.lock)
if let freeDisposable = freeDisposable {
withExtendedLifetime(freeDisposable, { })
}
pthread_mutex_destroy(&self.lock)
}
public func set(_ disposable: Disposable?) {
var previousDisposable: Disposable! = nil
var disposeImmediately = false
pthread_mutex_lock(&self.lock)
disposeImmediately = self.disposed
if !disposeImmediately {
previousDisposable = self.disposable
if let disposable = disposable {
self.disposable = disposable
} else {
self.disposable = nil
}
}
pthread_mutex_unlock(&self.lock)
if previousDisposable != nil {
previousDisposable.dispose()
}
if disposeImmediately {
if let disposable = disposable {
disposable.dispose()
}
}
}
public func dispose()
{
var disposable: Disposable! = nil
pthread_mutex_lock(&self.lock)
if !self.disposed {
self.disposed = true
disposable = self.disposable
self.disposable = nil
}
pthread_mutex_unlock(&self.lock)
if disposable != nil {
disposable.dispose()
}
}
}
public final class DisposableSet : Disposable {
private var lock = pthread_mutex_t()
private var disposed = false
private var disposables: [Disposable] = []
public init() {
pthread_mutex_init(&self.lock, nil)
}
deinit {
pthread_mutex_lock(&self.lock)
self.disposables.removeAll()
pthread_mutex_unlock(&self.lock)
pthread_mutex_destroy(&self.lock)
}
public func add(_ disposable: Disposable) {
var disposeImmediately = false
pthread_mutex_lock(&self.lock)
if self.disposed {
disposeImmediately = true
} else {
self.disposables.append(disposable)
}
pthread_mutex_unlock(&self.lock)
if disposeImmediately {
disposable.dispose()
}
}
public func remove(_ disposable: Disposable) {
pthread_mutex_lock(&self.lock)
if let index = self.disposables.firstIndex(where: { $0 === disposable }) {
self.disposables.remove(at: index)
}
pthread_mutex_unlock(&self.lock)
}
public func removeLast() {
pthread_mutex_lock(&self.lock)
self.disposables.removeLast()
pthread_mutex_unlock(&self.lock)
}
public func dispose() {
var disposables: [Disposable] = []
pthread_mutex_lock(&self.lock)
if !self.disposed {
self.disposed = true
disposables = self.disposables
self.disposables = []
}
pthread_mutex_unlock(&self.lock)
if disposables.count != 0 {
for disposable in disposables {
disposable.dispose()
}
}
}
}
public final class DisposableDict<T: Hashable> : Disposable {
private var lock = pthread_mutex_t()
private var disposed = false
private var disposables: [T: Disposable] = [:]
public init() {
pthread_mutex_init(&self.lock, nil)
}
deinit {
pthread_mutex_lock(&self.lock)
self.disposables.removeAll()
pthread_mutex_unlock(&self.lock)
pthread_mutex_destroy(&self.lock)
}
public func set(_ disposable: Disposable?, forKey key: T) {
var disposeImmediately = false
var disposePrevious: Disposable?
pthread_mutex_lock(&self.lock)
if self.disposed {
disposeImmediately = true
} else {
disposePrevious = self.disposables[key]
if let disposable = disposable {
self.disposables[key] = disposable
}
}
pthread_mutex_unlock(&self.lock)
if disposeImmediately {
disposable?.dispose()
}
disposePrevious?.dispose()
}
public func dispose() {
var disposables: [T: Disposable] = [:]
pthread_mutex_lock(&self.lock)
if !self.disposed {
self.disposed = true
disposables = self.disposables
self.disposables = [:]
}
pthread_mutex_unlock(&self.lock)
if disposables.count != 0 {
for disposable in disposables.values {
disposable.dispose()
}
}
}
}
@@ -0,0 +1,34 @@
import Foundation
public final class Lock {
private var mutex = pthread_mutex_t()
public init() {
pthread_mutex_init(&self.mutex, nil)
}
deinit {
pthread_mutex_destroy(&self.mutex)
}
public func locked(_ f: () -> ()) {
pthread_mutex_lock(&self.mutex)
f()
pthread_mutex_unlock(&self.mutex)
}
public func throwingLocked(_ f: () throws -> Void) throws {
var error: Error?
pthread_mutex_lock(&self.mutex)
do {
try f()
} catch let e {
error = e
}
pthread_mutex_unlock(&self.mutex)
if let error = error {
throw(error)
}
}
}
@@ -0,0 +1,85 @@
import Foundation
private final class MulticastInstance<T> {
let disposable: Disposable
var subscribers = Bag<(T) -> Void>()
var lock = Lock()
init(disposable: Disposable) {
self.disposable = disposable
}
}
public final class Multicast<T> {
private let lock = Lock()
private var instances: [String: MulticastInstance<T>] = [:]
public init() {
}
public func get(key: String, signal: Signal<T, NoError>) -> Signal<T, NoError> {
return Signal { subscriber in
var instance: MulticastInstance<T>!
var beginDisposable: MetaDisposable?
self.lock.locked {
if let existing = self.instances[key] {
instance = existing
} else {
let disposable = MetaDisposable()
instance = MulticastInstance(disposable: disposable)
beginDisposable = disposable
}
}
var index: Bag<(T) -> Void>.Index!
instance.lock.locked {
index = instance.subscribers.add({ next in
subscriber.putNext(next)
})
}
if let beginDisposable = beginDisposable {
beginDisposable.set(signal.start(next: { next in
var subscribers: [(T) -> Void]!
instance.lock.locked {
subscribers = instance.subscribers.copyItems()
}
for subscriber in subscribers {
subscriber(next)
}
}, error: { _ in
}, completed: {
self.lock.locked {
self.instances.removeValue(forKey: key)
}
}))
}
return ActionDisposable {
var remove = false
instance.lock.locked {
instance.subscribers.remove(index)
if instance.subscribers.isEmpty {
remove = true
}
}
if remove {
self.lock.locked {
let _ = self.instances.removeValue(forKey: key)
}
}
}
}
}
}
public final class MulticastPromise<T> {
public let subscribers = Bag<(T) -> Void>()
public let lock = Lock()
public var value: T?
public init() {
}
}
@@ -0,0 +1,139 @@
import Foundation
public final class Promise<T> {
private var initializeOnFirstAccess: Signal<T, NoError>?
private var value: T?
private var lock = pthread_mutex_t()
private let disposable = MetaDisposable()
private let subscribers = Bag<(T) -> Void>()
public var onDeinit: (() -> Void)?
public init(initializeOnFirstAccess: Signal<T, NoError>?) {
self.initializeOnFirstAccess = initializeOnFirstAccess
pthread_mutex_init(&self.lock, nil)
}
public init(_ value: T) {
self.value = value
pthread_mutex_init(&self.lock, nil)
}
public init() {
pthread_mutex_init(&self.lock, nil)
}
deinit {
self.onDeinit?()
pthread_mutex_destroy(&self.lock)
self.disposable.dispose()
}
public func set(_ signal: Signal<T, NoError>) {
pthread_mutex_lock(&self.lock)
self.value = nil
pthread_mutex_unlock(&self.lock)
self.disposable.set(signal.start(next: { [weak self] next in
if let strongSelf = self {
pthread_mutex_lock(&strongSelf.lock)
strongSelf.value = next
let subscribers = strongSelf.subscribers.copyItems()
pthread_mutex_unlock(&strongSelf.lock)
for subscriber in subscribers {
subscriber(next)
}
}
}))
}
public func get() -> Signal<T, NoError> {
return Signal { subscriber in
pthread_mutex_lock(&self.lock)
var initializeOnFirstAccessNow: Signal<T, NoError>?
if let initializeOnFirstAccess = self.initializeOnFirstAccess {
initializeOnFirstAccessNow = initializeOnFirstAccess
self.initializeOnFirstAccess = nil
}
let currentValue = self.value
let index = self.subscribers.add({ next in
subscriber.putNext(next)
})
pthread_mutex_unlock(&self.lock)
if let currentValue = currentValue {
subscriber.putNext(currentValue)
}
if let initializeOnFirstAccessNow = initializeOnFirstAccessNow {
self.set(initializeOnFirstAccessNow)
}
return ActionDisposable {
pthread_mutex_lock(&self.lock)
self.subscribers.remove(index)
pthread_mutex_unlock(&self.lock)
}
}
}
}
public final class ValuePromise<T: Equatable> {
private var value: T?
private var lock = pthread_mutex_t()
private let subscribers = Bag<(T) -> Void>()
public let ignoreRepeated: Bool
public init(_ value: T, ignoreRepeated: Bool = false) {
self.value = value
self.ignoreRepeated = ignoreRepeated
pthread_mutex_init(&self.lock, nil)
}
public init(ignoreRepeated: Bool = false) {
self.ignoreRepeated = ignoreRepeated
pthread_mutex_init(&self.lock, nil)
}
deinit {
pthread_mutex_destroy(&self.lock)
}
public func set(_ value: T) {
pthread_mutex_lock(&self.lock)
let subscribers: [(T) -> Void]
if !self.ignoreRepeated || self.value != value {
self.value = value
subscribers = self.subscribers.copyItems()
} else {
subscribers = []
}
pthread_mutex_unlock(&self.lock);
for subscriber in subscribers {
subscriber(value)
}
}
public func get() -> Signal<T, NoError> {
return Signal { subscriber in
pthread_mutex_lock(&self.lock)
let currentValue = self.value
let index = self.subscribers.add({ next in
subscriber.putNext(next)
})
pthread_mutex_unlock(&self.lock)
if let currentValue = currentValue {
subscriber.putNext(currentValue)
}
return ActionDisposable {
pthread_mutex_lock(&self.lock)
self.subscribers.remove(index)
pthread_mutex_unlock(&self.lock)
}
}
}
}
@@ -0,0 +1,88 @@
import Foundation
private let QueueSpecificKey = DispatchSpecificKey<NSObject>()
private let globalMainQueue = Queue(queue: DispatchQueue.main, specialIsMainQueue: true)
private let globalDefaultQueue = Queue(queue: DispatchQueue.global(qos: .default), specialIsMainQueue: false)
private let globalBackgroundQueue = Queue(queue: DispatchQueue.global(qos: .background), specialIsMainQueue: false)
public final class Queue {
private let nativeQueue: DispatchQueue
private var specific = NSObject()
private let specialIsMainQueue: Bool
public var queue: DispatchQueue {
get {
return self.nativeQueue
}
}
public class func mainQueue() -> Queue {
return globalMainQueue
}
public class func concurrentDefaultQueue() -> Queue {
return globalDefaultQueue
}
public class func concurrentBackgroundQueue() -> Queue {
return globalBackgroundQueue
}
public init(queue: DispatchQueue) {
self.nativeQueue = queue
self.specialIsMainQueue = false
}
fileprivate init(queue: DispatchQueue, specialIsMainQueue: Bool) {
self.nativeQueue = queue
self.specialIsMainQueue = specialIsMainQueue
}
public init(name: String? = nil, qos: DispatchQoS = .default) {
self.nativeQueue = DispatchQueue(label: name ?? "", qos: qos)
self.specialIsMainQueue = false
self.nativeQueue.setSpecific(key: QueueSpecificKey, value: self.specific)
}
public func isCurrent() -> Bool {
if DispatchQueue.getSpecific(key: QueueSpecificKey) === self.specific {
return true
} else if self.specialIsMainQueue && Thread.isMainThread {
return true
} else {
return false
}
}
public func async(_ f: @escaping () -> Void) {
if self.isCurrent() {
f()
} else {
self.nativeQueue.async(execute: f)
}
}
public func sync(_ f: () -> Void) {
if self.isCurrent() {
f()
} else {
self.nativeQueue.sync(execute: f)
}
}
public func justDispatch(_ f: @escaping () -> Void) {
self.nativeQueue.async(execute: f)
}
public func justDispatchWithQoS(qos: DispatchQoS, _ f: @escaping () -> Void) {
self.nativeQueue.async(group: nil, qos: qos, flags: [.enforceQoS], execute: f)
}
public func after(_ delay: Double, _ f: @escaping() -> Void) {
let time: DispatchTime = DispatchTime.now() + delay
self.nativeQueue.asyncAfter(deadline: time, execute: f)
}
}
@@ -0,0 +1,58 @@
import Foundation
public final class QueueLocalObject<T: AnyObject> {
public let queue: Queue
private var valueRef: Unmanaged<T>?
public init(queue: Queue, generate: @escaping () -> T) {
self.queue = queue
self.queue.async {
let value = generate()
self.valueRef = Unmanaged.passRetained(value)
}
}
deinit {
let valueRef = self.valueRef
self.queue.async {
valueRef?.release()
}
}
public func unsafeGet() -> T? {
assert(self.queue.isCurrent())
return self.valueRef?.takeUnretainedValue()
}
public func with(_ f: @escaping (T) -> Void) {
self.queue.async {
if let valueRef = self.valueRef {
let value = valueRef.takeUnretainedValue()
f(value)
}
}
}
public func syncWith<R>(_ f: @escaping (T) -> R) -> R {
var result: R?
self.queue.sync {
if let valueRef = self.valueRef {
let value = valueRef.takeUnretainedValue()
result = f(value)
}
}
return result!
}
public func signalWith<R, E>(_ f: @escaping (T, Subscriber<R, E>) -> Disposable) -> Signal<R, E> {
return Signal { [weak self] subscriber in
if let strongSelf = self, let valueRef = strongSelf.valueRef {
let value = valueRef.takeUnretainedValue()
return f(value, subscriber)
} else {
return EmptyDisposable
}
} |> runOn(self.queue)
}
}
@@ -0,0 +1,137 @@
import Foundation
let doNothing: () -> Void = { }
public enum NoValue {
}
public enum NoError {
}
public func identity<A>(a: A) -> A {
return a
}
precedencegroup PipeRight {
associativity: left
higherThan: DefaultPrecedence
}
infix operator |> : PipeRight
public func |> <T, U>(value: T, function: ((T) -> U)) -> U {
return function(value)
}
private final class SubscriberDisposable<T, E>: Disposable, CustomStringConvertible {
private weak var subscriber: Subscriber<T, E>?
private var lock = pthread_mutex_t()
private var disposable: Disposable?
init(subscriber: Subscriber<T, E>, disposable: Disposable?) {
self.subscriber = subscriber
self.disposable = disposable
pthread_mutex_init(&self.lock, nil)
}
deinit {
pthread_mutex_destroy(&self.lock)
}
func dispose() {
var subscriber: Subscriber<T, E>?
var disposeItem: Disposable?
pthread_mutex_lock(&self.lock)
disposeItem = self.disposable
subscriber = self.subscriber
self.subscriber = nil
self.disposable = nil
pthread_mutex_unlock(&self.lock)
disposeItem?.dispose()
subscriber?.markTerminatedWithoutDisposal()
}
public var description: String {
return "SubscriberDisposable { disposable: \(self.disposable == nil ? "nil" : "hasValue") }"
}
}
public final class Signal<T, E> {
private let generator: (Subscriber<T, E>) -> Disposable
public init(_ generator: @escaping(Subscriber<T, E>) -> Disposable) {
self.generator = generator
}
public func start(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) -> Disposable {
let subscriber = Subscriber<T, E>(next: next, error: error, completed: completed)
let disposable = self.generator(subscriber)
let wrappedDisposable = subscriber.assignDisposable(disposable)
return SubscriberDisposable(subscriber: subscriber, disposable: wrappedDisposable)
}
public func startStandalone(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) -> Disposable {
let subscriber = Subscriber<T, E>(next: next, error: error, completed: completed)
let disposable = self.generator(subscriber)
let wrappedDisposable = subscriber.assignDisposable(disposable)
return SubscriberDisposable(subscriber: subscriber, disposable: wrappedDisposable)
}
public func startStrict(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil, file: String = #file, line: Int = #line) -> Disposable {
let subscriber = Subscriber<T, E>(next: next, error: error, completed: completed)
let disposable = self.generator(subscriber)
let wrappedDisposable = subscriber.assignDisposable(disposable)
return SubscriberDisposable(subscriber: subscriber, disposable: wrappedDisposable).strict(file: file, line: line)
}
public static func single(_ value: T) -> Signal<T, E> {
return Signal<T, E> { subscriber in
subscriber.putNext(value)
subscriber.putCompletion()
return EmptyDisposable
}
}
public static func complete() -> Signal<T, E> {
return Signal<T, E> { subscriber in
subscriber.putCompletion()
return EmptyDisposable
}
}
public static func fail(_ error: E) -> Signal<T, E> {
return Signal<T, E> { subscriber in
subscriber.putError(error)
return EmptyDisposable
}
}
public static func never() -> Signal<T, E> {
return Signal<T, E> { _ in
return EmptyDisposable
}
}
}
@available(iOS 13.0, macOS 10.15, *)
public extension Signal where E == NoError {
func get() async -> T {
let disposable = MetaDisposable()
return await withTaskCancellationHandler(operation: {
return await withCheckedContinuation { continuation in
disposable.set((self |> take(1)).startStandalone(next: { value in
continuation.resume(returning: value)
}))
}
}, onCancel: {
disposable.dispose()
})
}
}
@@ -0,0 +1,263 @@
import Foundation
public func `catch`<T, E, R>(_ f: @escaping(E) -> Signal<T, R>) -> (Signal<T, E>) -> Signal<T, R> {
return { signal in
return Signal<T, R> { subscriber in
let mainDisposable = MetaDisposable()
let alternativeDisposable = MetaDisposable()
mainDisposable.set(signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
let anotherSignal = f(error)
alternativeDisposable.set(anotherSignal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
}))
}, completed: {
subscriber.putCompletion()
}))
return ActionDisposable {
mainDisposable.dispose()
alternativeDisposable.dispose()
}
}
}
}
private func recursiveFunction(_ f: @escaping(@escaping() -> Void) -> Void) -> (() -> Void) {
return {
f(recursiveFunction(f))
}
}
public func restart<T, E>(_ signal: Signal<T, E>) -> Signal<T, E> {
return Signal { subscriber in
let shouldRestart = Atomic(value: true)
let currentDisposable = MetaDisposable()
let start = recursiveFunction { recurse in
let currentShouldRestart = shouldRestart.with { value in
return value
}
if currentShouldRestart {
let disposable = signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
recurse()
})
currentDisposable.set(disposable)
}
}
start()
return ActionDisposable {
currentDisposable.dispose()
let _ = shouldRestart.swap(false)
}
}
}
public func recurse<T, E>(_ latestValue: T?) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let shouldRestart = Atomic(value: true)
let currentDisposable = MetaDisposable()
let start = recursiveFunction { recurse in
let currentShouldRestart = shouldRestart.with { value in
return value
}
if currentShouldRestart {
let disposable = signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
recurse()
})
currentDisposable.set(disposable)
}
}
start()
return ActionDisposable {
currentDisposable.dispose()
let _ = shouldRestart.swap(false)
}
}
}
}
public func retry<T, E>(_ delayIncrement: Double, maxDelay: Double, onQueue queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, NoError> {
return { signal in
return Signal { subscriber in
let shouldRetry = Atomic(value: true)
let currentDelay = Atomic(value: 0.0)
let currentDisposable = MetaDisposable()
let start = recursiveFunction { recurse in
let currentShouldRetry = shouldRetry.with { value in
return value
}
if currentShouldRetry {
let disposable = signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
let delay = currentDelay.modify { value in
return min(maxDelay, value + delayIncrement)
}
let time: DispatchTime = DispatchTime.now() + Double(delay)
queue.queue.asyncAfter(deadline: time, execute: {
recurse()
})
}, completed: {
let _ = shouldRetry.swap(false)
subscriber.putCompletion()
})
currentDisposable.set(disposable)
}
}
start()
return ActionDisposable {
currentDisposable.dispose()
let _ = shouldRetry.swap(false)
}
}
}
}
public func retry<T, E>(retryOnError: @escaping (E) -> Bool, delayIncrement: Double, maxDelay: Double, maxRetries: Int?, onQueue queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let shouldRetry = Atomic(value: true)
let currentDelay = Atomic<(Double, Int)>(value: (0.0, 0))
let currentDisposable = MetaDisposable()
let start = recursiveFunction { recurse in
let currentShouldRetry = shouldRetry.with { value in
return value
}
if currentShouldRetry {
let disposable = signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
if !retryOnError(error) {
subscriber.putError(error)
} else {
let (delay, count) = currentDelay.modify { value, count in
return (min(maxDelay, value + delayIncrement), count + 1)
}
if let maxRetries, count >= maxRetries {
subscriber.putError(error)
} else {
let time: DispatchTime = DispatchTime.now() + Double(delay)
queue.queue.asyncAfter(deadline: time, execute: {
recurse()
})
}
}
}, completed: {
let _ = shouldRetry.swap(false)
subscriber.putCompletion()
})
currentDisposable.set(disposable)
}
}
start()
return ActionDisposable {
currentDisposable.dispose()
let _ = shouldRetry.swap(false)
}
}
}
}
public func restartIfError<T, E>(_ signal: Signal<T, E>) -> Signal<T, NoError> {
return Signal<T, NoError> { subscriber in
let shouldRetry = Atomic(value: true)
let currentDisposable = MetaDisposable()
let start = recursiveFunction { recurse in
let currentShouldRetry = shouldRetry.with { value in
return value
}
if currentShouldRetry {
let disposable = signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
recurse()
}, completed: {
let _ = shouldRetry.swap(false)
subscriber.putCompletion()
})
currentDisposable.set(disposable)
}
}
start()
return ActionDisposable {
currentDisposable.dispose()
let _ = shouldRetry.swap(false)
}
}
}
public enum RestartOrMapErrorCondition<E> {
case restart
case error(E)
}
public func restartOrMapError<T, E, E2>(condition: @escaping (E) -> RestartOrMapErrorCondition<E2>) -> (Signal<T, E>) -> Signal<T, E2> {
return { signal in
return Signal<T, E2> { subscriber in
let shouldRetry = Atomic(value: true)
let currentDisposable = MetaDisposable()
let start = recursiveFunction { recurse in
let currentShouldRetry = shouldRetry.with { value in
return value
}
if currentShouldRetry {
let disposable = signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
switch condition(error) {
case .restart:
recurse()
case let .error(e2):
subscriber.putError(e2)
}
}, completed: {
let _ = shouldRetry.swap(false)
subscriber.putCompletion()
})
currentDisposable.set(disposable)
}
}
start()
return ActionDisposable {
currentDisposable.dispose()
let _ = shouldRetry.swap(false)
}
}
}
}
@@ -0,0 +1,259 @@
import Foundation
private struct SignalCombineState {
let values: [Int : Any]
let completed: Set<Int>
let error: Bool
}
private func combineLatestAny<E, R>(_ signals: [Signal<Any, E>], combine: @escaping([Any]) -> R, initialValues: [Int : Any], queue: Queue?) -> Signal<R, E> {
return Signal { subscriber in
let state = Atomic(value: SignalCombineState(values: initialValues, completed: Set(), error: false))
let disposable = DisposableSet()
if initialValues.count == signals.count {
var values: [Any] = []
for i in 0 ..< initialValues.count {
values.append(initialValues[i]!)
}
subscriber.putNext(combine(values))
}
let count = signals.count
for iterationIndex in 0 ..< count {
let index = iterationIndex
var signal = signals[index]
if let queue = queue {
signal = signal
|> deliverOn(queue)
}
let signalDisposable = signal.start(next: { next in
let currentState = state.modify { current in
var values = current.values
values[index] = next
return SignalCombineState(values: values, completed: current.completed, error: current.error)
}
if currentState.values.count == count {
var values: [Any] = []
for i in 0 ..< count {
values.append(currentState.values[i]!)
}
subscriber.putNext(combine(values))
}
}, error: { error in
var emitError = false
let _ = state.modify { current in
if !current.error {
emitError = true
return SignalCombineState(values: current.values, completed: current.completed, error: true)
} else {
return current
}
}
if emitError {
subscriber.putError(error)
}
}, completed: {
var emitCompleted = false
let _ = state.modify { current in
if !current.completed.contains(index) {
var completed = current.completed
completed.insert(index)
emitCompleted = completed.count == count
return SignalCombineState(values: current.values, completed: completed, error: current.error)
}
return current
}
if emitCompleted {
subscriber.putCompletion()
}
})
disposable.add(signalDisposable)
}
return disposable
}
}
private func signalOfAny<T, E>(_ signal: Signal<T, E>) -> Signal<Any, E> {
return Signal { subscriber in
return signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
public func combineLatest<T1, T2, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>) -> Signal<(T1, T2), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], combine: { values in
return (values[0] as! T1, values[1] as! T2)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ v1: T1, _ s2: Signal<T2, E>, _ v2: T2) -> Signal<(T1, T2), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], combine: { values in
return (values[0] as! T1, values[1] as! T2)
}, initialValues: [0: v1, 1: v2], queue: queue)
}
public func combineLatest<T1, T2, T3, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>) -> Signal<(T1, T2, T3), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>) -> Signal<(T1, T2, T3, T4), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>) -> Signal<(T1, T2, T3, T4, T5), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>) -> Signal<(T1, T2, T3, T4, T5, T6), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>, _ s20: Signal<T20, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>, _ s20: Signal<T20, E>, _ s21: Signal<T21, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20), signalOfAny(s21)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20, values[20] as! T21)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>, _ s20: Signal<T20, E>, _ s21: Signal<T21, E>, _ s22: Signal<T22, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20), signalOfAny(s21), signalOfAny(s22)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20, values[20] as! T21, values[21] as! T22)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>, _ s20: Signal<T20, E>, _ s21: Signal<T21, E>, _ s22: Signal<T22, E>, _ s23: Signal<T23, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20), signalOfAny(s21), signalOfAny(s22), signalOfAny(s23)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20, values[20] as! T21, values[21] as! T22, values[22] as! T23)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>, _ s20: Signal<T20, E>, _ s21: Signal<T21, E>, _ s22: Signal<T22, E>, _ s23: Signal<T23, E>, _ s24: Signal<T24, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20), signalOfAny(s21), signalOfAny(s22), signalOfAny(s23), signalOfAny(s24)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20, values[20] as! T21, values[21] as! T22, values[22] as! T23, values[23] as! T24)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>, _ s20: Signal<T20, E>, _ s21: Signal<T21, E>, _ s22: Signal<T22, E>, _ s23: Signal<T23, E>, _ s24: Signal<T24, E>, _ s25: Signal<T25, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20), signalOfAny(s21), signalOfAny(s22), signalOfAny(s23), signalOfAny(s24), signalOfAny(s25)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20, values[20] as! T21, values[21] as! T22, values[22] as! T23, values[23] as! T24, values[24] as! T25)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, E>(queue: Queue? = nil, _ s1: Signal<T1, E>, _ s2: Signal<T2, E>, _ s3: Signal<T3, E>, _ s4: Signal<T4, E>, _ s5: Signal<T5, E>, _ s6: Signal<T6, E>, _ s7: Signal<T7, E>, _ s8: Signal<T8, E>, _ s9: Signal<T9, E>, _ s10: Signal<T10, E>, _ s11: Signal<T11, E>, _ s12: Signal<T12, E>, _ s13: Signal<T13, E>, _ s14: Signal<T14, E>, _ s15: Signal<T15, E>, _ s16: Signal<T16, E>, _ s17: Signal<T17, E>, _ s18: Signal<T18, E>, _ s19: Signal<T19, E>, _ s20: Signal<T20, E>, _ s21: Signal<T21, E>, _ s22: Signal<T22, E>, _ s23: Signal<T23, E>, _ s24: Signal<T24, E>, _ s25: Signal<T25, E>, _ s26: Signal<T26, E>) -> Signal<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26), E> {
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4), signalOfAny(s5), signalOfAny(s6), signalOfAny(s7), signalOfAny(s8), signalOfAny(s9), signalOfAny(s10), signalOfAny(s11), signalOfAny(s12), signalOfAny(s13), signalOfAny(s14), signalOfAny(s15), signalOfAny(s16), signalOfAny(s17), signalOfAny(s18), signalOfAny(s19), signalOfAny(s20), signalOfAny(s21), signalOfAny(s22), signalOfAny(s23), signalOfAny(s24), signalOfAny(s25), signalOfAny(s26)], combine: { values in
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4, values[4] as! T5, values[5] as! T6, values[6] as! T7, values[7] as! T8, values[8] as! T9, values[9] as! T10, values[10] as! T11, values[11] as! T12, values[12] as! T13, values[13] as! T14, values[14] as! T15, values[15] as! T16, values[16] as! T17, values[17] as! T18, values[18] as! T19, values[19] as! T20, values[20] as! T21, values[21] as! T22, values[22] as! T23, values[23] as! T24, values[24] as! T25, values[25] as! T26)
}, initialValues: [:], queue: queue)
}
public func combineLatest<T, E>(queue: Queue? = nil, _ signals: [Signal<T, E>]) -> Signal<[T], E> {
if signals.count == 0 {
return single([T](), E.self)
}
return combineLatestAny(signals.map({signalOfAny($0)}), combine: { values in
var combined: [T] = []
for value in values {
combined.append(value as! T)
}
return combined
}, initialValues: [:], queue: queue)
}
@@ -0,0 +1,122 @@
import Foundation
public func deliverOn<T, E>(_ queue: Queue) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
return signal.start(next: { next in
queue.async {
subscriber.putNext(next)
}
}, error: { error in
queue.async {
subscriber.putError(error)
}
}, completed: {
queue.async {
subscriber.putCompletion()
}
})
}
}
}
public func deliverOnMainQueue<T, E>(_ signal: Signal<T, E>) -> Signal<T, E> {
return signal |> deliverOn(Queue.mainQueue())
}
public func deliverOn<T, E>(_ threadPool: ThreadPool) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let queue = threadPool.nextQueue()
return signal.start(next: { next in
queue.addTask(ThreadPoolTask { state in
if !state.cancelled.with({ $0 }) {
subscriber.putNext(next)
}
})
}, error: { error in
queue.addTask(ThreadPoolTask { state in
if !state.cancelled.with({ $0 }) {
subscriber.putError(error)
}
})
}, completed: {
queue.addTask(ThreadPoolTask { state in
if !state.cancelled.with({ $0 }) {
subscriber.putCompletion()
}
})
})
}
}
}
public func runOn<T, E>(_ queue: Queue) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
if queue.isCurrent() {
return signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
} else {
var cancelled = false
let disposable = MetaDisposable()
disposable.set(ActionDisposable {
cancelled = true
})
queue.async {
if cancelled {
return
}
disposable.set(signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
}))
}
return disposable
}
}
}
}
public func runOn<T, E>(_ threadPool: ThreadPool) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let cancelled = false
let disposable = MetaDisposable()
let task = ThreadPoolTask { state in
if cancelled || state.cancelled.with({ $0 }) {
return
}
disposable.set(signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
}))
}
disposable.set(ActionDisposable {
task.cancel()
})
threadPool.addTask(task)
return disposable
}
}
}
@@ -0,0 +1,46 @@
import Foundation
public enum SignalFeedbackLoopState<T> {
case initial
case loop(T)
}
public func feedbackLoop<R1, R, E>(once: @escaping (SignalFeedbackLoopState<R1>) -> Signal<R1, E>?, reduce: @escaping (R1, R1) -> R1) -> Signal<R, E> {
return Signal { subscriber in
let currentDisposable = MetaDisposable()
let state = Atomic<R1?>(value: nil)
var loopAgain: (() -> Void)?
let loopOnce: (MetaDisposable?) -> Void = { disposable in
if let signal = once(.initial) {
disposable?.set(signal.start(next: { next in
let _ = state.modify { value in
if let value = value {
return reduce(value, next)
} else {
return value
}
}
}, error: { error in
subscriber.putError(error)
}, completed: {
loopAgain?()
}))
} else {
subscriber.putCompletion()
}
}
loopAgain = { [weak currentDisposable] in
loopOnce(currentDisposable)
}
loopOnce(currentDisposable)
return ActionDisposable {
currentDisposable.dispose()
}
}
}
@@ -0,0 +1,130 @@
import Foundation
public func map<T, E, R>(_ f: @escaping(T) -> R) -> (Signal<T, E>) -> Signal<R, E> {
return { signal in
return Signal<R, E> { subscriber in
return signal.start(next: { next in
subscriber.putNext(f(next))
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func filter<T, E>(_ f: @escaping(T) -> Bool) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
return signal.start(next: { next in
if f(next) {
subscriber.putNext(next)
}
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func flatMap<T, E, R>(_ f: @escaping (T) -> R) -> (Signal<T?, E>) -> Signal<R?, E> {
return { signal in
return Signal<R?, E> { subscriber in
return signal.start(next: { next in
if let next = next {
subscriber.putNext(f(next))
} else {
subscriber.putNext(nil)
}
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func mapError<T, E, R>(_ f: @escaping(E) -> R) -> (Signal<T, E>) -> Signal<T, R> {
return { signal in
return Signal<T, R> { subscriber in
return signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(f(error))
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func castError<T, E>(_ type: E.Type) -> (Signal<T, NoError>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
return signal.start(next: { next in
subscriber.putNext(next)
}, error: { _ in
}, completed: {
subscriber.putCompletion()
})
}
}
}
private class DistinctUntilChangedContext<T> {
var value: T?
}
public func distinctUntilChanged<T: Equatable, E>(_ signal: Signal<T, E>) -> Signal<T, E> {
return Signal { subscriber in
let context = Atomic(value: DistinctUntilChangedContext<T>())
return signal.start(next: { next in
let pass = context.with { context -> Bool in
if let value = context.value, value == next {
return false
} else {
context.value = next
return true
}
}
if pass {
subscriber.putNext(next)
}
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
public func distinctUntilChanged<T, E>(isEqual: @escaping (T, T) -> Bool) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let context = Atomic(value: DistinctUntilChangedContext<T>())
return signal.start(next: { next in
let pass = context.with { context -> Bool in
if let value = context.value, isEqual(value, next) {
return false
} else {
context.value = next
return true
}
}
if pass {
subscriber.putNext(next)
}
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
@@ -0,0 +1,39 @@
import Foundation
public enum SignalEvent<T, E> {
case Next(T)
case Error(E)
case Completion
}
public func dematerialize<T, E>(signal: Signal<T, E>) -> Signal<SignalEvent<T, E>, NoError> {
return Signal { subscriber in
return signal.start(next: { next in
subscriber.putNext(.Next(next))
}, error: { error in
subscriber.putNext(.Error(error))
subscriber.putCompletion()
}, completed: {
subscriber.putNext(.Completion)
subscriber.putCompletion()
})
}
}
public func materialize<T, E>(signal: Signal<SignalEvent<T, E>, NoError>) -> Signal<T, E> {
return Signal { subscriber in
return signal.start(next: { next in
switch next {
case let .Next(next):
subscriber.putNext(next)
case let .Error(error):
subscriber.putError(error)
case .Completion:
subscriber.putCompletion()
}
}, error: { _ in
}, completed: {
subscriber.putCompletion()
})
}
}
@@ -0,0 +1,7 @@
import Foundation
/*public func merge<T, E>(signal1: Signal<T, E>, signal2: Signal<T, E>) -> Signal<T, E> {
return Signal { subscriber in
}
}*/
@@ -0,0 +1,270 @@
import Foundation
private final class SignalQueueState<T, E>: Disposable {
var lock = pthread_mutex_t()
var executingSignal = false
var terminated = false
var disposable: Disposable = EmptyDisposable
let currentDisposable = MetaDisposable()
var subscriber: Subscriber<T, E>?
var queuedSignals: [Signal<T, E>] = []
let queueMode: Bool
let throttleMode: Bool
init(subscriber: Subscriber<T, E>, queueMode: Bool, throttleMode: Bool) {
pthread_mutex_init(&self.lock, nil)
self.subscriber = subscriber
self.queueMode = queueMode
self.throttleMode = throttleMode
}
deinit {
pthread_mutex_destroy(&self.lock)
}
func beginWithDisposable(_ disposable: Disposable) {
self.disposable = disposable
}
func enqueueSignal(_ signal: Signal<T, E>) {
var startSignal = false
pthread_mutex_lock(&self.lock)
if self.queueMode && self.executingSignal {
if self.throttleMode {
self.queuedSignals.removeAll()
}
self.queuedSignals.append(signal)
} else {
self.executingSignal = true
startSignal = true
}
pthread_mutex_unlock(&self.lock)
if startSignal {
let disposable = signal.start(next: { next in
assert(self.subscriber != nil)
self.subscriber?.putNext(next)
}, error: { error in
assert(self.subscriber != nil)
self.subscriber?.putError(error)
}, completed: {
self.headCompleted()
})
self.currentDisposable.set(disposable)
}
}
func headCompleted() {
while true {
let leftFunction = Atomic(value: false)
var nextSignal: Signal<T, E>! = nil
var terminated = false
pthread_mutex_lock(&self.lock)
self.executingSignal = false
if self.queueMode {
if self.queuedSignals.count != 0 {
nextSignal = self.queuedSignals[0]
self.queuedSignals.remove(at: 0)
self.executingSignal = true
} else {
terminated = self.terminated
}
} else {
terminated = self.terminated
}
pthread_mutex_unlock(&self.lock)
if terminated {
self.subscriber?.putCompletion()
} else if nextSignal != nil {
let disposable = nextSignal.start(next: { next in
assert(self.subscriber != nil)
self.subscriber?.putNext(next)
}, error: { error in
assert(self.subscriber != nil)
self.subscriber?.putError(error)
}, completed: {
if leftFunction.swap(true) == true {
self.headCompleted()
}
})
currentDisposable.set(disposable)
}
if leftFunction.swap(true) == false {
break
}
}
}
func beginCompletion() {
var executingSignal = false
pthread_mutex_lock(&self.lock)
executingSignal = self.executingSignal
self.terminated = true
pthread_mutex_unlock(&self.lock)
if !executingSignal {
self.subscriber?.putCompletion()
}
}
func dispose() {
self.currentDisposable.dispose()
self.disposable.dispose()
}
}
public func switchToLatest<T, E>(_ signal: Signal<Signal<T, E>, E>) -> Signal<T, E> {
return Signal { subscriber in
let state = SignalQueueState(subscriber: subscriber, queueMode: false, throttleMode: false)
state.beginWithDisposable(signal.start(next: { next in
state.enqueueSignal(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
state.beginCompletion()
}))
return state
}
}
public func queue<T, E>(_ signal: Signal<Signal<T, E>, E>) -> Signal<T, E> {
return Signal { subscriber in
let state = SignalQueueState(subscriber: subscriber, queueMode: true, throttleMode: false)
state.beginWithDisposable(signal.start(next: { next in
state.enqueueSignal(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
state.beginCompletion()
}))
return state
}
}
public func throttled<T, E>(_ signal: Signal<Signal<T, E>, E>) -> Signal<T, E> {
return Signal { subscriber in
let state = SignalQueueState(subscriber: subscriber, queueMode: true, throttleMode: true)
state.beginWithDisposable(signal.start(next: { next in
state.enqueueSignal(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
state.beginCompletion()
}))
return state
}
}
public func mapToSignal<T, R, E>(_ f: @escaping(T) -> Signal<R, E>) -> (Signal<T, E>) -> Signal<R, E> {
return { signal -> Signal<R, E> in
return Signal<Signal<R, E>, E> { subscriber in
return signal.start(next: { next in
subscriber.putNext(f(next))
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
} |> switchToLatest
}
}
public func ignoreValues<T, E>(_ signal: Signal<T, E>) -> Signal<Never, E> {
return Signal { subscriber in
return signal.start(error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
public func mapToSignalPromotingError<T, R, E>(_ f: @escaping(T) -> Signal<R, E>) -> (Signal<T, NoError>) -> Signal<R, E> {
return { signal -> Signal<R, E> in
return Signal<Signal<R, E>, E> { subscriber in
return signal.start(next: { next in
subscriber.putNext(f(next))
}, completed: {
subscriber.putCompletion()
})
} |> switchToLatest
}
}
public func mapToQueue<T, R, E>(_ f: @escaping(T) -> Signal<R, E>) -> (Signal<T, E>) -> Signal<R, E> {
return { signal -> Signal<R, E> in
return signal |> map { f($0) } |> queue
}
}
public func mapToThrottled<T, R, E>(_ f: @escaping(T) -> Signal<R, E>) -> (Signal<T, E>) -> Signal<R, E> {
return { signal -> Signal<R, E> in
return signal |> map { f($0) } |> throttled
}
}
public func then<T, E>(_ nextSignal: Signal<T, E>) -> (Signal<T, E>) -> Signal<T, E> {
return { signal -> Signal<T, E> in
return Signal<T, E> { subscriber in
let disposable = DisposableSet()
disposable.add(signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
disposable.add(nextSignal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
}))
}))
return disposable
}
}
}
public func deferred<T, E>(_ generator: @escaping() -> Signal<T, E>) -> Signal<T, E> {
return Signal { subscriber in
return generator().start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
public func debug_measureTimeToFirstEvent<T, E>(label: String) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
#if DEBUG || true
if "".isEmpty {
var isFirst = true
return Signal { subscriber in
let startTimestamp = CFAbsoluteTimeGetCurrent()
return signal.start(next: { value in
if isFirst {
isFirst = false
let deltaTime = (CFAbsoluteTimeGetCurrent() - startTimestamp) * 1000.0
print("measureTimeToFirstEvent(\(label): \(deltaTime) ms")
}
subscriber.putNext(value)
}, error: subscriber.putError, completed: subscriber.putCompletion)
}
}
#endif
return signal
}
}
@@ -0,0 +1,189 @@
import Foundation
public func reduceLeft<T, E>(value: T, f: @escaping(T, T) -> T) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
var currentValue = value
return signal.start(next: { next in
currentValue = f(currentValue, next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putNext(currentValue)
subscriber.putCompletion()
})
}
}
}
public func reduceLeft<T, E>(value: T, f: @escaping(T, T, (T) -> Void) -> T) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
var currentValue = value
let emit: (T) -> Void = { next in
subscriber.putNext(next)
}
return signal.start(next: { next in
currentValue = f(currentValue, next, emit)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putNext(currentValue)
subscriber.putCompletion()
})
}
}
}
public enum Passthrough<T> {
case None
case Some(T)
}
private final class ReduceQueueState<T, E> : Disposable {
var lock = os_unfair_lock()
var executingSignal = false
var terminated = false
var disposable: Disposable = EmptyDisposable
let currentDisposable = MetaDisposable()
let subscriber: Subscriber<T, E>
var queuedValues: [T] = []
var generator: (T, T) -> Signal<(T, Passthrough<T>), E>
var value: T
init(subscriber: Subscriber<T, E>, value: T, generator: @escaping(T, T) -> Signal<(T, Passthrough<T>), E>) {
self.subscriber = subscriber
self.generator = generator
self.value = value
}
func beginWithDisposable(_ disposable: Disposable) {
self.disposable = disposable
}
func enqueueNext(_ next: T) {
var startSignal = false
var currentValue: T
os_unfair_lock_lock(&self.lock)
currentValue = self.value
if self.executingSignal {
self.queuedValues.append(next)
} else {
self.executingSignal = true
startSignal = true
}
os_unfair_lock_unlock(&self.lock)
if startSignal {
let disposable = generator(currentValue, next).start(next: { next in
self.updateValue(next.0)
switch next.1 {
case let .Some(value):
self.subscriber.putNext(value)
case .None:
break
}
}, error: { error in
self.subscriber.putError(error)
}, completed: {
self.headCompleted()
})
self.currentDisposable.set(disposable)
}
}
func updateValue(_ value: T) {
os_unfair_lock_lock(&self.lock)
self.value = value
os_unfair_lock_unlock(&self.lock)
}
func headCompleted() {
while true {
let leftFunction = Atomic(value: false)
var nextSignal: Signal<(T, Passthrough<T>), E>! = nil
var terminated = false
var currentValue: T!
os_unfair_lock_lock(&self.lock)
self.executingSignal = false
if self.queuedValues.count != 0 {
nextSignal = self.generator(self.value, self.queuedValues[0])
self.queuedValues.remove(at: 0)
self.executingSignal = true
} else {
currentValue = self.value
terminated = self.terminated
}
os_unfair_lock_unlock(&self.lock)
if terminated {
self.subscriber.putNext(currentValue)
self.subscriber.putCompletion()
} else if nextSignal != nil {
let disposable = nextSignal.start(next: { next in
self.updateValue(next.0)
switch next.1 {
case let .Some(value):
self.subscriber.putNext(value)
case .None:
break
}
}, error: { error in
self.subscriber.putError(error)
}, completed: {
if leftFunction.swap(true) == true {
self.headCompleted()
}
})
currentDisposable.set(disposable)
}
if leftFunction.swap(true) == false {
break
}
}
}
func beginCompletion() {
var executingSignal = false
let currentValue: T
os_unfair_lock_lock(&self.lock)
executingSignal = self.executingSignal
self.terminated = true
currentValue = self.value
os_unfair_lock_unlock(&self.lock)
if !executingSignal {
self.subscriber.putNext(currentValue)
self.subscriber.putCompletion()
}
}
func dispose() {
self.currentDisposable.dispose()
self.disposable.dispose()
}
}
public func reduceLeft<T, E>(_ value: T, generator: @escaping(T, T) -> Signal<(T, Passthrough<T>), E>) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let state = ReduceQueueState(subscriber: subscriber, value: value, generator: generator)
state.beginWithDisposable(signal.start(next: { next in
state.enqueueNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
state.beginCompletion()
}))
return state
}
}
}
@@ -0,0 +1,116 @@
import Foundation
public func beforeNext<T, E, R>(_ f: @escaping(T) -> R) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
return signal.start(next: { next in
let _ = f(next)
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func afterNext<T, E, R>(_ f: @escaping(T) -> R) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
return signal.start(next: { next in
subscriber.putNext(next)
let _ = f(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func beforeStarted<T, E>(_ f: @escaping() -> Void) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
f()
return signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func beforeCompleted<T, E>(_ f: @escaping() -> Void) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
return signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
f()
subscriber.putCompletion()
})
}
}
}
public func afterCompleted<T, E>(_ f: @escaping() -> Void) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
return signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
f()
})
}
}
}
public func afterDisposed<T, E, R>(_ f: @escaping() -> R) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
let disposable = DisposableSet()
disposable.add(signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
}))
disposable.add(ActionDisposable {
let _ = f()
})
return disposable
}
}
}
public func withState<T, E, S>(_ signal: Signal<T, E>, _ initialState: @escaping() -> S, next: @escaping(T, S) -> Void = { _, _ in }, error: @escaping(E, S) -> Void = { _, _ in }, completed: @escaping(S) -> Void = { _ in }, disposed: @escaping(S) -> Void = { _ in }) -> Signal<T, E> {
return Signal { subscriber in
let state = initialState()
let disposable = signal.start(next: { vNext in
next(vNext, state)
subscriber.putNext(vNext)
}, error: { vError in
error(vError, state)
subscriber.putError(vError)
}, completed: {
completed(state)
subscriber.putCompletion()
})
return ActionDisposable {
disposable.dispose()
disposed(state)
}
}
}
@@ -0,0 +1,32 @@
import Foundation
public func single<T, E>(_ value: T, _ errorType: E.Type) -> Signal<T, E> {
return Signal<T, E> { subscriber in
subscriber.putNext(value)
subscriber.putCompletion()
return EmptyDisposable
}
}
public func fail<T, E>(_ valueType: T.Type, _ error: E) -> Signal<T, E> {
return Signal<T, E> { subscriber in
subscriber.putError(error)
return EmptyDisposable
}
}
public func complete<T, E>(_ valueType: T.Type, _ error: E.Type) -> Signal<T, E> {
return Signal<T, E> { subscriber in
subscriber.putCompletion()
return EmptyDisposable
}
}
public func never<T, E>(_ valueType: T.Type, _ error: E.Type) -> Signal<T, E> {
return Signal { _ in
return EmptyDisposable
}
}
@@ -0,0 +1,89 @@
import Foundation
public func take<T, E>(_ count: Int) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let counter = Atomic(value: 0)
return signal.start(next: { next in
var passthrough = false
var complete = false
let _ = counter.modify { value in
let updatedCount = value + 1
passthrough = updatedCount <= count
complete = updatedCount == count
return updatedCount
}
if passthrough {
subscriber.putNext(next)
}
if complete {
subscriber.putCompletion()
}
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func takeLast<T, E>(_ signal: Signal<T, E>) -> Signal<T, E> {
return Signal { subscriber in
let lastValue = Atomic<T?>(value: nil)
return signal.start(next: { next in
let _ = lastValue.swap(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
if let value = lastValue.with({ $0 }) {
subscriber.putNext(value)
}
subscriber.putCompletion()
})
}
}
public struct SignalTakeAction {
public let passthrough: Bool
public let complete: Bool
public init(passthrough: Bool, complete: Bool) {
self.passthrough = passthrough
self.complete = complete
}
}
public func take<T, E>(until: @escaping (T) -> SignalTakeAction) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
return signal.start(next: { next in
let action = until(next)
if action.passthrough {
subscriber.putNext(next)
}
if action.complete {
subscriber.putCompletion()
}
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
})
}
}
}
public func last<T, E>(signal: Signal<T, E>) -> Signal<T?, E> {
return Signal { subscriber in
let value = Atomic<T?>(value: nil)
return signal.start(next: { next in
let _ = value.swap(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putNext(value.with({ $0 }))
subscriber.putCompletion()
})
}
}
@@ -0,0 +1,131 @@
import Foundation
public func delay<T, E>(_ timeout: Double, queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
let timerDisposable = MetaDisposable()
let runDisposable = MetaDisposable()
queue.async {
let timer = Timer(timeout: timeout, repeat: false, completion: {
runDisposable.set(signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
}))
}, queue: queue)
timerDisposable.set(ActionDisposable {
queue.async {
timer.invalidate()
}
})
timer.start()
}
return ActionDisposable {
timerDisposable.dispose()
runDisposable.dispose()
}
}
}
}
public func suspendAwareDelay<T, E>(_ timeout: Double, granularity: Double = 4.0, queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
let timerDisposable = MetaDisposable()
let runDisposable = MetaDisposable()
queue.async {
let beginTimestamp = CFAbsoluteTimeGetCurrent()
let startFinalTimer: () -> Void = {
let finalTimeout = beginTimestamp + timeout - CFAbsoluteTimeGetCurrent()
let timer = Timer(timeout: max(0.0, finalTimeout), repeat: false, completion: {
runDisposable.set(signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
}))
}, queue: queue)
timerDisposable.set(ActionDisposable {
queue.async {
timer.invalidate()
}
})
timer.start()
}
if timeout <= granularity * 1.1 {
startFinalTimer()
} else {
var invalidateImpl: (() -> Void)?
let timer = Timer(timeout: granularity, repeat: true, completion: { timer in
let currentTimestamp = CFAbsoluteTimeGetCurrent()
if beginTimestamp + timeout - granularity * 1.1 <= currentTimestamp {
timer.invalidate()
startFinalTimer()
}
}, queue: queue)
invalidateImpl = {
queue.async {
timer.invalidate()
}
}
timerDisposable.set(ActionDisposable {
invalidateImpl?()
})
timer.start()
}
}
return ActionDisposable {
timerDisposable.dispose()
runDisposable.dispose()
}
}
}
}
public func timeout<T, E>(_ timeout: Double, queue: Queue, alternate: Signal<T, E>) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal<T, E> { subscriber in
let disposable = MetaDisposable()
let timer = Timer(timeout: timeout, repeat: false, completion: {
disposable.set(alternate.start(next: { next in
subscriber.putNext(next)
}, error: { error in
subscriber.putError(error)
}, completed: {
subscriber.putCompletion()
}))
}, queue: queue)
disposable.set(signal.start(next: { next in
timer.invalidate()
subscriber.putNext(next)
}, error: { error in
timer.invalidate()
subscriber.putError(error)
}, completed: {
timer.invalidate()
subscriber.putCompletion()
}))
timer.start()
let disposableSet = DisposableSet()
disposableSet.add(ActionDisposable {
timer.invalidate()
})
disposableSet.add(disposable)
return disposableSet
}
}
}
@@ -0,0 +1,267 @@
import Foundation
final class WrappedSubscriberDisposable: Disposable {
private var lock = pthread_mutex_t()
private var disposable: Disposable?
init(_ disposable: Disposable) {
self.disposable = disposable
pthread_mutex_init(&self.lock, nil)
}
deinit {
pthread_mutex_destroy(&self.lock)
}
func dispose() {
var disposableValue: Disposable?
pthread_mutex_lock(&self.lock)
disposableValue = self.disposable
self.disposable = nil
pthread_mutex_unlock(&self.lock)
disposableValue?.dispose()
}
func markTerminated() {
var disposableValue: Disposable?
pthread_mutex_lock(&self.lock)
disposableValue = self.disposable
self.disposable = nil
pthread_mutex_unlock(&self.lock)
if let disposableValue = disposableValue {
withExtendedLifetime(disposableValue, {
})
}
}
}
public final class Subscriber<T, E>: CustomStringConvertible {
private var next: ((T) -> Void)!
private var error: ((E) -> Void)!
private var completed: (() -> Void)!
private var keepAliveObjects: [AnyObject]?
private var lock = pthread_mutex_t()
private var terminated = false
internal var disposable: Disposable?
private weak var wrappedDisposable: WrappedSubscriberDisposable?
public init(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) {
self.next = next
self.error = error
self.completed = completed
pthread_mutex_init(&self.lock, nil)
}
public var description: String {
return "Subscriber { next: \(self.next == nil ? "nil" : "hasValue"), error: \(self.error == nil ? "nil" : "hasValue"), completed: \(self.completed == nil ? "nil" : "hasValue"), disposable: \(self.disposable == nil ? "nil" : "hasValue"), terminated: \(self.terminated) }"
}
deinit {
var freeDisposable: Disposable?
var keepAliveObjects: [AnyObject]?
pthread_mutex_lock(&self.lock)
if let disposable = self.disposable {
freeDisposable = disposable
self.disposable = nil
}
keepAliveObjects = self.keepAliveObjects
self.keepAliveObjects = nil
pthread_mutex_unlock(&self.lock)
if let freeDisposableValue = freeDisposable {
withExtendedLifetime(freeDisposableValue, {
})
freeDisposable = nil
}
if let keepAliveObjects = keepAliveObjects {
withExtendedLifetime(keepAliveObjects, {
})
}
pthread_mutex_destroy(&self.lock)
}
internal func assignDisposable(_ disposable: Disposable) -> Disposable {
var updatedWrappedDisposable: WrappedSubscriberDisposable?
var dispose = false
pthread_mutex_lock(&self.lock)
if self.terminated {
dispose = true
} else {
self.disposable = disposable
updatedWrappedDisposable = WrappedSubscriberDisposable(disposable)
self.wrappedDisposable = updatedWrappedDisposable
}
pthread_mutex_unlock(&self.lock)
if dispose {
disposable.dispose()
}
if let updatedWrappedDisposable = updatedWrappedDisposable {
return updatedWrappedDisposable
} else {
return EmptyDisposable
}
}
internal func markTerminatedWithoutDisposal() {
var freeDisposable: Disposable?
var keepAliveObjects: [AnyObject]?
pthread_mutex_lock(&self.lock)
if !self.terminated {
self.terminated = true
self.next = nil
self.error = nil
self.completed = nil
if let disposable = self.disposable {
freeDisposable = disposable
self.disposable = nil
}
}
keepAliveObjects = self.keepAliveObjects
self.keepAliveObjects = nil
pthread_mutex_unlock(&self.lock)
if let freeDisposableValue = freeDisposable {
withExtendedLifetime(freeDisposableValue, {
})
freeDisposable = nil
}
if let wrappedDisposable = self.wrappedDisposable {
wrappedDisposable.markTerminated()
}
if let keepAliveObjects = keepAliveObjects {
withExtendedLifetime(keepAliveObjects, {
})
}
}
public func putNext(_ next: T) {
var action: ((T) -> Void)! = nil
pthread_mutex_lock(&self.lock)
if !self.terminated {
action = self.next
}
pthread_mutex_unlock(&self.lock)
if action != nil {
action(next)
}
}
public func putError(_ error: E) {
var action: ((E) -> Void)! = nil
var disposeDisposable: Disposable?
var keepAliveObjects: [AnyObject]?
pthread_mutex_lock(&self.lock)
if !self.terminated {
action = self.error
self.next = nil
self.error = nil
self.completed = nil;
self.terminated = true
disposeDisposable = self.disposable
self.disposable = nil
}
keepAliveObjects = self.keepAliveObjects
self.keepAliveObjects = nil
pthread_mutex_unlock(&self.lock)
if action != nil {
action(error)
}
if let disposeDisposable = disposeDisposable {
disposeDisposable.dispose()
}
if let wrappedDisposable = self.wrappedDisposable {
wrappedDisposable.markTerminated()
}
if let keepAliveObjects = keepAliveObjects {
withExtendedLifetime(keepAliveObjects, {
})
}
}
public func putCompletion() {
var action: (() -> Void)! = nil
var disposeDisposable: Disposable? = nil
var keepAliveObjects: [AnyObject]?
var next: ((T) -> Void)?
var error: ((E) -> Void)?
var completed: (() -> Void)?
pthread_mutex_lock(&self.lock)
if !self.terminated {
action = self.completed
next = self.next
self.next = nil
error = self.error
self.error = nil
completed = self.completed
self.completed = nil
self.terminated = true
disposeDisposable = self.disposable
self.disposable = nil
}
keepAliveObjects = self.keepAliveObjects
self.keepAliveObjects = nil
pthread_mutex_unlock(&self.lock)
if let next = next {
withExtendedLifetime(next, {})
}
if let error = error {
withExtendedLifetime(error, {})
}
if let completed = completed {
withExtendedLifetime(completed, {})
}
if action != nil {
action()
}
if let disposeDisposable = disposeDisposable {
disposeDisposable.dispose()
}
if let wrappedDisposable = self.wrappedDisposable {
wrappedDisposable.markTerminated()
}
if let keepAliveObjects = keepAliveObjects {
withExtendedLifetime(keepAliveObjects, {
})
}
}
public func keepAlive(_ object: AnyObject) {
pthread_mutex_lock(&self.lock)
if self.keepAliveObjects == nil {
self.keepAliveObjects = []
}
self.keepAliveObjects?.append(object)
pthread_mutex_unlock(&self.lock)
}
}
@@ -0,0 +1,168 @@
import Foundation
public final class ThreadPoolTaskState {
public let cancelled = Atomic<Bool>(value: false)
}
public final class ThreadPoolTask {
private let state = ThreadPoolTaskState()
private let action: (ThreadPoolTaskState) -> ()
public init(_ action: @escaping(ThreadPoolTaskState) -> ()) {
self.action = action
}
public func execute() {
if !state.cancelled.with({ $0 }) {
self.action(self.state)
}
}
public func cancel() {
let _ = self.state.cancelled.swap(true)
}
}
public final class ThreadPoolQueue : Equatable {
private weak var threadPool: ThreadPool?
private var tasks: [ThreadPoolTask] = []
public init(threadPool: ThreadPool) {
self.threadPool = threadPool
}
public func addTask(_ task: ThreadPoolTask) {
if let threadPool = self.threadPool {
threadPool.workOnQueue(self, action: {
self.tasks.append(task)
})
}
}
fileprivate func popFirstTask() -> ThreadPoolTask? {
if self.tasks.count != 0 {
let task = self.tasks[0];
self.tasks.remove(at: 0)
return task
} else {
return nil
}
}
fileprivate func hasTasks() -> Bool {
return self.tasks.count != 0
}
}
public func ==(lhs: ThreadPoolQueue, rhs: ThreadPoolQueue) -> Bool {
return lhs === rhs
}
@objc public final class ThreadPool: NSObject {
private var threads: [Thread] = []
private var queues: [ThreadPoolQueue] = []
private var takenQueues: [ThreadPoolQueue] = []
private var mutex: pthread_mutex_t
private var condition: pthread_cond_t
@objc class func threadEntryPoint(_ threadPool: ThreadPool) {
var queue: ThreadPoolQueue!
while (true) {
var task: ThreadPoolTask!
pthread_mutex_lock(&threadPool.mutex);
if queue != nil {
if let index = threadPool.takenQueues.firstIndex(of: queue) {
threadPool.takenQueues.remove(at: index)
}
if queue.hasTasks() {
threadPool.queues.append(queue);
}
}
while (true)
{
while threadPool.queues.count == 0 {
pthread_cond_wait(&threadPool.condition, &threadPool.mutex);
}
if threadPool.queues.count != 0 {
queue = threadPool.queues[0]
}
if queue != nil {
task = queue.popFirstTask()
threadPool.takenQueues.append(queue)
if let index = threadPool.queues.firstIndex(of: queue) {
threadPool.queues.remove(at: index)
}
break
}
}
pthread_mutex_unlock(&threadPool.mutex);
if task != nil {
autoreleasepool {
task.execute()
}
}
}
}
public init(threadCount: Int, threadPriority: Double) {
assert(threadCount > 0, "threadCount < 0")
self.mutex = pthread_mutex_t()
self.condition = pthread_cond_t()
pthread_mutex_init(&self.mutex, nil)
pthread_cond_init(&self.condition, nil)
super.init()
for _ in 0 ..< threadCount {
let thread = Thread(target: ThreadPool.self, selector: #selector(ThreadPool.threadEntryPoint(_:)), object: self)
thread.threadPriority = threadPriority
self.threads.append(thread)
thread.start()
}
}
deinit {
pthread_mutex_destroy(&self.mutex)
pthread_cond_destroy(&self.condition)
}
public func addTask(_ task: ThreadPoolTask) {
let tempQueue = self.nextQueue()
tempQueue.addTask(task)
}
fileprivate func workOnQueue(_ queue: ThreadPoolQueue, action: () -> ()) {
pthread_mutex_lock(&self.mutex)
action()
if !self.queues.contains(queue) && !self.takenQueues.contains(queue) {
self.queues.append(queue)
}
pthread_cond_broadcast(&self.condition)
pthread_mutex_unlock(&self.mutex)
}
public func nextQueue() -> ThreadPoolQueue {
return ThreadPoolQueue(threadPool: self)
}
public func isCurrentThreadInPool() -> Bool {
let currentThread = Thread.current
for thread in self.threads {
if currentThread.isEqual(thread) {
return true
}
}
return false
}
}
@@ -0,0 +1,61 @@
import Foundation
public final class Timer {
private let timer = Atomic<DispatchSourceTimer?>(value: nil)
private let timeout: Double
private let `repeat`: Bool
private let completion: (Timer) -> Void
private let queue: Queue
public init(timeout: Double, `repeat`: Bool, completion: @escaping () -> Void, queue: Queue) {
self.timeout = timeout
self.`repeat` = `repeat`
self.completion = { _ in
completion()
}
self.queue = queue
}
public init(timeout: Double, `repeat`: Bool, completion: @escaping (Timer) -> Void, queue: Queue) {
self.timeout = timeout
self.`repeat` = `repeat`
self.completion = completion
self.queue = queue
}
deinit {
self.invalidate()
}
public func start() {
let timer = DispatchSource.makeTimerSource(queue: self.queue.queue)
timer.setEventHandler(handler: { [weak self] in
if let strongSelf = self {
strongSelf.completion(strongSelf)
if !strongSelf.`repeat` {
strongSelf.invalidate()
}
}
})
let _ = self.timer.modify { _ in
return timer
}
if self.`repeat` {
let time: DispatchTime = DispatchTime.now() + self.timeout
timer.schedule(deadline: time, repeating: self.timeout)
} else {
let time: DispatchTime = DispatchTime.now() + self.timeout
timer.schedule(deadline: time)
}
timer.resume()
}
public func invalidate() {
let _ = self.timer.modify { timer in
timer?.cancel()
return nil
}
}
}
@@ -0,0 +1,39 @@
import Foundation
public final class ValuePipe<T> {
private let subscribers = Atomic(value: Bag<(T) -> Void>())
public init() {
}
public func signal() -> Signal<T, NoError> {
return Signal { [weak self] subscriber in
if let strongSelf = self {
let index = strongSelf.subscribers.with { value -> Bag<T>.Index in
return value.add { next in
subscriber.putNext(next)
}
}
return ActionDisposable { [weak strongSelf] in
if let strongSelf = strongSelf {
strongSelf.subscribers.with { value -> Void in
value.remove(index)
}
}
}
} else {
return EmptyDisposable
}
}
}
public func putNext(_ next: T) {
let items = self.subscribers.with { value -> [(T) -> Void] in
return value.copyItems()
}
for f in items {
f(next)
}
}
}