Skip to content

Commit 590a5d8

Browse files
committed
Add annotations (in this PR limited to subscribe only to support Chat SDK reactions - ably/ably-chat-swift#293).
Reference implementation - ably/ably-js#1953 Docstrings - ably/specification#292 Annotation subscription (both summary and raw) is tested in ably/ably-chat-swift#293 (see integration test).
1 parent 26b23e1 commit 590a5d8

30 files changed

Lines changed: 1075 additions & 19 deletions

Ably.xcodeproj/project.pbxproj

Lines changed: 88 additions & 0 deletions
Large diffs are not rendered by default.

Source/ARTAnnotation.m

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#import "ARTAnnotation.h"
2+
#import "ARTJsonEncoder.h"
3+
#import "ARTJsonLikeEncoder.h"
4+
#import "ARTBaseMessage+Private.h"
5+
#import "ARTNSArray+ARTFunctional.h"
6+
#import "ARTInternalLog.h"
7+
8+
@implementation ARTAnnotation
9+
10+
- (instancetype)initWithType:(NSString *)type data:(id)data {
11+
if (self = [self init]) {
12+
self.type = [type copy];
13+
if (data) {
14+
self.data = data;
15+
self.encoding = @"";
16+
}
17+
}
18+
return self;
19+
}
20+
21+
- (instancetype)initWithType:(NSString *)type data:(id)data clientId:(NSString *)clientId {
22+
if (self = [self initWithType:type data:data]) {
23+
self.clientId = clientId;
24+
}
25+
return self;
26+
}
27+
28+
- (NSString *)description {
29+
NSMutableString *description = [[super description] mutableCopy];
30+
[description deleteCharactersInRange:NSMakeRange(description.length - (description.length>2 ? 2:0), 2)];
31+
[description appendFormat:@",\n"];
32+
[description appendFormat:@" action: %@\n", ARTAnnotationActionToStr(self.action)];
33+
[description appendFormat:@" serial: %@\n", self.serial];
34+
[description appendFormat:@" messageSerial: %@\n", self.messageSerial];
35+
[description appendFormat:@" type: %@\n", self.type];
36+
[description appendFormat:@" name: %@\n", self.name];
37+
[description appendFormat:@" count: %@\n", self.count];
38+
[description appendFormat:@"}"];
39+
return description;
40+
}
41+
42+
- (id)copyWithZone:(NSZone *)zone {
43+
ARTAnnotation *annotation = [super copyWithZone:zone];
44+
annotation.action = self.action;
45+
annotation.serial = self.serial;
46+
annotation.messageSerial = self.messageSerial;
47+
annotation.type = self.type;
48+
annotation.name = self.name;
49+
annotation.count = self.count;
50+
return annotation;
51+
}
52+
53+
- (NSInteger)messageSize {
54+
// spec is not clear here, issue - https://github.com/ably/specification/issues/336
55+
return [super messageSize];
56+
}
57+
58+
@end
59+
60+
@implementation ARTAnnotation (Decoding)
61+
62+
+ (instancetype)fromEncoded:(NSDictionary *)jsonObject channelOptions:(ARTChannelOptions *)options error:(NSError **)error {
63+
// TODO: implement
64+
return nil;
65+
}
66+
67+
+ (NSArray<ARTMessage *> *)fromEncodedArray:(NSArray<NSDictionary *> *)jsonArray channelOptions:(ARTChannelOptions *)options error:(NSError **)error {
68+
// TODO: implement
69+
return nil;
70+
}
71+
72+
@end
73+
74+
NSString *ARTAnnotationActionToStr(ARTAnnotationAction action) {
75+
switch (action) {
76+
case ARTAnnotationCreate:
77+
return @"Create"; //0
78+
case ARTAnnotationDelete:
79+
return @"Delete"; //1
80+
}
81+
return @"Unknown";
82+
}
83+
84+
#pragma mark - ARTEvent
85+
86+
@implementation ARTEvent (AnnotationAction)
87+
88+
- (instancetype)initWithAnnotationAction:(ARTAnnotationAction)value {
89+
return [self initWithString:[NSString stringWithFormat:@"ARTAnnotation%@", ARTAnnotationActionToStr(value)]];
90+
}
91+
92+
+ (instancetype)newWithAnnotationAction:(ARTAnnotationAction)value {
93+
return [[self alloc] initWithAnnotationAction:value];
94+
}
95+
96+
- (instancetype)initWithAnnotationType:(NSString *)type {
97+
return [self initWithString:[NSString stringWithFormat:@"ARTAnnotation:%@", type]];
98+
}
99+
100+
+ (instancetype)newWithAnnotationType:(NSString *)type {
101+
return [[self alloc] initWithAnnotationType:type];
102+
}
103+
104+
@end

Source/ARTBaseMessage.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ - (NSString *)description {
5757
[description appendFormat:@" timestamp: %@,\n", self.timestamp];
5858
[description appendFormat:@" encoding: %@,\n", self.encoding];
5959
[description appendFormat:@" data: %@\n", self.data];
60+
[description appendFormat:@" extras: %@\n", self.extras];
6061
[description appendFormat:@"}"];
6162
return description;
6263
}

Source/ARTJsonLikeEncoder.m

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#import "ARTMessage.h"
66
#import "ARTPresence.h"
77
#import "ARTPresenceMessage.h"
8+
#import "ARTAnnotation.h"
89
#import "ARTProtocolMessage.h"
910
#import "ARTProtocolMessage+Private.h"
1011
#import "ARTNSDictionary+ARTDictionaryUtil.h"
@@ -298,6 +299,7 @@ - (ARTMessage *)messageFromDictionary:(NSDictionary *)input protocolMessage:(ART
298299
if (operation && [operation isKindOfClass:[NSDictionary class]]) {
299300
message.operation = [ARTMessageOperation createFromDictionary:operation];
300301
}
302+
message.summary = [input objectForKey:@"summary"] ?: (message.action == ARTMessageActionMessageSummary ? @{} : nil); // enforce TM2q (https://ably-real-time.slack.com/archives/C03JDBVM5MY/p1749124425224069?thread_ts=1749051904.445159&cid=C03JDBVM5MY)
301303

302304
return message;
303305
}
@@ -334,7 +336,18 @@ - (ARTPresenceAction)presenceActionFromInt:(int) action
334336
}
335337
ARTLogError(_logger, @"RS:%p ARTJsonEncoder invalid ARTPresenceAction %d", _rest, action);
336338
return ARTPresenceAbsent;
337-
339+
}
340+
341+
- (ARTAnnotationAction)annotationActionFromInt:(int)action
342+
{
343+
switch (action) {
344+
case 0:
345+
return ARTAnnotationCreate;
346+
case 1:
347+
return ARTAnnotationDelete;
348+
}
349+
ARTLogError(_logger, @"RS:%p ARTJsonEncoder invalid ARTAnnotationAction %d", _rest, action);
350+
return ARTAnnotationCreate;
338351
}
339352

340353
- (int)intFromPresenceMessageAction:(ARTPresenceAction) action
@@ -390,6 +403,47 @@ - (NSArray *)presenceMessagesFromArray:(NSArray *)input {
390403
return output;
391404
}
392405

406+
- (ARTAnnotation *)annotationFromDictionary:(NSDictionary *)input {
407+
ARTLogVerbose(_logger, @"RS:%p ARTJsonLikeEncoder<%@>: annotationFromDictionary %@", _rest, [_delegate formatAsString], input);
408+
if (![input isKindOfClass:[NSDictionary class]]) {
409+
return nil;
410+
}
411+
ARTAnnotation *annotation = [[ARTAnnotation alloc] init];
412+
annotation.id = [input artString:@"id"];
413+
annotation.data = [input objectForKey:@"data"];
414+
annotation.encoding = [input artString:@"encoding"];
415+
annotation.clientId = [input artString:@"clientId"];
416+
annotation.timestamp = [input artTimestamp:@"timestamp"];
417+
annotation.connectionId = [input artString:@"connectionId"];
418+
419+
int action = [[input artNumber:@"action"] intValue];
420+
annotation.action = [self annotationActionFromInt:action];
421+
422+
annotation.name = [input artString:@"name"];
423+
annotation.type = [input artString:@"type"];
424+
annotation.serial = [input artString:@"serial"];
425+
annotation.messageSerial = [input artString:@"messageSerial"];
426+
annotation.count = [input artNumber:@"count"];
427+
428+
return annotation;
429+
}
430+
431+
- (NSArray *)annotationsFromArray:(NSArray *)input {
432+
if (![input isKindOfClass:[NSArray class]]) {
433+
return nil;
434+
}
435+
436+
NSMutableArray *output = [NSMutableArray array];
437+
for (NSDictionary *item in input) {
438+
ARTAnnotation *annotation = [self annotationFromDictionary:item];
439+
if (!annotation) {
440+
return nil;
441+
}
442+
[output addObject:annotation];
443+
}
444+
return output;
445+
}
446+
393447
- (NSDictionary *)messageToDictionary:(ARTMessage *)message {
394448
NSMutableDictionary *output = [NSMutableDictionary dictionary];
395449
if (message.id) {
@@ -765,6 +819,7 @@ - (ARTProtocolMessage *)protocolMessageFromDictionary:(NSDictionary *)input {
765819
NSMutableArray *messages = [[input objectForKey:@"messages"] mutableCopy];
766820
message.messages = [self messagesFromArray:messages protocolMessage:message];
767821
message.presence = [self presenceMessagesFromArray:[input objectForKey:@"presence"]];
822+
message.annotations = [self annotationsFromArray:[input objectForKey:@"annotations"]];
768823

769824
return message;
770825
}

Source/ARTMessage.m

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,16 @@ - (NSString *)description {
3030
[description deleteCharactersInRange:NSMakeRange(description.length - (description.length>2 ? 2:0), 2)];
3131
[description appendFormat:@",\n"];
3232
[description appendFormat:@" name: %@\n", self.name];
33-
if (self.extras) {
34-
[description appendFormat:@" extras: %@\n", self.extras];
35-
}
33+
[description appendFormat:@" action: %@\n", ARTMessageActionToStr(self.action)];
34+
[description appendFormat:@" serial: %@\n", self.serial];
35+
[description appendFormat:@" updateSerial: %@\n", self.updateSerial];
36+
[description appendFormat:@" version: %@\n", self.version];
37+
[description appendFormat:@" createdAt: %@\n", self.createdAt];
38+
[description appendFormat:@" updatedAt: %@\n", self.updatedAt];
39+
[description appendFormat:@" refType: %@\n", self.refType];
40+
[description appendFormat:@" refSerial: %@\n", self.refSerial];
41+
[description appendFormat:@" operation: %@\n", self.operation];
42+
[description appendFormat:@" summary: %@\n", self.summary];
3643
[description appendFormat:@"}"];
3744
return description;
3845
}
@@ -49,6 +56,7 @@ - (id)copyWithZone:(NSZone *)zone {
4956
message.operation = self.operation;
5057
message.refType = self.refType;
5158
message.refSerial = self.refSerial;
59+
message.summary = self.summary;
5260
return message;
5361
}
5462

@@ -59,6 +67,22 @@ - (NSInteger)messageSize {
5967

6068
@end
6169

70+
NSString *ARTMessageActionToStr(ARTMessageAction action) {
71+
switch (action) {
72+
case ARTMessageActionCreate:
73+
return @"Create";
74+
case ARTMessageActionUpdate:
75+
return @"Update";
76+
case ARTMessageActionDelete:
77+
return @"Delete";
78+
case ARTMessageActionMeta:
79+
return @"Meta";
80+
case ARTMessageActionMessageSummary:
81+
return @"Summary";
82+
}
83+
return @"Unknown";
84+
}
85+
6286
@implementation ARTMessage (Decoding)
6387

6488
+ (instancetype)fromEncoded:(NSDictionary *)jsonObject channelOptions:(ARTChannelOptions *)options error:(NSError **)error {

Source/ARTProtocolMessage.m

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ - (id)init {
2121
_timestamp = nil;
2222
_messages = nil;
2323
_presence = nil;
24+
_annotations = nil;
2425
_flags = 0;
2526
_error = nil;
2627
_connectionDetails = nil;
@@ -52,6 +53,7 @@ - (NSString *)description {
5253
[description appendFormat:@" flags.resumed: %@,\n", NSStringFromBOOL(self.resumed)];
5354
[description appendFormat:@" messages: %@\n", self.messages];
5455
[description appendFormat:@" presence: %@\n", self.presence];
56+
[description appendFormat:@" annotations: %@\n", self.annotations];
5557
[description appendFormat:@" params: %@\n", self.params];
5658
[description appendFormat:@"}"];
5759
return description;
@@ -70,6 +72,7 @@ - (id)copyWithZone:(NSZone *)zone {
7072
pm.timestamp = self.timestamp;
7173
pm.messages = self.messages;
7274
pm.presence = self.presence;
75+
pm.annotations = self.annotations;
7376
pm.flags = self.flags;
7477
pm.error = self.error;
7578
pm.connectionDetails = self.connectionDetails;
@@ -100,6 +103,9 @@ - (BOOL)mergeFrom:(ARTProtocolMessage *)src maxSize:(NSInteger)maxSize {
100103
case ARTProtocolMessagePresence:
101104
proposed = [self.presence arrayByAddingObjectsFromArray:src.presence];
102105
break;
106+
case ARTProtocolMessageAnnotation:
107+
proposed = [self.annotations arrayByAddingObjectsFromArray:src.annotations];
108+
break;
103109
default:
104110
proposed = nil;
105111
return NO;
@@ -120,6 +126,9 @@ - (BOOL)mergeFrom:(ARTProtocolMessage *)src maxSize:(NSInteger)maxSize {
120126
case ARTProtocolMessagePresence:
121127
self.presence = proposed;
122128
return YES;
129+
case ARTProtocolMessageAnnotation:
130+
self.annotations = proposed;
131+
return YES;
123132
default:
124133
return NO;
125134
}
@@ -215,6 +224,8 @@ - (ARTConnectionDetails *)getConnectionDetails {
215224
return @"Sync"; //16
216225
case ARTProtocolMessageAuth:
217226
return @"Auth"; //17
227+
case ARTProtocolMessageAnnotation:
228+
return @"Annotation"; //21
218229
}
219230

220231
// Because we blindly assign the action field of a ProtocolMessage received over the wire to a variable of type ARTProtocolMessageAction, we can't rely on the compiler's exhaustive checking of switch statements for ARTProtocolMessageAction.

0 commit comments

Comments
 (0)