diff --git a/Ably.xcodeproj/project.pbxproj b/Ably.xcodeproj/project.pbxproj index 50a6cca16..3557087dc 100644 --- a/Ably.xcodeproj/project.pbxproj +++ b/Ably.xcodeproj/project.pbxproj @@ -158,6 +158,13 @@ EB1AE0CE1C5C3A4900D62250 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1AE0CD1C5C3A4900D62250 /* Utilities.swift */; }; EB20F8D71C653F2300EF3978 /* ARTPresence+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB20F8D61C653F1E00EF3978 /* ARTPresence+Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; EB2D5A911CC941A700AD1A67 /* ARTRealtimeTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D5A901CC941A700AD1A67 /* ARTRealtimeTransport.m */; }; + EB2D5A921CC941A700AD1A67 /* ARTRealtimeTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D5A901CC941A700AD1A67 /* ARTRealtimeTransport.m */; }; + EB2D5A931CC941A700AD1A67 /* ARTRealtimeTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D5A901CC941A700AD1A67 /* ARTRealtimeTransport.m */; }; + EB2D84F71CD75CCE00F23CDA /* ARTReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = EB2D84F61CD75CCE00F23CDA /* ARTReachability.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EB2D84FD1CD769B800F23CDA /* ARTOSReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */; }; + EB2D84FE1CD769B800F23CDA /* ARTOSReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */; }; + EB2D84FF1CD769B800F23CDA /* ARTOSReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */; }; + EB2D85011CD769C800F23CDA /* ARTOSReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = EB2D85001CD769C800F23CDA /* ARTOSReachability.h */; settings = {ATTRIBUTES = (Public, ); }; }; EB503C881C7E4A090053AF00 /* ARTClientOptions+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB503C871C7E4A090053AF00 /* ARTClientOptions+Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; EB503C8A1C7F1FE40053AF00 /* ARTLog+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB503C891C7F1FE40053AF00 /* ARTLog+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; EB5E058D1C77027600A48B39 /* ARTCrypto+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E058C1C77027600A48B39 /* ARTCrypto+Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -389,6 +396,9 @@ EB1AE0CD1C5C3A4900D62250 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; EB20F8D61C653F1E00EF3978 /* ARTPresence+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ARTPresence+Private.h"; sourceTree = ""; }; EB2D5A901CC941A700AD1A67 /* ARTRealtimeTransport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTRealtimeTransport.m; sourceTree = ""; }; + EB2D84F61CD75CCE00F23CDA /* ARTReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTReachability.h; sourceTree = ""; }; + EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTOSReachability.m; sourceTree = ""; }; + EB2D85001CD769C800F23CDA /* ARTOSReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTOSReachability.h; sourceTree = ""; }; EB3239421C59AB0400892664 /* ARTDataEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ARTDataEncoder.m; sourceTree = ""; }; EB3239461C59AB2C00892664 /* ARTDataEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARTDataEncoder.h; sourceTree = ""; }; EB503C871C7E4A090053AF00 /* ARTClientOptions+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ARTClientOptions+Private.h"; sourceTree = ""; }; @@ -725,6 +735,9 @@ 967A43201A39AEAF00E4CE23 /* ARTNSArray+ARTFunctional.m */, 96A507A31A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.h */, 96A507A41A377DE90077CDF8 /* ARTNSDictionary+ARTDictionaryUtil.m */, + EB2D84F61CD75CCE00F23CDA /* ARTReachability.h */, + EB2D84FC1CD769B700F23CDA /* ARTOSReachability.m */, + EB2D85001CD769C800F23CDA /* ARTOSReachability.h */, ); name = Utilities; sourceTree = ""; @@ -775,6 +788,7 @@ 961343D81A42E0B7006DC822 /* ARTClientOptions.h in Headers */, 96BF615E1A35C1C8004CF2B3 /* ARTTypes.h in Headers */, EB503C881C7E4A090053AF00 /* ARTClientOptions+Private.h in Headers */, + EB2D84F71CD75CCE00F23CDA /* ARTReachability.h in Headers */, 1C1EC3FA1AE26A8B00AAADD7 /* ARTStatus.h in Headers */, D70EAAED1BC3376200CD8B9E /* ARTRestChannel.h in Headers */, 96BF61581A35B52C004CF2B3 /* ARTHttp.h in Headers */, @@ -827,6 +841,7 @@ D7F1D3731BF4DE07001A4B5E /* ARTRestPresence.h in Headers */, D7B17EE31C07208B00A6958E /* ARTConnectionDetails.h in Headers */, EBFA366E1D58B05000B09AA7 /* ARTRestPresence+Private.h in Headers */, + EB2D85011CD769C800F23CDA /* ARTOSReachability.h in Headers */, 960D07971A46FFC300ED8C8C /* ARTRest+Private.h in Headers */, 1C05CF201AC1D7EB00687AC9 /* ARTRealtime+Private.h in Headers */, D7F1D37A1BF4E33A001A4B5E /* ARTRestChannel+Private.h in Headers */, @@ -1112,6 +1127,7 @@ D746AE2C1BBB625E003ECEF8 /* RestClientChannel.swift in Sources */, D71D30041C5F7B2F002115B0 /* RealtimeClientChannels.swift in Sources */, D714A63E1C74D4B2002F2CA0 /* NSObject+TestSuite.swift in Sources */, + EB2D84FF1CD769B800F23CDA /* ARTOSReachability.m in Sources */, 856AAC971B6E30C800B07119 /* TestUtilities.swift in Sources */, D72768211C9C19040022F8B2 /* RestClientPresence.swift in Sources */, D780846E1C68B3E50083009D /* NSObject+TestSuite.m in Sources */, @@ -1138,6 +1154,7 @@ D746AE391BBC3201003ECEF8 /* ARTMessage.m in Sources */, EB89D40B1C61C6EA007FA5B7 /* ARTRealtimeChannels.m in Sources */, D746AE231BBB60EE003ECEF8 /* ARTChannel.m in Sources */, + EB2D84FD1CD769B800F23CDA /* ARTOSReachability.m in Sources */, D746AE481BBD6FE9003ECEF8 /* ARTQueuedMessage.m in Sources */, D746AE3D1BBC5AE1003ECEF8 /* ARTRealtimeChannel.m in Sources */, 96A507A21A377AA50077CDF8 /* ARTPresenceMessage.m in Sources */, @@ -1187,6 +1204,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EB2D84FE1CD769B800F23CDA /* ARTOSReachability.m in Sources */, 96E4083C1A38622D00087F77 /* ARTTestUtil.m in Sources */, EBAB9A711C69719900AF036B /* ARTReadmeExamples.m in Sources */, 1C1E52EE1AB32EB9004A690F /* ARTRestCapabilityTest.m in Sources */, diff --git a/Source/ARTOSReachability.h b/Source/ARTOSReachability.h new file mode 100644 index 000000000..3f490e450 --- /dev/null +++ b/Source/ARTOSReachability.h @@ -0,0 +1,17 @@ +// +// ARTOSReachability.h +// Ably +// +// Created by Toni Cárdenas on 2/5/16. +// Copyright © 2016 Ably. All rights reserved. +// + +#ifndef ARTOSReachability_h +#define ARTOSReachability_h + +#import "ARTReachability.h" + +@interface ARTOSReachability : NSObject +@end + +#endif /* ARTOSReachability_h */ diff --git a/Source/ARTOSReachability.m b/Source/ARTOSReachability.m new file mode 100644 index 000000000..2aebbe1e9 --- /dev/null +++ b/Source/ARTOSReachability.m @@ -0,0 +1,87 @@ +// +// ARTReachability.m +// Ably +// +// Created by Toni Cárdenas on 2/5/16. +// Copyright © 2016 Ably. All rights reserved. +// + +#import +#import +#import +#import +#import +#import +#import + +#import "ARTOSReachability.h" + +@implementation ARTOSReachability { + ARTLog *_logger; + NSString *_host; + void (^_callback)(BOOL); + SCNetworkReachabilityRef _reachabilityRef; + NSMutableDictionary *_instances; +} + +- (instancetype)initWithLogger:(ARTLog *)logger { + self = [super self]; + if (self) { + _logger = logger; + if (ARTOSReachability_instances == nil) { + _instances = [[NSMutableDictionary alloc] init]; + ARTOSReachability_instances = _instances; + } else { + _instances = ARTOSReachability_instances; + } + } + return self; +} + +__weak NSMutableDictionary *ARTOSReachability_instances; + +static void ARTOSReachability_Callback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) { + [(ARTOSReachability *)ARTOSReachability_instances[[NSValue valueWithPointer:target]] internalCallback:flags & kSCNetworkReachabilityFlagsReachable]; +} + +- (void)listenForHost:(NSString *)host callback:(void (^)(BOOL))callback { + [self off]; + _host = host; + _callback = callback; + + _reachabilityRef = SCNetworkReachabilityCreateWithName(NULL, [host UTF8String]); + SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; + + if (SCNetworkReachabilitySetCallback(_reachabilityRef, ARTOSReachability_Callback, &context)) { + [ARTOSReachability_instances setObject:self forKey:[NSValue valueWithPointer:_reachabilityRef]]; + if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { + [_logger info:@"Reachability: started listening for host %@", _host]; + } else { + [_logger warn:@"Reachability: failed starting listener for host %@", _host]; + } + } +} + +- (void)off { + if (_reachabilityRef != NULL) { + [ARTOSReachability_instances removeObjectForKey:[NSValue valueWithPointer:_reachabilityRef]]; + SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + [_logger info:@"Reachability: stopped listening for host %@", _host]; + } + _callback = nil; + _host = nil; +} + +- (void)internalCallback:(BOOL)reachable { + [_logger info:@"Reachability: host %@: %d", _host, reachable]; + _callback(reachable); +} + +- (void)dealloc { + [self off]; + if (_reachabilityRef != NULL) { + CFRelease(_reachabilityRef); + } +} + +@end \ No newline at end of file diff --git a/Source/ARTReachability.h b/Source/ARTReachability.h new file mode 100644 index 000000000..d76108446 --- /dev/null +++ b/Source/ARTReachability.h @@ -0,0 +1,28 @@ +// +// ARTReachability.h +// Ably +// +// Created by Toni Cárdenas on 2/5/16. +// Copyright © 2016 Ably. All rights reserved. +// + +#ifndef ARTReachability_h +#define ARTReachability_h + +#import "CompatibilityMacros.h" +#import "ARTLog.h" + +ART_ASSUME_NONNULL_BEGIN + +@protocol ARTReachability + +- (instancetype)initWithLogger:(ARTLog *)logger; + +- (void)listenForHost:(NSString *)host callback:(void (^)(BOOL))callback; +- (void)off; + +@end + +ART_ASSUME_NONNULL_END + +#endif /* ARTReachability_h */ diff --git a/Source/ARTRealtime+Private.h b/Source/ARTRealtime+Private.h index 5807dc324..8c30abf80 100644 --- a/Source/ARTRealtime+Private.h +++ b/Source/ARTRealtime+Private.h @@ -11,6 +11,7 @@ #import "ARTTypes.h" #import "ARTQueuedMessage.h" #import "ARTProtocolMessage.h" +#import "ARTReachability.h" #import "ARTRealtimeTransport.h" @@ -45,6 +46,7 @@ ART_ASSUME_NONNULL_BEGIN @property (readwrite, strong, nonatomic) ARTRest *rest; @property (readonly, getter=getTransport) id transport; +@property (readonly, strong, nonatomic, art_nonnull) id reachability; @property (readonly, getter=getLogger) ARTLog *logger; /// Current protocol `msgSerial`. Starts at zero. @@ -81,8 +83,8 @@ ART_ASSUME_NONNULL_BEGIN - (void)onNack:(ARTProtocolMessage *)message; - (void)onChannelMessage:(ARTProtocolMessage *)message; -// FIXME: Connection should manage the transport - (void)setTransportClass:(Class)transportClass; +- (void)setReachabilityClass:(Class)reachabilityClass; - (void)resetEventEmitter; diff --git a/Source/ARTRealtime.m b/Source/ARTRealtime.m index 836d8ee56..9896ed20f 100644 --- a/Source/ARTRealtime.m +++ b/Source/ARTRealtime.m @@ -19,6 +19,7 @@ #import "ARTChannelOptions.h" #import "ARTPresenceMessage.h" #import "ARTWebSocketTransport.h" +#import "ARTOSReachability.h" #import "ARTNSArray+ARTFunctional.h" #import "ARTPresenceMap.h" #import "ARTProtocolMessage.h" @@ -46,6 +47,7 @@ @implementation ARTRealtime { NSDate *_startedReconnection; NSTimeInterval _connectionStateTtl; Class _transportClass; + Class _reachabilityClass; id _transport; ARTFallback *_fallbacks; } @@ -71,6 +73,7 @@ - (instancetype)initWithOptions:(ARTClientOptions *)options { _channels = [[ARTRealtimeChannels alloc] initWithRealtime:self]; _transport = nil; _transportClass = [ARTWebSocketTransport class]; + _reachabilityClass = [ARTOSReachability class]; _msgSerial = 0; _queuedMessages = [NSMutableArray array]; _pendingMessages = [NSMutableArray array]; @@ -232,6 +235,36 @@ - (void)transitionSideEffects:(ARTConnectionStateChange *)stateChange { [self transition:ARTRealtimeDisconnected withErrorInfo:[ARTErrorInfo createWithCode:0 status:ARTStateConnectionFailed message:@"timed out"]]; }]; + if (!_reachability) { + _reachability = [[_reachabilityClass alloc] initWithLogger:self.logger]; + } + + // TODO: Do also for fallback hosts once https://github.com/ably/ably-ios/pull/385 + // is merged. + [_reachability listenForHost:self.options.realtimeHost callback:^(BOOL reachable) { + if (reachable) { + switch (_connection.state) { + case ARTRealtimeDisconnected: + case ARTRealtimeSuspended: + [self transition:ARTRealtimeConnecting]; + default: + break; + } + } else { + switch (_connection.state) { + case ARTRealtimeConnecting: + case ARTRealtimeConnected: { + // TODO: Trigger host fallback behavior. + ARTErrorInfo *unreachable = [ARTErrorInfo createWithCode:-1003 message:@"unreachable host"]; + [self transition:ARTRealtimeDisconnected withErrorInfo:unreachable]; + break; + } + default: + break; + } + } + }]; + if (!_transport) { NSString *resumeKey = nil; NSNumber *connectionSerial = nil; @@ -430,26 +463,15 @@ - (void)onDisconnected { - (void)onDisconnected:(ARTProtocolMessage *)message { [self.logger info:@"R:%p ARTRealtime disconnected", self]; - switch (self.connection.state) { - case ARTRealtimeConnected: { - ARTErrorInfo *error; - if (message) { - error = message.error; - } - if (!_renewingToken && error && error.statusCode == 401 && error.code >= 40140 && error.code < 40150 && [self isTokenRenewable]) { - [self connectWithRenewedToken]; - return; - } - if (error) { - - } - [self transition:ARTRealtimeDisconnected withErrorInfo:error]; - break; - } - default: - NSAssert(false, @"Invalid Realtime state transitioning to Disconnected: expected Connected"); - break; + ARTErrorInfo *error; + if (message) { + error = message.error; + } + if (!_renewingToken && error && error.statusCode == 401 && error.code >= 40140 && error.code < 40150 && [self isTokenRenewable]) { + [self connectWithRenewedToken]; + return; } + [self transition:ARTRealtimeDisconnected withErrorInfo:error]; } - (void)onClosed { @@ -815,6 +837,10 @@ - (void)setTransportClass:(Class)transportClass { _transportClass = transportClass; } +- (void)setReachabilityClass:(Class)reachabilityClass { + _reachabilityClass = reachabilityClass; +} + + (NSString *)protocolStr:(ARTProtocolMessageAction) action { switch(action) { case ARTProtocolMessageHeartbeat: diff --git a/Source/AblyRealtime.h b/Source/AblyRealtime.h index 86667506f..7d73ce429 100644 --- a/Source/AblyRealtime.h +++ b/Source/AblyRealtime.h @@ -54,6 +54,8 @@ FOUNDATION_EXPORT const unsigned char ablyVersionString[]; #import "ARTJsonEncoder.h" #import "ARTMsgpackEncoder.h" #import "ARTPaginatedResult.h" +#import "ARTReachability.h" +#import "ARTOSReachability.h" #import "ARTNSDictionary+ARTDictionaryUtil.h" #import "ARTNSDate+ARTUtil.h" diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index 28faaf19a..f8259e6ea 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -396,20 +396,24 @@ class RealtimeClientConnection: QuickSpec { // TODO: ConnectionStateChange object var errorInfo: ARTErrorInfo? - waitUntil(timeout: testTimeout) { done in + waitUntil(timeout: testTimeout * 1000 ) { done in connection.on { stateChange in - let stateChange = stateChange! - let state = stateChange.current - let reason = stateChange.reason - switch state { - case .Connected: - client.onError(AblyTests.newErrorProtocolMessage()) - case .Failed: - errorInfo = reason - done() - default: - break + print("CHANGE \(stateChange!.current)") + if stateChange!.current == .Failed { + print("FAILED") } + // let stateChange = stateChange! + // let state = stateChange.current + // let reason = stateChange.reason + // switch state { + // case .Connected: + // client.onError(AblyTests.newErrorProtocolMessage()) + // case .Failed: + // errorInfo = reason + // done() + // default: + // break + // } } } @@ -2967,50 +2971,110 @@ class RealtimeClientConnection: QuickSpec { context("Operating System events for network/internet connectivity changes") { // RTN20a - pending("should immediately change the state to DISCONNECTED if the operating system indicates that the underlying internet connection is no longer available") { - let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 0.5 - let client = ARTRealtime(options: options) - defer { client.close() } - waitUntil(timeout: testTimeout) { done in - client.connection.once(.Connecting) { stateChange in - expect(stateChange!.reason).to(beNil()) - client.simulateOSEventNoInternetConnection() - done() - } + context("should immediately change the state to DISCONNECTED if the operating system indicates that the underlying internet connection is no longer available") { + var client: ARTRealtime! + + beforeEach { + let options = AblyTests.commonAppSetup() + options.autoConnect = false + client = ARTRealtime(options: options) + client.setReachabilityClass(TestReachability.self) } - waitUntil(timeout: testTimeout) { done in - client.connection.once(.Connected) { stateChange in - client.simulateOSEventNoInternetConnection() - done() + + afterEach { + client.close() + } + + it("when CONNECTING") { + waitUntil(timeout: testTimeout) { done in + client.connection.on { stateChange in + switch stateChange!.current { + case .Connecting: + expect(stateChange!.reason).to(beNil()) + guard let reachability = client.reachability as? TestReachability else { + fail("expected test reachability") + done(); return + } + expect(reachability.host).to(equal(client.options.realtimeHost)) + reachability.simulate(reachable: false) + case .Disconnected: + guard let reason = stateChange!.reason else { + fail("expected error reason") + done(); return + } + expect(reason.code).to(equal(-1003)) + done() + default: + break + } + } + client.connect() } } - waitUntil(timeout: testTimeout) { done in - client.connection.once(.Connecting) { stateChange in - expect(stateChange!.reason).toNot(beNil()) - done() + + it("when CONNECTED") { + waitUntil(timeout: testTimeout) { done in + client.connection.on { stateChange in + switch stateChange!.current { + case .Connected: + expect(stateChange!.reason).to(beNil()) + guard let reachability = client.reachability as? TestReachability else { + fail("expected test reachability") + done(); return + } + expect(reachability.host).to(equal(client.options.realtimeHost)) + reachability.simulate(reachable: false) + case .Disconnected: + guard let reason = stateChange!.reason else { + fail("expected error reason") + done(); return + } + expect(reason.code).to(equal(-1003)) + done() + default: + break + } + } + client.connect() } - done() } } // RTN20b - pending("should immediately attempt to connect if the operating system indicates that the underlying internet connection is now available when DISCONNECTED or SUSPENDED") { + it("should immediately attempt to connect if the operating system indicates that the underlying internet connection is now available when DISCONNECTED or SUSPENDED") { + var client: ARTRealtime! let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = testTimeout + 1.0 - let client = ARTRealtime(options: options) + // Ensure it won't reconnect because of timeouts. + options.disconnectedRetryTimeout = testTimeout + 10 + options.suspendedRetryTimeout = testTimeout + 10 + options.autoConnect = false + client = ARTRealtime(options: options) + client.setReachabilityClass(TestReachability.self) defer { client.close() } + waitUntil(timeout: testTimeout) { done in - client.connection.once(.Connecting) { stateChange in - expect(stateChange!.reason).to(beNil()) - client.simulateOSEventNoInternetConnection() - done() - } - } - waitUntil(timeout: testTimeout) { done in - client.connection.once(.Connected) { stateChange in + client.connection.on { stateChange in + switch stateChange!.current { + case .Connecting: + if stateChange!.previous == .Disconnected { + client.onSuspended() + } else if stateChange!.previous == .Suspended { + done() + } + case .Connected: + client.onDisconnected() + case .Disconnected, .Suspended: + guard let reachability = client.reachability as? TestReachability else { + fail("expected test reachability") + done(); return + } + expect(reachability.host).to(equal(client.options.realtimeHost)) + reachability.simulate(reachable: true) + default: + break + } } - client.simulateOSEventReachableInternetConnection() + client.connect() } } diff --git a/Spec/TestUtilities.swift b/Spec/TestUtilities.swift index 83922aba2..7d9d87f91 100644 --- a/Spec/TestUtilities.swift +++ b/Spec/TestUtilities.swift @@ -910,20 +910,6 @@ extension ARTRealtime { } } - func simulateOSEventNoInternetConnection() { - // TODO - // It depends on how the SystemConfiguration Reachablity APIs is used. - // Basic demonstration: https://developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html - self.onDisconnected() - } - - func simulateOSEventReachableInternetConnection() { - // TODO - // It depends on how the SystemConfiguration Reachablity APIs is used. - // Basic demonstration: https://developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html - //self.onConnecting() - } - func dispose() { let names = self.channels.map({ $0.name }) for name in names { @@ -1103,3 +1089,24 @@ extension String { return data } } + +@objc class TestReachability : NSObject, ARTReachability { + var host: String? + var callback: ((Bool) -> Void)? + + required init(logger: ARTLog) {} + + func listenForHost(host: String, callback: (Bool) -> Void) { + self.host = host + self.callback = callback + } + + func off() { + self.host = nil + self.callback = nil + } + + func simulate(reachable reachable: Bool) { + self.callback!(reachable) + } +} diff --git a/ably-common b/ably-common index 33d08824d..34b393747 160000 --- a/ably-common +++ b/ably-common @@ -1 +1 @@ -Subproject commit 33d08824d3ce42988305dcf6af63642e65239cd1 +Subproject commit 34b3937475b037e5827cafd1b6b1f3a57b7f06b2