Skip to content

Commit

Permalink
Added attachOnSubscribe to ARTRealtimeChannelOptions and updated …
Browse files Browse the repository at this point in the history
…tests (RTL7g, RTL7h, RTP6d, RTP6e).
  • Loading branch information
maratal committed Sep 21, 2024
1 parent 80f05fd commit d15fcc8
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 12 deletions.
8 changes: 6 additions & 2 deletions Source/ARTRealtimeChannel.m
Original file line number Diff line number Diff line change
Expand Up @@ -435,12 +435,16 @@ - (ARTEventListener *)_subscribe:(nullable NSString *)name onAttach:(nullable AR

__block ARTEventListener *listener = nil;
dispatch_sync(_queue, ^{
ARTRealtimeChannelOptions *options = self.getOptions_nosync;
BOOL attachOnSubscribe = options != nil ? options.attachOnSubscribe : true;
if (self.state_nosync == ARTRealtimeChannelFailed) {
if (onAttach) onAttach([ARTErrorInfo createWithCode:ARTErrorChannelOperationFailedInvalidState message:@"attempted to subscribe while channel is in FAILED state."]);
if (onAttach && attachOnSubscribe) { // RTL7h
onAttach([ARTErrorInfo createWithCode:ARTErrorChannelOperationFailedInvalidState message:@"attempted to subscribe while channel is in FAILED state."]);
}
ARTLogWarn(self.logger, @"R:%p C:%p (%@) subscribe of '%@' has been ignored (attempted to subscribe while channel is in FAILED state)", self->_realtime, self, self.name, name == nil ? @"all" : name);
return;
}
if (self.shouldAttach) { // RTL7c
if (self.shouldAttach && attachOnSubscribe) { // RTL7g
[self _attach:onAttach];
}
listener = name == nil ? [self.messagesEventEmitter on:cb] : [self.messagesEventEmitter on:name callback:cb];
Expand Down
28 changes: 28 additions & 0 deletions Source/ARTRealtimeChannelOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@
@implementation ARTRealtimeChannelOptions {
NSStringDictionary *_params;
ARTChannelMode _modes;
BOOL _attachOnSubscribe;
}

- (instancetype)init {
if (self = [super init]) {
_attachOnSubscribe = true;
}
return self;
}

- (instancetype)initWithCipher:(id<ARTCipherParamsCompatible>)cipherParams {
if (self = [super initWithCipher:cipherParams]) {
_attachOnSubscribe = true;
}
return self;
}

- (NSStringDictionary *)params {
Expand Down Expand Up @@ -32,4 +47,17 @@ - (void)setModes:(ARTChannelMode)modes {
_modes = modes;
}

- (BOOL)attachOnSubscribe {
return _attachOnSubscribe;
}

- (void)setAttachOnSubscribe:(BOOL)value {
if (self.isFrozen) {
@throw [NSException exceptionWithName:NSObjectInaccessibleException
reason:[NSString stringWithFormat:@"%@: You can't change options after you've passed it to receiver.", self.class]
userInfo:nil];
}
_attachOnSubscribe = value;
}

@end
9 changes: 7 additions & 2 deletions Source/ARTRealtimePresence.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#import "ARTProtocolMessage+Private.h"
#import "ARTEventEmitter+Private.h"
#import "ARTClientOptions.h"
#import "ARTRealtimeChannelOptions.h"

#pragma mark - ARTRealtimePresenceQuery

Expand Down Expand Up @@ -497,12 +498,16 @@ - (ARTEventListener *)_subscribe:(ARTPresenceAction)action onAttach:(nullable AR

__block ARTEventListener *listener = nil;
dispatch_sync(_queue, ^{
ARTRealtimeChannelOptions *options = self->_channel.getOptions_nosync;
BOOL attachOnSubscribe = options != nil ? options.attachOnSubscribe : true;
if (self->_channel.state_nosync == ARTRealtimeChannelFailed) {
if (onAttach) onAttach([ARTErrorInfo createWithCode:ARTErrorChannelOperationFailedInvalidState message:@"attempted to subscribe while channel is in Failed state."]);
if (onAttach && attachOnSubscribe) { // RTL7h
onAttach([ARTErrorInfo createWithCode:ARTErrorChannelOperationFailedInvalidState message:@"attempted to subscribe while channel is in Failed state."]);
}
ARTLogWarn(self.logger, @"R:%p C:%p (%@) presence subscribe to '%@' action(s) has been ignored (attempted to subscribe while channel is in FAILED state)", self->_realtime, self->_channel, self->_channel.name, ARTPresenceActionToStr(action));
return;
}
if (self->_channel.shouldAttach) { // RTP6c
if (self->_channel.shouldAttach && attachOnSubscribe) { // RTP6c
[self->_channel _attach:onAttach];
}
listener = action == ARTPresenceActionAll ? [_eventEmitter on:cb] : [_eventEmitter on:[ARTEvent newWithPresenceAction:action] callback:cb];
Expand Down
5 changes: 5 additions & 0 deletions Source/include/Ably/ARTRealtimeChannelOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic) ARTChannelMode modes;

/**
* Determines whether calling `subscribe` on a channel or presence object should trigger an implicit attach. Defaults to `true`.
*/
@property (nonatomic) BOOL attachOnSubscribe;

@end

NS_ASSUME_NONNULL_END
57 changes: 53 additions & 4 deletions Test/Tests/RealtimeClientChannelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3314,8 +3314,8 @@ class RealtimeClientChannelTests: XCTestCase {
}
}

// RTL7c
func test__109__Channel__subscribe__should_implicitly_attach_the_channel() throws {
// RTL7g
func test__109__Channel__subscribe__should_implicitly_attach_the_channel_if_options_attachOnSubscribe_is_true() throws {
let test = Test()
let client = ARTRealtime(options: try AblyTests.commonAppSetup(for: test))
defer { client.dispose(); client.close() }
Expand All @@ -3340,8 +3340,35 @@ class RealtimeClientChannelTests: XCTestCase {
expect(channel.state).toEventually(equal(ARTRealtimeChannelState.attached), timeout: testTimeout)
}

// RTL7c
func test__110__Channel__subscribe__should_result_in_an_error_if_channel_is_in_the_FAILED_state() throws {
// RTL7h
func test__109b__Channel__subscribe__should_not_implicitly_attach_the_channel_if_options_attachOnSubscribe_is_false() throws {
let test = Test()
let client = ARTRealtime(options: try AblyTests.commonAppSetup(for: test))
defer { client.dispose(); client.close() }

let channelOptions = ARTRealtimeChannelOptions()
channelOptions.attachOnSubscribe = false
let channel = client.channels.get(test.uniqueChannelName(), options: channelOptions)

let exception = tryInObjC {
channelOptions.attachOnSubscribe = true
}
XCTAssertNotNil(exception)
XCTAssertEqual(exception!.name, NSExceptionName.objectInaccessibleException)

// Initialized
XCTAssertEqual(channel.state, ARTRealtimeChannelState.initialized)
channel.subscribe(attachCallback: { errorInfo in
fail("Attach callback should not be called.")
}) { _ in }
// Make sure that channel stays initialized
delay(1) {
XCTAssertEqual(channel.state, ARTRealtimeChannelState.initialized)
}
}

// RTL7g
func test__110__Channel__subscribe__should_result_in_an_error_if_channel_is_in_the_FAILED_state_and_options_attachOnSubscribe_is_true() throws {
let test = Test()
let client = ARTRealtime(options: try AblyTests.commonAppSetup(for: test))
defer { client.dispose(); client.close() }
Expand All @@ -3362,6 +3389,28 @@ class RealtimeClientChannelTests: XCTestCase {
}
}

// RTL7g
func test__110b__Channel__subscribe__should_not_result_in_an_error_if_channel_is_in_the_FAILED_state_and_options_attachOnSubscribe_is_false() throws {
let test = Test()
let client = ARTRealtime(options: try AblyTests.commonAppSetup(for: test))
defer { client.dispose(); client.close() }

let channelOptions = ARTRealtimeChannelOptions()
channelOptions.attachOnSubscribe = false
let channel = client.channels.get(test.uniqueChannelName(), options: channelOptions)

channel.internal.onError(AblyTests.newErrorProtocolMessage())
XCTAssertEqual(channel.state, ARTRealtimeChannelState.failed)

channel.subscribe(attachCallback: { errorInfo in
fail("Attach callback should not be called.")
}) { _ in }
// Make sure that channel stays failed
delay(1) {
XCTAssertEqual(channel.state, ARTRealtimeChannelState.failed)
}
}

// RTL7d

func test__112__Channel__subscribe__should_deliver_the_message_even_if_there_is_an_error_while_decoding__using_crypto_data_128() throws {
Expand Down
51 changes: 47 additions & 4 deletions Test/Tests/RealtimeClientPresenceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -886,8 +886,8 @@ class RealtimeClientPresenceTests: XCTestCase {

// RTP6

// RTP6c
func test__026__Presence__subscribe__should_implicitly_attach_the_channel() throws {
// RTP6d
func test__026__Presence__subscribe__should_implicitly_attach_the_channel_if_options_attachOnSubscribe_is_true() throws {
let test = Test()
let client = ARTRealtime(options: try AblyTests.commonAppSetup(for: test))
defer { client.dispose(); client.close() }
Expand All @@ -912,8 +912,29 @@ class RealtimeClientPresenceTests: XCTestCase {
expect(channel.state).toEventually(equal(ARTRealtimeChannelState.attached), timeout: testTimeout)
}

// RTP6c
func test__027__Presence__subscribe__should_result_in_an_error_if_the_channel_is_in_the_FAILED_state() throws {
// RTP6d
func test__026b__Presence__subscribe__should_not_implicitly_attach_the_channel_if_options_attachOnSubscribe_is_false() throws {
let test = Test()
let client = ARTRealtime(options: try AblyTests.commonAppSetup(for: test))
defer { client.dispose(); client.close() }

let channelOptions = ARTRealtimeChannelOptions()
channelOptions.attachOnSubscribe = false
let channel = client.channels.get(test.uniqueChannelName(), options: channelOptions)

// Initialized
XCTAssertEqual(channel.state, ARTRealtimeChannelState.initialized)
channel.presence.subscribe(attachCallback: { errorInfo in
fail("Attach callback should not be called.")
}) { _ in }
// Make sure that channel stays initialized
delay(1) {
XCTAssertEqual(channel.state, ARTRealtimeChannelState.initialized)
}
}

// RTP6d
func test__027__Presence__subscribe__should_result_in_an_error_if_the_channel_is_in_the_FAILED_state_and_options_attachOnSubscribe_is_true() throws {
let test = Test()
let client = ARTRealtime(options: try AblyTests.commonAppSetup(for: test))
defer { client.dispose(); client.close() }
Expand All @@ -931,6 +952,28 @@ class RealtimeClientPresenceTests: XCTestCase {
})
}
}

// RTP6e
func test__027b__Presence__subscribe__should_not_result_in_an_error_if_the_channel_is_in_the_FAILED_state_and_options_attachOnSubscribe_is_false() throws {
let test = Test()
let client = ARTRealtime(options: try AblyTests.commonAppSetup(for: test))
defer { client.dispose(); client.close() }

let channelOptions = ARTRealtimeChannelOptions()
channelOptions.attachOnSubscribe = false
let channel = client.channels.get(test.uniqueChannelName(), options: channelOptions)

channel.internal.onError(AblyTests.newErrorProtocolMessage())
XCTAssertEqual(channel.state, ARTRealtimeChannelState.failed)

channel.presence.subscribe(attachCallback: { errorInfo in
fail("Attach callback should not be called.")
}) { _ in }
// Make sure that channel stays failed
delay(1) {
XCTAssertEqual(channel.state, ARTRealtimeChannelState.failed)
}
}

// RTP6c
func test__028__Presence__subscribe__should_result_in_an_error_if_the_channel_moves_to_the_FAILED_state() throws {
Expand Down

0 comments on commit d15fcc8

Please sign in to comment.