├── .gitignore
├── SSignalKit.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── SSignalKit
├── Info.plist
├── SAtomic.h
├── SAtomic.m
├── SBag.h
├── SBag.m
├── SBlockDisposable.h
├── SBlockDisposable.m
├── SDisposable.h
├── SDisposableSet.h
├── SDisposableSet.m
├── SMetaDisposable.h
├── SMetaDisposable.m
├── SMulticastSignalManager.h
├── SMulticastSignalManager.m
├── SQueue.h
├── SQueue.m
├── SSignal+Accumulate.h
├── SSignal+Accumulate.m
├── SSignal+Catch.h
├── SSignal+Catch.m
├── SSignal+Combine.h
├── SSignal+Combine.m
├── SSignal+Dispatch.h
├── SSignal+Dispatch.m
├── SSignal+Mapping.h
├── SSignal+Mapping.m
├── SSignal+Meta.h
├── SSignal+Meta.m
├── SSignal+Multicast.h
├── SSignal+Multicast.m
├── SSignal+Pipe.h
├── SSignal+Pipe.m
├── SSignal+SideEffects.h
├── SSignal+SideEffects.m
├── SSignal+Single.h
├── SSignal+Single.m
├── SSignal+Take.h
├── SSignal+Take.m
├── SSignal+Timing.h
├── SSignal+Timing.m
├── SSignal.h
├── SSignal.m
├── SSignalKit.h
├── SSubscriber.h
├── SSubscriber.m
├── SThreadPool.h
├── SThreadPool.m
├── SThreadPoolQueue.h
├── SThreadPoolQueue.m
├── SThreadPoolTask.h
├── SThreadPoolTask.m
├── STimer.h
├── STimer.m
├── SVariable.h
└── SVariable.m
├── SSignalKitTests
├── DeallocatingObject.h
├── DeallocatingObject.m
├── Info.plist
├── SDisposableTests.m
├── SSignalBasicTests.m
└── SSignalPerformanceTests.m
├── SwiftSignalKit copy-Info.plist
├── SwiftSignalKit
├── Atomic.swift
├── Bag.swift
├── Disposable.swift
├── Info.plist
├── Lock.swift
├── Multicast.swift
├── Promise.swift
├── Queue.swift
├── QueueLocalObject.swift
├── Signal.swift
├── Signal_Catch.swift
├── Signal_Combine.swift
├── Signal_Dispatch.swift
├── Signal_Loop.swift
├── Signal_Mapping.swift
├── Signal_Materialize.swift
├── Signal_Merge.swift
├── Signal_Meta.swift
├── Signal_Reduce.swift
├── Signal_SideEffects.swift
├── Signal_Single.swift
├── Signal_Take.swift
├── Signal_Timing.swift
├── Subscriber.swift
├── SwiftSignalKit.h
├── ThreadPool.swift
├── Timer.swift
└── ValuePipe.swift
├── SwiftSignalKitMac
├── Info.plist
└── SwiftSignalKitMac.h
└── SwiftSignalKitTests
├── DeallocatingObject.swift
├── Info.plist
├── PerformanceTests.swift
├── SwiftSignalKitBasicTests.swift
└── SwiftSignalKitFunctionsTests.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | fastlane/README.md
2 | fastlane/report.xml
3 | fastlane/test_output/*
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.xcscmblueprint
15 | *.moved-aside
16 | DerivedData
17 | *.hmap
18 | *.ipa
19 | *.xcuserstate
20 | .DS_Store
21 | *.dSYM
22 | *.dSYM.zip
23 | *.ipa
24 | */xcuserdata/*
25 |
--------------------------------------------------------------------------------
/SSignalKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SSignalKit/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/SSignalKit/SAtomic.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SAtomic : NSObject
4 |
5 | - (instancetype)initWithValue:(id)value;
6 | - (instancetype)initWithValue:(id)value recursive:(bool)recursive;
7 | - (id)swap:(id)newValue;
8 | - (id)value;
9 | - (id)modify:(id (^)(id))f;
10 | - (id)with:(id (^)(id))f;
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/SSignalKit/SAtomic.m:
--------------------------------------------------------------------------------
1 | #import "SAtomic.h"
2 |
3 | #import
4 |
5 | @interface SAtomic ()
6 | {
7 | pthread_mutex_t _lock;
8 | pthread_mutexattr_t _attr;
9 | bool _isRecursive;
10 | id _value;
11 | }
12 |
13 | @end
14 |
15 | @implementation SAtomic
16 |
17 | - (instancetype)initWithValue:(id)value
18 | {
19 | self = [super init];
20 | if (self != nil)
21 | {
22 | pthread_mutex_init(&_lock, NULL);
23 | _value = value;
24 | }
25 | return self;
26 | }
27 |
28 | - (instancetype)initWithValue:(id)value recursive:(bool)recursive {
29 | self = [super init];
30 | if (self != nil)
31 | {
32 | _isRecursive = recursive;
33 |
34 | if (recursive) {
35 | pthread_mutexattr_init(&_attr);
36 | pthread_mutexattr_settype(&_attr, PTHREAD_MUTEX_RECURSIVE);
37 | pthread_mutex_init(&_lock, &_attr);
38 | } else {
39 | pthread_mutex_init(&_lock, NULL);
40 | }
41 |
42 | _value = value;
43 | }
44 | return self;
45 | }
46 |
47 | - (void)dealloc {
48 | if (_isRecursive) {
49 | pthread_mutexattr_destroy(&_attr);
50 | }
51 | pthread_mutex_destroy(&_lock);
52 | }
53 |
54 | - (id)swap:(id)newValue
55 | {
56 | id previousValue = nil;
57 | pthread_mutex_lock(&_lock);
58 | previousValue = _value;
59 | _value = newValue;
60 | pthread_mutex_unlock(&_lock);
61 | return previousValue;
62 | }
63 |
64 | - (id)value
65 | {
66 | id previousValue = nil;
67 | pthread_mutex_lock(&_lock);
68 | previousValue = _value;
69 | pthread_mutex_unlock(&_lock);
70 |
71 | return previousValue;
72 | }
73 |
74 | - (id)modify:(id (^)(id))f
75 | {
76 | id newValue = nil;
77 | pthread_mutex_lock(&_lock);
78 | newValue = f(_value);
79 | _value = newValue;
80 | pthread_mutex_unlock(&_lock);
81 | return newValue;
82 | }
83 |
84 | - (id)with:(id (^)(id))f
85 | {
86 | id result = nil;
87 | pthread_mutex_lock(&_lock);
88 | result = f(_value);
89 | pthread_mutex_unlock(&_lock);
90 | return result;
91 | }
92 |
93 | @end
94 |
--------------------------------------------------------------------------------
/SSignalKit/SBag.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SBag : NSObject
4 |
5 | - (NSInteger)addItem:(id)item;
6 | - (void)enumerateItems:(void (^)(id))block;
7 | - (void)removeItem:(NSInteger)key;
8 | - (bool)isEmpty;
9 | - (NSArray *)copyItems;
10 |
11 | @end
12 |
--------------------------------------------------------------------------------
/SSignalKit/SBag.m:
--------------------------------------------------------------------------------
1 | #import "SBag.h"
2 |
3 | @interface SBag ()
4 | {
5 | NSInteger _nextKey;
6 | NSMutableArray *_items;
7 | NSMutableArray *_itemKeys;
8 | }
9 |
10 | @end
11 |
12 | @implementation SBag
13 |
14 | - (instancetype)init
15 | {
16 | self = [super init];
17 | if (self != nil)
18 | {
19 | _items = [[NSMutableArray alloc] init];
20 | _itemKeys = [[NSMutableArray alloc] init];
21 | }
22 | return self;
23 | }
24 |
25 | - (NSInteger)addItem:(id)item
26 | {
27 | if (item == nil)
28 | return -1;
29 |
30 | NSInteger key = _nextKey;
31 | [_items addObject:item];
32 | [_itemKeys addObject:@(key)];
33 | _nextKey++;
34 |
35 | return key;
36 | }
37 |
38 | - (void)enumerateItems:(void (^)(id))block
39 | {
40 | if (block)
41 | {
42 | for (id item in _items)
43 | {
44 | block(item);
45 | }
46 | }
47 | }
48 |
49 | - (void)removeItem:(NSInteger)key
50 | {
51 | NSUInteger index = 0;
52 | for (NSNumber *itemKey in _itemKeys)
53 | {
54 | if ([itemKey integerValue] == key)
55 | {
56 | [_items removeObjectAtIndex:index];
57 | [_itemKeys removeObjectAtIndex:index];
58 | break;
59 | }
60 | index++;
61 | }
62 | }
63 |
64 | - (bool)isEmpty
65 | {
66 | return _items.count == 0;
67 | }
68 |
69 | - (NSArray *)copyItems
70 | {
71 | return [[NSArray alloc] initWithArray:_items];
72 | }
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/SSignalKit/SBlockDisposable.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SBlockDisposable : NSObject
4 |
5 | - (instancetype)initWithBlock:(void (^)())block;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/SSignalKit/SBlockDisposable.m:
--------------------------------------------------------------------------------
1 | #import "SBlockDisposable.h"
2 |
3 | #import
4 | #import
5 |
6 | @interface SBlockDisposable ()
7 | {
8 | void *_block;
9 | }
10 |
11 | @end
12 |
13 | @implementation SBlockDisposable
14 |
15 | - (instancetype)initWithBlock:(void (^)())block
16 | {
17 | self = [super init];
18 | if (self != nil)
19 | {
20 | _block = (__bridge_retained void *)[block copy];
21 | }
22 | return self;
23 | }
24 |
25 | - (void)dealloc
26 | {
27 | void *block = _block;
28 | if (block != NULL)
29 | {
30 | if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
31 | {
32 | if (block != nil)
33 | {
34 | __strong id strongBlock = (__bridge_transfer id)block;
35 | strongBlock = nil;
36 | }
37 | }
38 | }
39 | }
40 |
41 | - (void)dispose
42 | {
43 | void *block = _block;
44 | if (block != NULL)
45 | {
46 | if (OSAtomicCompareAndSwapPtr(block, 0, &_block))
47 | {
48 | if (block != nil)
49 | {
50 | __strong id strongBlock = (__bridge_transfer id)block;
51 | ((dispatch_block_t)strongBlock)();
52 | strongBlock = nil;
53 | }
54 | }
55 | }
56 | }
57 |
58 | @end
59 |
--------------------------------------------------------------------------------
/SSignalKit/SDisposable.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @protocol SDisposable
4 |
5 | - (void)dispose;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/SSignalKit/SDisposableSet.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class SSignal;
4 |
5 | @interface SDisposableSet : NSObject
6 |
7 | - (void)add:(id)disposable;
8 | - (void)remove:(id)disposable;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/SSignalKit/SDisposableSet.m:
--------------------------------------------------------------------------------
1 | #import "SDisposableSet.h"
2 |
3 | #import "SSignal.h"
4 |
5 | #import
6 |
7 | @interface SDisposableSet ()
8 | {
9 | OSSpinLock _lock;
10 | bool _disposed;
11 | id _singleDisposable;
12 | NSArray *_multipleDisposables;
13 | }
14 |
15 | @end
16 |
17 | @implementation SDisposableSet
18 |
19 | - (void)add:(id)disposable
20 | {
21 | if (disposable == nil)
22 | return;
23 |
24 | bool dispose = false;
25 |
26 | OSSpinLockLock(&_lock);
27 | dispose = _disposed;
28 | if (!dispose)
29 | {
30 | if (_multipleDisposables != nil)
31 | {
32 | NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables];
33 | [multipleDisposables addObject:disposable];
34 | _multipleDisposables = multipleDisposables;
35 | }
36 | else if (_singleDisposable != nil)
37 | {
38 | NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithObjects:_singleDisposable, disposable, nil];
39 | _multipleDisposables = multipleDisposables;
40 | _singleDisposable = nil;
41 | }
42 | else
43 | {
44 | _singleDisposable = disposable;
45 | }
46 | }
47 | OSSpinLockUnlock(&_lock);
48 |
49 | if (dispose)
50 | [disposable dispose];
51 | }
52 |
53 | - (void)remove:(id)disposable {
54 | OSSpinLockLock(&_lock);
55 | if (_multipleDisposables != nil)
56 | {
57 | NSMutableArray *multipleDisposables = [[NSMutableArray alloc] initWithArray:_multipleDisposables];
58 | [multipleDisposables removeObject:disposable];
59 | _multipleDisposables = multipleDisposables;
60 | }
61 | else if (_singleDisposable == disposable)
62 | {
63 | _singleDisposable = nil;
64 | }
65 | OSSpinLockUnlock(&_lock);
66 | }
67 |
68 | - (void)dispose
69 | {
70 | id singleDisposable = nil;
71 | NSArray *multipleDisposables = nil;
72 |
73 | OSSpinLockLock(&_lock);
74 | if (!_disposed)
75 | {
76 | _disposed = true;
77 | singleDisposable = _singleDisposable;
78 | multipleDisposables = _multipleDisposables;
79 | _singleDisposable = nil;
80 | _multipleDisposables = nil;
81 | }
82 | OSSpinLockUnlock(&_lock);
83 |
84 | if (singleDisposable != nil)
85 | [singleDisposable dispose];
86 | if (multipleDisposables != nil)
87 | {
88 | for (id disposable in multipleDisposables)
89 | {
90 | [disposable dispose];
91 | }
92 | }
93 | }
94 |
95 | @end
96 |
--------------------------------------------------------------------------------
/SSignalKit/SMetaDisposable.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SMetaDisposable : NSObject
4 |
5 | - (void)setDisposable:(id)disposable;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/SSignalKit/SMetaDisposable.m:
--------------------------------------------------------------------------------
1 | #import "SMetaDisposable.h"
2 |
3 | #import
4 |
5 | @interface SMetaDisposable ()
6 | {
7 | OSSpinLock _lock;
8 | bool _disposed;
9 | id _disposable;
10 | }
11 |
12 | @end
13 |
14 | @implementation SMetaDisposable
15 |
16 | - (void)setDisposable:(id)disposable
17 | {
18 | id previousDisposable = nil;
19 | bool dispose = false;
20 |
21 | OSSpinLockLock(&_lock);
22 | dispose = _disposed;
23 | if (!dispose)
24 | {
25 | previousDisposable = _disposable;
26 | _disposable = disposable;
27 | }
28 | OSSpinLockUnlock(&_lock);
29 |
30 | if (previousDisposable != nil)
31 | [previousDisposable dispose];
32 |
33 | if (dispose)
34 | [disposable dispose];
35 | }
36 |
37 | - (void)dispose
38 | {
39 | id disposable = nil;
40 |
41 | OSSpinLockLock(&_lock);
42 | if (!_disposed)
43 | {
44 | disposable = _disposable;
45 | _disposed = true;
46 | }
47 | OSSpinLockUnlock(&_lock);
48 |
49 | if (disposable != nil)
50 | [disposable dispose];
51 | }
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/SSignalKit/SMulticastSignalManager.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SMulticastSignalManager : NSObject
4 |
5 | - (SSignal *)multicastedSignalForKey:(NSString *)key producer:(SSignal *(^)())producer;
6 | - (void)startStandaloneSignalIfNotRunningForKey:(NSString *)key producer:(SSignal *(^)())producer;
7 |
8 | - (SSignal *)multicastedPipeForKey:(NSString *)key;
9 | - (void)putNext:(id)next toMulticastedPipeForKey:(NSString *)key;
10 |
11 | @end
12 |
--------------------------------------------------------------------------------
/SSignalKit/SMulticastSignalManager.m:
--------------------------------------------------------------------------------
1 | #import "SMulticastSignalManager.h"
2 |
3 | #import "SSignal+Multicast.h"
4 | #import "SSignal+SideEffects.h"
5 | #import "SBag.h"
6 | #import "SMetaDisposable.h"
7 | #import "SBlockDisposable.h"
8 |
9 | #import
10 |
11 | @interface SMulticastSignalManager ()
12 | {
13 | OSSpinLock _lock;
14 | NSMutableDictionary *_multicastSignals;
15 | NSMutableDictionary *_standaloneSignalDisposables;
16 | NSMutableDictionary *_pipeListeners;
17 | }
18 |
19 | @end
20 |
21 | @implementation SMulticastSignalManager
22 |
23 | - (instancetype)init
24 | {
25 | self = [super init];
26 | if (self != nil)
27 | {
28 | _multicastSignals = [[NSMutableDictionary alloc] init];
29 | _standaloneSignalDisposables = [[NSMutableDictionary alloc] init];
30 | _pipeListeners = [[NSMutableDictionary alloc] init];
31 | }
32 | return self;
33 | }
34 |
35 | - (void)dealloc
36 | {
37 | NSArray *disposables = nil;
38 | OSSpinLockLock(&_lock);
39 | disposables = [_standaloneSignalDisposables allValues];
40 | OSSpinLockUnlock(&_lock);
41 |
42 | for (id disposable in disposables)
43 | {
44 | [disposable dispose];
45 | }
46 | }
47 |
48 | - (SSignal *)multicastedSignalForKey:(NSString *)key producer:(SSignal *(^)())producer
49 | {
50 | if (key == nil)
51 | {
52 | if (producer)
53 | return producer();
54 | else
55 | return nil;
56 | }
57 |
58 | SSignal *signal = nil;
59 | OSSpinLockLock(&_lock);
60 | signal = _multicastSignals[key];
61 | if (signal == nil)
62 | {
63 | __weak SMulticastSignalManager *weakSelf = self;
64 | if (producer)
65 | signal = producer();
66 | if (signal != nil)
67 | {
68 | signal = [[signal onDispose:^
69 | {
70 | __strong SMulticastSignalManager *strongSelf = weakSelf;
71 | if (strongSelf != nil)
72 | {
73 | OSSpinLockLock(&strongSelf->_lock);
74 | [strongSelf->_multicastSignals removeObjectForKey:key];
75 | OSSpinLockUnlock(&strongSelf->_lock);
76 | }
77 | }] multicast];
78 | _multicastSignals[key] = signal;
79 | }
80 | }
81 | OSSpinLockUnlock(&_lock);
82 |
83 | return signal;
84 | }
85 |
86 | - (void)startStandaloneSignalIfNotRunningForKey:(NSString *)key producer:(SSignal *(^)())producer
87 | {
88 | if (key == nil)
89 | return;
90 |
91 | bool produce = false;
92 | OSSpinLockLock(&_lock);
93 | if (_standaloneSignalDisposables[key] == nil)
94 | {
95 | _standaloneSignalDisposables[key] = [[SMetaDisposable alloc] init];
96 | produce = true;
97 | }
98 | OSSpinLockUnlock(&_lock);
99 |
100 | if (produce)
101 | {
102 | __weak SMulticastSignalManager *weakSelf = self;
103 | id disposable = [producer() startWithNext:nil error:^(__unused id error)
104 | {
105 | __strong SMulticastSignalManager *strongSelf = weakSelf;
106 | if (strongSelf != nil)
107 | {
108 | OSSpinLockLock(&strongSelf->_lock);
109 | [strongSelf->_standaloneSignalDisposables removeObjectForKey:key];
110 | OSSpinLockUnlock(&strongSelf->_lock);
111 | }
112 | } completed:^
113 | {
114 | __strong SMulticastSignalManager *strongSelf = weakSelf;
115 | if (strongSelf != nil)
116 | {
117 | OSSpinLockLock(&strongSelf->_lock);
118 | [strongSelf->_standaloneSignalDisposables removeObjectForKey:key];
119 | OSSpinLockUnlock(&strongSelf->_lock);
120 | }
121 | }];
122 |
123 | OSSpinLockLock(&_lock);
124 | [(SMetaDisposable *)_standaloneSignalDisposables[key] setDisposable:disposable];
125 | OSSpinLockUnlock(&_lock);
126 | }
127 | }
128 |
129 | - (SSignal *)multicastedPipeForKey:(NSString *)key
130 | {
131 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
132 | {
133 | OSSpinLockLock(&_lock);
134 | SBag *bag = _pipeListeners[key];
135 | if (bag == nil)
136 | {
137 | bag = [[SBag alloc] init];
138 | _pipeListeners[key] = bag;
139 | }
140 | NSInteger index = [bag addItem:[^(id next)
141 | {
142 | [subscriber putNext:next];
143 | } copy]];
144 | OSSpinLockUnlock(&_lock);
145 |
146 | return [[SBlockDisposable alloc] initWithBlock:^
147 | {
148 | OSSpinLockLock(&_lock);
149 | SBag *bag = _pipeListeners[key];
150 | [bag removeItem:index];
151 | if ([bag isEmpty]) {
152 | [_pipeListeners removeObjectForKey:key];
153 | }
154 | OSSpinLockUnlock(&_lock);
155 | }];
156 | }];
157 | }
158 |
159 | - (void)putNext:(id)next toMulticastedPipeForKey:(NSString *)key
160 | {
161 | OSSpinLockLock(&_lock);
162 | NSArray *pipeListeners = [(SBag *)_pipeListeners[key] copyItems];
163 | OSSpinLockUnlock(&_lock);
164 |
165 | for (void (^listener)(id) in pipeListeners)
166 | {
167 | listener(next);
168 | }
169 | }
170 |
171 | @end
172 |
--------------------------------------------------------------------------------
/SSignalKit/SQueue.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SQueue : NSObject
4 |
5 | + (SQueue *)mainQueue;
6 | + (SQueue *)concurrentDefaultQueue;
7 | + (SQueue *)concurrentBackgroundQueue;
8 |
9 | + (SQueue *)wrapConcurrentNativeQueue:(dispatch_queue_t)nativeQueue;
10 |
11 | - (void)dispatch:(dispatch_block_t)block;
12 | - (void)dispatchSync:(dispatch_block_t)block;
13 | - (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous;
14 |
15 | - (dispatch_queue_t)_dispatch_queue;
16 |
17 | - (bool)isCurrentQueue;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/SSignalKit/SQueue.m:
--------------------------------------------------------------------------------
1 | #import "SQueue.h"
2 |
3 | static const void *SQueueSpecificKey = &SQueueSpecificKey;
4 |
5 | @interface SQueue ()
6 | {
7 | dispatch_queue_t _queue;
8 | void *_queueSpecific;
9 | bool _specialIsMainQueue;
10 | }
11 |
12 | @end
13 |
14 | @implementation SQueue
15 |
16 | + (SQueue *)mainQueue
17 | {
18 | static SQueue *queue = nil;
19 | static dispatch_once_t onceToken;
20 | dispatch_once(&onceToken, ^
21 | {
22 | queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_main_queue() queueSpecific:NULL];
23 | queue->_specialIsMainQueue = true;
24 | });
25 |
26 | return queue;
27 | }
28 |
29 | + (SQueue *)concurrentDefaultQueue
30 | {
31 | static SQueue *queue = nil;
32 | static dispatch_once_t onceToken;
33 | dispatch_once(&onceToken, ^
34 | {
35 | queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) queueSpecific:NULL];
36 | });
37 |
38 | return queue;
39 | }
40 |
41 | + (SQueue *)concurrentBackgroundQueue
42 | {
43 | static SQueue *queue = nil;
44 | static dispatch_once_t onceToken;
45 | dispatch_once(&onceToken, ^
46 | {
47 | queue = [[SQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) queueSpecific:NULL];
48 | });
49 |
50 | return queue;
51 | }
52 |
53 | + (SQueue *)wrapConcurrentNativeQueue:(dispatch_queue_t)nativeQueue
54 | {
55 | return [[SQueue alloc] initWithNativeQueue:nativeQueue queueSpecific:NULL];
56 | }
57 |
58 | - (instancetype)init
59 | {
60 | dispatch_queue_t queue = dispatch_queue_create(NULL, NULL);
61 | dispatch_queue_set_specific(queue, SQueueSpecificKey, (__bridge void *)self, NULL);
62 | return [self initWithNativeQueue:queue queueSpecific:(__bridge void *)self];
63 | }
64 |
65 | - (instancetype)initWithNativeQueue:(dispatch_queue_t)queue queueSpecific:(void *)queueSpecific
66 | {
67 | self = [super init];
68 | if (self != nil)
69 | {
70 | _queue = queue;
71 | _queueSpecific = queueSpecific;
72 | }
73 | return self;
74 | }
75 |
76 | - (dispatch_queue_t)_dispatch_queue
77 | {
78 | return _queue;
79 | }
80 |
81 | - (void)dispatch:(dispatch_block_t)block
82 | {
83 | if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
84 | block();
85 | else if (_specialIsMainQueue && [NSThread isMainThread])
86 | block();
87 | else
88 | dispatch_async(_queue, block);
89 | }
90 |
91 | - (void)dispatchSync:(dispatch_block_t)block
92 | {
93 | if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
94 | block();
95 | else if (_specialIsMainQueue && [NSThread isMainThread])
96 | block();
97 | else
98 | dispatch_sync(_queue, block);
99 | }
100 |
101 | - (void)dispatch:(dispatch_block_t)block synchronous:(bool)synchronous {
102 | if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
103 | block();
104 | else if (_specialIsMainQueue && [NSThread isMainThread])
105 | block();
106 | else {
107 | if (synchronous) {
108 | dispatch_sync(_queue, block);
109 | } else {
110 | dispatch_async(_queue, block);
111 | }
112 | }
113 | }
114 |
115 | - (bool)isCurrentQueue
116 | {
117 | if (_queueSpecific != NULL && dispatch_get_specific(SQueueSpecificKey) == _queueSpecific)
118 | return true;
119 | else if (_specialIsMainQueue && [NSThread isMainThread])
120 | return true;
121 | return false;
122 | }
123 |
124 | @end
125 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Accumulate.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal (Accumulate)
4 |
5 | - (SSignal *)reduceLeft:(id)value with:(id (^)(id, id))f;
6 | - (SSignal *)reduceLeftWithPassthrough:(id)value with:(id (^)(id, id, void (^)(id)))f;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Accumulate.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Accumulate.h"
2 |
3 | @implementation SSignal (Accumulate)
4 |
5 | - (SSignal *)reduceLeft:(id)value with:(id (^)(id, id))f
6 | {
7 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
8 | {
9 | __block id intermediateResult = value;
10 |
11 | return [self startWithNext:^(id next)
12 | {
13 | intermediateResult = f(intermediateResult, next);
14 | } error:^(id error)
15 | {
16 | [subscriber putError:error];
17 | } completed:^
18 | {
19 | if (intermediateResult != nil)
20 | [subscriber putNext:intermediateResult];
21 | [subscriber putCompletion];
22 | }];
23 | }];
24 | }
25 |
26 | - (SSignal *)reduceLeftWithPassthrough:(id)value with:(id (^)(id, id, void (^)(id)))f
27 | {
28 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
29 | {
30 | __block id intermediateResult = value;
31 |
32 | void (^emit)(id) = ^(id next)
33 | {
34 | [subscriber putNext:next];
35 | };
36 |
37 | return [self startWithNext:^(id next)
38 | {
39 | intermediateResult = f(intermediateResult, next, emit);
40 | } error:^(id error)
41 | {
42 | [subscriber putError:error];
43 | } completed:^
44 | {
45 | if (intermediateResult != nil)
46 | [subscriber putNext:intermediateResult];
47 | [subscriber putCompletion];
48 | }];
49 | }];
50 | }
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Catch.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal (Catch)
4 |
5 | - (SSignal *)catch:(SSignal *(^)(id error))f;
6 | - (SSignal *)restart;
7 | - (SSignal *)retryIf:(bool (^)(id error))predicate;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Catch.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Catch.h"
2 |
3 | #import "SMetaDisposable.h"
4 | #import "SDisposableSet.h"
5 | #import "SBlockDisposable.h"
6 | #import "SAtomic.h"
7 |
8 | @implementation SSignal (Catch)
9 |
10 | - (SSignal *)catch:(SSignal *(^)(id error))f
11 | {
12 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
13 | {
14 | SDisposableSet *disposable = [[SDisposableSet alloc] init];
15 |
16 | [disposable add:[self startWithNext:^(id next)
17 | {
18 | [subscriber putNext:next];
19 | } error:^(id error)
20 | {
21 | SSignal *signal = f(error);
22 | [disposable add:[signal startWithNext:^(id next)
23 | {
24 | [subscriber putNext:next];
25 | } error:^(id error)
26 | {
27 | [subscriber putError:error];
28 | } completed:^
29 | {
30 | [subscriber putCompletion];
31 | }]];
32 | } completed:^
33 | {
34 | [subscriber putCompletion];
35 | }]];
36 |
37 | return disposable;
38 | }];
39 | }
40 |
41 | static dispatch_block_t recursiveBlock(void (^block)(dispatch_block_t recurse))
42 | {
43 | return ^
44 | {
45 | block(recursiveBlock(block));
46 | };
47 | }
48 |
49 | - (SSignal *)restart
50 | {
51 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
52 | {
53 | SAtomic *shouldRestart = [[SAtomic alloc] initWithValue:@true];
54 |
55 | SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
56 |
57 | void (^start)() = recursiveBlock(^(dispatch_block_t recurse)
58 | {
59 | NSNumber *currentShouldRestart = [shouldRestart with:^id(NSNumber *current)
60 | {
61 | return current;
62 | }];
63 |
64 | if ([currentShouldRestart boolValue])
65 | {
66 | id disposable = [self startWithNext:^(id next)
67 | {
68 | [subscriber putNext:next];
69 | } error:^(id error)
70 | {
71 | [subscriber putError:error];
72 | } completed:^
73 | {
74 | recurse();
75 | }];
76 | [currentDisposable setDisposable:disposable];
77 | }
78 | });
79 |
80 | start();
81 |
82 | return [[SBlockDisposable alloc] initWithBlock:^
83 | {
84 | [currentDisposable dispose];
85 |
86 | [shouldRestart modify:^id(__unused id current)
87 | {
88 | return @false;
89 | }];
90 | }];
91 | }];
92 | }
93 |
94 | - (SSignal *)retryIf:(bool (^)(id error))predicate {
95 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
96 | {
97 | SAtomic *shouldRestart = [[SAtomic alloc] initWithValue:@true];
98 |
99 | SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
100 |
101 | void (^start)() = recursiveBlock(^(dispatch_block_t recurse)
102 | {
103 | NSNumber *currentShouldRestart = [shouldRestart with:^id(NSNumber *current)
104 | {
105 | return current;
106 | }];
107 |
108 | if ([currentShouldRestart boolValue])
109 | {
110 | id disposable = [self startWithNext:^(id next)
111 | {
112 | [subscriber putNext:next];
113 | } error:^(id error)
114 | {
115 | if (predicate(error)) {
116 | recurse();
117 | } else {
118 | [subscriber putError:error];
119 | }
120 | } completed:^
121 | {
122 | [shouldRestart modify:^id(__unused id current) {
123 | return @false;
124 | }];
125 | [subscriber putCompletion];
126 | }];
127 | [currentDisposable setDisposable:disposable];
128 | } else {
129 | [subscriber putCompletion];
130 | }
131 | });
132 |
133 | start();
134 |
135 | return [[SBlockDisposable alloc] initWithBlock:^
136 | {
137 | [currentDisposable dispose];
138 |
139 | [shouldRestart modify:^id(__unused id current)
140 | {
141 | return @false;
142 | }];
143 | }];
144 | }];
145 | }
146 |
147 | @end
148 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Combine.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal (Combine)
4 |
5 | + (SSignal *)combineSignals:(NSArray *)signals;
6 | + (SSignal *)combineSignals:(NSArray *)signals withInitialStates:(NSArray *)initialStates;
7 |
8 | + (SSignal *)mergeSignals:(NSArray *)signals;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Combine.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Combine.h"
2 |
3 | #import "SAtomic.h"
4 | #import "SDisposableSet.h"
5 | #import "SSignal+Single.h"
6 |
7 | @interface SSignalCombineState : NSObject
8 |
9 | @property (nonatomic, strong, readonly) NSDictionary *latestValues;
10 | @property (nonatomic, strong, readonly) NSArray *completedStatuses;
11 | @property (nonatomic) bool error;
12 |
13 | @end
14 |
15 | @implementation SSignalCombineState
16 |
17 | - (instancetype)initWithLatestValues:(NSDictionary *)latestValues completedStatuses:(NSArray *)completedStatuses error:(bool)error
18 | {
19 | self = [super init];
20 | if (self != nil)
21 | {
22 | _latestValues = latestValues;
23 | _completedStatuses = completedStatuses;
24 | _error = error;
25 | }
26 | return self;
27 | }
28 |
29 | @end
30 |
31 | @implementation SSignal (Combine)
32 |
33 | + (SSignal *)combineSignals:(NSArray *)signals
34 | {
35 | if (signals.count == 0)
36 | return [SSignal single:@[]];
37 | else
38 | return [self combineSignals:signals withInitialStates:nil];
39 | }
40 |
41 | + (SSignal *)combineSignals:(NSArray *)signals withInitialStates:(NSArray *)initialStates
42 | {
43 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
44 | {
45 | NSMutableArray *completedStatuses = [[NSMutableArray alloc] init];
46 | for (NSUInteger i = 0; i < signals.count; i++)
47 | {
48 | [completedStatuses addObject:@false];
49 | }
50 | NSMutableDictionary *initialLatestValues = [[NSMutableDictionary alloc] init];
51 | for (NSUInteger i = 0; i < initialStates.count; i++)
52 | {
53 | initialLatestValues[@(i)] = initialStates[i];
54 | }
55 | SAtomic *combineState = [[SAtomic alloc] initWithValue:[[SSignalCombineState alloc] initWithLatestValues:initialLatestValues completedStatuses:completedStatuses error:false]];
56 |
57 | SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
58 |
59 | NSUInteger index = 0;
60 | NSUInteger count = signals.count;
61 | for (SSignal *signal in signals)
62 | {
63 | id disposable = [signal startWithNext:^(id next)
64 | {
65 | SSignalCombineState *currentState = [combineState modify:^id(SSignalCombineState *state)
66 | {
67 | NSMutableDictionary *latestValues = [[NSMutableDictionary alloc] initWithDictionary:state.latestValues];
68 | latestValues[@(index)] = next;
69 | return [[SSignalCombineState alloc] initWithLatestValues:latestValues completedStatuses:state.completedStatuses error:state.error];
70 | }];
71 | NSMutableArray *latestValues = [[NSMutableArray alloc] init];
72 | for (NSUInteger i = 0; i < count; i++)
73 | {
74 | id value = currentState.latestValues[@(i)];
75 | if (value == nil)
76 | {
77 | latestValues = nil;
78 | break;
79 | }
80 | latestValues[i] = value;
81 | }
82 | if (latestValues != nil)
83 | [subscriber putNext:latestValues];
84 | }
85 | error:^(id error)
86 | {
87 | __block bool hadError = false;
88 | [combineState modify:^id(SSignalCombineState *state)
89 | {
90 | hadError = state.error;
91 | return [[SSignalCombineState alloc] initWithLatestValues:state.latestValues completedStatuses:state.completedStatuses error:true];
92 | }];
93 | if (!hadError)
94 | [subscriber putError:error];
95 | } completed:^
96 | {
97 | __block bool wasCompleted = false;
98 | __block bool isCompleted = false;
99 | [combineState modify:^id(SSignalCombineState *state)
100 | {
101 | NSMutableArray *completedStatuses = [[NSMutableArray alloc] initWithArray:state.completedStatuses];
102 | bool everyStatusWasCompleted = true;
103 | for (NSNumber *nStatus in completedStatuses)
104 | {
105 | if (![nStatus boolValue])
106 | {
107 | everyStatusWasCompleted = false;
108 | break;
109 | }
110 | }
111 | completedStatuses[index] = @true;
112 | bool everyStatusIsCompleted = true;
113 | for (NSNumber *nStatus in completedStatuses)
114 | {
115 | if (![nStatus boolValue])
116 | {
117 | everyStatusIsCompleted = false;
118 | break;
119 | }
120 | }
121 |
122 | wasCompleted = everyStatusWasCompleted;
123 | isCompleted = everyStatusIsCompleted;
124 |
125 | return [[SSignalCombineState alloc] initWithLatestValues:state.latestValues completedStatuses:completedStatuses error:state.error];
126 | }];
127 | if (!wasCompleted && isCompleted)
128 | [subscriber putCompletion];
129 | }];
130 | [compositeDisposable add:disposable];
131 | index++;
132 | }
133 |
134 | return compositeDisposable;
135 | }];
136 | }
137 |
138 | + (SSignal *)mergeSignals:(NSArray *)signals
139 | {
140 | if (signals.count == 0)
141 | return [SSignal complete];
142 |
143 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
144 | {
145 | SDisposableSet *disposables = [[SDisposableSet alloc] init];
146 | SAtomic *completedStates = [[SAtomic alloc] initWithValue:[[NSSet alloc] init]];
147 |
148 | NSInteger index = -1;
149 | NSUInteger count = signals.count;
150 | for (SSignal *signal in signals)
151 | {
152 | index++;
153 |
154 | id disposable = [signal startWithNext:^(id next)
155 | {
156 | [subscriber putNext:next];
157 | } error:^(id error)
158 | {
159 | [subscriber putError:error];
160 | } completed:^
161 | {
162 | NSSet *set = [completedStates modify:^id(NSSet *set)
163 | {
164 | return [set setByAddingObject:@(index)];
165 | }];
166 | if (set.count == count)
167 | [subscriber putCompletion];
168 | }];
169 |
170 | [disposables add:disposable];
171 | }
172 |
173 | return disposables;
174 | }];
175 | }
176 |
177 | @end
178 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Dispatch.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import
4 | #import
5 |
6 | @interface SSignal (Dispatch)
7 |
8 | - (SSignal *)deliverOn:(SQueue *)queue;
9 | - (SSignal *)deliverOnThreadPool:(SThreadPool *)threadPool;
10 | - (SSignal *)startOn:(SQueue *)queue;
11 | - (SSignal *)startOnThreadPool:(SThreadPool *)threadPool;
12 | - (SSignal *)throttleOn:(SQueue *)queue delay:(NSTimeInterval)delay;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Dispatch.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Dispatch.h"
2 | #import "SAtomic.h"
3 | #import "STimer.h"
4 | #import "SBlockDisposable.h"
5 | #import "SMetaDisposable.h"
6 |
7 | @interface SSignal_ThrottleContainer : NSObject
8 |
9 | @property (nonatomic, strong, readonly) id value;
10 | @property (nonatomic, readonly) bool committed;
11 | @property (nonatomic, readonly) bool last;
12 |
13 | @end
14 |
15 | @implementation SSignal_ThrottleContainer
16 |
17 | - (instancetype)initWithValue:(id)value committed:(bool)committed last:(bool)last {
18 | self = [super init];
19 | if (self != nil) {
20 | _value = value;
21 | _committed = committed;
22 | _last = last;
23 | }
24 | return self;
25 | }
26 |
27 | @end
28 |
29 | @implementation SSignal (Dispatch)
30 |
31 | - (SSignal *)deliverOn:(SQueue *)queue
32 | {
33 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
34 | {
35 | return [self startWithNext:^(id next)
36 | {
37 | [queue dispatch:^
38 | {
39 | [subscriber putNext:next];
40 | }];
41 | } error:^(id error)
42 | {
43 | [queue dispatch:^
44 | {
45 | [subscriber putError:error];
46 | }];
47 | } completed:^
48 | {
49 | [queue dispatch:^
50 | {
51 | [subscriber putCompletion];
52 | }];
53 | }];
54 | }];
55 | }
56 |
57 | - (SSignal *)deliverOnThreadPool:(SThreadPool *)threadPool
58 | {
59 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
60 | {
61 | SThreadPoolQueue *queue = [threadPool nextQueue];
62 | return [self startWithNext:^(id next)
63 | {
64 | SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
65 | {
66 | if (!cancelled())
67 | [subscriber putNext:next];
68 | }];
69 | [queue addTask:task];
70 | } error:^(id error)
71 | {
72 | SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
73 | {
74 | if (!cancelled())
75 | [subscriber putError:error];
76 | }];
77 | [queue addTask:task];
78 | } completed:^
79 | {
80 | SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
81 | {
82 | if (!cancelled())
83 | [subscriber putCompletion];
84 | }];
85 | [queue addTask:task];
86 | }];
87 | }];
88 | }
89 |
90 | - (SSignal *)startOn:(SQueue *)queue
91 | {
92 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
93 | {
94 | __block bool isCancelled = false;
95 | SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
96 | [disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
97 | {
98 | isCancelled = true;
99 | }]];
100 |
101 | [queue dispatch:^
102 | {
103 | if (!isCancelled)
104 | {
105 | [disposable setDisposable:[self startWithNext:^(id next)
106 | {
107 | [subscriber putNext:next];
108 | } error:^(id error)
109 | {
110 | [subscriber putError:error];
111 | } completed:^
112 | {
113 | [subscriber putCompletion];
114 | }]];
115 | }
116 | }];
117 |
118 | return disposable;
119 | }];
120 | }
121 |
122 | - (SSignal *)startOnThreadPool:(SThreadPool *)threadPool
123 | {
124 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
125 | {
126 | SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
127 |
128 | SThreadPoolTask *task = [[SThreadPoolTask alloc] initWithBlock:^(bool (^cancelled)())
129 | {
130 | if (cancelled && cancelled())
131 | return;
132 |
133 | [disposable setDisposable:[self startWithNext:^(id next)
134 | {
135 | [subscriber putNext:next];
136 | } error:^(id error)
137 | {
138 | [subscriber putError:error];
139 | } completed:^
140 | {
141 | [subscriber putCompletion];
142 | }]];
143 | }];
144 |
145 | [disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
146 | {
147 | [task cancel];
148 | }]];
149 |
150 | [threadPool addTask:task];
151 |
152 | return disposable;
153 | }];
154 | }
155 |
156 | - (SSignal *)throttleOn:(SQueue *)queue delay:(NSTimeInterval)delay
157 | {
158 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) {
159 | SAtomic *value = [[SAtomic alloc] initWithValue:nil];
160 | STimer *timer = [[STimer alloc] initWithTimeout:delay repeat:false completion:^{
161 | [value modify:^id(SSignal_ThrottleContainer *container) {
162 | if (container != nil) {
163 | if (!container.committed) {
164 | [subscriber putNext:container.value];
165 | container = [[SSignal_ThrottleContainer alloc] initWithValue:container.value committed:true last:container.last];
166 | }
167 |
168 | if (container.last) {
169 | [subscriber putCompletion];
170 | }
171 | }
172 | return container;
173 | }];
174 | } queue:queue];
175 |
176 | return [[self deliverOn:queue] startWithNext:^(id next) {
177 | [value modify:^id(SSignal_ThrottleContainer *container) {
178 | if (container == nil) {
179 | container = [[SSignal_ThrottleContainer alloc] initWithValue:next committed:false last:false];
180 | }
181 | return container;
182 | }];
183 | [timer invalidate];
184 | [timer start];
185 | } error:^(id error) {
186 | [timer invalidate];
187 | [subscriber putError:error];
188 | } completed:^{
189 | [timer invalidate];
190 | __block bool start = false;
191 | [value modify:^id(SSignal_ThrottleContainer *container) {
192 | bool wasCommitted = false;
193 | if (container == nil) {
194 | wasCommitted = true;
195 | container = [[SSignal_ThrottleContainer alloc] initWithValue:nil committed:true last:true];
196 | } else {
197 | wasCommitted = container.committed;
198 | container = [[SSignal_ThrottleContainer alloc] initWithValue:container.value committed:container.committed last:true];
199 | }
200 | start = wasCommitted;
201 | return container;
202 | }];
203 | if (start) {
204 | [timer start];
205 | } else {
206 | [timer fireAndInvalidate];
207 | }
208 | }];
209 | }];
210 | }
211 |
212 | @end
213 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Mapping.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal (Mapping)
4 |
5 | - (SSignal *)map:(id (^)(id))f;
6 | - (SSignal *)filter:(bool (^)(id))f;
7 | - (SSignal *)ignoreRepeated;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Mapping.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Mapping.h"
2 |
3 | #import "SAtomic.h"
4 |
5 | @interface SSignalIgnoreRepeatedState: NSObject
6 |
7 | @property (nonatomic, strong) id value;
8 | @property (nonatomic) bool hasValue;
9 |
10 | @end
11 |
12 | @implementation SSignalIgnoreRepeatedState
13 |
14 | @end
15 |
16 | @implementation SSignal (Mapping)
17 |
18 | - (SSignal *)map:(id (^)(id))f
19 | {
20 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
21 | {
22 | return [self startWithNext:^(id next)
23 | {
24 | [subscriber putNext:f(next)];
25 | } error:^(id error)
26 | {
27 | [subscriber putError:error];
28 | } completed:^
29 | {
30 | [subscriber putCompletion];
31 | }];
32 | }];
33 | }
34 |
35 | - (SSignal *)filter:(bool (^)(id))f
36 | {
37 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
38 | {
39 | return [self startWithNext:^(id next)
40 | {
41 | if (f(next))
42 | [subscriber putNext:next];
43 | } error:^(id error)
44 | {
45 | [subscriber putError:error];
46 | } completed:^
47 | {
48 | [subscriber putCompletion];
49 | }];
50 | }];
51 | }
52 |
53 | - (SSignal *)ignoreRepeated {
54 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) {
55 | SAtomic *state = [[SAtomic alloc] initWithValue:[[SSignalIgnoreRepeatedState alloc] init]];
56 |
57 | return [self startWithNext:^(id next) {
58 | bool shouldPassthrough = [[state with:^id(SSignalIgnoreRepeatedState *state) {
59 | if (!state.hasValue) {
60 | state.hasValue = true;
61 | state.value = next;
62 | return @true;
63 | } else if ((state.value == nil && next == nil) || [(id)state.value isEqual:next]) {
64 | return @false;
65 | }
66 | state.value = next;
67 | return @true;
68 | }] boolValue];
69 |
70 | if (shouldPassthrough) {
71 | [subscriber putNext:next];
72 | }
73 | } error:^(id error)
74 | {
75 | [subscriber putError:error];
76 | } completed:^
77 | {
78 | [subscriber putCompletion];
79 | }];
80 | }];
81 | }
82 |
83 | @end
84 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Meta.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class SQueue;
4 |
5 | @interface SSignal (Meta)
6 |
7 | - (SSignal *)switchToLatest;
8 | - (SSignal *)mapToSignal:(SSignal *(^)(id))f;
9 | - (SSignal *)mapToQueue:(SSignal *(^)(id))f;
10 | - (SSignal *)mapToThrottled:(SSignal *(^)(id))f;
11 | - (SSignal *)then:(SSignal *)signal;
12 | - (SSignal *)queue;
13 | - (SSignal *)throttled;
14 | + (SSignal *)defer:(SSignal *(^)())generator;
15 |
16 | @end
17 |
18 | @interface SSignalQueue : NSObject
19 |
20 | - (SSignal *)enqueue:(SSignal *)signal;
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Meta.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Meta.h"
2 |
3 | #import "SDisposableSet.h"
4 | #import "SMetaDisposable.h"
5 | #import "SSignal+Mapping.h"
6 | #import "SAtomic.h"
7 | #import "SSignal+Pipe.h"
8 |
9 | #import
10 |
11 | @interface SSignalQueueState : NSObject
12 | {
13 | OSSpinLock _lock;
14 | bool _executingSignal;
15 | bool _terminated;
16 |
17 | id _disposable;
18 | SMetaDisposable *_currentDisposable;
19 | SSubscriber *_subscriber;
20 |
21 | NSMutableArray *_queuedSignals;
22 | bool _queueMode;
23 | bool _throttleMode;
24 | }
25 |
26 | @end
27 |
28 | @implementation SSignalQueueState
29 |
30 | - (instancetype)initWithSubscriber:(SSubscriber *)subscriber queueMode:(bool)queueMode throttleMode:(bool)throttleMode
31 | {
32 | self = [super init];
33 | if (self != nil)
34 | {
35 | _subscriber = subscriber;
36 | _currentDisposable = [[SMetaDisposable alloc] init];
37 | _queuedSignals = queueMode ? [[NSMutableArray alloc] init] : nil;
38 | _queueMode = queueMode;
39 | _throttleMode = throttleMode;
40 | }
41 | return self;
42 | }
43 |
44 | - (void)beginWithDisposable:(id)disposable
45 | {
46 | _disposable = disposable;
47 | }
48 |
49 | - (void)enqueueSignal:(SSignal *)signal
50 | {
51 | bool startSignal = false;
52 | OSSpinLockLock(&_lock);
53 | if (_queueMode && _executingSignal) {
54 | if (_throttleMode) {
55 | [_queuedSignals removeAllObjects];
56 | }
57 | [_queuedSignals addObject:signal];
58 | }
59 | else
60 | {
61 | _executingSignal = true;
62 | startSignal = true;
63 | }
64 | OSSpinLockUnlock(&_lock);
65 |
66 | if (startSignal)
67 | {
68 | __weak SSignalQueueState *weakSelf = self;
69 | id disposable = [signal startWithNext:^(id next)
70 | {
71 | [_subscriber putNext:next];
72 | } error:^(id error)
73 | {
74 | [_subscriber putError:error];
75 | } completed:^
76 | {
77 | __strong SSignalQueueState *strongSelf = weakSelf;
78 | if (strongSelf != nil) {
79 | [strongSelf headCompleted];
80 | }
81 | }];
82 |
83 | [_currentDisposable setDisposable:disposable];
84 | }
85 | }
86 |
87 | - (void)headCompleted
88 | {
89 | SSignal *nextSignal = nil;
90 |
91 | bool terminated = false;
92 | OSSpinLockLock(&_lock);
93 | _executingSignal = false;
94 |
95 | if (_queueMode)
96 | {
97 | if (_queuedSignals.count != 0)
98 | {
99 | nextSignal = _queuedSignals[0];
100 | [_queuedSignals removeObjectAtIndex:0];
101 | _executingSignal = true;
102 | }
103 | else
104 | terminated = _terminated;
105 | }
106 | else
107 | terminated = _terminated;
108 | OSSpinLockUnlock(&_lock);
109 |
110 | if (terminated)
111 | [_subscriber putCompletion];
112 | else if (nextSignal != nil)
113 | {
114 | __weak SSignalQueueState *weakSelf = self;
115 | id disposable = [nextSignal startWithNext:^(id next)
116 | {
117 | [_subscriber putNext:next];
118 | } error:^(id error)
119 | {
120 | [_subscriber putError:error];
121 | } completed:^
122 | {
123 | __strong SSignalQueueState *strongSelf = weakSelf;
124 | if (strongSelf != nil) {
125 | [strongSelf headCompleted];
126 | }
127 | }];
128 |
129 | [_currentDisposable setDisposable:disposable];
130 | }
131 | }
132 |
133 | - (void)beginCompletion
134 | {
135 | bool executingSignal = false;
136 | OSSpinLockLock(&_lock);
137 | executingSignal = _executingSignal;
138 | _terminated = true;
139 | OSSpinLockUnlock(&_lock);
140 |
141 | if (!executingSignal)
142 | [_subscriber putCompletion];
143 | }
144 |
145 | - (void)dispose
146 | {
147 | [_currentDisposable dispose];
148 | [_disposable dispose];
149 | }
150 |
151 | @end
152 |
153 | @implementation SSignal (Meta)
154 |
155 | - (SSignal *)switchToLatest
156 | {
157 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
158 | {
159 | SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:false throttleMode:false];
160 |
161 | [state beginWithDisposable:[self startWithNext:^(id next)
162 | {
163 | [state enqueueSignal:next];
164 | } error:^(id error)
165 | {
166 | [subscriber putError:error];
167 | } completed:^
168 | {
169 | [state beginCompletion];
170 | }]];
171 |
172 | return state;
173 | }];
174 | }
175 |
176 | - (SSignal *)mapToSignal:(SSignal *(^)(id))f
177 | {
178 | return [[self map:f] switchToLatest];
179 | }
180 |
181 | - (SSignal *)mapToQueue:(SSignal *(^)(id))f
182 | {
183 | return [[self map:f] queue];
184 | }
185 |
186 | - (SSignal *)mapToThrottled:(SSignal *(^)(id))f {
187 | return [[self map:f] throttled];
188 | }
189 |
190 | - (SSignal *)then:(SSignal *)signal
191 | {
192 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
193 | {
194 | SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
195 |
196 | SMetaDisposable *currentDisposable = [[SMetaDisposable alloc] init];
197 | [compositeDisposable add:currentDisposable];
198 |
199 | [currentDisposable setDisposable:[self startWithNext:^(id next)
200 | {
201 | [subscriber putNext:next];
202 | } error:^(id error)
203 | {
204 | [subscriber putError:error];
205 | } completed:^
206 | {
207 | [compositeDisposable add:[signal startWithNext:^(id next)
208 | {
209 | [subscriber putNext:next];
210 | } error:^(id error)
211 | {
212 | [subscriber putError:error];
213 | } completed:^
214 | {
215 | [subscriber putCompletion];
216 | }]];
217 | }]];
218 |
219 | return compositeDisposable;
220 | }];
221 | }
222 |
223 | - (SSignal *)queue
224 | {
225 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
226 | {
227 | SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:true throttleMode:false];
228 |
229 | [state beginWithDisposable:[self startWithNext:^(id next)
230 | {
231 | [state enqueueSignal:next];
232 | } error:^(id error)
233 | {
234 | [subscriber putError:error];
235 | } completed:^
236 | {
237 | [state beginCompletion];
238 | }]];
239 |
240 | return state;
241 | }];
242 | }
243 |
244 | - (SSignal *)throttled {
245 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) {
246 | SSignalQueueState *state = [[SSignalQueueState alloc] initWithSubscriber:subscriber queueMode:true throttleMode:true];
247 | [state beginWithDisposable:[self startWithNext:^(id next)
248 | {
249 | [state enqueueSignal:next];
250 | } error:^(id error)
251 | {
252 | [subscriber putError:error];
253 | } completed:^
254 | {
255 | [state beginCompletion];
256 | }]];
257 |
258 | return state;
259 | }];
260 | }
261 |
262 | + (SSignal *)defer:(SSignal *(^)())generator
263 | {
264 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
265 | {
266 | return [generator() startWithNext:^(id next)
267 | {
268 | [subscriber putNext:next];
269 | } error:^(id error)
270 | {
271 | [subscriber putError:error];
272 | } completed:^
273 | {
274 | [subscriber putCompletion];
275 | }];
276 | }];
277 | }
278 |
279 | @end
280 |
281 | @interface SSignalQueue () {
282 | SPipe *_pipe;
283 | id _disposable;
284 | }
285 |
286 | @end
287 |
288 | @implementation SSignalQueue
289 |
290 | - (instancetype)init {
291 | self = [super init];
292 | if (self != nil) {
293 | _pipe = [[SPipe alloc] init];
294 | _disposable = [[_pipe.signalProducer() queue] startWithNext:nil];
295 | }
296 | return self;
297 | }
298 |
299 | - (void)dealloc {
300 | [_disposable dispose];
301 | }
302 |
303 | - (SSignal *)enqueue:(SSignal *)signal {
304 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) {
305 | SPipe *disposePipe = [[SPipe alloc] init];
306 |
307 | SSignal *proxy = [[[[signal onNext:^(id next) {
308 | [subscriber putNext:next];
309 | }] onError:^(id error) {
310 | [subscriber putError:error];
311 | }] onCompletion:^{
312 | [subscriber putCompletion];
313 | }] catch:^SSignal *(__unused id error) {
314 | return [SSignal complete];
315 | }];
316 |
317 | _pipe.sink([proxy takeUntilReplacement:disposePipe.signalProducer()]);
318 |
319 | return [[SBlockDisposable alloc] initWithBlock:^{
320 | disposePipe.sink([SSignal complete]);
321 | }];
322 | }];
323 | }
324 |
325 | @end
326 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Multicast.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal (Multicast)
4 |
5 | - (SSignal *)multicast;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Multicast.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Multicast.h"
2 |
3 | #import
4 | #import "SBag.h"
5 | #import "SBlockDisposable.h"
6 |
7 | typedef enum {
8 | SSignalMulticastStateReady,
9 | SSignalMulticastStateStarted,
10 | SSignalMulticastStateCompleted
11 | } SSignalMulticastState;
12 |
13 | @interface SSignalMulticastSubscribers : NSObject
14 | {
15 | volatile OSSpinLock _lock;
16 | SBag *_subscribers;
17 | SSignalMulticastState _state;
18 | id _disposable;
19 | }
20 |
21 | @end
22 |
23 | @implementation SSignalMulticastSubscribers
24 |
25 | - (instancetype)init
26 | {
27 | self = [super init];
28 | if (self != nil)
29 | {
30 | _subscribers = [[SBag alloc] init];
31 | }
32 | return self;
33 | }
34 |
35 | - (void)setDisposable:(id)disposable
36 | {
37 | [_disposable dispose];
38 | _disposable = disposable;
39 | }
40 |
41 | - (id)addSubscriber:(SSubscriber *)subscriber start:(bool *)start
42 | {
43 | OSSpinLockLock(&_lock);
44 | NSInteger index = [_subscribers addItem:subscriber];
45 | switch (_state) {
46 | case SSignalMulticastStateReady:
47 | *start = true;
48 | _state = SSignalMulticastStateStarted;
49 | break;
50 | default:
51 | break;
52 | }
53 | OSSpinLockUnlock(&_lock);
54 |
55 | return [[SBlockDisposable alloc] initWithBlock:^
56 | {
57 | [self remove:index];
58 | }];
59 | }
60 |
61 | - (void)remove:(NSInteger)index
62 | {
63 | id currentDisposable = nil;
64 |
65 | OSSpinLockLock(&_lock);
66 | [_subscribers removeItem:index];
67 | switch (_state) {
68 | case SSignalMulticastStateStarted:
69 | if ([_subscribers isEmpty])
70 | {
71 | currentDisposable = _disposable;
72 | _disposable = nil;
73 | }
74 | break;
75 | default:
76 | break;
77 | }
78 | OSSpinLockUnlock(&_lock);
79 |
80 | [currentDisposable dispose];
81 | }
82 |
83 | - (void)notifyNext:(id)next
84 | {
85 | NSArray *currentSubscribers = nil;
86 | OSSpinLockLock(&_lock);
87 | currentSubscribers = [_subscribers copyItems];
88 | OSSpinLockUnlock(&_lock);
89 |
90 | for (SSubscriber *subscriber in currentSubscribers)
91 | {
92 | [subscriber putNext:next];
93 | }
94 | }
95 |
96 | - (void)notifyError:(id)error
97 | {
98 | NSArray *currentSubscribers = nil;
99 | OSSpinLockLock(&_lock);
100 | currentSubscribers = [_subscribers copyItems];
101 | _state = SSignalMulticastStateCompleted;
102 | OSSpinLockUnlock(&_lock);
103 |
104 | for (SSubscriber *subscriber in currentSubscribers)
105 | {
106 | [subscriber putError:error];
107 | }
108 | }
109 |
110 | - (void)notifyCompleted
111 | {
112 | NSArray *currentSubscribers = nil;
113 | OSSpinLockLock(&_lock);
114 | currentSubscribers = [_subscribers copyItems];
115 | _state = SSignalMulticastStateCompleted;
116 | OSSpinLockUnlock(&_lock);
117 |
118 | for (SSubscriber *subscriber in currentSubscribers)
119 | {
120 | [subscriber putCompletion];
121 | }
122 | }
123 |
124 | @end
125 |
126 | @implementation SSignal (Multicast)
127 |
128 | - (SSignal *)multicast
129 | {
130 | SSignalMulticastSubscribers *subscribers = [[SSignalMulticastSubscribers alloc] init];
131 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
132 | {
133 | bool start = false;
134 | id currentDisposable = [subscribers addSubscriber:subscriber start:&start];
135 | if (start)
136 | {
137 | id disposable = [self startWithNext:^(id next)
138 | {
139 | [subscribers notifyNext:next];
140 | } error:^(id error)
141 | {
142 | [subscribers notifyError:error];
143 | } completed:^
144 | {
145 | [subscribers notifyCompleted];
146 | }];
147 |
148 | [subscribers setDisposable:[[SBlockDisposable alloc] initWithBlock:^
149 | {
150 | [disposable dispose];
151 | }]];
152 | }
153 |
154 | return currentDisposable;
155 | }];
156 | }
157 |
158 | @end
159 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Pipe.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SPipe : NSObject
4 |
5 | @property (nonatomic, copy, readonly) SSignal *(^signalProducer)();
6 | @property (nonatomic, copy, readonly) void (^sink)(id);
7 |
8 | - (instancetype)initWithReplay:(bool)replay;
9 |
10 | @end
11 |
12 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Pipe.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Pipe.h"
2 |
3 | #import "SBlockDisposable.h"
4 | #import "SAtomic.h"
5 | #import "SBag.h"
6 |
7 | @interface SPipeReplayState : NSObject
8 |
9 | @property (nonatomic, readonly) bool hasReceivedValue;
10 | @property (nonatomic, strong, readonly) id recentValue;
11 |
12 | @end
13 |
14 | @implementation SPipeReplayState
15 |
16 | - (instancetype)initWithReceivedValue:(bool)receivedValue recentValue:(id)recentValue
17 | {
18 | self = [super init];
19 | if (self != nil)
20 | {
21 | _hasReceivedValue = receivedValue;
22 | _recentValue = recentValue;
23 | }
24 | return self;
25 | }
26 |
27 | @end
28 |
29 | @implementation SPipe
30 |
31 | - (instancetype)init
32 | {
33 | return [self initWithReplay:false];
34 | }
35 |
36 | - (instancetype)initWithReplay:(bool)replay
37 | {
38 | self = [super init];
39 | if (self != nil)
40 | {
41 | SAtomic *subscribers = [[SAtomic alloc] initWithValue:[[SBag alloc] init]];
42 | SAtomic *replayState = replay ? [[SAtomic alloc] initWithValue:[[SPipeReplayState alloc] initWithReceivedValue:false recentValue:nil]] : nil;
43 |
44 | _signalProducer = [^SSignal *
45 | {
46 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
47 | {
48 | __block NSUInteger index = 0;
49 | [subscribers with:^id(SBag *bag)
50 | {
51 | index = [bag addItem:[^(id next)
52 | {
53 | [subscriber putNext:next];
54 | } copy]];
55 | return nil;
56 | }];
57 |
58 | if (replay)
59 | {
60 | [replayState with:^id(SPipeReplayState *state)
61 | {
62 | if (state.hasReceivedValue)
63 | [subscriber putNext:state.recentValue];
64 | return nil;
65 | }];
66 | }
67 |
68 | return [[SBlockDisposable alloc] initWithBlock:^
69 | {
70 | [subscribers with:^id(SBag *bag)
71 | {
72 | [bag removeItem:index];
73 | return nil;
74 | }];
75 | }];
76 | }];
77 | } copy];
78 |
79 | _sink = [^(id next)
80 | {
81 | NSArray *items = [subscribers with:^id(SBag *bag)
82 | {
83 | return [bag copyItems];
84 | }];
85 |
86 | for (void (^item)(id) in items)
87 | {
88 | item(next);
89 | }
90 |
91 | if (replay)
92 | {
93 | [replayState modify:^id(__unused SPipeReplayState *state)
94 | {
95 | return [[SPipeReplayState alloc] initWithReceivedValue:true recentValue:next];
96 | }];
97 | }
98 | } copy];
99 | }
100 | return self;
101 | }
102 |
103 | @end
104 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+SideEffects.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal (SideEffects)
4 |
5 | - (SSignal *)onStart:(void (^)())f;
6 | - (SSignal *)onNext:(void (^)(id next))f;
7 | - (SSignal *)afterNext:(void (^)(id next))f;
8 | - (SSignal *)onError:(void (^)(id error))f;
9 | - (SSignal *)onCompletion:(void (^)())f;
10 | - (SSignal *)afterCompletion:(void (^)())f;
11 | - (SSignal *)onDispose:(void (^)())f;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+SideEffects.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+SideEffects.h"
2 |
3 | #import "SBlockDisposable.h"
4 | #import "SDisposableSet.h"
5 |
6 | @implementation SSignal (SideEffects)
7 |
8 | - (SSignal *)onStart:(void (^)())f
9 | {
10 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
11 | {
12 | f();
13 | return [self startWithNext:^(id next)
14 | {
15 | [subscriber putNext:next];
16 | } error:^(id error)
17 | {
18 | [subscriber putError:error];
19 | } completed:^
20 | {
21 | [subscriber putCompletion];
22 | }];
23 | }];
24 | }
25 |
26 | - (SSignal *)onNext:(void (^)(id next))f
27 | {
28 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
29 | {
30 | return [self startWithNext:^(id next)
31 | {
32 | f(next);
33 | [subscriber putNext:next];
34 | } error:^(id error)
35 | {
36 | [subscriber putError:error];
37 | } completed:^
38 | {
39 | [subscriber putCompletion];
40 | }];
41 | }];
42 | }
43 |
44 | - (SSignal *)afterNext:(void (^)(id next))f
45 | {
46 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
47 | {
48 | return [self startWithNext:^(id next)
49 | {
50 | [subscriber putNext:next];
51 | f(next);
52 | } error:^(id error)
53 | {
54 | [subscriber putError:error];
55 | } completed:^
56 | {
57 | [subscriber putCompletion];
58 | }];
59 | }];
60 | }
61 |
62 | - (SSignal *)onError:(void (^)(id error))f
63 | {
64 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
65 | {
66 | return [self startWithNext:^(id next)
67 | {
68 | [subscriber putNext:next];
69 | } error:^(id error)
70 | {
71 | f(error);
72 | [subscriber putError:error];
73 | } completed:^
74 | {
75 | [subscriber putCompletion];
76 | }];
77 | }];
78 | }
79 |
80 | - (SSignal *)onCompletion:(void (^)())f
81 | {
82 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
83 | {
84 | return [self startWithNext:^(id next)
85 | {
86 | [subscriber putNext:next];
87 | } error:^(id error)
88 | {
89 | [subscriber putError:error];
90 | } completed:^
91 | {
92 | f();
93 | [subscriber putCompletion];
94 | }];
95 | }];
96 | }
97 |
98 | - (SSignal *)afterCompletion:(void (^)())f {
99 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
100 | {
101 | return [self startWithNext:^(id next)
102 | {
103 | [subscriber putNext:next];
104 | } error:^(id error)
105 | {
106 | [subscriber putError:error];
107 | } completed:^
108 | {
109 | [subscriber putCompletion];
110 | f();
111 | }];
112 | }];
113 | }
114 |
115 | - (SSignal *)onDispose:(void (^)())f
116 | {
117 | return [[SSignal alloc] initWithGenerator:^(SSubscriber *subscriber)
118 | {
119 | SDisposableSet *compositeDisposable = [[SDisposableSet alloc] init];
120 |
121 | [compositeDisposable add:[self startWithNext:^(id next)
122 | {
123 | [subscriber putNext:next];
124 | } error:^(id error)
125 | {
126 | [subscriber putError:error];
127 | } completed:^
128 | {
129 | [subscriber putCompletion];
130 | }]];
131 |
132 | [compositeDisposable add:[[SBlockDisposable alloc] initWithBlock:^
133 | {
134 | f();
135 | }]];
136 |
137 | return compositeDisposable;
138 | }];
139 | }
140 |
141 | @end
142 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Single.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal (Single)
4 |
5 | + (SSignal *)single:(id)next;
6 | + (SSignal *)fail:(id)error;
7 | + (SSignal *)never;
8 | + (SSignal *)complete;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Single.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Single.h"
2 |
3 | @implementation SSignal (Single)
4 |
5 | + (SSignal *)single:(id)next
6 | {
7 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
8 | {
9 | [subscriber putNext:next];
10 | [subscriber putCompletion];
11 | return nil;
12 | }];
13 | }
14 |
15 | + (SSignal *)fail:(id)error
16 | {
17 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
18 | {
19 | [subscriber putError:error];
20 | return nil;
21 | }];
22 | }
23 |
24 | + (SSignal *)never
25 | {
26 | return [[SSignal alloc] initWithGenerator:^id (__unused SSubscriber *subscriber)
27 | {
28 | return nil;
29 | }];
30 | }
31 |
32 | + (SSignal *)complete
33 | {
34 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
35 | {
36 | [subscriber putCompletion];
37 | return nil;
38 | }];
39 | }
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Take.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal (Take)
4 |
5 | - (SSignal *)take:(NSUInteger)count;
6 | - (SSignal *)takeLast;
7 | - (SSignal *)takeUntilReplacement:(SSignal *)replacement;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Take.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Take.h"
2 |
3 | #import "SAtomic.h"
4 |
5 | @interface SSignal_ValueContainer : NSObject
6 |
7 | @property (nonatomic, strong, readonly) id value;
8 |
9 | @end
10 |
11 | @implementation SSignal_ValueContainer
12 |
13 | - (instancetype)initWithValue:(id)value {
14 | self = [super init];
15 | if (self != nil) {
16 | _value = value;
17 | }
18 | return self;
19 | }
20 |
21 | @end
22 |
23 | @implementation SSignal (Take)
24 |
25 | - (SSignal *)take:(NSUInteger)count
26 | {
27 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
28 | {
29 | SAtomic *counter = [[SAtomic alloc] initWithValue:@(0)];
30 | return [self startWithNext:^(id next)
31 | {
32 | __block bool passthrough = false;
33 | __block bool complete = false;
34 | [counter modify:^id(NSNumber *currentCount)
35 | {
36 | NSUInteger updatedCount = [currentCount unsignedIntegerValue] + 1;
37 | if (updatedCount <= count)
38 | passthrough = true;
39 | if (updatedCount == count)
40 | complete = true;
41 | return @(updatedCount);
42 | }];
43 |
44 | if (passthrough)
45 | [subscriber putNext:next];
46 | if (complete)
47 | [subscriber putCompletion];
48 | } error:^(id error)
49 | {
50 | [subscriber putError:error];
51 | } completed:^
52 | {
53 | [subscriber putCompletion];
54 | }];
55 | }];
56 | }
57 |
58 | - (SSignal *)takeLast
59 | {
60 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
61 | {
62 | SAtomic *last = [[SAtomic alloc] initWithValue:nil];
63 | return [self startWithNext:^(id next)
64 | {
65 | [last swap:[[SSignal_ValueContainer alloc] initWithValue:next]];
66 | } error:^(id error)
67 | {
68 | [subscriber putError:error];
69 | } completed:^
70 | {
71 | SSignal_ValueContainer *value = [last with:^id(id value) {
72 | return value;
73 | }];
74 | if (value != nil)
75 | {
76 | [subscriber putNext:value.value];
77 | }
78 | [subscriber putCompletion];
79 | }];
80 | }];
81 | }
82 |
83 | - (SSignal *)takeUntilReplacement:(SSignal *)replacement {
84 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber) {
85 | SDisposableSet *disposable = [[SDisposableSet alloc] init];
86 |
87 | SMetaDisposable *selfDisposable = [[SMetaDisposable alloc] init];
88 | SMetaDisposable *replacementDisposable = [[SMetaDisposable alloc] init];
89 |
90 | [disposable add:selfDisposable];
91 | [disposable add:replacementDisposable];
92 |
93 | [disposable add:[replacement startWithNext:^(SSignal *next) {
94 | [selfDisposable dispose];
95 |
96 | [replacementDisposable setDisposable:[next startWithNext:^(id next) {
97 | [subscriber putNext:next];
98 | } error:^(id error) {
99 | [subscriber putError:error];
100 | } completed:^{
101 | [subscriber putCompletion];
102 | }]];
103 | } error:^(id error) {
104 | [subscriber putError:error];
105 | } completed:^{
106 | }]];
107 |
108 | [selfDisposable setDisposable:[self startWithNext:^(id next) {
109 | [subscriber putNext:next];
110 | } error:^(id error) {
111 | [replacementDisposable dispose];
112 | [subscriber putError:error];
113 | } completed:^{
114 | [replacementDisposable dispose];
115 | [subscriber putCompletion];
116 | }]];
117 |
118 | return disposable;
119 | }];
120 | }
121 |
122 | @end
123 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Timing.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import
4 |
5 | @interface SSignal (Timing)
6 |
7 | - (SSignal *)delay:(NSTimeInterval)seconds onQueue:(SQueue *)queue;
8 | - (SSignal *)timeout:(NSTimeInterval)seconds onQueue:(SQueue *)queue orSignal:(SSignal *)signal;
9 | - (SSignal *)wait:(NSTimeInterval)seconds;
10 |
11 | @end
12 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal+Timing.m:
--------------------------------------------------------------------------------
1 | #import "SSignal+Timing.h"
2 |
3 | #import "SMetaDisposable.h"
4 | #import "SDisposableSet.h"
5 | #import "SBlockDisposable.h"
6 |
7 | #import "SSignal+Dispatch.h"
8 |
9 | #import "STimer.h"
10 |
11 | @implementation SSignal (Timing)
12 |
13 | - (SSignal *)delay:(NSTimeInterval)seconds onQueue:(SQueue *)queue
14 | {
15 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
16 | {
17 | SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
18 |
19 | STimer *timer = [[STimer alloc] initWithTimeout:seconds repeat:false completion:^
20 | {
21 | [disposable setDisposable:[self startWithNext:^(id next)
22 | {
23 | [subscriber putNext:next];
24 | } error:^(id error)
25 | {
26 | [subscriber putError:error];
27 | } completed:^
28 | {
29 | [subscriber putCompletion];
30 | }]];
31 | } queue:queue];
32 |
33 | [timer start];
34 |
35 | [disposable setDisposable:[[SBlockDisposable alloc] initWithBlock:^
36 | {
37 | [timer invalidate];
38 | }]];
39 |
40 | return disposable;
41 | }];
42 | }
43 |
44 | - (SSignal *)timeout:(NSTimeInterval)seconds onQueue:(SQueue *)queue orSignal:(SSignal *)signal
45 | {
46 | return [[SSignal alloc] initWithGenerator:^id (SSubscriber *subscriber)
47 | {
48 | SMetaDisposable *disposable = [[SMetaDisposable alloc] init];
49 |
50 | STimer *timer = [[STimer alloc] initWithTimeout:seconds repeat:false completion:^
51 | {
52 | [disposable setDisposable:[signal startWithNext:^(id next)
53 | {
54 | [subscriber putNext:next];
55 | } error:^(id error)
56 | {
57 | [subscriber putError:error];
58 | } completed:^
59 | {
60 | [subscriber putCompletion];
61 | }]];
62 | } queue:queue];
63 | [timer start];
64 |
65 | [disposable setDisposable:[self startWithNext:^(id next)
66 | {
67 | [timer invalidate];
68 | [subscriber putNext:next];
69 | } error:^(id error)
70 | {
71 | [timer invalidate];
72 | [subscriber putError:error];
73 | } completed:^
74 | {
75 | [timer invalidate];
76 | [subscriber putCompletion];
77 | }]];
78 |
79 | return disposable;
80 | }];
81 | }
82 |
83 | - (SSignal *)wait:(NSTimeInterval)seconds
84 | {
85 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
86 | {
87 | dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
88 |
89 | id disposable = [self startWithNext:^(id next)
90 | {
91 | dispatch_semaphore_signal(semaphore);
92 | [subscriber putNext:next];
93 | } error:^(id error)
94 | {
95 | dispatch_semaphore_signal(semaphore);
96 | [subscriber putError:error];
97 | } completed:^
98 | {
99 | dispatch_semaphore_signal(semaphore);
100 | [subscriber putCompletion];
101 | }];
102 |
103 | dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)));
104 |
105 | return disposable;
106 | }];
107 | }
108 |
109 | @end
110 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSignal : NSObject
4 | {
5 | @public
6 | id (^_generator)(SSubscriber *);
7 | }
8 |
9 | - (instancetype)initWithGenerator:(id (^)(SSubscriber *))generator;
10 |
11 | - (id)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed;
12 | - (id)startWithNext:(void (^)(id next))next;
13 | - (id)startWithNext:(void (^)(id next))next completed:(void (^)())completed;
14 |
15 | - (SSignal *)trace:(NSString *)name;
16 |
17 | @end
18 |
19 |
--------------------------------------------------------------------------------
/SSignalKit/SSignal.m:
--------------------------------------------------------------------------------
1 | #import "SSignal.h"
2 |
3 | #import "SBlockDisposable.h"
4 |
5 | @interface SSubscriberDisposable : NSObject
6 | {
7 | SSubscriber *_subscriber;
8 | id _disposable;
9 | }
10 |
11 | @end
12 |
13 | @implementation SSubscriberDisposable
14 |
15 | - (instancetype)initWithSubscriber:(SSubscriber *)subscriber disposable:(id)disposable
16 | {
17 | self = [super init];
18 | if (self != nil)
19 | {
20 | _subscriber = subscriber;
21 | _disposable = disposable;
22 | }
23 | return self;
24 | }
25 |
26 | - (void)dispose
27 | {
28 | [_subscriber _markTerminatedWithoutDisposal];
29 | [_disposable dispose];
30 | }
31 |
32 | @end
33 |
34 | @interface SSignal ()
35 | {
36 | }
37 |
38 | @end
39 |
40 | @implementation SSignal
41 |
42 | - (instancetype)initWithGenerator:(id (^)(SSubscriber *))generator
43 | {
44 | self = [super init];
45 | if (self != nil)
46 | {
47 | _generator = [generator copy];
48 | }
49 | return self;
50 | }
51 |
52 | - (id)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed traceName:(NSString *)traceName
53 | {
54 | STracingSubscriber *subscriber = [[STracingSubscriber alloc] initWithName:traceName next:next error:error completed:completed];
55 | id disposable = _generator(subscriber);
56 | [subscriber _assignDisposable:disposable];
57 | return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
58 | }
59 |
60 | - (id)startWithNext:(void (^)(id next))next error:(void (^)(id error))error completed:(void (^)())completed
61 | {
62 | SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:error completed:completed];
63 | id disposable = _generator(subscriber);
64 | [subscriber _assignDisposable:disposable];
65 | return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
66 | }
67 |
68 | - (id)startWithNext:(void (^)(id next))next
69 | {
70 | SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:nil];
71 | id disposable = _generator(subscriber);
72 | [subscriber _assignDisposable:disposable];
73 | return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
74 | }
75 |
76 | - (id)startWithNext:(void (^)(id next))next completed:(void (^)())completed
77 | {
78 | SSubscriber *subscriber = [[SSubscriber alloc] initWithNext:next error:nil completed:completed];
79 | id disposable = _generator(subscriber);
80 | [subscriber _assignDisposable:disposable];
81 | return [[SSubscriberDisposable alloc] initWithSubscriber:subscriber disposable:disposable];
82 | }
83 |
84 | - (SSignal *)trace:(NSString *)name
85 | {
86 | #ifdef DEBUG
87 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
88 | {
89 | NSString *traceName = [[NSString alloc] initWithFormat:@"%@#0x%x", name, (int)random()];
90 | NSLog(@"trace(%@ start)", traceName);
91 | return [self startWithNext:^(id next)
92 | {
93 | [subscriber putNext:next];
94 | } error:^(id error)
95 | {
96 | [subscriber putError:error];
97 | } completed:^
98 | {
99 | [subscriber putCompletion];
100 | } traceName:traceName];
101 | }];
102 | #else
103 | return self;
104 | #endif
105 | }
106 |
107 | @end
108 |
--------------------------------------------------------------------------------
/SSignalKit/SSignalKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // SSignalKit.h
3 | // SSignalKit
4 | //
5 | // Created by Peter on 31/01/15.
6 | // Copyright (c) 2015 Telegram. All rights reserved.
7 | //
8 |
9 | #if __IPHONE_OS_VERSION_MIN_REQUIRED
10 | #import
11 | #else
12 | #import
13 | #endif
14 |
15 | //! Project version number for SSignalKit.
16 | FOUNDATION_EXPORT double SSignalKitVersionNumber;
17 |
18 | //! Project version string for SSignalKit.
19 | FOUNDATION_EXPORT const unsigned char SSignalKitVersionString[];
20 |
21 | // In this header, you should import all the public headers of your framework using statements like #import
22 |
23 | #import
24 | #import
25 | #import
26 | #import
27 | #import
28 | #import
29 | #import
30 | #import
31 | #import
32 | #import
33 | #import
34 | #import
35 | #import
36 | #import
37 | #import
38 | #import
39 | #import
40 | #import
41 | #import
42 | #import
43 | #import
44 | #import
45 | #import
46 |
--------------------------------------------------------------------------------
/SSignalKit/SSubscriber.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SSubscriber : NSObject
4 | {
5 | }
6 |
7 | - (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed;
8 |
9 | - (void)_assignDisposable:(id)disposable;
10 | - (void)_markTerminatedWithoutDisposal;
11 |
12 | - (void)putNext:(id)next;
13 | - (void)putError:(id)error;
14 | - (void)putCompletion;
15 |
16 | @end
17 |
18 | @interface STracingSubscriber : SSubscriber
19 |
20 | - (instancetype)initWithName:(NSString *)name next:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed;
21 |
22 | @end
--------------------------------------------------------------------------------
/SSignalKit/SSubscriber.m:
--------------------------------------------------------------------------------
1 | #import "SSubscriber.h"
2 |
3 | #import
4 |
5 | @interface SSubscriberBlocks : NSObject {
6 | @public
7 | void (^_next)(id);
8 | void (^_error)(id);
9 | void (^_completed)();
10 | }
11 |
12 | @end
13 |
14 | @implementation SSubscriberBlocks
15 |
16 | - (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed {
17 | self = [super init];
18 | if (self != nil) {
19 | _next = [next copy];
20 | _error = [error copy];
21 | _completed = [completed copy];
22 | }
23 | return self;
24 | }
25 |
26 | @end
27 |
28 | @interface SSubscriber ()
29 | {
30 | @protected
31 | OSSpinLock _lock;
32 | bool _terminated;
33 | id _disposable;
34 | SSubscriberBlocks *_blocks;
35 | }
36 |
37 | @end
38 |
39 | @implementation SSubscriber
40 |
41 | - (instancetype)initWithNext:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed
42 | {
43 | self = [super init];
44 | if (self != nil)
45 | {
46 | _blocks = [[SSubscriberBlocks alloc] initWithNext:next error:error completed:completed];
47 | }
48 | return self;
49 | }
50 |
51 | - (void)_assignDisposable:(id)disposable
52 | {
53 | bool dispose = false;
54 | OSSpinLockLock(&_lock);
55 | if (_terminated) {
56 | dispose = true;
57 | } else {
58 | _disposable = disposable;
59 | }
60 | OSSpinLockUnlock(&_lock);
61 | if (dispose) {
62 | [disposable dispose];
63 | }
64 | }
65 |
66 | - (void)_markTerminatedWithoutDisposal
67 | {
68 | OSSpinLockLock(&_lock);
69 | SSubscriberBlocks *blocks = nil;
70 | if (!_terminated)
71 | {
72 | blocks = _blocks;
73 | _blocks = nil;
74 |
75 | _terminated = true;
76 | }
77 | OSSpinLockUnlock(&_lock);
78 |
79 | if (blocks) {
80 | blocks = nil;
81 | }
82 | }
83 |
84 | - (void)putNext:(id)next
85 | {
86 | SSubscriberBlocks *blocks = nil;
87 |
88 | OSSpinLockLock(&_lock);
89 | if (!_terminated) {
90 | blocks = _blocks;
91 | }
92 | OSSpinLockUnlock(&_lock);
93 |
94 | if (blocks && blocks->_next) {
95 | blocks->_next(next);
96 | }
97 | }
98 |
99 | - (void)putError:(id)error
100 | {
101 | bool shouldDispose = false;
102 | SSubscriberBlocks *blocks = nil;
103 |
104 | OSSpinLockLock(&_lock);
105 | if (!_terminated)
106 | {
107 | blocks = _blocks;
108 | _blocks = nil;
109 |
110 | shouldDispose = true;
111 | _terminated = true;
112 | }
113 | OSSpinLockUnlock(&_lock);
114 |
115 | if (blocks && blocks->_error) {
116 | blocks->_error(error);
117 | }
118 |
119 | if (shouldDispose)
120 | [self->_disposable dispose];
121 | }
122 |
123 | - (void)putCompletion
124 | {
125 | bool shouldDispose = false;
126 | SSubscriberBlocks *blocks = nil;
127 |
128 | OSSpinLockLock(&_lock);
129 | if (!_terminated)
130 | {
131 | blocks = _blocks;
132 | _blocks = nil;
133 |
134 | shouldDispose = true;
135 | _terminated = true;
136 | }
137 | OSSpinLockUnlock(&_lock);
138 |
139 | if (blocks && blocks->_completed)
140 | blocks->_completed();
141 |
142 | if (shouldDispose)
143 | [self->_disposable dispose];
144 | }
145 |
146 | - (void)dispose
147 | {
148 | [self->_disposable dispose];
149 | }
150 |
151 | @end
152 |
153 | @interface STracingSubscriber ()
154 | {
155 | NSString *_name;
156 | }
157 |
158 | @end
159 |
160 | @implementation STracingSubscriber
161 |
162 | - (instancetype)initWithName:(NSString *)name next:(void (^)(id))next error:(void (^)(id))error completed:(void (^)())completed
163 | {
164 | self = [super initWithNext:next error:error completed:completed];
165 | if (self != nil)
166 | {
167 | _name = name;
168 | }
169 | return self;
170 | }
171 |
172 | /*- (void)_assignDisposable:(id)disposable
173 | {
174 | if (_terminated)
175 | [disposable dispose];
176 | else
177 | _disposable = disposable;
178 | }
179 |
180 | - (void)_markTerminatedWithoutDisposal
181 | {
182 | OSSpinLockLock(&_lock);
183 | if (!_terminated)
184 | {
185 | NSLog(@"trace(%@ terminated)", _name);
186 | _terminated = true;
187 | _next = nil;
188 | _error = nil;
189 | _completed = nil;
190 | }
191 | OSSpinLockUnlock(&_lock);
192 | }
193 |
194 | - (void)putNext:(id)next
195 | {
196 | void (^fnext)(id) = nil;
197 |
198 | OSSpinLockLock(&_lock);
199 | if (!_terminated)
200 | fnext = self->_next;
201 | OSSpinLockUnlock(&_lock);
202 |
203 | if (fnext)
204 | {
205 | NSLog(@"trace(%@ next: %@)", _name, next);
206 | fnext(next);
207 | }
208 | else
209 | NSLog(@"trace(%@ next: %@, not accepted)", _name, next);
210 | }
211 |
212 | - (void)putError:(id)error
213 | {
214 | bool shouldDispose = false;
215 | void (^ferror)(id) = nil;
216 |
217 | OSSpinLockLock(&_lock);
218 | if (!_terminated)
219 | {
220 | ferror = self->_error;
221 | shouldDispose = true;
222 | self->_next = nil;
223 | self->_error = nil;
224 | self->_completed = nil;
225 | _terminated = true;
226 | }
227 | OSSpinLockUnlock(&_lock);
228 |
229 | if (ferror)
230 | {
231 | NSLog(@"trace(%@ error: %@)", _name, error);
232 | ferror(error);
233 | }
234 | else
235 | NSLog(@"trace(%@ error: %@, not accepted)", _name, error);
236 |
237 | if (shouldDispose)
238 | [self->_disposable dispose];
239 | }
240 |
241 | - (void)putCompletion
242 | {
243 | bool shouldDispose = false;
244 | void (^completed)() = nil;
245 |
246 | OSSpinLockLock(&_lock);
247 | if (!_terminated)
248 | {
249 | completed = self->_completed;
250 | shouldDispose = true;
251 | self->_next = nil;
252 | self->_error = nil;
253 | self->_completed = nil;
254 | _terminated = true;
255 | }
256 | OSSpinLockUnlock(&_lock);
257 |
258 | if (completed)
259 | {
260 | NSLog(@"trace(%@ completed)", _name);
261 | completed();
262 | }
263 | else
264 | NSLog(@"trace(%@ completed, not accepted)", _name);
265 |
266 | if (shouldDispose)
267 | [self->_disposable dispose];
268 | }
269 |
270 | - (void)dispose
271 | {
272 | NSLog(@"trace(%@ dispose)", _name);
273 | [self->_disposable dispose];
274 | }*/
275 |
276 | @end
277 |
--------------------------------------------------------------------------------
/SSignalKit/SThreadPool.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import
4 | #import
5 |
6 | @interface SThreadPool : NSObject
7 |
8 | - (instancetype)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority;
9 |
10 | - (void)addTask:(SThreadPoolTask *)task;
11 |
12 | - (SThreadPoolQueue *)nextQueue;
13 | - (void)_workOnQueue:(SThreadPoolQueue *)queue block:(void (^)())block;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/SSignalKit/SThreadPool.m:
--------------------------------------------------------------------------------
1 | #import "SThreadPool.h"
2 |
3 | #import
4 | #import
5 | #import "SQueue.h"
6 |
7 | @interface SThreadPool ()
8 | {
9 | SQueue *_managementQueue;
10 | NSMutableArray *_threads;
11 |
12 | NSMutableArray *_queues;
13 | NSMutableArray *_takenQueues;
14 |
15 | pthread_mutex_t _mutex;
16 | pthread_cond_t _cond;
17 | }
18 |
19 | @end
20 |
21 | @implementation SThreadPool
22 |
23 | + (void)threadEntryPoint:(SThreadPool *)threadPool
24 | {
25 | SThreadPoolQueue *queue = nil;
26 |
27 | while (true)
28 | {
29 | SThreadPoolTask *task = nil;
30 |
31 | pthread_mutex_lock(&threadPool->_mutex);
32 |
33 | if (queue != nil)
34 | {
35 | [threadPool->_takenQueues removeObject:queue];
36 | if ([queue _hasTasks])
37 | [threadPool->_queues addObject:queue];
38 | }
39 |
40 | while (true)
41 | {
42 | while (threadPool->_queues.count == 0)
43 | pthread_cond_wait(&threadPool->_cond, &threadPool->_mutex);
44 |
45 | queue = threadPool->_queues.firstObject;
46 | task = [queue _popFirstTask];
47 |
48 | if (queue != nil)
49 | {
50 | [threadPool->_takenQueues addObject:queue];
51 | [threadPool->_queues removeObjectAtIndex:0];
52 |
53 | break;
54 | }
55 | }
56 | pthread_mutex_unlock(&threadPool->_mutex);
57 |
58 | @autoreleasepool
59 | {
60 | [task execute];
61 | }
62 | }
63 | }
64 |
65 | - (instancetype)init
66 | {
67 | return [self initWithThreadCount:2 threadPriority:0.5];
68 | }
69 |
70 | - (instancetype)initWithThreadCount:(NSUInteger)threadCount threadPriority:(double)threadPriority
71 | {
72 | self = [super init];
73 | if (self != nil)
74 | {
75 | pthread_mutex_init(&_mutex, 0);
76 | pthread_cond_init(&_cond, 0);
77 |
78 | _managementQueue = [[SQueue alloc] init];
79 |
80 | [_managementQueue dispatch:^
81 | {
82 | _threads = [[NSMutableArray alloc] init];
83 | _queues = [[NSMutableArray alloc] init];
84 | _takenQueues = [[NSMutableArray alloc] init];
85 | for (NSUInteger i = 0; i < threadCount; i++)
86 | {
87 | NSThread *thread = [[NSThread alloc] initWithTarget:[SThreadPool class] selector:@selector(threadEntryPoint:) object:self];
88 | thread.name = [[NSString alloc] initWithFormat:@"SThreadPool-%p-%d", self, (int)i];
89 | [thread setThreadPriority:threadPriority];
90 | [_threads addObject:thread];
91 | [thread start];
92 | }
93 | }];
94 | }
95 | return self;
96 | }
97 |
98 | - (void)dealloc
99 | {
100 | pthread_mutex_destroy(&_mutex);
101 | pthread_cond_destroy(&_cond);
102 | }
103 |
104 | - (void)addTask:(SThreadPoolTask *)task
105 | {
106 | SThreadPoolQueue *tempQueue = [self nextQueue];
107 | [tempQueue addTask:task];
108 | }
109 |
110 | - (SThreadPoolQueue *)nextQueue
111 | {
112 | return [[SThreadPoolQueue alloc] initWithThreadPool:self];
113 | }
114 |
115 | - (void)_workOnQueue:(SThreadPoolQueue *)queue block:(void (^)())block
116 | {
117 | [_managementQueue dispatch:^
118 | {
119 | pthread_mutex_lock(&_mutex);
120 | block();
121 | if (![_queues containsObject:queue] && ![_takenQueues containsObject:queue])
122 | [_queues addObject:queue];
123 | pthread_cond_broadcast(&_cond);
124 | pthread_mutex_unlock(&_mutex);
125 | }];
126 | }
127 |
128 | @end
129 |
--------------------------------------------------------------------------------
/SSignalKit/SThreadPoolQueue.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class SThreadPool;
4 | @class SThreadPoolTask;
5 |
6 | @interface SThreadPoolQueue : NSObject
7 |
8 | - (instancetype)initWithThreadPool:(SThreadPool *)threadPool;
9 | - (void)addTask:(SThreadPoolTask *)task;
10 | - (SThreadPoolTask *)_popFirstTask;
11 | - (bool)_hasTasks;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/SSignalKit/SThreadPoolQueue.m:
--------------------------------------------------------------------------------
1 | #import "SThreadPoolQueue.h"
2 |
3 | #import "SThreadPool.h"
4 |
5 | @interface SThreadPoolQueue ()
6 | {
7 | __weak SThreadPool *_threadPool;
8 | NSMutableArray *_tasks;
9 | }
10 |
11 | @end
12 |
13 | @implementation SThreadPoolQueue
14 |
15 | - (instancetype)initWithThreadPool:(SThreadPool *)threadPool
16 | {
17 | self = [super init];
18 | if (self != nil)
19 | {
20 | _threadPool = threadPool;
21 | _tasks = [[NSMutableArray alloc] init];
22 | }
23 | return self;
24 | }
25 |
26 | - (void)addTask:(SThreadPoolTask *)task
27 | {
28 | SThreadPool *threadPool = _threadPool;
29 | [threadPool _workOnQueue:self block:^
30 | {
31 | [_tasks addObject:task];
32 | }];
33 | }
34 |
35 | - (SThreadPoolTask *)_popFirstTask
36 | {
37 | if (_tasks.count != 0)
38 | {
39 | SThreadPoolTask *task = _tasks[0];
40 | [_tasks removeObjectAtIndex:0];
41 | return task;
42 | }
43 | return nil;
44 | }
45 |
46 | - (bool)_hasTasks
47 | {
48 | return _tasks.count != 0;
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/SSignalKit/SThreadPoolTask.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface SThreadPoolTask : NSObject
4 |
5 | - (instancetype)initWithBlock:(void (^)(bool (^)()))block;
6 | - (void)execute;
7 | - (void)cancel;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/SSignalKit/SThreadPoolTask.m:
--------------------------------------------------------------------------------
1 | #import "SThreadPoolTask.h"
2 |
3 | @interface SThreadPoolTaskState : NSObject
4 | {
5 | @public
6 | bool _cancelled;
7 | }
8 |
9 | @end
10 |
11 | @implementation SThreadPoolTaskState
12 |
13 | @end
14 |
15 | @interface SThreadPoolTask ()
16 | {
17 | void (^_block)(bool (^)());
18 | SThreadPoolTaskState *_state;
19 | }
20 |
21 | @end
22 |
23 | @implementation SThreadPoolTask
24 |
25 | - (instancetype)initWithBlock:(void (^)(bool (^)()))block
26 | {
27 | self = [super init];
28 | if (self != nil)
29 | {
30 | _block = [block copy];
31 | _state = [[SThreadPoolTaskState alloc] init];
32 | }
33 | return self;
34 | }
35 |
36 | - (void)execute
37 | {
38 | if (_state->_cancelled)
39 | return;
40 |
41 | SThreadPoolTaskState *state = _state;
42 | _block(^bool
43 | {
44 | return state->_cancelled;
45 | });
46 | }
47 |
48 | - (void)cancel
49 | {
50 | _state->_cancelled = true;
51 | }
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/SSignalKit/STimer.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class SQueue;
4 |
5 | @interface STimer : NSObject
6 |
7 | - (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion queue:(SQueue *)queue;
8 | - (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion nativeQueue:(dispatch_queue_t)nativeQueue;
9 |
10 | - (void)start;
11 | - (void)invalidate;
12 | - (void)fireAndInvalidate;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/SSignalKit/STimer.m:
--------------------------------------------------------------------------------
1 | #import "STimer.h"
2 |
3 | #import "SQueue.h"
4 |
5 | @interface STimer ()
6 | {
7 | dispatch_source_t _timer;
8 | NSTimeInterval _timeout;
9 | NSTimeInterval _timeoutDate;
10 | bool _repeat;
11 | dispatch_block_t _completion;
12 | dispatch_queue_t _nativeQueue;
13 | }
14 |
15 | @end
16 |
17 | @implementation STimer
18 |
19 | - (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion queue:(SQueue *)queue {
20 | return [self initWithTimeout:timeout repeat:repeat completion:completion nativeQueue:queue._dispatch_queue];
21 | }
22 |
23 | - (id)initWithTimeout:(NSTimeInterval)timeout repeat:(bool)repeat completion:(dispatch_block_t)completion nativeQueue:(dispatch_queue_t)nativeQueue
24 | {
25 | self = [super init];
26 | if (self != nil)
27 | {
28 | _timeoutDate = INT_MAX;
29 |
30 | _timeout = timeout;
31 | _repeat = repeat;
32 | _completion = [completion copy];
33 | _nativeQueue = nativeQueue;
34 | }
35 | return self;
36 | }
37 |
38 | - (void)dealloc
39 | {
40 | if (_timer != nil)
41 | {
42 | dispatch_source_cancel(_timer);
43 | _timer = nil;
44 | }
45 | }
46 |
47 | - (void)start
48 | {
49 | _timeoutDate = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970 + _timeout;
50 |
51 | _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _nativeQueue);
52 | 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);
53 |
54 | dispatch_source_set_event_handler(_timer, ^
55 | {
56 | if (_completion)
57 | _completion();
58 | if (!_repeat)
59 | [self invalidate];
60 | });
61 | dispatch_resume(_timer);
62 | }
63 |
64 | - (void)fireAndInvalidate
65 | {
66 | if (_completion)
67 | _completion();
68 |
69 | [self invalidate];
70 | }
71 |
72 | - (void)invalidate
73 | {
74 | _timeoutDate = 0;
75 |
76 | if (_timer != nil)
77 | {
78 | dispatch_source_cancel(_timer);
79 | _timer = nil;
80 | }
81 | }
82 |
83 | @end
84 |
--------------------------------------------------------------------------------
/SSignalKit/SVariable.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class SSignal;
4 |
5 | @interface SVariable : NSObject
6 |
7 | - (instancetype)init;
8 |
9 | - (void)set:(SSignal *)signal;
10 | - (SSignal *)signal;
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/SSignalKit/SVariable.m:
--------------------------------------------------------------------------------
1 | #import "SVariable.h"
2 |
3 | #import
4 |
5 | #import "SSignal.h"
6 | #import "SBag.h"
7 | #import "SBlockDisposable.h"
8 | #import "SMetaDisposable.h"
9 |
10 | @interface SVariable ()
11 | {
12 | OSSpinLock _lock;
13 | id _value;
14 | bool _hasValue;
15 | SBag *_subscribers;
16 | SMetaDisposable *_disposable;
17 | }
18 |
19 | @end
20 |
21 | @implementation SVariable
22 |
23 | - (instancetype)init
24 | {
25 | self = [super init];
26 | if (self != nil)
27 | {
28 | _subscribers = [[SBag alloc] init];
29 | _disposable = [[SMetaDisposable alloc] init];
30 | }
31 | return self;
32 | }
33 |
34 | - (void)dealloc
35 | {
36 | [_disposable dispose];
37 | }
38 |
39 | - (SSignal *)signal
40 | {
41 | return [[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
42 | {
43 | OSSpinLockLock(&self->_lock);
44 | id currentValue = _value;
45 | bool hasValue = _hasValue;
46 | NSInteger index = [self->_subscribers addItem:[^(id value)
47 | {
48 | [subscriber putNext:value];
49 | } copy]];
50 | OSSpinLockUnlock(&self->_lock);
51 |
52 | if (hasValue)
53 | {
54 | [subscriber putNext:currentValue];
55 | }
56 |
57 | return [[SBlockDisposable alloc] initWithBlock:^
58 | {
59 | OSSpinLockLock(&self->_lock);
60 | [self->_subscribers removeItem:index];
61 | OSSpinLockUnlock(&self->_lock);
62 | }];
63 | }];
64 | }
65 |
66 | - (void)set:(SSignal *)signal
67 | {
68 | OSSpinLockLock(&_lock);
69 | _hasValue = false;
70 | OSSpinLockUnlock(&_lock);
71 |
72 | __weak SVariable *weakSelf = self;
73 | [_disposable setDisposable:[signal startWithNext:^(id next)
74 | {
75 | __strong SVariable *strongSelf = weakSelf;
76 | if (strongSelf != nil)
77 | {
78 | NSArray *subscribers = nil;
79 | OSSpinLockLock(&strongSelf->_lock);
80 | strongSelf->_value = next;
81 | strongSelf->_hasValue = true;
82 | subscribers = [strongSelf->_subscribers copyItems];
83 | OSSpinLockUnlock(&strongSelf->_lock);
84 |
85 | for (void (^subscriber)(id) in subscribers)
86 | {
87 | subscriber(next);
88 | }
89 | }
90 | }]];
91 | }
92 |
93 | @end
94 |
--------------------------------------------------------------------------------
/SSignalKitTests/DeallocatingObject.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface DeallocatingObject : NSObject
4 |
5 | - (instancetype)initWithDeallocated:(bool *)deallocated;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/SSignalKitTests/DeallocatingObject.m:
--------------------------------------------------------------------------------
1 | #import "DeallocatingObject.h"
2 |
3 | @interface DeallocatingObject ()
4 | {
5 | bool *_deallocated;
6 | }
7 |
8 | @end
9 |
10 | @implementation DeallocatingObject
11 |
12 | - (instancetype)initWithDeallocated:(bool *)deallocated
13 | {
14 | self = [super init];
15 | if (self != nil)
16 | {
17 | _deallocated = deallocated;
18 | }
19 | return self;
20 | }
21 |
22 | - (void)dealloc
23 | {
24 | *_deallocated = true;
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/SSignalKitTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SSignalKitTests/SSignalPerformanceTests.m:
--------------------------------------------------------------------------------
1 | #if __IPHONE_OS_VERSION_MIN_REQUIRED
2 | #import
3 | #else
4 | #import
5 | #endif
6 | #import
7 |
8 | @import SSignalKit;
9 |
10 | @interface SSignalPerformanceTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation SSignalPerformanceTests
15 |
16 | - (void)setUp
17 | {
18 | [super setUp];
19 | }
20 |
21 | - (void)tearDown
22 | {
23 | [super tearDown];
24 | }
25 |
26 | - (void)testMap
27 | {
28 | [self measureBlock:^
29 | {
30 | SSignal *signal = [[[SSignal alloc] initWithGenerator:^id(SSubscriber *subscriber)
31 | {
32 | [subscriber putNext:@1];
33 | [subscriber putCompletion];
34 | return nil;
35 | }] map:^id (id value)
36 | {
37 | return value;
38 | }];
39 |
40 | for (int i = 0; i < 100000; i++)
41 | {
42 | [signal startWithNext:^(__unused id next)
43 | {
44 |
45 | }];
46 | }
47 | }];
48 | }
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/SwiftSignalKit copy-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Atomic.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class Atomic {
4 | private var lock: pthread_mutex_t
5 | private var value: T
6 |
7 | public init(value: T) {
8 | self.lock = pthread_mutex_t()
9 | self.value = value
10 |
11 | pthread_mutex_init(&self.lock, nil)
12 | }
13 |
14 | deinit {
15 | pthread_mutex_destroy(&self.lock)
16 | }
17 |
18 | public func with(_ f: (T) -> R) -> R {
19 | pthread_mutex_lock(&self.lock)
20 | let result = f(self.value)
21 | pthread_mutex_unlock(&self.lock)
22 |
23 | return result
24 | }
25 |
26 | public func modify(_ f: (T) -> T) -> T {
27 | pthread_mutex_lock(&self.lock)
28 | let result = f(self.value)
29 | self.value = result
30 | pthread_mutex_unlock(&self.lock)
31 |
32 | return result
33 | }
34 |
35 | public func swap(_ value: T) -> T {
36 | pthread_mutex_lock(&self.lock)
37 | let previous = self.value
38 | self.value = value
39 | pthread_mutex_unlock(&self.lock)
40 |
41 | return previous
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Bag.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class Bag {
4 | public typealias Index = Int
5 | private var nextIndex: Index = 0
6 | private var items: [T] = []
7 | private var itemKeys: [Index] = []
8 |
9 | public init() {
10 | }
11 |
12 | public func add(_ item: T) -> Index {
13 | let key = self.nextIndex
14 | self.nextIndex += 1
15 | self.items.append(item)
16 | self.itemKeys.append(key)
17 |
18 | return key
19 | }
20 |
21 | public func get(_ index: Index) -> T? {
22 | var i = 0
23 | for key in self.itemKeys {
24 | if key == index {
25 | return self.items[i]
26 | }
27 | i += 1
28 | }
29 | return nil
30 | }
31 |
32 | public func remove(_ index: Index) {
33 | var i = 0
34 | for key in self.itemKeys {
35 | if key == index {
36 | self.items.remove(at: i)
37 | self.itemKeys.remove(at: i)
38 | break
39 | }
40 | i += 1
41 | }
42 | }
43 |
44 | public func removeAll() {
45 | self.items.removeAll()
46 | self.itemKeys.removeAll()
47 | }
48 |
49 | public func copyItems() -> [T] {
50 | return self.items
51 | }
52 |
53 | public func copyItemsWithIndices() -> [(Index, T)] {
54 | var result: [(Index, T)] = []
55 | var i = 0
56 | for key in self.itemKeys {
57 | result.append((key, self.items[i]))
58 | i += 1
59 | }
60 | return result
61 | }
62 |
63 | public var isEmpty: Bool {
64 | return self.items.isEmpty
65 | }
66 |
67 | public var first: (Index, T)? {
68 | if !self.items.isEmpty {
69 | return (self.itemKeys[0], self.items[0])
70 | } else {
71 | return nil
72 | }
73 | }
74 | }
75 |
76 | public final class CounterBag {
77 | private var nextIndex: Int = 1
78 | private var items = Set()
79 |
80 | public init() {
81 | }
82 |
83 | public func add() -> Int {
84 | let index = self.nextIndex
85 | self.nextIndex += 1
86 | self.items.insert(index)
87 | return index
88 | }
89 |
90 | public func remove(_ index: Int) {
91 | self.items.remove(index)
92 | }
93 |
94 | public var isEmpty: Bool {
95 | return self.items.isEmpty
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Disposable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public protocol Disposable: class {
4 | func dispose()
5 | }
6 |
7 | final class _EmptyDisposable: Disposable {
8 | func dispose() {
9 | }
10 | }
11 |
12 | public let EmptyDisposable: Disposable = _EmptyDisposable()
13 |
14 | public final class ActionDisposable : Disposable {
15 | private var lock = pthread_mutex_t()
16 |
17 | private var action: (() -> Void)?
18 |
19 | public init(action: @escaping() -> Void) {
20 | self.action = action
21 |
22 | pthread_mutex_init(&self.lock, nil)
23 | }
24 |
25 | deinit {
26 | var freeAction: (() -> Void)?
27 | pthread_mutex_lock(&self.lock)
28 | freeAction = self.action
29 | self.action = nil
30 | pthread_mutex_unlock(&self.lock)
31 |
32 | if let freeAction = freeAction {
33 | withExtendedLifetime(freeAction, {})
34 | }
35 |
36 | pthread_mutex_destroy(&self.lock)
37 | }
38 |
39 | public func dispose() {
40 | let disposeAction: (() -> Void)?
41 |
42 | pthread_mutex_lock(&self.lock)
43 | disposeAction = self.action
44 | self.action = nil
45 | pthread_mutex_unlock(&self.lock)
46 |
47 | disposeAction?()
48 | }
49 | }
50 |
51 | public final class MetaDisposable : Disposable {
52 | private var lock = pthread_mutex_t()
53 | private var disposed = false
54 | private var disposable: Disposable! = nil
55 |
56 | public init() {
57 | pthread_mutex_init(&self.lock, nil)
58 | }
59 |
60 | deinit {
61 | var freeDisposable: Disposable?
62 | pthread_mutex_lock(&self.lock)
63 | if let disposable = self.disposable {
64 | freeDisposable = disposable
65 | self.disposable = nil
66 | }
67 | pthread_mutex_unlock(&self.lock)
68 | if let freeDisposable = freeDisposable {
69 | withExtendedLifetime(freeDisposable, { })
70 | }
71 |
72 | pthread_mutex_destroy(&self.lock)
73 | }
74 |
75 | public func set(_ disposable: Disposable?) {
76 | var previousDisposable: Disposable! = nil
77 | var disposeImmediately = false
78 |
79 | pthread_mutex_lock(&self.lock)
80 | disposeImmediately = self.disposed
81 | if !disposeImmediately {
82 | previousDisposable = self.disposable
83 | if let disposable = disposable {
84 | self.disposable = disposable
85 | } else {
86 | self.disposable = nil
87 | }
88 | }
89 | pthread_mutex_unlock(&self.lock)
90 |
91 | if previousDisposable != nil {
92 | previousDisposable.dispose()
93 | }
94 |
95 | if disposeImmediately {
96 | if let disposable = disposable {
97 | disposable.dispose()
98 | }
99 | }
100 | }
101 |
102 | public func dispose()
103 | {
104 | var disposable: Disposable! = nil
105 |
106 | pthread_mutex_lock(&self.lock)
107 | if !self.disposed {
108 | self.disposed = true
109 | disposable = self.disposable
110 | self.disposable = nil
111 | }
112 | pthread_mutex_unlock(&self.lock)
113 |
114 | if disposable != nil {
115 | disposable.dispose()
116 | }
117 | }
118 | }
119 |
120 | public final class DisposableSet : Disposable {
121 | private var lock = pthread_mutex_t()
122 | private var disposed = false
123 | private var disposables: [Disposable] = []
124 |
125 | public init() {
126 | pthread_mutex_init(&self.lock, nil)
127 | }
128 |
129 | deinit {
130 | pthread_mutex_lock(&self.lock)
131 | self.disposables.removeAll()
132 | pthread_mutex_unlock(&self.lock)
133 |
134 | pthread_mutex_destroy(&self.lock)
135 | }
136 |
137 | public func add(_ disposable: Disposable) {
138 | var disposeImmediately = false
139 |
140 | pthread_mutex_lock(&self.lock)
141 | if self.disposed {
142 | disposeImmediately = true
143 | } else {
144 | self.disposables.append(disposable)
145 | }
146 | pthread_mutex_unlock(&self.lock)
147 |
148 | if disposeImmediately {
149 | disposable.dispose()
150 | }
151 | }
152 |
153 | public func remove(_ disposable: Disposable) {
154 | pthread_mutex_lock(&self.lock)
155 | if let index = self.disposables.index(where: { $0 === disposable }) {
156 | self.disposables.remove(at: index)
157 | }
158 | pthread_mutex_unlock(&self.lock)
159 | }
160 |
161 | public func dispose() {
162 | var disposables: [Disposable] = []
163 | pthread_mutex_lock(&self.lock)
164 | if !self.disposed {
165 | self.disposed = true
166 | disposables = self.disposables
167 | self.disposables = []
168 | }
169 | pthread_mutex_unlock(&self.lock)
170 |
171 | if disposables.count != 0 {
172 | for disposable in disposables {
173 | disposable.dispose()
174 | }
175 | }
176 | }
177 | }
178 |
179 | public final class DisposableDict : Disposable {
180 | private var lock = pthread_mutex_t()
181 | private var disposed = false
182 | private var disposables: [T: Disposable] = [:]
183 |
184 | public init() {
185 | pthread_mutex_init(&self.lock, nil)
186 | }
187 |
188 | deinit {
189 | pthread_mutex_lock(&self.lock)
190 | self.disposables.removeAll()
191 | pthread_mutex_unlock(&self.lock)
192 |
193 | pthread_mutex_destroy(&self.lock)
194 | }
195 |
196 | public func set(_ disposable: Disposable?, forKey key: T) {
197 | var disposeImmediately = false
198 | var disposePrevious: Disposable?
199 |
200 | pthread_mutex_lock(&self.lock)
201 | if self.disposed {
202 | disposeImmediately = true
203 | } else {
204 | disposePrevious = self.disposables[key]
205 | if let disposable = disposable {
206 | self.disposables[key] = disposable
207 | }
208 | }
209 | pthread_mutex_unlock(&self.lock)
210 |
211 | if disposeImmediately {
212 | disposable?.dispose()
213 | }
214 | disposePrevious?.dispose()
215 | }
216 |
217 | public func dispose() {
218 | var disposables: [T: Disposable] = [:]
219 | pthread_mutex_lock(&self.lock)
220 | if !self.disposed {
221 | self.disposed = true
222 | disposables = self.disposables
223 | self.disposables = [:]
224 | }
225 | pthread_mutex_unlock(&self.lock)
226 |
227 | if disposables.count != 0 {
228 | for disposable in disposables.values {
229 | disposable.dispose()
230 | }
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Lock.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class Lock {
4 | private var mutex = pthread_mutex_t()
5 |
6 | public init() {
7 | pthread_mutex_init(&self.mutex, nil)
8 | }
9 |
10 | deinit {
11 | pthread_mutex_destroy(&self.mutex)
12 | }
13 |
14 | public func locked(_ f: () -> ()) {
15 | pthread_mutex_lock(&self.mutex)
16 | f()
17 | pthread_mutex_unlock(&self.mutex)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Multicast.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | private final class MulticastInstance {
4 | let disposable: Disposable
5 | var subscribers = Bag<(T) -> Void>()
6 | var lock = Lock()
7 |
8 | init(disposable: Disposable) {
9 | self.disposable = disposable
10 | }
11 | }
12 |
13 | public final class Multicast {
14 | private let lock = Lock()
15 | private var instances: [String: MulticastInstance] = [:]
16 |
17 | public init() {
18 | }
19 |
20 | public func get(key: String, signal: Signal) -> Signal {
21 | return Signal { subscriber in
22 | var instance: MulticastInstance!
23 | var beginDisposable: MetaDisposable?
24 | self.lock.locked {
25 | if let existing = self.instances[key] {
26 | instance = existing
27 | } else {
28 | let disposable = MetaDisposable()
29 | instance = MulticastInstance(disposable: disposable)
30 | beginDisposable = disposable
31 | }
32 | }
33 |
34 | var index: Bag<(T) -> Void>.Index!
35 | instance.lock.locked {
36 | index = instance.subscribers.add({ next in
37 | subscriber.putNext(next)
38 | })
39 | }
40 |
41 | if let beginDisposable = beginDisposable {
42 | beginDisposable.set(signal.start(next: { next in
43 | var subscribers: [(T) -> Void]!
44 | instance.lock.locked {
45 | subscribers = instance.subscribers.copyItems()
46 | }
47 | for subscriber in subscribers {
48 | subscriber(next)
49 | }
50 | }, error: { _ in
51 | self.lock.locked {
52 | let _ = self.instances.removeValue(forKey: key)
53 | }
54 | }, completed: {
55 | self.lock.locked {
56 | self.instances.removeValue(forKey: key)
57 | }
58 | }))
59 | }
60 |
61 | return ActionDisposable {
62 | var remove = false
63 | instance.lock.locked {
64 | instance.subscribers.remove(index)
65 | if instance.subscribers.isEmpty {
66 | remove = true
67 | }
68 | }
69 |
70 | if remove {
71 | self.lock.locked {
72 | let _ = self.instances.removeValue(forKey: key)
73 | }
74 | }
75 | }
76 | }
77 | }
78 | }
79 |
80 | public final class MulticastPromise {
81 | public let subscribers = Bag<(T) -> Void>()
82 | public let lock = Lock()
83 | public var value: T?
84 |
85 | public init() {
86 |
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Promise.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class Promise {
4 | private var initializeOnFirstAccess: Signal?
5 | private var value: T?
6 | private var lock = pthread_mutex_t()
7 | private let disposable = MetaDisposable()
8 | private let subscribers = Bag<(T) -> Void>()
9 |
10 | public var onDeinit: (() -> Void)?
11 |
12 | public init(initializeOnFirstAccess: Signal?) {
13 | self.initializeOnFirstAccess = initializeOnFirstAccess
14 | pthread_mutex_init(&self.lock, nil)
15 | }
16 |
17 | public init(_ value: T) {
18 | self.value = value
19 | pthread_mutex_init(&self.lock, nil)
20 | }
21 |
22 | public init() {
23 | pthread_mutex_init(&self.lock, nil)
24 | }
25 |
26 | deinit {
27 | self.onDeinit?()
28 | pthread_mutex_destroy(&self.lock)
29 | self.disposable.dispose()
30 | }
31 |
32 | public func set(_ signal: Signal) {
33 | pthread_mutex_lock(&self.lock)
34 | self.value = nil
35 | pthread_mutex_unlock(&self.lock)
36 |
37 | self.disposable.set(signal.start(next: { [weak self] next in
38 | if let strongSelf = self {
39 | pthread_mutex_lock(&strongSelf.lock)
40 | strongSelf.value = next
41 | let subscribers = strongSelf.subscribers.copyItems()
42 | pthread_mutex_unlock(&strongSelf.lock)
43 |
44 | for subscriber in subscribers {
45 | subscriber(next)
46 | }
47 | }
48 | }))
49 | }
50 |
51 | public func get() -> Signal {
52 | return Signal { subscriber in
53 | pthread_mutex_lock(&self.lock)
54 | var initializeOnFirstAccessNow: Signal?
55 | if let initializeOnFirstAccess = self.initializeOnFirstAccess {
56 | initializeOnFirstAccessNow = initializeOnFirstAccess
57 | self.initializeOnFirstAccess = nil
58 | }
59 | let currentValue = self.value
60 | let index = self.subscribers.add({ next in
61 | subscriber.putNext(next)
62 | })
63 | pthread_mutex_unlock(&self.lock)
64 |
65 | if let currentValue = currentValue {
66 | subscriber.putNext(currentValue)
67 | }
68 |
69 | if let initializeOnFirstAccessNow = initializeOnFirstAccessNow {
70 | self.set(initializeOnFirstAccessNow)
71 | }
72 |
73 | return ActionDisposable {
74 | pthread_mutex_lock(&self.lock)
75 | self.subscribers.remove(index)
76 | pthread_mutex_unlock(&self.lock)
77 | }
78 | }
79 | }
80 | }
81 |
82 | public final class ValuePromise {
83 | private var value: T?
84 | private var lock = pthread_mutex_t()
85 | private let subscribers = Bag<(T) -> Void>()
86 | public let ignoreRepeated: Bool
87 |
88 | public init(_ value: T, ignoreRepeated: Bool = false) {
89 | self.value = value
90 | self.ignoreRepeated = ignoreRepeated
91 | pthread_mutex_init(&self.lock, nil)
92 | }
93 |
94 | public init(ignoreRepeated: Bool = false) {
95 | self.ignoreRepeated = ignoreRepeated
96 | pthread_mutex_init(&self.lock, nil)
97 | }
98 |
99 | deinit {
100 | pthread_mutex_destroy(&self.lock)
101 | }
102 |
103 | public func set(_ value: T) {
104 | pthread_mutex_lock(&self.lock)
105 | let subscribers: [(T) -> Void]
106 | if !self.ignoreRepeated || self.value != value {
107 | self.value = value
108 | subscribers = self.subscribers.copyItems()
109 | } else {
110 | subscribers = []
111 | }
112 | pthread_mutex_unlock(&self.lock);
113 |
114 | for subscriber in subscribers {
115 | subscriber(value)
116 | }
117 | }
118 |
119 | public func get() -> Signal {
120 | return Signal { subscriber in
121 | pthread_mutex_lock(&self.lock)
122 | let currentValue = self.value
123 | let index = self.subscribers.add({ next in
124 | subscriber.putNext(next)
125 | })
126 | pthread_mutex_unlock(&self.lock)
127 |
128 | if let currentValue = currentValue {
129 | subscriber.putNext(currentValue)
130 | }
131 |
132 | return ActionDisposable {
133 | pthread_mutex_lock(&self.lock)
134 | self.subscribers.remove(index)
135 | pthread_mutex_unlock(&self.lock)
136 | }
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Queue.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | private let QueueSpecificKey = DispatchSpecificKey()
4 |
5 | private let globalMainQueue = Queue(queue: DispatchQueue.main, specialIsMainQueue: true)
6 | private let globalDefaultQueue = Queue(queue: DispatchQueue.global(qos: .default), specialIsMainQueue: false)
7 | private let globalBackgroundQueue = Queue(queue: DispatchQueue.global(qos: .background), specialIsMainQueue: false)
8 |
9 | public final class Queue {
10 | private let nativeQueue: DispatchQueue
11 | private var specific = NSObject()
12 | private let specialIsMainQueue: Bool
13 |
14 | public var queue: DispatchQueue {
15 | get {
16 | return self.nativeQueue
17 | }
18 | }
19 |
20 | public class func mainQueue() -> Queue {
21 | return globalMainQueue
22 | }
23 |
24 | public class func concurrentDefaultQueue() -> Queue {
25 | return globalDefaultQueue
26 | }
27 |
28 | public class func concurrentBackgroundQueue() -> Queue {
29 | return globalBackgroundQueue
30 | }
31 |
32 | public init(queue: DispatchQueue) {
33 | self.nativeQueue = queue
34 | self.specialIsMainQueue = false
35 | }
36 |
37 | fileprivate init(queue: DispatchQueue, specialIsMainQueue: Bool) {
38 | self.nativeQueue = queue
39 | self.specialIsMainQueue = specialIsMainQueue
40 | }
41 |
42 | public init(name: String? = nil, qos: DispatchQoS = .default) {
43 | self.nativeQueue = DispatchQueue(label: name ?? "", qos: qos)
44 |
45 | self.specialIsMainQueue = false
46 |
47 | self.nativeQueue.setSpecific(key: QueueSpecificKey, value: self.specific)
48 | }
49 |
50 | public func isCurrent() -> Bool {
51 | if DispatchQueue.getSpecific(key: QueueSpecificKey) === self.specific {
52 | return true
53 | } else if self.specialIsMainQueue && Thread.isMainThread {
54 | return true
55 | } else {
56 | return false
57 | }
58 | }
59 |
60 | public func async(_ f: @escaping () -> Void) {
61 | if self.isCurrent() {
62 | f()
63 | } else {
64 | self.nativeQueue.async(execute: f)
65 | }
66 | }
67 |
68 | public func sync(_ f: () -> Void) {
69 | if self.isCurrent() {
70 | f()
71 | } else {
72 | self.nativeQueue.sync(execute: f)
73 | }
74 | }
75 |
76 | public func justDispatch(_ f: @escaping () -> Void) {
77 | self.nativeQueue.async(execute: f)
78 | }
79 |
80 | public func justDispatchWithQoS(qos: DispatchQoS, _ f: @escaping () -> Void) {
81 | self.nativeQueue.async(group: nil, qos: qos, flags: [.enforceQoS], execute: f)
82 | }
83 |
84 | public func after(_ delay: Double, _ f: @escaping() -> Void) {
85 | let time: DispatchTime = DispatchTime.now() + delay
86 | self.nativeQueue.asyncAfter(deadline: time, execute: f)
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/SwiftSignalKit/QueueLocalObject.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class QueueLocalObject {
4 | private let queue: Queue
5 | private var valueRef: Unmanaged?
6 |
7 | public init(queue: Queue, generate: @escaping () -> T) {
8 | self.queue = queue
9 |
10 | self.queue.async {
11 | let value = generate()
12 | self.valueRef = Unmanaged.passRetained(value)
13 | }
14 | }
15 |
16 | deinit {
17 | let valueRef = self.valueRef
18 | self.queue.async {
19 | valueRef?.release()
20 | }
21 | }
22 |
23 | public func with(_ f: @escaping (T) -> Void) {
24 | self.queue.async {
25 | if let valueRef = self.valueRef {
26 | let value = valueRef.takeUnretainedValue()
27 | f(value)
28 | }
29 | }
30 | }
31 |
32 | public func syncWith(_ f: @escaping (T) -> R) -> R? {
33 | var result: R?
34 | self.queue.sync {
35 | if let valueRef = self.valueRef {
36 | let value = valueRef.takeUnretainedValue()
37 | result = f(value)
38 | }
39 | }
40 | return result
41 | }
42 |
43 | public func signalWith(_ f: @escaping (T, Subscriber) -> Disposable) -> Signal {
44 | return Signal { [weak self] subscriber in
45 | if let strongSelf = self, let valueRef = strongSelf.valueRef {
46 | let value = valueRef.takeUnretainedValue()
47 | return f(value, subscriber)
48 | } else {
49 | return EmptyDisposable
50 | }
51 | } |> runOn(self.queue)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Signal.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | let doNothing: () -> Void = { }
4 |
5 | public enum NoValue {
6 | }
7 |
8 | public enum NoError {
9 | }
10 |
11 | public func identity(a: A) -> A {
12 | return a
13 | }
14 |
15 | precedencegroup PipeRight {
16 | associativity: left
17 | higherThan: DefaultPrecedence
18 | }
19 |
20 | infix operator |> : PipeRight
21 |
22 | public func |> (value: T, function: ((T) -> U)) -> U {
23 | return function(value)
24 | }
25 |
26 | private final class SubscriberDisposable : Disposable {
27 | private let subscriber: Subscriber
28 | private let disposable: Disposable
29 |
30 | init(subscriber: Subscriber, disposable: Disposable) {
31 | self.subscriber = subscriber
32 | self.disposable = disposable
33 | }
34 |
35 | func dispose() {
36 | subscriber.markTerminatedWithoutDisposal()
37 | disposable.dispose()
38 | }
39 | }
40 |
41 | public struct Signal {
42 | private let generator: (Subscriber) -> Disposable
43 |
44 | public init(_ generator: @escaping(Subscriber) -> Disposable) {
45 | self.generator = generator
46 | }
47 |
48 | public func start(next: ((T) -> Void)! = nil, error: ((E) -> Void)! = nil, completed: (() -> Void)! = nil) -> Disposable {
49 | let subscriber = Subscriber(next: next, error: error, completed: completed)
50 | let disposable = self.generator(subscriber)
51 | subscriber.assignDisposable(disposable)
52 | return SubscriberDisposable(subscriber: subscriber, disposable: disposable)
53 | }
54 |
55 | public static func single(_ value: T) -> Signal {
56 | return Signal { subscriber in
57 | subscriber.putNext(value)
58 | subscriber.putCompletion()
59 |
60 | return EmptyDisposable
61 | }
62 | }
63 |
64 | public static func complete() -> Signal {
65 | return Signal { subscriber in
66 | subscriber.putCompletion()
67 |
68 | return EmptyDisposable
69 | }
70 | }
71 |
72 | public static func fail(_ error: E) -> Signal {
73 | return Signal { subscriber in
74 | subscriber.putError(error)
75 |
76 | return EmptyDisposable
77 | }
78 | }
79 |
80 | public static func never() -> Signal {
81 | return Signal { _ in
82 | return EmptyDisposable
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Signal_Catch.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public func `catch`(_ f: @escaping(E) -> Signal) -> (Signal) -> Signal {
4 | return { signal in
5 | return Signal { subscriber in
6 | let disposable = DisposableSet()
7 |
8 | disposable.add(signal.start(next: { next in
9 | subscriber.putNext(next)
10 | }, error: { error in
11 | let anotherSignal = f(error)
12 |
13 | disposable.add(anotherSignal.start(next: { next in
14 | subscriber.putNext(next)
15 | }, error: { error in
16 | subscriber.putError(error)
17 | }, completed: {
18 | subscriber.putCompletion()
19 | }))
20 | }, completed: {
21 | subscriber.putCompletion()
22 | }))
23 |
24 | return disposable
25 | }
26 | }
27 | }
28 |
29 | private func recursiveFunction(_ f: @escaping(@escaping() -> Void) -> Void) -> (() -> Void) {
30 | return {
31 | f(recursiveFunction(f))
32 | }
33 | }
34 |
35 | public func restart(_ signal: Signal) -> Signal {
36 | return Signal { subscriber in
37 | let shouldRestart = Atomic(value: true)
38 | let currentDisposable = MetaDisposable()
39 |
40 | let start = recursiveFunction { recurse in
41 | let currentShouldRestart = shouldRestart.with { value in
42 | return value
43 | }
44 | if currentShouldRestart {
45 | let disposable = signal.start(next: { next in
46 | subscriber.putNext(next)
47 | }, error: { error in
48 | subscriber.putError(error)
49 | }, completed: {
50 | recurse()
51 | })
52 | currentDisposable.set(disposable)
53 | }
54 | }
55 |
56 | start()
57 |
58 | return ActionDisposable {
59 | currentDisposable.dispose()
60 | let _ = shouldRestart.swap(false)
61 | }
62 | }
63 | }
64 |
65 | public func recurse(_ latestValue: T?) -> (Signal) -> Signal {
66 | return { signal in
67 | return Signal { subscriber in
68 | let shouldRestart = Atomic(value: true)
69 | let currentDisposable = MetaDisposable()
70 |
71 | let start = recursiveFunction { recurse in
72 | let currentShouldRestart = shouldRestart.with { value in
73 | return value
74 | }
75 | if currentShouldRestart {
76 | let disposable = signal.start(next: { next in
77 | subscriber.putNext(next)
78 | }, error: { error in
79 | subscriber.putError(error)
80 | }, completed: {
81 | recurse()
82 | })
83 | currentDisposable.set(disposable)
84 | }
85 | }
86 |
87 | start()
88 |
89 | return ActionDisposable {
90 | currentDisposable.dispose()
91 | let _ = shouldRestart.swap(false)
92 | }
93 | }
94 | }
95 | }
96 |
97 | public func retry(_ delayIncrement: Double, maxDelay: Double, onQueue queue: Queue) -> (_ signal: Signal) -> Signal {
98 | return { signal in
99 | return Signal { subscriber in
100 | let shouldRetry = Atomic(value: true)
101 | let currentDelay = Atomic(value: 0.0)
102 | let currentDisposable = MetaDisposable()
103 |
104 | let start = recursiveFunction { recurse in
105 | let currentShouldRetry = shouldRetry.with { value in
106 | return value
107 | }
108 | if currentShouldRetry {
109 | let disposable = signal.start(next: { next in
110 | subscriber.putNext(next)
111 | }, error: { error in
112 | let delay = currentDelay.modify { value in
113 | return min(maxDelay, value + delayIncrement)
114 | }
115 |
116 | let time: DispatchTime = DispatchTime.now() + Double(delay)
117 | queue.queue.asyncAfter(deadline: time, execute: {
118 | recurse()
119 | })
120 | }, completed: {
121 | let _ = shouldRetry.swap(false)
122 | subscriber.putCompletion()
123 | })
124 | currentDisposable.set(disposable)
125 | }
126 | }
127 |
128 | start()
129 |
130 | return ActionDisposable {
131 | currentDisposable.dispose()
132 | let _ = shouldRetry.swap(false)
133 | }
134 | }
135 | }
136 | }
137 |
138 | public func restartIfError(_ signal: Signal) -> Signal {
139 | return Signal { subscriber in
140 | let shouldRetry = Atomic(value: true)
141 | let currentDisposable = MetaDisposable()
142 |
143 | let start = recursiveFunction { recurse in
144 | let currentShouldRetry = shouldRetry.with { value in
145 | return value
146 | }
147 | if currentShouldRetry {
148 | let disposable = signal.start(next: { next in
149 | subscriber.putNext(next)
150 | }, error: { error in
151 | recurse()
152 | }, completed: {
153 | let _ = shouldRetry.swap(false)
154 | subscriber.putCompletion()
155 | })
156 | currentDisposable.set(disposable)
157 | }
158 | }
159 |
160 | start()
161 |
162 | return ActionDisposable {
163 | currentDisposable.dispose()
164 | let _ = shouldRetry.swap(false)
165 | }
166 | }
167 | }
168 |
169 |
--------------------------------------------------------------------------------
/SwiftSignalKit/Signal_Combine.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | private struct SignalCombineState {
4 | let values: [Int : Any]
5 | let completed: Set
6 | let error: Bool
7 | }
8 |
9 | private func combineLatestAny(_ signals: [Signal], combine: @escaping([Any]) -> R, initialValues: [Int : Any], queue: Queue?) -> Signal {
10 | return Signal { subscriber in
11 | let state = Atomic(value: SignalCombineState(values: initialValues, completed: Set(), error: false))
12 | let disposable = DisposableSet()
13 |
14 | if initialValues.count == signals.count {
15 | var values: [Any] = []
16 | for i in 0 ..< initialValues.count {
17 | values.append(initialValues[i]!)
18 | }
19 | subscriber.putNext(combine(values))
20 | }
21 |
22 | let count = signals.count
23 | for iterationIndex in 0 ..< count {
24 | let index = iterationIndex
25 | var signal = signals[index]
26 | if let queue = queue {
27 | signal = signal
28 | |> deliverOn(queue)
29 | }
30 | let signalDisposable = signal.start(next: { next in
31 | let currentState = state.modify { current in
32 | var values = current.values
33 | values[index] = next
34 | return SignalCombineState(values: values, completed: current.completed, error: current.error)
35 | }
36 | if currentState.values.count == count {
37 | var values: [Any] = []
38 | for i in 0 ..< count {
39 | values.append(currentState.values[i]!)
40 | }
41 | subscriber.putNext(combine(values))
42 | }
43 | }, error: { error in
44 | var emitError = false
45 | let _ = state.modify { current in
46 | if !current.error {
47 | emitError = true
48 | return SignalCombineState(values: current.values, completed: current.completed, error: true)
49 | } else {
50 | return current
51 | }
52 | }
53 | if emitError {
54 | subscriber.putError(error)
55 | }
56 | }, completed: {
57 | var emitCompleted = false
58 | let _ = state.modify { current in
59 | if !current.completed.contains(index) {
60 | var completed = current.completed
61 | completed.insert(index)
62 | emitCompleted = completed.count == count
63 | return SignalCombineState(values: current.values, completed: completed, error: current.error)
64 | }
65 | return current
66 | }
67 | if emitCompleted {
68 | subscriber.putCompletion()
69 | }
70 | })
71 |
72 | disposable.add(signalDisposable)
73 | }
74 |
75 | return disposable;
76 | }
77 | }
78 |
79 | private func signalOfAny(_ signal: Signal) -> Signal {
80 | return Signal { subscriber in
81 | return signal.start(next: { next in
82 | subscriber.putNext(next)
83 | }, error: { error in
84 | subscriber.putError(error)
85 | }, completed: {
86 | subscriber.putCompletion()
87 | })
88 | }
89 | }
90 |
91 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal) -> Signal<(T1, T2), E> {
92 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], combine: { values in
93 | return (values[0] as! T1, values[1] as! T2)
94 | }, initialValues: [:], queue: queue)
95 | }
96 |
97 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ v1: T1, _ s2: Signal, _ v2: T2) -> Signal<(T1, T2), E> {
98 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], combine: { values in
99 | return (values[0] as! T1, values[1] as! T2)
100 | }, initialValues: [0: v1, 1: v2], queue: queue)
101 | }
102 |
103 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal) -> Signal<(T1, T2, T3), E> {
104 | return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3)], combine: { values in
105 | return (values[0] as! T1, values[1] as! T2, values[2] as! T3)
106 | }, initialValues: [:], queue: queue)
107 | }
108 |
109 | public func combineLatest(queue: Queue? = nil, _ s1: Signal, _ s2: Signal, _ s3: Signal, _ s4: Signal