From 982ae3709d35df2566458274dc736f0e64df024c Mon Sep 17 00:00:00 2001
From: Marat Al <m.alekperov@gmail.com>
Date: Sun, 19 May 2024 16:04:29 +0200
Subject: [PATCH] Got back `resumed` property in `ARTChannelStateChangeParams`
 along with the removed test (for RTL2f).

---
 Source/ARTRealtimeChannel.m                   |  3 +-
 .../Ably/ARTChannelStateChangeParams.h        |  5 ++
 Test/Tests/RealtimeClientChannelTests.swift   | 49 ++++++++++++++++++-
 3 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/Source/ARTRealtimeChannel.m b/Source/ARTRealtimeChannel.m
index 7b226c6b7..ed860c3ea 100644
--- a/Source/ARTRealtimeChannel.m
+++ b/Source/ARTRealtimeChannel.m
@@ -545,7 +545,7 @@ - (void)emit:(ARTChannelEvent)event with:(ARTChannelStateChange *)data {
 
 - (void)performTransitionToState:(ARTRealtimeChannelState)state withParams:(ARTChannelStateChangeParams *)params {
     ARTLogDebug(self.logger, @"RT:%p C:%p (%@) channel state transitions from %tu - %@ to %tu - %@%@", _realtime, self, self.name, self.state_nosync, ARTRealtimeChannelStateToStr(self.state_nosync), state, ARTRealtimeChannelStateToStr(state), params.retryAttempt ? [NSString stringWithFormat: @" (result of %@)", params.retryAttempt.id] : @"");
-    ARTChannelStateChange *stateChange = [[ARTChannelStateChange alloc] initWithCurrent:state previous:self.state_nosync event:(ARTChannelEvent)state reason:params.errorInfo resumed:NO retryAttempt:params.retryAttempt];
+    ARTChannelStateChange *stateChange = [[ARTChannelStateChange alloc] initWithCurrent:state previous:self.state_nosync event:(ARTChannelEvent)state reason:params.errorInfo resumed:params.resumed retryAttempt:params.retryAttempt];
     self.state = state;
 
     if (params.storeErrorInfo) {
@@ -680,6 +680,7 @@ - (void)setAttached:(ARTProtocolMessage *)message {
     } else {
         params = [[ARTChannelStateChangeParams alloc] initWithState:ARTStateOk];
     }
+    params.resumed = message.resumed;
     [self performTransitionToState:ARTRealtimeChannelAttached withParams:params];
     [self.presence onAttached:message];
     [_attachedEventEmitter emit:nil with:nil];
diff --git a/Source/PrivateHeaders/Ably/ARTChannelStateChangeParams.h b/Source/PrivateHeaders/Ably/ARTChannelStateChangeParams.h
index d5a24128a..3e3171d30 100644
--- a/Source/PrivateHeaders/Ably/ARTChannelStateChangeParams.h
+++ b/Source/PrivateHeaders/Ably/ARTChannelStateChangeParams.h
@@ -31,6 +31,11 @@ NS_SWIFT_NAME(ChannelStateChangeParams)
 
 @property (nullable, nonatomic, readonly) ARTRetryAttempt *retryAttempt;
 
+/**
+ The `resumed` value of the `ARTProtocolMessage` that triggered this state change.
+ */
+@property (nonatomic) BOOL resumed;
+
 - (instancetype)init NS_UNAVAILABLE;
 
 /**
diff --git a/Test/Tests/RealtimeClientChannelTests.swift b/Test/Tests/RealtimeClientChannelTests.swift
index 92254672e..714382006 100644
--- a/Test/Tests/RealtimeClientChannelTests.swift
+++ b/Test/Tests/RealtimeClientChannelTests.swift
@@ -497,8 +497,55 @@ class RealtimeClientChannelTests: XCTestCase {
         }
     }
 
-    // TODO: RTL2f
+    // RTL2f (connection resumption case)
+    func test__011__Channel__EventEmitter__channel_states_and_events__ChannelStateChange_will_contain_a_resumed_boolean_attribute_with_value__true__if_the_bit_flag_RESUMED_was_included() throws {
+        let test = Test()
+        let options = try AblyTests.commonAppSetup(for: test)
+        options.testOptions.transportFactory = TestProxyTransportFactory()
+        options.tokenDetails = try getTestTokenDetails(for: test, ttl: 5.0)
+        let client = ARTRealtime(options: options)
+        defer { client.dispose(); client.close() }
+        let channel = client.channels.get(test.uniqueChannelName())
 
+        waitUntil(timeout: testTimeout) { done in
+            let partialDone = AblyTests.splitDone(4, done: done)
+            channel.on { stateChange in
+                switch stateChange.current {
+                case .attached:
+                    XCTAssertFalse(stateChange.resumed)
+                    partialDone()
+                default:
+                    XCTAssertFalse(stateChange.resumed)
+                }
+            }
+            client.connection.once(.disconnected) { stateChange in
+                channel.off()
+                guard let error = stateChange.reason else {
+                    fail("Error is nil"); done(); return
+                }
+                XCTAssertEqual(error.code, ARTErrorCode.tokenExpired.intValue)
+                XCTAssertEqual(channel.state, ARTRealtimeChannelState.attached)
+                client.connection.once(.connected) { stateChange in
+                    XCTAssertEqual(channel.state, ARTRealtimeChannelState.attaching)
+                    partialDone()
+                }
+                channel.on { stateChange in
+                    switch stateChange.current {
+                    case .attached:
+                        XCTAssertTrue(stateChange.resumed)
+                        partialDone()
+                    default:
+                        XCTAssertFalse(stateChange.resumed)
+                    }
+                }
+                partialDone()
+            }
+            channel.attach()
+        }
+    }
+    
+    // TODO: RTL2f (connection recovery case)
+    
     // RTL3
 
     // RTL3a