From 2de04bc01dfc9facbba38c88264e0cd4a0a977e5 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Fri, 21 Jul 2023 13:47:30 +0530 Subject: [PATCH 01/14] inbox fixes --- .../Inbox/controllers/CTInboxController.m | 76 ++++++++----------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 67bc65da..396c2460 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -1,4 +1,3 @@ - @import CoreData; #import "CTInboxController.h" #import "CTConstants.h" @@ -37,11 +36,11 @@ - (instancetype)initWithAccountId:(NSString *)accountId guid:(NSString *)guid { _guid = guid; NSString *userIdentifier = [NSString stringWithFormat:@"%@:%@", accountId, guid]; _userIdentifier = userIdentifier; - [privateContext performBlock:^{ - CT_TRY + [privateContext performBlockAndWait:^{ + self->_user = [CTUserMO fetchOrCreateFromJSON:@{@"accountId":accountId, @"guid":guid, @"identifier": userIdentifier} forContext:privateContext]; [self _save]; - CT_END_TRY + }]; } } @@ -87,14 +86,14 @@ - (void)staticInit { - (void)updateMessages:(NSArray *)messages { if (!self.isInitialized) return; [privateContext performBlock:^{ - CT_TRY + CleverTapLogStaticInternal(@"%@: updating messages: %@", self.user, messages); BOOL haveUpdates = [self.user updateMessages:messages forContext:privateContext]; if (haveUpdates) { [self _save]; [self notifyUpdate]; } - CT_END_TRY + }]; } @@ -127,20 +126,20 @@ - (void)deleteMessagesWithId:(NSArray *_Nonnull)messageIds { - (void)markReadMessageWithId:(NSString *)messageId { [privateContext performBlock:^{ - CT_TRY + CTMessageMO *message = [self _messageForId:messageId]; if (message) { [message setValue:@YES forKey:@"isRead"]; [self _save]; [self notifyUpdate]; } - CT_END_TRY + }]; } - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { [privateContext performBlock:^{ - CT_TRY + for (NSString *ids in messageIds) { if (ids != nil && ![ids isEqualToString:@""]){ CTMessageMO *message = [self _messageForId:ids]; @@ -157,7 +156,7 @@ - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { } [self _save]; [self notifyUpdate]; - CT_END_TRY + }]; } @@ -189,8 +188,8 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - [privateContext performBlock:^{ - CT_TRY + [privateContext performBlockAndWait:^{ + for (CTMessageMO *msg in self.user.messages) { int ttl = (int)msg.expires; if (ttl > 0 && now >= ttl) { @@ -206,7 +205,7 @@ - (NSInteger)unreadCount { if ([toDelete count] > 0) { [self _deleteMessages:toDelete]; } - CT_END_TRY + }]; return messages; } @@ -221,8 +220,8 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - [privateContext performBlock:^{ - CT_TRY + [privateContext performBlockAndWait:^{ + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; for (CTMessageMO *msg in results) { int ttl = (int)msg.expires; @@ -239,7 +238,7 @@ - (NSInteger)unreadCount { if ([toDelete count] > 0) { [self _deleteMessages:toDelete]; } - CT_END_TRY + }]; @@ -256,10 +255,10 @@ - (CTMessageMO *)_messageForId:(NSString *)messageId { if (!hasMessages) return nil; __block NSOrderedSet *results; - [privateContext performBlock:^{ - CT_TRY + [privateContext performBlockAndWait:^{ + results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; - CT_END_TRY + }]; BOOL existing = results && [results count] > 0; @@ -267,42 +266,29 @@ - (CTMessageMO *)_messageForId:(NSString *)messageId { } - (void)_deleteMessages:(NSArray*)messages { - [privateContext performBlock:^{ - CT_TRY + [privateContext performBlockAndWait:^{ + for (CTMessageMO *msg in messages) { [privateContext deleteObject:msg]; } [self _save]; [self notifyUpdate]; - CT_END_TRY + }]; } // always call from inside privateContext performBlock - (BOOL)_save { - __block BOOL res = YES; - [privateContext performBlock:^{ - CT_TRY - NSError *error = nil; - if ([privateContext hasChanges]) { - res = [privateContext save:&error]; - } - if (!res) { - CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); - } - CT_END_TRY - }]; - [mainContext performBlock:^{ - CT_TRY - NSError *error = nil; - if ([mainContext hasChanges]) { - res = [mainContext save:&error]; - } - if (!res) { - CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); - } - CT_END_TRY - }]; + NSError *error = nil; + BOOL res = YES; + res = [privateContext save:&error]; + if (!res) { + CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); + } + res = [mainContext save:&error]; + if (!res) { + CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); + } return res; } From 6c4dc23bc70e841307d047317be40b0b45902a50 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Fri, 21 Jul 2023 22:01:29 +0530 Subject: [PATCH 02/14] minor changes --- .../Inbox/controllers/CTInboxController.m | 91 +++++++------------ 1 file changed, 33 insertions(+), 58 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 396c2460..33de3038 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -1,3 +1,4 @@ + @import CoreData; #import "CTInboxController.h" #import "CTConstants.h" @@ -37,10 +38,8 @@ - (instancetype)initWithAccountId:(NSString *)accountId guid:(NSString *)guid { NSString *userIdentifier = [NSString stringWithFormat:@"%@:%@", accountId, guid]; _userIdentifier = userIdentifier; [privateContext performBlockAndWait:^{ - self->_user = [CTUserMO fetchOrCreateFromJSON:@{@"accountId":accountId, @"guid":guid, @"identifier": userIdentifier} forContext:privateContext]; [self _save]; - }]; } } @@ -86,14 +85,12 @@ - (void)staticInit { - (void)updateMessages:(NSArray *)messages { if (!self.isInitialized) return; [privateContext performBlock:^{ - CleverTapLogStaticInternal(@"%@: updating messages: %@", self.user, messages); BOOL haveUpdates = [self.user updateMessages:messages forContext:privateContext]; if (haveUpdates) { [self _save]; [self notifyUpdate]; } - }]; } @@ -126,20 +123,18 @@ - (void)deleteMessagesWithId:(NSArray *_Nonnull)messageIds { - (void)markReadMessageWithId:(NSString *)messageId { [privateContext performBlock:^{ - CTMessageMO *message = [self _messageForId:messageId]; if (message) { [message setValue:@YES forKey:@"isRead"]; [self _save]; [self notifyUpdate]; } - }]; } - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { [privateContext performBlock:^{ - + for (NSString *ids in messageIds) { if (ids != nil && ![ids isEqualToString:@""]){ CTMessageMO *message = [self _messageForId:ids]; @@ -156,7 +151,6 @@ - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { } [self _save]; [self notifyUpdate]; - }]; } @@ -187,26 +181,22 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - - [privateContext performBlockAndWait:^{ - - for (CTMessageMO *msg in self.user.messages) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; - } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; + + for (CTMessageMO *msg in self.user.messages) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; } - - }]; + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } return messages; } @@ -216,32 +206,25 @@ - (NSInteger)unreadCount { NSMutableArray *messages = [NSMutableArray new]; NSMutableArray *toDelete = [NSMutableArray new]; - BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - [privateContext performBlockAndWait:^{ - - NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; - for (CTMessageMO *msg in results) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; - } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; + for (CTMessageMO *msg in results) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; } - - }]; - + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } return messages; } @@ -253,27 +236,19 @@ - (CTMessageMO *)_messageForId:(NSString *)messageId { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - __block NSOrderedSet *results; - - [privateContext performBlockAndWait:^{ - - results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; - - }]; - + + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; BOOL existing = results && [results count] > 0; return existing ? results[0] : nil; } - (void)_deleteMessages:(NSArray*)messages { [privateContext performBlockAndWait:^{ - for (CTMessageMO *msg in messages) { [privateContext deleteObject:msg]; } [self _save]; [self notifyUpdate]; - }]; } From 859ccf76a26cf6781fd26109ebe05480a7f691a6 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Mon, 24 Jul 2023 09:12:18 +0530 Subject: [PATCH 03/14] added a check via hasChanges method to only save context if changes exist --- CleverTapSDK/Inbox/controllers/CTInboxController.m | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 33de3038..8c8578fb 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -122,6 +122,10 @@ - (void)deleteMessagesWithId:(NSArray *_Nonnull)messageIds { } - (void)markReadMessageWithId:(NSString *)messageId { + if (!messageId || messageId.length == 0) { + CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or empty."); + return; + } [privateContext performBlock:^{ CTMessageMO *message = [self _messageForId:messageId]; if (message) { @@ -134,7 +138,6 @@ - (void)markReadMessageWithId:(NSString *)messageId { - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { [privateContext performBlock:^{ - for (NSString *ids in messageIds) { if (ids != nil && ![ids isEqualToString:@""]){ CTMessageMO *message = [self _messageForId:ids]; @@ -256,11 +259,15 @@ - (void)_deleteMessages:(NSArray*)messages { - (BOOL)_save { NSError *error = nil; BOOL res = YES; - res = [privateContext save:&error]; + if ([privateContext hasChanges]) { + res = [privateContext save:&error]; + } if (!res) { CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); } - res = [mainContext save:&error]; + if ([mainContext hasChanges]) { + res = [mainContext save:&error]; + } if (!res) { CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); } From 8c7855e23c25268a2e1209e093daa8a94cc03a17 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Mon, 24 Jul 2023 10:35:19 +0530 Subject: [PATCH 04/14] minor fixes --- .../Inbox/controllers/CTInboxController.m | 57 ++++++++++++------- .../models/CTMessageMO+CoreDataProperties.m | 12 ++-- 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 8c8578fb..a2c21a53 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -126,7 +126,7 @@ - (void)markReadMessageWithId:(NSString *)messageId { CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or empty."); return; } - [privateContext performBlock:^{ + [privateContext performBlockAndWait:^{ CTMessageMO *message = [self _messageForId:messageId]; if (message) { [message setValue:@YES forKey:@"isRead"]; @@ -184,22 +184,24 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - - for (CTMessageMO *msg in self.user.messages) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; - } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; - } + [privateContext performBlockAndWait:^{ + for (CTMessageMO *msg in self.user.messages) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; + } + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } + }]; return messages; } @@ -212,6 +214,7 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; + [privateContext performBlockAndWait:^{ NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; for (CTMessageMO *msg in results) { int ttl = (int)msg.expires; @@ -228,6 +231,7 @@ - (NSInteger)unreadCount { if ([toDelete count] > 0) { [self _deleteMessages:toDelete]; } + }]; return messages; } @@ -239,9 +243,13 @@ - (CTMessageMO *)_messageForId:(NSString *)messageId { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - - NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; - BOOL existing = results && [results count] > 0; + __block BOOL existing; + __block NSOrderedSet *results; + + [privateContext performBlockAndWait:^{ + results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; + existing = (results && [results count] > 0); + }]; return existing ? results[0] : nil; } @@ -265,9 +273,14 @@ - (BOOL)_save { if (!res) { CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); } - if ([mainContext hasChanges]) { - res = [mainContext save:&error]; - } + + [mainContext performBlock:^{ + if ([mainContext hasChanges]) { + NSError *error = nil; + [mainContext save:&error]; + } + }]; + if (!res) { CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); } diff --git a/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m b/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m index 88eb9b94..ff1aa538 100755 --- a/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m +++ b/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m @@ -37,10 +37,14 @@ - (instancetype)initWithJSON:(NSDictionary *)json forContext:(NSManagedObjectCon } - (NSDictionary *)toJSON { - NSMutableDictionary *json = [NSMutableDictionary dictionaryWithDictionary:self.json]; - json[@"isRead"] = @(self.isRead); - json[@"date"] = @(self.date); - return json; + __block NSDictionary *json = nil; + [self.managedObjectContext performBlockAndWait:^{ + NSMutableDictionary *mutableJson = [NSMutableDictionary dictionaryWithDictionary:self.json]; + [mutableJson setObject:@(self.isRead) forKey:@"isRead"]; + [mutableJson setObject:@(self.date) forKey:@"date"]; + json = [NSDictionary dictionaryWithDictionary:mutableJson]; + }]; + return json; } @dynamic date; From e35436e4f869f31013208023e4131b270c5b2750 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Wed, 26 Jul 2023 13:10:58 +0530 Subject: [PATCH 05/14] minor fixes --- .../Inbox/controllers/CTInboxController.m | 65 ++++++++++--------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index a2c21a53..026fa4ec 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -126,14 +126,16 @@ - (void)markReadMessageWithId:(NSString *)messageId { CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or empty."); return; } + __block CTMessageMO *message; [privateContext performBlockAndWait:^{ - CTMessageMO *message = [self _messageForId:messageId]; - if (message) { - [message setValue:@YES forKey:@"isRead"]; - [self _save]; - [self notifyUpdate]; - } + message = [self _messageForId:messageId]; }]; + if (message) { + [message setValue:@YES forKey:@"isRead"]; + [self _save]; + [self notifyUpdate]; + } + } - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { @@ -215,22 +217,22 @@ - (NSInteger)unreadCount { if (!hasMessages) return nil; [privateContext performBlockAndWait:^{ - NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; - for (CTMessageMO *msg in results) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; + for (CTMessageMO *msg in results) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; + } + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; - } }]; return messages; } @@ -247,8 +249,8 @@ - (CTMessageMO *)_messageForId:(NSString *)messageId { __block NSOrderedSet *results; [privateContext performBlockAndWait:^{ - results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; - existing = (results && [results count] > 0); + results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; + existing = (results && [results count] > 0); }]; return existing ? results[0] : nil; } @@ -258,9 +260,10 @@ - (void)_deleteMessages:(NSArray*)messages { for (CTMessageMO *msg in messages) { [privateContext deleteObject:msg]; } - [self _save]; - [self notifyUpdate]; }]; + [self _save]; + [self notifyUpdate]; + } // always call from inside privateContext performBlock @@ -274,12 +277,12 @@ - (BOOL)_save { CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); } - [mainContext performBlock:^{ - if ([mainContext hasChanges]) { - NSError *error = nil; - [mainContext save:&error]; - } - }]; + [mainContext performBlock:^{ + if ([mainContext hasChanges]) { + NSError *error = nil; + [mainContext save:&error]; + } + }]; if (!res) { CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); From 432ce661037357121653fabbf69e9fbabe84c64e Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Wed, 26 Jul 2023 14:25:43 +0530 Subject: [PATCH 06/14] minor changes --- .../Inbox/controllers/CTInboxController.m | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 026fa4ec..21211479 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -129,12 +129,13 @@ - (void)markReadMessageWithId:(NSString *)messageId { __block CTMessageMO *message; [privateContext performBlockAndWait:^{ message = [self _messageForId:messageId]; + if (message) { + [message setValue:@YES forKey:@"isRead"]; + } }]; - if (message) { - [message setValue:@YES forKey:@"isRead"]; - [self _save]; - [self notifyUpdate]; - } + + [self _save]; + [self notifyUpdate]; } @@ -268,21 +269,25 @@ - (void)_deleteMessages:(NSArray*)messages { // always call from inside privateContext performBlock - (BOOL)_save { - NSError *error = nil; - BOOL res = YES; - if ([privateContext hasChanges]) { - res = [privateContext save:&error]; - } + __block NSError *error = nil; + __block BOOL res = YES; + [privateContext performBlockAndWait:^{ + if ([privateContext hasChanges]) { + res = [privateContext save:&error]; + [mainContext performBlock:^{ + if ([mainContext hasChanges]) { + NSError *error = nil; + [mainContext save:&error]; + } + }]; + + } + }]; if (!res) { CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); } - [mainContext performBlock:^{ - if ([mainContext hasChanges]) { - NSError *error = nil; - [mainContext save:&error]; - } - }]; + if (!res) { CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); From 62a44e5dfbfd4b517b9cee84dd218a1fd0353715 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Wed, 26 Jul 2023 17:11:10 +0530 Subject: [PATCH 07/14] minor fixes --- CleverTapSDK/Inbox/controllers/CTInboxController.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 21211479..b1f17e6f 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -140,7 +140,7 @@ - (void)markReadMessageWithId:(NSString *)messageId { } - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { - [privateContext performBlock:^{ + [privateContext performBlockAndWait:^{ for (NSString *ids in messageIds) { if (ids != nil && ![ids isEqualToString:@""]){ CTMessageMO *message = [self _messageForId:ids]; @@ -155,9 +155,10 @@ - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or not a string."); } } + }]; [self _save]; [self notifyUpdate]; - }]; + } - (NSDictionary *)messageForId:(NSString *)messageId { From b42449e155f86d7f9e3bd2dd104e2acdd77f4b48 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Wed, 26 Jul 2023 18:26:39 +0530 Subject: [PATCH 08/14] updated versions and changelog --- CHANGELOG.md | 5 +++++ CleverTapSDK/CleverTapBuildInfo.h | 2 +- sdk-version.txt | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8aa6928..89264da4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log All notable changes to this project will be documented in this file. +### [Version 5.1.2](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/5.1.2) (July 27, 2023) + +#### Fixed +- Fixed a bug where the App Inbox would appear empty. + ### [Version 5.1.1](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/5.1.1) (July 13, 2023) #### Fixed diff --git a/CleverTapSDK/CleverTapBuildInfo.h b/CleverTapSDK/CleverTapBuildInfo.h index 4b78e0a6..5abb90be 100644 --- a/CleverTapSDK/CleverTapBuildInfo.h +++ b/CleverTapSDK/CleverTapBuildInfo.h @@ -1 +1 @@ -#define WR_SDK_REVISION @"50101" +#define WR_SDK_REVISION @"50102" diff --git a/sdk-version.txt b/sdk-version.txt index 3bff0591..1b47e8f3 100644 --- a/sdk-version.txt +++ b/sdk-version.txt @@ -1 +1 @@ -5.1.1 \ No newline at end of file +5.1.2 \ No newline at end of file From f41f0950097091f8a6a4bc5721f528b291417a84 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Wed, 26 Jul 2023 18:42:41 +0530 Subject: [PATCH 09/14] removed duplicate code --- CleverTapSDK/Inbox/controllers/CTInboxController.m | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index b1f17e6f..5c29116b 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -287,12 +287,6 @@ - (BOOL)_save { if (!res) { CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); } - - - - if (!res) { - CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); - } return res; } From fe39a7860074af3580969a8134c202b9d2b54a47 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Thu, 27 Jul 2023 11:41:05 +0530 Subject: [PATCH 10/14] minor changes --- .../Inbox/controllers/CTInboxController.m | 154 ++++++++---------- .../models/CTMessageMO+CoreDataProperties.m | 12 +- 2 files changed, 69 insertions(+), 97 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 5c29116b..335807c9 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -102,45 +102,38 @@ - (void)deleteMessageWithId:(NSString *)messageId { - (void)deleteMessagesWithId:(NSArray *_Nonnull)messageIds { NSMutableArray *toDeleteInboxMessages = [NSMutableArray new]; - for (NSString *ids in messageIds) { - if (ids != nil && ![ids isEqualToString:@""]){ - CTMessageMO *msg = [self _messageForId:ids]; - if (msg) { - [toDeleteInboxMessages addObject:msg]; - } - else { - CleverTapLogStaticDebug(@"Cannot delete App Inbox Message because Message ID %@ is invalid.", ids) - } - } - else { - CleverTapLogStaticDebug(@"Cannot delete App Inbox Message because Message ID is null or not a string."); - } - } - if ([toDeleteInboxMessages count] > 0) { - [self _deleteMessages:toDeleteInboxMessages]; - } + for (NSString *ids in messageIds) { + if (ids != nil && ![ids isEqualToString:@""]){ + CTMessageMO *msg = [self _messageForId:ids]; + if (msg) { + [toDeleteInboxMessages addObject:msg]; + } + else { + CleverTapLogStaticDebug(@"Cannot delete App Inbox Message because Message ID %@ is invalid.", ids) + } + } + else { + CleverTapLogStaticDebug(@"Cannot delete App Inbox Message because Message ID is null or not a string."); + } + } + if ([toDeleteInboxMessages count] > 0) { + [self _deleteMessages:toDeleteInboxMessages]; + } } - (void)markReadMessageWithId:(NSString *)messageId { - if (!messageId || messageId.length == 0) { - CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or empty."); - return; - } - __block CTMessageMO *message; - [privateContext performBlockAndWait:^{ - message = [self _messageForId:messageId]; + [privateContext performBlock:^{ + CTMessageMO *message = [self _messageForId:messageId]; if (message) { [message setValue:@YES forKey:@"isRead"]; + [self _save]; + [self notifyUpdate]; } }]; - - [self _save]; - [self notifyUpdate]; - } - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { - [privateContext performBlockAndWait:^{ + [privateContext performBlock:^{ for (NSString *ids in messageIds) { if (ids != nil && ![ids isEqualToString:@""]){ CTMessageMO *message = [self _messageForId:ids]; @@ -155,10 +148,9 @@ - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or not a string."); } } - }]; [self _save]; [self notifyUpdate]; - + }]; } - (NSDictionary *)messageForId:(NSString *)messageId { @@ -188,24 +180,22 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - - [privateContext performBlockAndWait:^{ - for (CTMessageMO *msg in self.user.messages) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; - } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; + + for (CTMessageMO *msg in self.user.messages) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; } - }]; + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } return messages; } @@ -218,24 +208,22 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - [privateContext performBlockAndWait:^{ - NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; - for (CTMessageMO *msg in results) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; - } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; + for (CTMessageMO *msg in results) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; } - }]; + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } return messages; } @@ -247,13 +235,9 @@ - (CTMessageMO *)_messageForId:(NSString *)messageId { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - __block BOOL existing; - __block NSOrderedSet *results; - - [privateContext performBlockAndWait:^{ - results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; - existing = (results && [results count] > 0); - }]; + + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; + BOOL existing = results && [results count] > 0; return existing ? results[0] : nil; } @@ -262,31 +246,23 @@ - (void)_deleteMessages:(NSArray*)messages { for (CTMessageMO *msg in messages) { [privateContext deleteObject:msg]; } + [self _save]; + [self notifyUpdate]; }]; - [self _save]; - [self notifyUpdate]; - } // always call from inside privateContext performBlock - (BOOL)_save { - __block NSError *error = nil; - __block BOOL res = YES; - [privateContext performBlockAndWait:^{ - if ([privateContext hasChanges]) { - res = [privateContext save:&error]; - [mainContext performBlock:^{ - if ([mainContext hasChanges]) { - NSError *error = nil; - [mainContext save:&error]; - } - }]; - - } - }]; + NSError *error = nil; + BOOL res = YES; + res = [privateContext save:&error]; if (!res) { CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); } + res = [mainContext save:&error]; + if (!res) { + CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); + } return res; } diff --git a/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m b/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m index ff1aa538..88eb9b94 100755 --- a/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m +++ b/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m @@ -37,14 +37,10 @@ - (instancetype)initWithJSON:(NSDictionary *)json forContext:(NSManagedObjectCon } - (NSDictionary *)toJSON { - __block NSDictionary *json = nil; - [self.managedObjectContext performBlockAndWait:^{ - NSMutableDictionary *mutableJson = [NSMutableDictionary dictionaryWithDictionary:self.json]; - [mutableJson setObject:@(self.isRead) forKey:@"isRead"]; - [mutableJson setObject:@(self.date) forKey:@"date"]; - json = [NSDictionary dictionaryWithDictionary:mutableJson]; - }]; - return json; + NSMutableDictionary *json = [NSMutableDictionary dictionaryWithDictionary:self.json]; + json[@"isRead"] = @(self.isRead); + json[@"date"] = @(self.date); + return json; } @dynamic date; From 464a373cc32b6fde2453ff5674935cb2c5bd7b63 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Thu, 27 Jul 2023 14:07:40 +0530 Subject: [PATCH 11/14] Revert "minor changes" This reverts commit fe39a7860074af3580969a8134c202b9d2b54a47. --- .../Inbox/controllers/CTInboxController.m | 154 ++++++++++-------- .../models/CTMessageMO+CoreDataProperties.m | 12 +- 2 files changed, 97 insertions(+), 69 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 335807c9..5c29116b 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -102,38 +102,45 @@ - (void)deleteMessageWithId:(NSString *)messageId { - (void)deleteMessagesWithId:(NSArray *_Nonnull)messageIds { NSMutableArray *toDeleteInboxMessages = [NSMutableArray new]; - for (NSString *ids in messageIds) { - if (ids != nil && ![ids isEqualToString:@""]){ - CTMessageMO *msg = [self _messageForId:ids]; - if (msg) { - [toDeleteInboxMessages addObject:msg]; - } - else { - CleverTapLogStaticDebug(@"Cannot delete App Inbox Message because Message ID %@ is invalid.", ids) - } - } - else { - CleverTapLogStaticDebug(@"Cannot delete App Inbox Message because Message ID is null or not a string."); - } - } - if ([toDeleteInboxMessages count] > 0) { - [self _deleteMessages:toDeleteInboxMessages]; - } + for (NSString *ids in messageIds) { + if (ids != nil && ![ids isEqualToString:@""]){ + CTMessageMO *msg = [self _messageForId:ids]; + if (msg) { + [toDeleteInboxMessages addObject:msg]; + } + else { + CleverTapLogStaticDebug(@"Cannot delete App Inbox Message because Message ID %@ is invalid.", ids) + } + } + else { + CleverTapLogStaticDebug(@"Cannot delete App Inbox Message because Message ID is null or not a string."); + } + } + if ([toDeleteInboxMessages count] > 0) { + [self _deleteMessages:toDeleteInboxMessages]; + } } - (void)markReadMessageWithId:(NSString *)messageId { - [privateContext performBlock:^{ - CTMessageMO *message = [self _messageForId:messageId]; + if (!messageId || messageId.length == 0) { + CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or empty."); + return; + } + __block CTMessageMO *message; + [privateContext performBlockAndWait:^{ + message = [self _messageForId:messageId]; if (message) { [message setValue:@YES forKey:@"isRead"]; - [self _save]; - [self notifyUpdate]; } }]; + + [self _save]; + [self notifyUpdate]; + } - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { - [privateContext performBlock:^{ + [privateContext performBlockAndWait:^{ for (NSString *ids in messageIds) { if (ids != nil && ![ids isEqualToString:@""]){ CTMessageMO *message = [self _messageForId:ids]; @@ -148,9 +155,10 @@ - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or not a string."); } } + }]; [self _save]; [self notifyUpdate]; - }]; + } - (NSDictionary *)messageForId:(NSString *)messageId { @@ -180,22 +188,24 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - - for (CTMessageMO *msg in self.user.messages) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; - } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; - } + [privateContext performBlockAndWait:^{ + for (CTMessageMO *msg in self.user.messages) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; + } + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } + }]; return messages; } @@ -208,22 +218,24 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; - for (CTMessageMO *msg in results) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; + [privateContext performBlockAndWait:^{ + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; + for (CTMessageMO *msg in results) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; + } } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; - } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } + }]; return messages; } @@ -235,9 +247,13 @@ - (CTMessageMO *)_messageForId:(NSString *)messageId { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - - NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; - BOOL existing = results && [results count] > 0; + __block BOOL existing; + __block NSOrderedSet *results; + + [privateContext performBlockAndWait:^{ + results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; + existing = (results && [results count] > 0); + }]; return existing ? results[0] : nil; } @@ -246,23 +262,31 @@ - (void)_deleteMessages:(NSArray*)messages { for (CTMessageMO *msg in messages) { [privateContext deleteObject:msg]; } - [self _save]; - [self notifyUpdate]; }]; + [self _save]; + [self notifyUpdate]; + } // always call from inside privateContext performBlock - (BOOL)_save { - NSError *error = nil; - BOOL res = YES; - res = [privateContext save:&error]; + __block NSError *error = nil; + __block BOOL res = YES; + [privateContext performBlockAndWait:^{ + if ([privateContext hasChanges]) { + res = [privateContext save:&error]; + [mainContext performBlock:^{ + if ([mainContext hasChanges]) { + NSError *error = nil; + [mainContext save:&error]; + } + }]; + + } + }]; if (!res) { CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); } - res = [mainContext save:&error]; - if (!res) { - CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); - } return res; } diff --git a/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m b/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m index 88eb9b94..ff1aa538 100755 --- a/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m +++ b/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m @@ -37,10 +37,14 @@ - (instancetype)initWithJSON:(NSDictionary *)json forContext:(NSManagedObjectCon } - (NSDictionary *)toJSON { - NSMutableDictionary *json = [NSMutableDictionary dictionaryWithDictionary:self.json]; - json[@"isRead"] = @(self.isRead); - json[@"date"] = @(self.date); - return json; + __block NSDictionary *json = nil; + [self.managedObjectContext performBlockAndWait:^{ + NSMutableDictionary *mutableJson = [NSMutableDictionary dictionaryWithDictionary:self.json]; + [mutableJson setObject:@(self.isRead) forKey:@"isRead"]; + [mutableJson setObject:@(self.date) forKey:@"date"]; + json = [NSDictionary dictionaryWithDictionary:mutableJson]; + }]; + return json; } @dynamic date; From 6e5ddb7ba9b15a338079828f372f2e7633d4e1c4 Mon Sep 17 00:00:00 2001 From: Akash Malhotra <> Date: Fri, 28 Jul 2023 10:29:46 +0530 Subject: [PATCH 12/14] minor fixes --- .../Inbox/controllers/CTInboxController.m | 116 +++++++----------- .../models/CTMessageMO+CoreDataProperties.m | 12 +- 2 files changed, 50 insertions(+), 78 deletions(-) diff --git a/CleverTapSDK/Inbox/controllers/CTInboxController.m b/CleverTapSDK/Inbox/controllers/CTInboxController.m index 5c29116b..aa0652db 100755 --- a/CleverTapSDK/Inbox/controllers/CTInboxController.m +++ b/CleverTapSDK/Inbox/controllers/CTInboxController.m @@ -122,25 +122,18 @@ - (void)deleteMessagesWithId:(NSArray *_Nonnull)messageIds { } - (void)markReadMessageWithId:(NSString *)messageId { - if (!messageId || messageId.length == 0) { - CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or empty."); - return; - } - __block CTMessageMO *message; - [privateContext performBlockAndWait:^{ - message = [self _messageForId:messageId]; + [privateContext performBlock:^{ + CTMessageMO *message = [self _messageForId:messageId]; if (message) { [message setValue:@YES forKey:@"isRead"]; + [self _save]; + [self notifyUpdate]; } }]; - - [self _save]; - [self notifyUpdate]; - } - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { - [privateContext performBlockAndWait:^{ + [privateContext performBlock:^{ for (NSString *ids in messageIds) { if (ids != nil && ![ids isEqualToString:@""]){ CTMessageMO *message = [self _messageForId:ids]; @@ -155,10 +148,9 @@ - (void)markReadMessagesWithId:(NSArray *_Nonnull)messageIds { CleverTapLogStaticDebug(@"Cannot mark App Inbox Message as read because Message ID is null or not a string."); } } - }]; [self _save]; [self notifyUpdate]; - + }]; } - (NSDictionary *)messageForId:(NSString *)messageId { @@ -189,23 +181,21 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - [privateContext performBlockAndWait:^{ - for (CTMessageMO *msg in self.user.messages) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; - } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; + for (CTMessageMO *msg in self.user.messages) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; } - }]; + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } return messages; } @@ -218,24 +208,22 @@ - (NSInteger)unreadCount { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - [privateContext performBlockAndWait:^{ - NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; - for (CTMessageMO *msg in results) { - int ttl = (int)msg.expires; - if (ttl > 0 && now >= ttl) { - CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); - [toDelete addObject:msg]; - } else { - [messages addObject:[msg toJSON]]; - } - } - NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; - [messages sortUsingDescriptors:@[sortDescriptor]]; - - if ([toDelete count] > 0) { - [self _deleteMessages:toDelete]; + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:[NSString stringWithFormat:@"isRead == NO"]]]; + for (CTMessageMO *msg in results) { + int ttl = (int)msg.expires; + if (ttl > 0 && now >= ttl) { + CleverTapLogStaticInternal(@"%@: message expires: %@, deleting", self, msg); + [toDelete addObject:msg]; + } else { + [messages addObject:[msg toJSON]]; } - }]; + } + NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO]; + [messages sortUsingDescriptors:@[sortDescriptor]]; + + if ([toDelete count] > 0) { + [self _deleteMessages:toDelete]; + } return messages; } @@ -247,13 +235,9 @@ - (CTMessageMO *)_messageForId:(NSString *)messageId { BOOL hasMessages = ([[self.user.entity propertiesByName] objectForKey:@"messages"] != nil); if (!hasMessages) return nil; - __block BOOL existing; - __block NSOrderedSet *results; - [privateContext performBlockAndWait:^{ - results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; - existing = (results && [results count] > 0); - }]; + NSOrderedSet *results = [self.user.messages filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"id == %@", messageId]]; + BOOL existing = results && [results count] > 0; return existing ? results[0] : nil; } @@ -262,31 +246,23 @@ - (void)_deleteMessages:(NSArray*)messages { for (CTMessageMO *msg in messages) { [privateContext deleteObject:msg]; } + [self _save]; + [self notifyUpdate]; }]; - [self _save]; - [self notifyUpdate]; - } // always call from inside privateContext performBlock - (BOOL)_save { - __block NSError *error = nil; - __block BOOL res = YES; - [privateContext performBlockAndWait:^{ - if ([privateContext hasChanges]) { - res = [privateContext save:&error]; - [mainContext performBlock:^{ - if ([mainContext hasChanges]) { - NSError *error = nil; - [mainContext save:&error]; - } - }]; - - } - }]; + NSError *error = nil; + BOOL res = YES; + res = [privateContext save:&error]; if (!res) { CleverTapLogStaticDebug(@"Error saving core data private context: %@\n%@", [error localizedDescription], [error userInfo]); } + res = [mainContext save:&error]; + if (!res) { + CleverTapLogStaticDebug(@"Error saving core data main context: %@\n%@", [error localizedDescription], [error userInfo]); + } return res; } diff --git a/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m b/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m index ff1aa538..88eb9b94 100755 --- a/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m +++ b/CleverTapSDK/Inbox/models/CTMessageMO+CoreDataProperties.m @@ -37,14 +37,10 @@ - (instancetype)initWithJSON:(NSDictionary *)json forContext:(NSManagedObjectCon } - (NSDictionary *)toJSON { - __block NSDictionary *json = nil; - [self.managedObjectContext performBlockAndWait:^{ - NSMutableDictionary *mutableJson = [NSMutableDictionary dictionaryWithDictionary:self.json]; - [mutableJson setObject:@(self.isRead) forKey:@"isRead"]; - [mutableJson setObject:@(self.date) forKey:@"date"]; - json = [NSDictionary dictionaryWithDictionary:mutableJson]; - }]; - return json; + NSMutableDictionary *json = [NSMutableDictionary dictionaryWithDictionary:self.json]; + json[@"isRead"] = @(self.isRead); + json[@"date"] = @(self.date); + return json; } @dynamic date; From 3c2675201897d6380e4b0d0201c85d7817c6a74a Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 28 Jul 2023 10:55:04 +0530 Subject: [PATCH 13/14] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89264da4..e56ddc04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Change Log All notable changes to this project will be documented in this file. -### [Version 5.1.2](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/5.1.2) (July 27, 2023) +### [Version 5.1.2](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/5.1.2) (July 28, 2023) #### Fixed - Fixed a bug where the App Inbox would appear empty. From f27d8db19b24f022f2e9b71a3509eb9deed9308b Mon Sep 17 00:00:00 2001 From: Akash Malhotra Date: Fri, 28 Jul 2023 11:50:41 +0530 Subject: [PATCH 14/14] Update action.yml --- .github/mini_flows/mandatory_filechanges/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/mini_flows/mandatory_filechanges/action.yml b/.github/mini_flows/mandatory_filechanges/action.yml index 2950aa11..09e279fa 100644 --- a/.github/mini_flows/mandatory_filechanges/action.yml +++ b/.github/mini_flows/mandatory_filechanges/action.yml @@ -7,7 +7,7 @@ runs: with: filters: | md: ['CHANGELOG.md'] - podspec: ['CleverTap-iOS-SDK.podspec'] + txt: ['sdk-version.txt'] h: ['CleverTapSDK/CleverTapBuildInfo.h'] - name: FAIL if mandatory files are not changed