mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-25 17:43:18 +00:00
Swift version
This commit is contained in:
parent
babd3ca900
commit
0db026e823
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
#import "SDisposable.h"
|
#import <SSignalKit/SDisposable.h>
|
||||||
|
|
||||||
@interface SBlockDisposable : NSObject <SDisposable>
|
@interface SBlockDisposable : NSObject <SDisposable>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SDisposable.h"
|
#import <SSignalKit/SDisposable.h>
|
||||||
|
|
||||||
@interface SDisposableSet : NSObject <SDisposable>
|
@interface SDisposableSet : NSObject <SDisposable>
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
SEventTypeNext,
|
|
||||||
SEventTypeError,
|
|
||||||
SEventTypeCompleted
|
|
||||||
} SEventType;
|
|
||||||
|
|
||||||
@interface SEvent : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, readonly) SEventType type;
|
|
||||||
@property (nonatomic, strong, readonly) id data;
|
|
||||||
|
|
||||||
- (instancetype)initWithNext:(id)next;
|
|
||||||
- (instancetype)initWithError:(id)error;
|
|
||||||
- (instancetype)initWithCompleted;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
#import "SEvent.h"
|
|
||||||
|
|
||||||
@implementation SEvent
|
|
||||||
|
|
||||||
- (instancetype)initWithNext:(id)next
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
_type = SEventTypeNext;
|
|
||||||
_data = next;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithError:(id)error
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
_type = SEventTypeError;
|
|
||||||
_data = error;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithCompleted
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self != nil)
|
|
||||||
{
|
|
||||||
_type = SEventTypeCompleted;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SDisposable.h"
|
#import <SSignalKit/SDisposable.h>
|
||||||
|
|
||||||
@interface SMetaDisposable : NSObject <SDisposable>
|
@interface SMetaDisposable : NSObject <SDisposable>
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@interface SMulticastSignalManager : NSObject
|
@interface SMulticastSignalManager : NSObject
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@interface SSignal (Accumulate)
|
@interface SSignal (Accumulate)
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@interface SSignal (Catch)
|
@interface SSignal (Catch)
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@interface SSignal (Combine)
|
@interface SSignal (Combine)
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
#import "SQueue.h"
|
#import <SSignalKit/SQueue.h>
|
||||||
#import "SThreadPool.h"
|
#import <SSignalKit/SThreadPool.h>
|
||||||
|
|
||||||
@interface SSignal (Dispatch)
|
@interface SSignal (Dispatch)
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@interface SSignal (Mapping)
|
@interface SSignal (Mapping)
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@class SQueue;
|
@class SQueue;
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
NSMutableArray *_queuedSignals;
|
NSMutableArray *_queuedSignals;
|
||||||
bool _queueMode;
|
bool _queueMode;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@interface SSignal (Multicast)
|
@interface SSignal (Multicast)
|
||||||
|
|
||||||
|
|||||||
@ -80,30 +80,45 @@ typedef enum {
|
|||||||
[currentDisposable dispose];
|
[currentDisposable dispose];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)notify:(SEvent *)event
|
- (void)notifyNext:(id)next
|
||||||
|
{
|
||||||
|
NSArray *currentSubscribers = nil;
|
||||||
|
OSSpinLockLock(&_lock);
|
||||||
|
currentSubscribers = [_subscribers copyItems];
|
||||||
|
OSSpinLockUnlock(&_lock);
|
||||||
|
|
||||||
|
for (SSubscriber *subscriber in currentSubscribers)
|
||||||
|
{
|
||||||
|
[subscriber putNext:next];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)notifyError:(id)error
|
||||||
{
|
{
|
||||||
NSArray *currentSubscribers = nil;
|
NSArray *currentSubscribers = nil;
|
||||||
OSSpinLockLock(&_lock);
|
OSSpinLockLock(&_lock);
|
||||||
currentSubscribers = [_subscribers copyItems];
|
currentSubscribers = [_subscribers copyItems];
|
||||||
if (event.type != SEventTypeNext)
|
|
||||||
_state = SSignalMulticastStateCompleted;
|
_state = SSignalMulticastStateCompleted;
|
||||||
OSSpinLockUnlock(&_lock);
|
OSSpinLockUnlock(&_lock);
|
||||||
|
|
||||||
for (SSubscriber *subscriber in currentSubscribers)
|
for (SSubscriber *subscriber in currentSubscribers)
|
||||||
{
|
{
|
||||||
switch (event.type)
|
[subscriber putError:error];
|
||||||
{
|
|
||||||
case SEventTypeNext:
|
|
||||||
[subscriber putNext:event.data];
|
|
||||||
break;
|
|
||||||
case SEventTypeError:
|
|
||||||
[subscriber putError:event.data];
|
|
||||||
break;
|
|
||||||
case SEventTypeCompleted:
|
|
||||||
[subscriber putCompletion];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)notifyCompleted
|
||||||
|
{
|
||||||
|
NSArray *currentSubscribers = nil;
|
||||||
|
OSSpinLockLock(&_lock);
|
||||||
|
currentSubscribers = [_subscribers copyItems];
|
||||||
|
_state = SSignalMulticastStateCompleted;
|
||||||
|
OSSpinLockUnlock(&_lock);
|
||||||
|
|
||||||
|
for (SSubscriber *subscriber in currentSubscribers)
|
||||||
|
{
|
||||||
|
[subscriber putCompletion];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@ -121,13 +136,13 @@ typedef enum {
|
|||||||
{
|
{
|
||||||
id<SDisposable> disposable = [self startWithNext:^(id next)
|
id<SDisposable> disposable = [self startWithNext:^(id next)
|
||||||
{
|
{
|
||||||
[subscribers notify:[[SEvent alloc] initWithNext:next]];
|
[subscribers notifyNext:next];
|
||||||
} error:^(id error)
|
} error:^(id error)
|
||||||
{
|
{
|
||||||
[subscribers notify:[[SEvent alloc] initWithError:error]];
|
[subscribers notifyError:error];
|
||||||
} completed:^
|
} completed:^
|
||||||
{
|
{
|
||||||
[subscribers notify:[[SEvent alloc] initWithCompleted]];
|
[subscribers notifyCompleted];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[subscribers setDisposable:[[SBlockDisposable alloc] initWithBlock:^
|
[subscribers setDisposable:[[SBlockDisposable alloc] initWithBlock:^
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@interface SSignal (SideEffects)
|
@interface SSignal (SideEffects)
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
@interface SSignal (Single)
|
@interface SSignal (Single)
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#import "SSignal.h"
|
#import <SSignalKit/SSignal.h>
|
||||||
|
|
||||||
#import "SQueue.h"
|
#import <SSignalKit/SQueue.h>
|
||||||
|
|
||||||
@interface SSignal (Timing)
|
@interface SSignal (Timing)
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#import "SSubscriber.h"
|
#import <SSignalKit/SSubscriber.h>
|
||||||
|
|
||||||
@interface SSignal : NSObject
|
@interface SSignal : NSObject
|
||||||
{
|
{
|
||||||
|
|||||||
@ -19,7 +19,6 @@ FOUNDATION_EXPORT const unsigned char SSignalKitVersionString[];
|
|||||||
#import <SSignalKit/SAtomic.h>
|
#import <SSignalKit/SAtomic.h>
|
||||||
#import <SSignalKit/SBag.h>
|
#import <SSignalKit/SBag.h>
|
||||||
#import <SSignalKit/SSignal.h>
|
#import <SSignalKit/SSignal.h>
|
||||||
#import <SSignalKit/SEvent.h>
|
|
||||||
#import <SSignalKit/SSubscriber.h>
|
#import <SSignalKit/SSubscriber.h>
|
||||||
#import <SSignalKit/SDisposable.h>
|
#import <SSignalKit/SDisposable.h>
|
||||||
#import <SSignalKit/SDisposableSet.h>
|
#import <SSignalKit/SDisposableSet.h>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
#import "SDisposable.h"
|
#import <SSignalKit/SDisposable.h>
|
||||||
#import "SEvent.h"
|
|
||||||
|
|
||||||
@interface SSubscriber : NSObject <SDisposable>
|
@interface SSubscriber : NSObject <SDisposable>
|
||||||
{
|
{
|
||||||
|
|||||||
36
SwiftSignalKit/Atomic.swift
Normal file
36
SwiftSignalKit/Atomic.swift
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Atomic<T> {
|
||||||
|
private var lock: OSSpinLock = 0
|
||||||
|
private var value: T
|
||||||
|
|
||||||
|
public init(value: T) {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
public func with<R>(f: T -> R) -> R {
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
let result = f(self.value)
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func modify(f: T -> T) -> T {
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
let result = f(self.value)
|
||||||
|
self.value = result
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func swap(value: T) -> T {
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
let previous = self.value
|
||||||
|
self.value = value
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
return previous
|
||||||
|
}
|
||||||
|
}
|
||||||
33
SwiftSignalKit/Bag.swift
Normal file
33
SwiftSignalKit/Bag.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Bag<T> {
|
||||||
|
public typealias Index = Int
|
||||||
|
private var nextIndex: Index = 0
|
||||||
|
private var items: [T] = []
|
||||||
|
private var itemKeys: [Index] = []
|
||||||
|
|
||||||
|
public func add(item: T) -> Index {
|
||||||
|
let key = self.nextIndex
|
||||||
|
self.nextIndex++
|
||||||
|
self.items.append(item)
|
||||||
|
self.itemKeys.append(key)
|
||||||
|
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
public func remove(index: Index) {
|
||||||
|
var i = 0
|
||||||
|
for key in self.itemKeys {
|
||||||
|
if key == index {
|
||||||
|
self.items.removeAtIndex(i)
|
||||||
|
self.itemKeys.removeAtIndex(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func copyItems() -> [T] {
|
||||||
|
return self.items
|
||||||
|
}
|
||||||
|
}
|
||||||
129
SwiftSignalKit/Disposable.swift
Normal file
129
SwiftSignalKit/Disposable.swift
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol Disposable
|
||||||
|
{
|
||||||
|
func dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct _EmptyDisposable : Disposable {
|
||||||
|
internal func dispose() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public let EmptyDisposable: Disposable = _EmptyDisposable()
|
||||||
|
|
||||||
|
public final class ActionDisposable : Disposable
|
||||||
|
{
|
||||||
|
private var action: () -> Void
|
||||||
|
private var lock: OSSpinLock = 0
|
||||||
|
|
||||||
|
public init(action: () -> Void) {
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
var action = doNothing
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
action = self.action
|
||||||
|
self.action = doNothing
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class MetaDisposable : Disposable
|
||||||
|
{
|
||||||
|
private var lock: OSSpinLock = 0
|
||||||
|
private var disposed = false
|
||||||
|
private var disposable: Disposable! = nil
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func set(disposable: Disposable?) {
|
||||||
|
var previousDisposable: Disposable! = nil
|
||||||
|
var disposeImmediately = false
|
||||||
|
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
disposeImmediately = self.disposed
|
||||||
|
if !disposeImmediately {
|
||||||
|
previousDisposable = self.disposable
|
||||||
|
if let disposable = disposable {
|
||||||
|
self.disposable = disposable
|
||||||
|
} else {
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if previousDisposable != nil {
|
||||||
|
previousDisposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
if disposeImmediately {
|
||||||
|
if let disposable = disposable {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose()
|
||||||
|
{
|
||||||
|
var disposable: Disposable! = nil
|
||||||
|
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
if !self.disposed {
|
||||||
|
self.disposed = true
|
||||||
|
disposable = self.disposable
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if disposable != nil {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class DisposableSet : Disposable {
|
||||||
|
private var lock: OSSpinLock = 0
|
||||||
|
private var disposed = false
|
||||||
|
private var disposables: [Disposable] = []
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public func add(disposable: Disposable) {
|
||||||
|
var disposeImmediately = false
|
||||||
|
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
if self.disposed {
|
||||||
|
disposeImmediately = true
|
||||||
|
} else {
|
||||||
|
self.disposables.append(disposable)
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if disposeImmediately {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
var disposables: [Disposable] = []
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
if !self.disposed {
|
||||||
|
self.disposed = true
|
||||||
|
disposables = self.disposables
|
||||||
|
self.disposables = []
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if disposables.count != 0 {
|
||||||
|
for disposable in disposables {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
SwiftSignalKit/Info.plist
Normal file
26
SwiftSignalKit/Info.plist
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.telegram.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string></string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
40
SwiftSignalKit/Pipe.swift
Normal file
40
SwiftSignalKit/Pipe.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Pipe<T> {
|
||||||
|
let subscribers = Atomic(value: Bag<T -> Void>())
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public func signal() -> Signal<T, Void> {
|
||||||
|
return Signal { [weak self] subscriber in
|
||||||
|
if let strongSelf = self {
|
||||||
|
var index = strongSelf.subscribers.with { value -> Bag<T>.Index in
|
||||||
|
return value.add { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ActionDisposable { [weak strongSelf] in
|
||||||
|
if let strongSelf = strongSelf {
|
||||||
|
strongSelf.subscribers.with { value -> Void in
|
||||||
|
value.remove(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func putNext(next: T) {
|
||||||
|
let items = self.subscribers.with { value -> [T -> Void] in
|
||||||
|
return value.copyItems()
|
||||||
|
}
|
||||||
|
for f in items {
|
||||||
|
f(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
SwiftSignalKit/Queue.swift
Normal file
69
SwiftSignalKit/Queue.swift
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
private let _QueueSpecificKey = NSObject()
|
||||||
|
private let QueueSpecificKey: UnsafePointer<Void> = UnsafePointer<Void>(Unmanaged<AnyObject>.passUnretained(_QueueSpecificKey).toOpaque())
|
||||||
|
|
||||||
|
public final class Queue {
|
||||||
|
private let nativeQueue: dispatch_queue_t
|
||||||
|
private var specific: UnsafeMutablePointer<Void>
|
||||||
|
private let specialIsMainQueue: Bool
|
||||||
|
|
||||||
|
public var queue: dispatch_queue_t {
|
||||||
|
get {
|
||||||
|
return self.nativeQueue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class func mainQueue() -> Queue {
|
||||||
|
return Queue(queue: dispatch_get_main_queue(), specialIsMainQueue: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class func concurrentDefaultQueue() -> Queue {
|
||||||
|
return Queue(queue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), specialIsMainQueue: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
public class func concurrentBackgroundQueue() -> Queue {
|
||||||
|
return Queue(queue: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), specialIsMainQueue: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(queue: dispatch_queue_t) {
|
||||||
|
self.nativeQueue = queue
|
||||||
|
self.specific = nil
|
||||||
|
self.specialIsMainQueue = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private init(queue: dispatch_queue_t, specialIsMainQueue: Bool) {
|
||||||
|
self.nativeQueue = queue
|
||||||
|
self.specific = nil
|
||||||
|
self.specialIsMainQueue = specialIsMainQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
self.nativeQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
|
||||||
|
self.specific = nil
|
||||||
|
self.specialIsMainQueue = false
|
||||||
|
|
||||||
|
self.specific = UnsafeMutablePointer<Void>(Unmanaged<Queue>.passUnretained(self).toOpaque())
|
||||||
|
dispatch_queue_set_specific(self.nativeQueue, QueueSpecificKey, self.specific, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func async(f: Void -> Void) {
|
||||||
|
if self.specific != nil && dispatch_get_specific(QueueSpecificKey) == self.specific {
|
||||||
|
f()
|
||||||
|
} else if self.specialIsMainQueue && NSThread.isMainThread() {
|
||||||
|
f()
|
||||||
|
} else {
|
||||||
|
dispatch_async(self.nativeQueue, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dispatch(f: Void -> Void) {
|
||||||
|
if self.specific != nil && dispatch_get_specific(QueueSpecificKey) == self.specific {
|
||||||
|
f()
|
||||||
|
} else if self.specialIsMainQueue && NSThread.isMainThread() {
|
||||||
|
f()
|
||||||
|
} else {
|
||||||
|
dispatch_async(self.nativeQueue, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
SwiftSignalKit/Signal.swift
Normal file
43
SwiftSignalKit/Signal.swift
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
internal let doNothing: () -> Void = { _ in }
|
||||||
|
|
||||||
|
public func identity<A>(a: A) -> A {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
infix operator |> { associativity left precedence 95 }
|
||||||
|
|
||||||
|
public func |> <T, U>(value: T, function: (T -> U)) -> U {
|
||||||
|
return function(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class SubscriberDisposable<T, E> : Disposable {
|
||||||
|
private let subscriber: Subscriber<T, E>
|
||||||
|
private let disposable: Disposable
|
||||||
|
|
||||||
|
init(subscriber: Subscriber<T, E>, disposable: Disposable) {
|
||||||
|
self.subscriber = subscriber
|
||||||
|
self.disposable = disposable
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
subscriber.markTerminatedWithoutDisposal()
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Signal<T, E> {
|
||||||
|
private let generator: Subscriber<T, E> -> Disposable
|
||||||
|
|
||||||
|
public init(_ generator: Subscriber<T, E> -> Disposable) {
|
||||||
|
self.generator = generator
|
||||||
|
}
|
||||||
|
|
||||||
|
public func start(next: (T -> Void)! = nil, error: (E -> Void)! = nil, completed: (() -> Void)! = nil) -> Disposable {
|
||||||
|
let subscriber = Subscriber<T, E>(next: next, error: error, completed: completed)
|
||||||
|
let disposable = self.generator(subscriber)
|
||||||
|
subscriber.assignDisposable(disposable)
|
||||||
|
return SubscriberDisposable(subscriber: subscriber, disposable: disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
61
SwiftSignalKit/Signal_Catch.swift
Normal file
61
SwiftSignalKit/Signal_Catch.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func catch<T, E>(f: E -> Signal<T, E>)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = DisposableSet()
|
||||||
|
|
||||||
|
disposable.add(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
let anotherSignal = f(error)
|
||||||
|
|
||||||
|
disposable.add(anotherSignal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func recursiveFunction(f: (Void -> Void) -> Void) -> (Void -> Void) {
|
||||||
|
return {
|
||||||
|
f(recursiveFunction(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func restart<T, E>(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let shouldRestart = Atomic(value: true)
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
|
||||||
|
let start = recursiveFunction { recurse in
|
||||||
|
let currentShouldRestart = shouldRestart.with { value in
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if currentShouldRestart {
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
recurse()
|
||||||
|
})
|
||||||
|
currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start()
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
currentDisposable.dispose()
|
||||||
|
shouldRestart.swap(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
SwiftSignalKit/Signal_Combine.swift
Normal file
91
SwiftSignalKit/Signal_Combine.swift
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
private struct SignalCombineState {
|
||||||
|
let values: [Int : Any]
|
||||||
|
let completed: Set<Int>
|
||||||
|
let error: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
private func combineLatestAny<E, R>(signals: [Signal<Any, E>], combine: [Any] -> R) -> Signal<R, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
|
||||||
|
let state = Atomic(value: SignalCombineState(values: [:], completed: Set(), error: false))
|
||||||
|
let disposable = DisposableSet()
|
||||||
|
|
||||||
|
let count = signals.count
|
||||||
|
for index in 0 ..< count {
|
||||||
|
let signalDisposable = signals[index].start(next: { next in
|
||||||
|
let currentState = state.modify { current in
|
||||||
|
var values = current.values
|
||||||
|
values[index] = next
|
||||||
|
return SignalCombineState(values: values, completed: current.completed, error: current.error)
|
||||||
|
}
|
||||||
|
if currentState.values.count == count {
|
||||||
|
var values: [Any] = []
|
||||||
|
for i in 0 ..< count {
|
||||||
|
values.append(currentState.values[i]!)
|
||||||
|
}
|
||||||
|
subscriber.putNext(combine(values))
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
var emitError = false
|
||||||
|
state.modify { current in
|
||||||
|
if !current.error {
|
||||||
|
emitError = true
|
||||||
|
return SignalCombineState(values: current.values, completed: current.completed, error: true)
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, completed: {
|
||||||
|
var emitCompleted = false
|
||||||
|
state.modify { current in
|
||||||
|
if !current.completed.contains(index) {
|
||||||
|
var completed = current.completed
|
||||||
|
completed.insert(index)
|
||||||
|
emitCompleted = completed.count == count
|
||||||
|
return SignalCombineState(values: current.values, completed: completed, error: current.error)
|
||||||
|
}
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
if emitCompleted {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
disposable.add(signalDisposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return disposable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func signalOfAny<T, E>(signal: Signal<T, E>) -> Signal<Any, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, E>(s1: Signal<T1, E>, s2: Signal<T2, E>) -> Signal<(T1, T2), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2)], { values in
|
||||||
|
return (values[0] as! T1, values[1] as! T2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, E>(s1: Signal<T1, E>, s2: Signal<T2, E>, s3: Signal<T3, E>) -> Signal<(T1, T2, T3), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3)], { values in
|
||||||
|
return (values[0] as! T1, values[1] as! T2, values[2] as! T3)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public func combineLatest<T1, T2, T3, T4, E>(s1: Signal<T1, E>, s2: Signal<T2, E>, s3: Signal<T3, E>, s4: Signal<T4, E>) -> Signal<(T1, T2, T3, T4), E> {
|
||||||
|
return combineLatestAny([signalOfAny(s1), signalOfAny(s2), signalOfAny(s3), signalOfAny(s4)], { values in
|
||||||
|
return (values[0] as! T1, values[1] as! T2, values[2] as! T3, values[3] as! T4)
|
||||||
|
})
|
||||||
|
}
|
||||||
77
SwiftSignalKit/Signal_Dispatch.swift
Normal file
77
SwiftSignalKit/Signal_Dispatch.swift
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func deliverOn<T, E>(queue: Queue)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
queue.dispatch {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
queue.dispatch {
|
||||||
|
subscriber.putError(error)
|
||||||
|
}
|
||||||
|
}, completed: {
|
||||||
|
queue.dispatch {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deliverOnMainQueue<T, E>(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return signal |> deliverOn(Queue.mainQueue())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deliverOn<T, E>(threadPool: ThreadPool)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let queue = threadPool.nextQueue()
|
||||||
|
return signal.start(next: { next in
|
||||||
|
queue.addTask(ThreadPoolTask { state in
|
||||||
|
if !state.cancelled {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, error: { error in
|
||||||
|
queue.addTask(ThreadPoolTask { state in
|
||||||
|
if !state.cancelled {
|
||||||
|
subscriber.putError(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, completed: {
|
||||||
|
queue.addTask(ThreadPoolTask { state in
|
||||||
|
if !state.cancelled {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func runOn<T, E>(threadPool: ThreadPool)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
var cancelled = false
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
let task = ThreadPoolTask { state in
|
||||||
|
if cancelled || state.cancelled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.set(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable.set(ActionDisposable {
|
||||||
|
task.cancel()
|
||||||
|
})
|
||||||
|
|
||||||
|
threadPool.addTask(task)
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
27
SwiftSignalKit/Signal_Mapping.swift
Normal file
27
SwiftSignalKit/Signal_Mapping.swift
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func map<T, E, R>(f: T -> R)(signal: Signal<T, E>) -> Signal<R, E> {
|
||||||
|
return Signal<R, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(f(next))
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func filter<T, E>(f: T -> Bool)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
if f(next) {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
153
SwiftSignalKit/Signal_Meta.swift
Normal file
153
SwiftSignalKit/Signal_Meta.swift
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
private final class SignalQueueState<T, E> : Disposable {
|
||||||
|
var lock: OSSpinLock = 0
|
||||||
|
var executingSignal = false
|
||||||
|
var terminated = false
|
||||||
|
|
||||||
|
var disposable: Disposable = EmptyDisposable
|
||||||
|
let currentDisposable = MetaDisposable()
|
||||||
|
let subscriber: Subscriber<T, E>
|
||||||
|
|
||||||
|
var queuedSignals: [Signal<T, E>] = []
|
||||||
|
let queueMode: Bool
|
||||||
|
|
||||||
|
init(subscriber: Subscriber<T, E>, queueMode: Bool) {
|
||||||
|
self.subscriber = subscriber
|
||||||
|
self.queueMode = queueMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginWithDisposable(disposable: Disposable) {
|
||||||
|
self.disposable = disposable
|
||||||
|
}
|
||||||
|
|
||||||
|
func enqueueSignal(signal: Signal<T, E>) {
|
||||||
|
var startSignal = false
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
if self.queueMode && self.executingSignal {
|
||||||
|
self.queuedSignals.append(signal)
|
||||||
|
} else {
|
||||||
|
self.executingSignal = true
|
||||||
|
startSignal = true
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if startSignal {
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
self.subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
self.subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
self.headCompleted()
|
||||||
|
})
|
||||||
|
self.currentDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func headCompleted() {
|
||||||
|
var nextSignal: Signal<T, E>! = nil
|
||||||
|
|
||||||
|
var terminated = false
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
self.executingSignal = false
|
||||||
|
if self.queueMode {
|
||||||
|
if self.queuedSignals.count != 0 {
|
||||||
|
nextSignal = self.queuedSignals[0]
|
||||||
|
self.queuedSignals.removeAtIndex(0)
|
||||||
|
self.executingSignal = true
|
||||||
|
} else {
|
||||||
|
terminated = self.terminated
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
terminated = self.terminated
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if terminated {
|
||||||
|
self.subscriber.putCompletion()
|
||||||
|
} else if nextSignal != nil {
|
||||||
|
let disposable = nextSignal.start(next: { next in
|
||||||
|
self.subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
self.subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
self.headCompleted()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginCompletion() {
|
||||||
|
var executingSignal = false
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
executingSignal = self.executingSignal
|
||||||
|
self.terminated = true
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if !executingSignal {
|
||||||
|
self.subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dispose() {
|
||||||
|
self.currentDisposable.dispose()
|
||||||
|
self.disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func switchToLatest<T, E>(signal: Signal<Signal<T, E>, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let state = SignalQueueState(subscriber: subscriber, queueMode: false)
|
||||||
|
state.beginWithDisposable(signal.start(next: { next in
|
||||||
|
state.enqueueSignal(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
state.beginCompletion()
|
||||||
|
}))
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func queue<T, E>(signal: Signal<Signal<T, E>, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let state = SignalQueueState(subscriber: subscriber, queueMode: true)
|
||||||
|
state.beginWithDisposable(signal.start(next: { next in
|
||||||
|
state.enqueueSignal(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
state.beginCompletion()
|
||||||
|
}))
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapToSignal<T, R, E>(f: T -> Signal<R, E>)(signal: Signal<T, E>) -> Signal<R, E> {
|
||||||
|
return signal |> map { f($0) } |> switchToLatest
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mapToQueue<T, R, E>(f: T -> Signal<R, E>)(signal: Signal<T, E>) -> Signal<R, E> {
|
||||||
|
return signal |> map { f($0) } |> queue
|
||||||
|
}
|
||||||
|
|
||||||
|
public func then<T, E>(nextSignal: Signal<T, E>)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = DisposableSet()
|
||||||
|
|
||||||
|
disposable.add(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
disposable.add(nextSignal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
34
SwiftSignalKit/Signal_Reduce.swift
Normal file
34
SwiftSignalKit/Signal_Reduce.swift
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func reduceLeft<T, E>(value: T, f: (T, T) -> T)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
var currentValue = value
|
||||||
|
|
||||||
|
return signal.start(next: { next in
|
||||||
|
currentValue = f(currentValue, next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putNext(currentValue)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func reduceLeft<T, E>(value: T, f: (T, T, T -> Void) -> T)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
var currentValue = value
|
||||||
|
let emit: T -> Void = { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
return signal.start(next: { next in
|
||||||
|
currentValue = f(currentValue, next, emit)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putNext(currentValue)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
45
SwiftSignalKit/Signal_SideEffects.swift
Normal file
45
SwiftSignalKit/Signal_SideEffects.swift
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func beforeNext<T, E, R>(f: T -> R)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
let unused = f(next)
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func afterNext<T, E, R>(f: T -> R)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
return signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
let unused = f(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func afterDisposed<T, E, R>(f: Void -> R)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = DisposableSet()
|
||||||
|
disposable.add(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
disposable.add(ActionDisposable {
|
||||||
|
let unused = f()
|
||||||
|
})
|
||||||
|
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
28
SwiftSignalKit/Signal_Single.swift
Normal file
28
SwiftSignalKit/Signal_Single.swift
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Signal {
|
||||||
|
public static func single(value: T) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putNext(value)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func fail(error: E) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putError(error)
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func complete() -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
SwiftSignalKit/Signal_Take.swift
Normal file
27
SwiftSignalKit/Signal_Take.swift
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func take<T, E>(count: Int)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
let counter = Atomic(value: 0)
|
||||||
|
return signal.start(next: { next in
|
||||||
|
var passthrough = false
|
||||||
|
var complete = false
|
||||||
|
counter.modify { value in
|
||||||
|
let updatedCount = value + 1
|
||||||
|
passthrough = updatedCount <= count
|
||||||
|
complete = updatedCount == count
|
||||||
|
return updatedCount
|
||||||
|
}
|
||||||
|
if passthrough {
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}
|
||||||
|
if complete {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
56
SwiftSignalKit/Signal_Timing.swift
Normal file
56
SwiftSignalKit/Signal_Timing.swift
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public func delay<T, E>(timeout: NSTimeInterval, queue: Queue)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
let timer = Timer(timeout: timeout, repeat: false, completion: {
|
||||||
|
disposable.set(signal.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}, queue: queue)
|
||||||
|
disposable.set(ActionDisposable {
|
||||||
|
timer.invalidate()
|
||||||
|
})
|
||||||
|
timer.start()
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func timeout<T, E>(timeout: NSTimeInterval, queue: Queue, alternate: Signal<T, E>)(signal: Signal<T, E>) -> Signal<T, E> {
|
||||||
|
return Signal<T, E> { subscriber in
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
let timer = Timer(timeout: timeout, repeat: false, completion: {
|
||||||
|
disposable.set(alternate.start(next: { next in
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}, queue: queue)
|
||||||
|
|
||||||
|
disposable.set(signal.start(next: { next in
|
||||||
|
timer.invalidate()
|
||||||
|
subscriber.putNext(next)
|
||||||
|
}, error: { error in
|
||||||
|
timer.invalidate()
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
timer.invalidate()
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
timer.start()
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(ActionDisposable {
|
||||||
|
timer.invalidate()
|
||||||
|
})
|
||||||
|
disposableSet.add(disposable)
|
||||||
|
|
||||||
|
return disposableSet
|
||||||
|
}
|
||||||
|
}
|
||||||
101
SwiftSignalKit/Subscriber.swift
Normal file
101
SwiftSignalKit/Subscriber.swift
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Subscriber<T, E> {
|
||||||
|
private var next: (T -> Void)!
|
||||||
|
private var error: (E -> Void)!
|
||||||
|
private var completed: (() -> Void)!
|
||||||
|
|
||||||
|
private var lock: OSSpinLock = 0
|
||||||
|
private var terminated = false
|
||||||
|
internal var disposable: Disposable!
|
||||||
|
|
||||||
|
public init(next: (T -> Void)! = nil, error: (E -> Void)! = nil, completed: (() -> Void)! = nil) {
|
||||||
|
self.next = next
|
||||||
|
self.error = error
|
||||||
|
self.completed = completed
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func assignDisposable(disposable: Disposable) {
|
||||||
|
if self.terminated {
|
||||||
|
disposable.dispose()
|
||||||
|
} else {
|
||||||
|
self.disposable = disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func markTerminatedWithoutDisposal() {
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
if !self.terminated {
|
||||||
|
self.terminated = true
|
||||||
|
self.next = nil
|
||||||
|
self.error = nil
|
||||||
|
self.completed = nil
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func putNext(next: T) {
|
||||||
|
var action: (T -> Void)! = nil
|
||||||
|
OSSpinLockLock(&self.lock)
|
||||||
|
if !self.terminated {
|
||||||
|
action = self.next
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock)
|
||||||
|
|
||||||
|
if action != nil {
|
||||||
|
action(next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func putError(error: E) {
|
||||||
|
var shouldDispose = false
|
||||||
|
var action: (E -> Void)! = nil
|
||||||
|
|
||||||
|
OSSpinLockLock(&self.lock);
|
||||||
|
if !self.terminated {
|
||||||
|
action = self.error
|
||||||
|
shouldDispose = true;
|
||||||
|
self.next = nil
|
||||||
|
self.error = nil
|
||||||
|
self.completed = nil;
|
||||||
|
self.terminated = true
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock);
|
||||||
|
|
||||||
|
if action != nil {
|
||||||
|
action(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldDispose && self.disposable != nil {
|
||||||
|
let disposable = self.disposable
|
||||||
|
disposable.dispose()
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func putCompletion() {
|
||||||
|
var shouldDispose = false
|
||||||
|
var action: (() -> Void)! = nil
|
||||||
|
|
||||||
|
OSSpinLockLock(&self.lock);
|
||||||
|
if !self.terminated {
|
||||||
|
action = self.completed
|
||||||
|
shouldDispose = true;
|
||||||
|
self.next = nil
|
||||||
|
self.error = nil
|
||||||
|
self.completed = nil;
|
||||||
|
self.terminated = true
|
||||||
|
}
|
||||||
|
OSSpinLockUnlock(&self.lock);
|
||||||
|
|
||||||
|
if action != nil {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldDispose && self.disposable != nil {
|
||||||
|
let disposable = self.disposable
|
||||||
|
disposable.dispose()
|
||||||
|
self.disposable = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
SwiftSignalKit/SwiftSignalKit.h
Normal file
19
SwiftSignalKit/SwiftSignalKit.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// SwiftSignalKit.h
|
||||||
|
// SwiftSignalKit
|
||||||
|
//
|
||||||
|
// Created by Peter on 10/06/15.
|
||||||
|
// Copyright (c) 2015 Telegram. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
//! Project version number for SwiftSignalKit.
|
||||||
|
FOUNDATION_EXPORT double SwiftSignalKitVersionNumber;
|
||||||
|
|
||||||
|
//! Project version string for SwiftSignalKit.
|
||||||
|
FOUNDATION_EXPORT const unsigned char SwiftSignalKitVersionString[];
|
||||||
|
|
||||||
|
// In this header, you should import all the public headers of your framework using statements like #import <SwiftSignalKit/PublicHeader.h>
|
||||||
|
|
||||||
|
|
||||||
154
SwiftSignalKit/ThreadPool.swift
Normal file
154
SwiftSignalKit/ThreadPool.swift
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class ThreadPoolTaskState {
|
||||||
|
public var cancelled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ThreadPoolTask {
|
||||||
|
private let state = ThreadPoolTaskState()
|
||||||
|
private let action: ThreadPoolTaskState -> ()
|
||||||
|
|
||||||
|
public init(_ action: ThreadPoolTaskState -> ()) {
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
internal func execute() {
|
||||||
|
if !state.cancelled {
|
||||||
|
self.action(self.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func cancel() {
|
||||||
|
self.state.cancelled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ThreadPoolQueue : Equatable {
|
||||||
|
private weak var threadPool: ThreadPool?
|
||||||
|
private var tasks: [ThreadPoolTask] = []
|
||||||
|
|
||||||
|
public init(threadPool: ThreadPool) {
|
||||||
|
self.threadPool = threadPool
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addTask(task: ThreadPoolTask) {
|
||||||
|
if let threadPool = self.threadPool {
|
||||||
|
threadPool.workOnQueue(self, action: {
|
||||||
|
self.tasks.append(task)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func popFirstTask() -> ThreadPoolTask? {
|
||||||
|
if self.tasks.count != 0 {
|
||||||
|
let task = self.tasks[0];
|
||||||
|
self.tasks.removeAtIndex(0)
|
||||||
|
return task
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func hasTasks() -> Bool {
|
||||||
|
return self.tasks.count != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ==(lhs: ThreadPoolQueue, rhs: ThreadPoolQueue) -> Bool {
|
||||||
|
return lhs === rhs
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public final class ThreadPool {
|
||||||
|
private var threads: [NSThread] = []
|
||||||
|
private var queues: [ThreadPoolQueue] = []
|
||||||
|
private var takenQueues: [ThreadPoolQueue] = []
|
||||||
|
private var mutex: pthread_mutex_t
|
||||||
|
private var condition: pthread_cond_t
|
||||||
|
|
||||||
|
private class func threadEntryPoint(threadPool: ThreadPool) {
|
||||||
|
var queue: ThreadPoolQueue!
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var task: ThreadPoolTask!
|
||||||
|
|
||||||
|
pthread_mutex_lock(&threadPool.mutex);
|
||||||
|
|
||||||
|
if queue != nil {
|
||||||
|
if let index = find(threadPool.takenQueues, queue) {
|
||||||
|
threadPool.takenQueues.removeAtIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if queue.hasTasks() {
|
||||||
|
threadPool.queues.append(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
while threadPool.queues.count == 0 {
|
||||||
|
pthread_cond_wait(&threadPool.condition, &threadPool.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if threadPool.queues.count != 0 {
|
||||||
|
queue = threadPool.queues[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if queue != nil {
|
||||||
|
task = queue.popFirstTask()
|
||||||
|
threadPool.takenQueues.append(queue)
|
||||||
|
|
||||||
|
if let index = find(threadPool.queues, queue) {
|
||||||
|
threadPool.queues.removeAtIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&threadPool.mutex);
|
||||||
|
|
||||||
|
if task != nil {
|
||||||
|
task.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(threadCount: Int, threadPriority: Double) {
|
||||||
|
assert(threadCount > 0, "threadCount < 0")
|
||||||
|
|
||||||
|
self.mutex = pthread_mutex_t()
|
||||||
|
self.condition = pthread_cond_t()
|
||||||
|
pthread_mutex_init(&self.mutex, nil)
|
||||||
|
pthread_cond_init(&self.condition, nil)
|
||||||
|
|
||||||
|
for i in 0 ..< threadCount {
|
||||||
|
var thread = NSThread(target: ThreadPool.self, selector: Selector("threadEntryPoint"), object: self)
|
||||||
|
thread.threadPriority = threadPriority
|
||||||
|
self.threads.append(thread)
|
||||||
|
thread.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
pthread_mutex_destroy(&self.mutex)
|
||||||
|
pthread_cond_destroy(&self.condition)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addTask(task: ThreadPoolTask) {
|
||||||
|
let tempQueue = self.nextQueue()
|
||||||
|
tempQueue.addTask(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func workOnQueue(queue: ThreadPoolQueue, action: () -> ()) {
|
||||||
|
pthread_mutex_lock(&self.mutex)
|
||||||
|
action()
|
||||||
|
if !contains(self.queues, queue) && !contains(self.takenQueues, queue) {
|
||||||
|
self.queues.append(queue)
|
||||||
|
}
|
||||||
|
pthread_cond_broadcast(&self.condition)
|
||||||
|
pthread_mutex_unlock(&self.mutex)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func nextQueue() -> ThreadPoolQueue {
|
||||||
|
return ThreadPoolQueue(threadPool: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
41
SwiftSignalKit/Timer.swift
Normal file
41
SwiftSignalKit/Timer.swift
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public final class Timer {
|
||||||
|
private var timer: dispatch_source_t!
|
||||||
|
private var timeout: NSTimeInterval
|
||||||
|
private var repeat: Bool
|
||||||
|
private var completion: Void -> Void
|
||||||
|
private var queue: Queue
|
||||||
|
|
||||||
|
public init(timeout: NSTimeInterval, repeat: Bool, completion: Void -> Void, queue: Queue) {
|
||||||
|
self.timeout = timeout
|
||||||
|
self.repeat = repeat
|
||||||
|
self.completion = completion
|
||||||
|
self.queue = queue
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func start() {
|
||||||
|
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue.queue)
|
||||||
|
dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, Int64(self.timeout * NSTimeInterval(NSEC_PER_SEC))), self.repeat ? UInt64(self.timeout * NSTimeInterval(NSEC_PER_SEC)) : DISPATCH_TIME_FOREVER, 0);
|
||||||
|
dispatch_source_set_event_handler(self.timer, { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.completion()
|
||||||
|
if !strongSelf.repeat {
|
||||||
|
strongSelf.invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dispatch_resume(self.timer)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func invalidate() {
|
||||||
|
if self.timer != nil {
|
||||||
|
dispatch_source_cancel(self.timer)
|
||||||
|
self.timer = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
SwiftSignalKitTests/DeallocatingObject.swift
Normal file
19
SwiftSignalKitTests/DeallocatingObject.swift
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
internal class DeallocatingObject : Printable {
|
||||||
|
private var deallocated: UnsafeMutablePointer<Bool>
|
||||||
|
|
||||||
|
init(deallocated: UnsafeMutablePointer<Bool>) {
|
||||||
|
self.deallocated = deallocated
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.deallocated.memory = true
|
||||||
|
}
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
get {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
SwiftSignalKitTests/Info.plist
Normal file
24
SwiftSignalKitTests/Info.plist
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.telegram.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
259
SwiftSignalKitTests/SwiftSignalKitBasicTests.swift
Normal file
259
SwiftSignalKitTests/SwiftSignalKitBasicTests.swift
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
import UIKit
|
||||||
|
import XCTest
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
|
class SwiftSignalKitTests: XCTestCase {
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() {
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testActionDisposableDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
object = nil
|
||||||
|
XCTAssertFalse(deallocated, "deallocated != false")
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testActionDisposableNotDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertFalse(disposed, "disposed != false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMetaDisposableDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let metaDisposable = MetaDisposable()
|
||||||
|
metaDisposable.set(disposable)
|
||||||
|
metaDisposable.dispose()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMetaDisposableDisposedMultipleTimes() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
var object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
var object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let metaDisposable = MetaDisposable()
|
||||||
|
metaDisposable.set(actionDisposable1)
|
||||||
|
metaDisposable.set(actionDisposable2)
|
||||||
|
metaDisposable.dispose()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertTrue(disposed1, "disposed1 != true")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertTrue(disposed2, "disposed2 != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMetaDisposableNotDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let metaDisposable = MetaDisposable()
|
||||||
|
metaDisposable.set(disposable)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertFalse(disposed, "disposed != false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetSingleDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(disposable)
|
||||||
|
disposableSet.dispose()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetMultipleDisposed() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
var object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
var object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(actionDisposable1)
|
||||||
|
disposableSet.add(actionDisposable2)
|
||||||
|
disposableSet.dispose()
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertTrue(disposed1, "disposed1 != true")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertTrue(disposed2, "disposed2 != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetSingleNotDisposed() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let disposable = ActionDisposable(action: { [object] () -> Void in
|
||||||
|
object.debugDescription
|
||||||
|
disposed = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(disposable)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertFalse(disposed, "disposed != false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetMultipleNotDisposed() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
var object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
var object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(actionDisposable1)
|
||||||
|
disposableSet.add(actionDisposable2)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertFalse(disposed1, "disposed1 != false")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertFalse(disposed2, "disposed2 != false")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMetaDisposableAlreadyDisposed() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
var object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
var object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let metaDisposable = MetaDisposable()
|
||||||
|
metaDisposable.set(actionDisposable1)
|
||||||
|
metaDisposable.dispose()
|
||||||
|
metaDisposable.set(actionDisposable2)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertTrue(disposed1, "disposed1 != true")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertTrue(disposed2, "disposed2 != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisposableSetAlreadyDisposed() {
|
||||||
|
var deallocated1 = false
|
||||||
|
var disposed1 = false
|
||||||
|
var deallocated2 = false
|
||||||
|
var disposed2 = false
|
||||||
|
if true {
|
||||||
|
var object1: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated1)
|
||||||
|
let actionDisposable1 = ActionDisposable(action: { [object1] () -> Void in
|
||||||
|
object1.debugDescription
|
||||||
|
disposed1 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
var object2: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated2)
|
||||||
|
let actionDisposable2 = ActionDisposable(action: { [object2] () -> Void in
|
||||||
|
object2.debugDescription
|
||||||
|
disposed2 = true
|
||||||
|
})
|
||||||
|
|
||||||
|
let disposableSet = DisposableSet()
|
||||||
|
disposableSet.add(actionDisposable1)
|
||||||
|
disposableSet.dispose()
|
||||||
|
disposableSet.add(actionDisposable2)
|
||||||
|
}
|
||||||
|
XCTAssertTrue(deallocated1, "deallocated1 != true")
|
||||||
|
XCTAssertTrue(disposed1, "disposed1 != true")
|
||||||
|
XCTAssertTrue(deallocated2, "deallocated2 != true")
|
||||||
|
XCTAssertTrue(disposed2, "disposed2 != true")
|
||||||
|
}
|
||||||
|
}
|
||||||
660
SwiftSignalKitTests/SwiftSignalKitFunctionsTests.swift
Normal file
660
SwiftSignalKitTests/SwiftSignalKitFunctionsTests.swift
Normal file
@ -0,0 +1,660 @@
|
|||||||
|
import UIKit
|
||||||
|
import XCTest
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
|
func singleSignalInt(value: Signal<Int, Void>) -> Signal<Signal<Int, Void>, Void> {
|
||||||
|
return Signal { subscriber in
|
||||||
|
subscriber.putNext(value)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SwiftSignalKitFunctionsTests: XCTestCase {
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
super.setUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() {
|
||||||
|
super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSignalGenerated() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
var generated = false
|
||||||
|
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let signal = Signal<Int, Void> { [object] subscriber in
|
||||||
|
subscriber.putNext(1)
|
||||||
|
return ActionDisposable {
|
||||||
|
object?.description
|
||||||
|
disposed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let disposable = signal.start(next: { [object] next in
|
||||||
|
generated = true
|
||||||
|
object?.description
|
||||||
|
})
|
||||||
|
|
||||||
|
object = nil
|
||||||
|
|
||||||
|
XCTAssertFalse(deallocated, "deallocated != false")
|
||||||
|
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
XCTAssertTrue(generated, "generated != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSignalGeneratedCompleted() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
var generated = false
|
||||||
|
var completed = false
|
||||||
|
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let signal = Signal<Int, Void> { [object] subscriber in
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
object?.description
|
||||||
|
disposed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let disposable = signal.start(next: { [object] next in
|
||||||
|
generated = true
|
||||||
|
object?.description
|
||||||
|
}, completed: { [object]
|
||||||
|
completed = true
|
||||||
|
object?.description
|
||||||
|
})
|
||||||
|
|
||||||
|
object = nil
|
||||||
|
|
||||||
|
XCTAssertFalse(deallocated, "deallocated != false")
|
||||||
|
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
XCTAssertTrue(generated, "generated != true")
|
||||||
|
XCTAssertTrue(completed, "completed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSignalGeneratedError() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
var generated = false
|
||||||
|
var completed = false
|
||||||
|
var error = false
|
||||||
|
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
let signal = Signal<Int, Void> { [object] subscriber in
|
||||||
|
subscriber.putError()
|
||||||
|
subscriber.putNext(1)
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
object?.description
|
||||||
|
disposed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let disposable = signal.start(next: { [object] next in
|
||||||
|
generated = true
|
||||||
|
object?.description
|
||||||
|
}, error: { [object] _ in
|
||||||
|
error = true
|
||||||
|
object?.description
|
||||||
|
},
|
||||||
|
completed: { [object]
|
||||||
|
completed = true
|
||||||
|
object?.description
|
||||||
|
})
|
||||||
|
|
||||||
|
object = nil
|
||||||
|
|
||||||
|
XCTAssertFalse(deallocated, "deallocated != false")
|
||||||
|
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
XCTAssertFalse(generated, "generated != false")
|
||||||
|
XCTAssertFalse(completed, "completed != false")
|
||||||
|
XCTAssertTrue(error, "error != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMap() {
|
||||||
|
var deallocated = false
|
||||||
|
var disposed = false
|
||||||
|
var generated = false
|
||||||
|
|
||||||
|
if true {
|
||||||
|
var object: DeallocatingObject? = DeallocatingObject(deallocated: &deallocated)
|
||||||
|
var signal = Signal<Int, Void> { [object] subscriber in
|
||||||
|
subscriber.putNext(1)
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
object?.description
|
||||||
|
disposed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signal = signal |> map { $0 * 2}
|
||||||
|
|
||||||
|
let disposable = signal.start(next: { [object] next in
|
||||||
|
generated = next == 2
|
||||||
|
object?.description
|
||||||
|
})
|
||||||
|
|
||||||
|
object = nil
|
||||||
|
|
||||||
|
XCTAssertFalse(deallocated, "deallocated != false")
|
||||||
|
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertTrue(deallocated, "deallocated != true")
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
XCTAssertTrue(generated, "generated != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCatch() {
|
||||||
|
let failingSignal = Signal<Int, Int> { subscriber in
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putError(1)
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
|
||||||
|
let catchSignal = failingSignal |> catch { error in
|
||||||
|
return Signal<Int, Int> { subscriber in
|
||||||
|
subscriber.putNext(error * 2)
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = 0
|
||||||
|
catchSignal.start(next: { next in
|
||||||
|
result += next
|
||||||
|
})
|
||||||
|
|
||||||
|
XCTAssertTrue(result == 3, "result != 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSubscriberDisposal() {
|
||||||
|
var disposed = false
|
||||||
|
var generated = false
|
||||||
|
var queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
|
if true {
|
||||||
|
let signal = Signal<Int, Void> { subscriber in
|
||||||
|
dispatch_async(queue, {
|
||||||
|
usleep(200)
|
||||||
|
subscriber.putNext(1)
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
disposed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let disposable = signal.start(next: { next in
|
||||||
|
generated = true
|
||||||
|
})
|
||||||
|
disposable.dispose()
|
||||||
|
|
||||||
|
dispatch_barrier_sync(queue, {})
|
||||||
|
|
||||||
|
XCTAssertTrue(disposed, "disposed != true")
|
||||||
|
XCTAssertFalse(generated, "generated != false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testThen() {
|
||||||
|
var generatedFirst = false
|
||||||
|
var disposedFirst = false
|
||||||
|
var generatedSecond = false
|
||||||
|
var disposedSecond = false
|
||||||
|
var result = 0
|
||||||
|
|
||||||
|
var signal = Signal<Int, Void> { subscriber in
|
||||||
|
generatedFirst = true
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return ActionDisposable {
|
||||||
|
disposedFirst = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signal = signal |> then (Signal<Int, Void> { subscriber in
|
||||||
|
generatedSecond = true
|
||||||
|
subscriber.putNext(2)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return ActionDisposable {
|
||||||
|
disposedSecond = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
signal.start(next: { next in
|
||||||
|
result += next
|
||||||
|
})
|
||||||
|
|
||||||
|
XCTAssertTrue(generatedFirst, "generatedFirst != true");
|
||||||
|
XCTAssertTrue(disposedFirst, "disposedFirst != true");
|
||||||
|
XCTAssertTrue(generatedSecond, "generatedSecond !+ true");
|
||||||
|
XCTAssertTrue(disposedSecond, "disposedSecond != true");
|
||||||
|
XCTAssertTrue(result == 3, "result != 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCombineLatest2() {
|
||||||
|
let s1 = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
let s2 = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putNext(2)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = combineLatest(s1, s2)
|
||||||
|
|
||||||
|
var completed = false
|
||||||
|
signal.start(next: { next in
|
||||||
|
XCTAssert(next.0 == 1 && next.1 == 2, "next != (1, 2)")
|
||||||
|
return
|
||||||
|
}, completed: {
|
||||||
|
completed = true
|
||||||
|
})
|
||||||
|
XCTAssert(completed == true, "completed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCombineLatest3() {
|
||||||
|
let s1 = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
let s2 = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putNext(2)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
let s3 = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putNext(3)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = combineLatest(s1, s2, s3)
|
||||||
|
|
||||||
|
var completed = false
|
||||||
|
signal.start(next: { next in
|
||||||
|
XCTAssert(next.0 == 1 && next.1 == 2 && next.2 == 3, "next != (1, 2, 3)")
|
||||||
|
return
|
||||||
|
}, completed: {
|
||||||
|
completed = true
|
||||||
|
})
|
||||||
|
XCTAssert(completed == true, "completed != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func testSingle() {
|
||||||
|
let s1 = Signal<Int, Void>.single(1)
|
||||||
|
let s2 = Signal<Int, Void>.fail(Void())
|
||||||
|
let s3 = Signal<Int, Void>.complete()
|
||||||
|
|
||||||
|
var singleEmitted = false
|
||||||
|
s1.start(next: { next in
|
||||||
|
singleEmitted = next == 1
|
||||||
|
})
|
||||||
|
XCTAssert(singleEmitted == true, "singleEmitted != true")
|
||||||
|
|
||||||
|
var errorEmitted = false
|
||||||
|
s2.start(error: { error in
|
||||||
|
errorEmitted = true
|
||||||
|
})
|
||||||
|
XCTAssert(errorEmitted == true, "errorEmitted != true")
|
||||||
|
|
||||||
|
var completedEmitted = false
|
||||||
|
s3.start(completed: {
|
||||||
|
completedEmitted = true
|
||||||
|
})
|
||||||
|
XCTAssert(completedEmitted == true, "errorEmitted != true")
|
||||||
|
}*/
|
||||||
|
|
||||||
|
func testSwitchToLatest() {
|
||||||
|
var result: [Int] = []
|
||||||
|
var disposedOne = false
|
||||||
|
var disposedTwo = false
|
||||||
|
var disposedThree = false
|
||||||
|
var completedAll = false
|
||||||
|
|
||||||
|
var deallocatedOne = false
|
||||||
|
var deallocatedTwo = false
|
||||||
|
var deallocatedThree = false
|
||||||
|
|
||||||
|
if true {
|
||||||
|
var objectOne: DeallocatingObject? = DeallocatingObject(deallocated: &deallocatedOne)
|
||||||
|
var objectTwo: DeallocatingObject? = DeallocatingObject(deallocated: &deallocatedTwo)
|
||||||
|
var objectThree: DeallocatingObject? = DeallocatingObject(deallocated: &deallocatedThree)
|
||||||
|
|
||||||
|
let one = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return ActionDisposable { [objectOne] in
|
||||||
|
objectOne?.description
|
||||||
|
disposedOne = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let two = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putNext(2)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return ActionDisposable { [objectTwo] in
|
||||||
|
objectTwo?.description
|
||||||
|
disposedTwo = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let three = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putNext(3)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
return ActionDisposable { [objectThree] in
|
||||||
|
objectThree?.description
|
||||||
|
disposedThree = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = singleSignalInt(one) |> then(singleSignalInt(two)) |> then(singleSignalInt(three)) |> switchToLatest
|
||||||
|
|
||||||
|
signal.start(next: { next in
|
||||||
|
result.append(next)
|
||||||
|
}, completed: {
|
||||||
|
completedAll = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssert(result.count == 3 && result[0] == 1 && result[1] == 2 && result[2] == 3, "result != [1, 2, 3]");
|
||||||
|
XCTAssert(disposedOne == true, "disposedOne != true");
|
||||||
|
XCTAssert(disposedTwo == true, "disposedTwo != true");
|
||||||
|
XCTAssert(disposedThree == true, "disposedThree != true");
|
||||||
|
XCTAssert(deallocatedOne == true, "deallocatedOne != true");
|
||||||
|
XCTAssert(deallocatedTwo == true, "deallocatedTwo != true");
|
||||||
|
XCTAssert(deallocatedThree == true, "deallocatedThree != true");
|
||||||
|
XCTAssert(completedAll == true, "completedAll != true");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSwitchToLatestError() {
|
||||||
|
var errorGenerated = false
|
||||||
|
|
||||||
|
let one = Signal<Int, Void> { subscriber in
|
||||||
|
subscriber.putError(Void())
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = singleSignalInt(one) |> switchToLatest
|
||||||
|
|
||||||
|
signal.start(error: { error in
|
||||||
|
errorGenerated = true
|
||||||
|
})
|
||||||
|
|
||||||
|
XCTAssert(errorGenerated == true, "errorGenerated != true")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testQueue() {
|
||||||
|
let q = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
|
||||||
|
|
||||||
|
var disposedOne = false
|
||||||
|
var disposedTwo = false
|
||||||
|
var disposedThree = false
|
||||||
|
var completedAll = false
|
||||||
|
var result: [Int] = []
|
||||||
|
|
||||||
|
let one = Signal<Int, Void> { subscriber in
|
||||||
|
dispatch_async(q, {
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
disposedOne = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let two = Signal<Int, Void> { subscriber in
|
||||||
|
dispatch_async(q, {
|
||||||
|
subscriber.putNext(2)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
disposedTwo = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let three = Signal<Int, Void> { subscriber in
|
||||||
|
dispatch_async(q, {
|
||||||
|
subscriber.putNext(3)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
disposedThree = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = singleSignalInt(one) |> then(singleSignalInt(two)) |> then(singleSignalInt(three)) |> queue
|
||||||
|
|
||||||
|
signal.start(next: { next in
|
||||||
|
println("next: \(next)")
|
||||||
|
result.append(next)
|
||||||
|
}, completed: {
|
||||||
|
completedAll = true
|
||||||
|
})
|
||||||
|
|
||||||
|
usleep(1000 * 200)
|
||||||
|
|
||||||
|
XCTAssert(result.count == 3 && result[0] == 1 && result[1] == 2 && result[2] == 3, "result != [1, 2, 3]");
|
||||||
|
XCTAssert(disposedOne == true, "disposedOne != true");
|
||||||
|
XCTAssert(disposedTwo == true, "disposedTwo != true");
|
||||||
|
XCTAssert(disposedThree == true, "disposedThree != true");
|
||||||
|
XCTAssert(completedAll == true, "completedAll != true");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testQueueInterrupted() {
|
||||||
|
let q = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
|
||||||
|
|
||||||
|
var disposedOne = false
|
||||||
|
var disposedTwo = false
|
||||||
|
var disposedThree = false
|
||||||
|
var startedThird = false
|
||||||
|
var completedAll = false
|
||||||
|
var result: [Int] = []
|
||||||
|
|
||||||
|
let one = Signal<Int, Void> { subscriber in
|
||||||
|
dispatch_async(q, {
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
disposedOne = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let two = Signal<Int, Void> { subscriber in
|
||||||
|
dispatch_async(q, {
|
||||||
|
subscriber.putNext(2)
|
||||||
|
subscriber.putError(Void())
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
disposedTwo = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let three = Signal<Int, Void> { subscriber in
|
||||||
|
startedThird = true
|
||||||
|
dispatch_async(q, {
|
||||||
|
subscriber.putNext(3)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
disposedThree = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = singleSignalInt(one) |> then(singleSignalInt(two)) |> then(singleSignalInt(three)) |> queue
|
||||||
|
|
||||||
|
signal.start(next: { next in
|
||||||
|
result.append(next)
|
||||||
|
}, completed: {
|
||||||
|
completedAll = true
|
||||||
|
})
|
||||||
|
|
||||||
|
usleep(1000 * 200)
|
||||||
|
|
||||||
|
XCTAssert(result.count == 2 && result[0] == 1 && result[1] == 2, "result != [1, 2]");
|
||||||
|
XCTAssert(disposedOne == true, "disposedOne != true");
|
||||||
|
XCTAssert(disposedTwo == true, "disposedTwo != true");
|
||||||
|
XCTAssert(disposedThree == false, "disposedThree != false");
|
||||||
|
XCTAssert(startedThird == false, "startedThird != false");
|
||||||
|
XCTAssert(completedAll == false, "completedAll != false");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testQueueDisposed() {
|
||||||
|
let q = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
|
||||||
|
|
||||||
|
var disposedOne = false
|
||||||
|
var disposedTwo = false
|
||||||
|
var disposedThree = false
|
||||||
|
var startedFirst = false
|
||||||
|
var startedSecond = false
|
||||||
|
var startedThird = false
|
||||||
|
var result: [Int] = []
|
||||||
|
|
||||||
|
let one = Signal<Int, Void> { subscriber in
|
||||||
|
startedFirst = true
|
||||||
|
var cancelled = false
|
||||||
|
dispatch_async(q, {
|
||||||
|
if !cancelled {
|
||||||
|
usleep(100 * 1000)
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
cancelled = true
|
||||||
|
disposedOne = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let two = Signal<Int, Void> { subscriber in
|
||||||
|
startedSecond = true
|
||||||
|
var cancelled = false
|
||||||
|
dispatch_async(q, {
|
||||||
|
if !cancelled {
|
||||||
|
usleep(100 * 1000)
|
||||||
|
subscriber.putNext(2)
|
||||||
|
subscriber.putError(Void())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
cancelled = true
|
||||||
|
disposedTwo = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let three = Signal<Int, Void> { subscriber in
|
||||||
|
startedThird = true
|
||||||
|
var cancelled = false
|
||||||
|
dispatch_async(q, {
|
||||||
|
if !cancelled {
|
||||||
|
usleep(100 * 1000)
|
||||||
|
subscriber.putNext(3)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ActionDisposable {
|
||||||
|
cancelled = true
|
||||||
|
disposedThree = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let signal = singleSignalInt(one) |> then(singleSignalInt(two)) |> then(singleSignalInt(three)) |> queue
|
||||||
|
|
||||||
|
signal.start(next: { next in
|
||||||
|
result.append(next)
|
||||||
|
}).dispose()
|
||||||
|
|
||||||
|
usleep(1000 * 200)
|
||||||
|
|
||||||
|
XCTAssert(result.count == 0, "result != []");
|
||||||
|
XCTAssert(disposedOne == true, "disposedOne != true");
|
||||||
|
XCTAssert(disposedTwo == false, "disposedTwo != false");
|
||||||
|
XCTAssert(disposedThree == false, "disposedThree != false");
|
||||||
|
XCTAssert(startedFirst == true, "startedFirst != false");
|
||||||
|
XCTAssert(startedSecond == false, "startedSecond != false");
|
||||||
|
XCTAssert(startedThird == false, "startedThird != false");
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRestart() {
|
||||||
|
let q = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT)
|
||||||
|
let signal = Signal<Int, Void> { subscriber in
|
||||||
|
dispatch_async(q, {
|
||||||
|
subscriber.putNext(1)
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
return EmptyDisposable
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = 0
|
||||||
|
|
||||||
|
(signal |> restart |> take(3)).start(next: { next in
|
||||||
|
result += next
|
||||||
|
})
|
||||||
|
|
||||||
|
usleep(100 * 1000)
|
||||||
|
|
||||||
|
XCTAssert(result == 3, "result != 3")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPipe() {
|
||||||
|
let pipe = Pipe<Int>()
|
||||||
|
|
||||||
|
var result1 = 0
|
||||||
|
let disposable1 = pipe.signal().start(next: { next in
|
||||||
|
result1 += next
|
||||||
|
})
|
||||||
|
|
||||||
|
var result2 = 0
|
||||||
|
let disposable2 = pipe.signal().start(next: { next in
|
||||||
|
result2 += next
|
||||||
|
})
|
||||||
|
|
||||||
|
pipe.putNext(1)
|
||||||
|
|
||||||
|
XCTAssert(result1 == 1, "result1 != 1")
|
||||||
|
XCTAssert(result2 == 1, "result2 != 1")
|
||||||
|
|
||||||
|
disposable1.dispose()
|
||||||
|
|
||||||
|
pipe.putNext(1)
|
||||||
|
|
||||||
|
XCTAssert(result1 == 1, "result1 != 1")
|
||||||
|
XCTAssert(result2 == 2, "result2 != 2")
|
||||||
|
|
||||||
|
disposable2.dispose()
|
||||||
|
|
||||||
|
pipe.putNext(1)
|
||||||
|
|
||||||
|
XCTAssert(result1 == 1, "result1 != 1")
|
||||||
|
XCTAssert(result2 == 2, "result2 != 2")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user