From b8394c4751fe591eaf89c8fa2049e2b7d06fe566 Mon Sep 17 00:00:00 2001 From: Vikram Kalta Date: Thu, 8 Feb 2024 09:58:45 +0000 Subject: [PATCH 1/6] feat: added changes for seq_id --- Contentstack/Stack.h | 21 ++++++++++++ Contentstack/Stack.m | 9 ++++- Contentstack/SyncStack.h | 4 +++ Contentstack/SyncStack.m | 38 +++++++++++++++++++--- ContentstackInternal/CSIOInternalHeaders.h | 1 + 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/Contentstack/Stack.h b/Contentstack/Stack.h index 18e43c3..e0097d9 100644 --- a/Contentstack/Stack.h +++ b/Contentstack/Stack.h @@ -229,6 +229,27 @@ BUILT_ASSUME_NONNULL_BEGIN */ - (void)sync:(void (^)(SyncStack * BUILT_NULLABLE_P syncStack, NSError * BUILT_NULLABLE_P error))completionBlock; +/** + You can use the seq_id (that you receive after every sync call) to get the updated content next time. + The seq id is alternative to pagination token where it fetches all the events data in sequence, and the details of the content that was deleted or updated. + + //Obj-C + + NSString *seqId = @"seq_id"; //Seq id + [stack seqId:seqId completion:^(SyncStack * _Nullable syncStack, NSError * _Nullable error) { + + }]; + + //Swift + var seqId = @"seq_id"; //Seq id + stack.seqId(seqId, completion: { ( syncStack:SyncStack, error: NSError) in + + }) + + @param token Sync token from where to perform sync + @param completionBlock called synchronization is done. + */ +-(void)syncSeqId:(NSString *)seqId completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock; /** If the result of the initial sync (or subsequent sync) contains more than 100 records, the response would be paginated. It provides pagination token in the response. However, diff --git a/Contentstack/Stack.m b/Contentstack/Stack.m index 569dae4..83db934 100644 --- a/Contentstack/Stack.m +++ b/Contentstack/Stack.m @@ -160,6 +160,13 @@ -(void)sync:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_N }]; } +-(void)syncSeqId:(NSString *)seqId completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { + SyncStack *syncStack = [self getCurrentSyncStack:@{@"seq_id": seqId}]; + [self sync:syncStack completion:^(SyncStack * _Nullable syncResult, NSError * _Nullable error) { + completionBlock(syncResult, error); + }]; +} + -(void)syncToken:(NSString *)token completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { SyncStack *syncStack = [self getCurrentSyncStack:@{@"sync_token": token}]; [self sync:syncStack completion:^(SyncStack * _Nullable syncResult, NSError * _Nullable error) { @@ -247,7 +254,7 @@ -(void)syncOnly:(NSString *)contentType locale:(NSString*)locale from:(NSDate *) - (void)sync:(SyncStack *)syncResult completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:[syncResult getParameters]]; - if (syncResult.paginationToken == nil && syncResult.syncToken == nil) { + if (syncResult.seqId == nil && syncResult.syncToken == nil) { [params setValue:self.environment forKey:@"environment"]; } [self syncCallWithParams:params withCompletion:^(NSDictionary *responseDictionary, NSError *error) { diff --git a/Contentstack/SyncStack.h b/Contentstack/SyncStack.h index 8c6990d..eb0a0ff 100644 --- a/Contentstack/SyncStack.h +++ b/Contentstack/SyncStack.h @@ -25,6 +25,10 @@ NS_ASSUME_NONNULL_BEGIN *Readonly property to delta sync. */ @property (nonatomic, copy, readonly) NSString * _Nullable syncToken; +/** + *Readonly property to sync. + */ +@property (nonatomic, copy, readonly) NSString * _Nullable seqId; /** * Readonly property to check skip count */ diff --git a/Contentstack/SyncStack.m b/Contentstack/SyncStack.m index 9dd427f..78ea01b 100644 --- a/Contentstack/SyncStack.m +++ b/Contentstack/SyncStack.m @@ -27,8 +27,26 @@ -(void)parseSyncResult:(NSDictionary *)dictionary { self.syncToken = nil; self.paginationToken = nil; self.hasMorePages = false; + if ([dictionary objectForKey:@"sync_token"]) { - self.syncToken = [dictionary objectForKey:@"sync_token"]; + /* For existing persisted data sync token will be deleted. Seq Id will be generated for sync with the help of event_at field. + */ + if ([dictionary objectForKey:@"items"] && [[dictionary objectForKey:@"items"] isKindOfClass:[NSArray class]]) { + NSMutableArray * items = [NSMutableArray array]; + items = [dictionary objectForKey:@"items"]; + if (items.count > 0) { + self.syncToken = nil; + self.seqId = [self generateSeqId:[dictionary objectForKey:@"event_at"]]; + } else { + self.syncToken = [dictionary objectForKey:@"sync_token"]; + } + } else { + self.syncToken = [dictionary objectForKey:@"sync_token"]; + } + } + + if ([dictionary objectForKey:@"last_seq_id"]) { + self.seqId = [dictionary objectForKey:@"last_seq_id"]; } if ([dictionary objectForKey:@"pagination_token"]) { self.hasMorePages = true; @@ -44,21 +62,31 @@ -(void)parseSyncResult:(NSDictionary *)dictionary { self.limit = [[dictionary objectForKey:@"limit"] unsignedIntValue]; } if ([dictionary objectForKey:@"items"] && [[dictionary objectForKey:@"items"] isKindOfClass:[NSArray class]]) { - self.items = [dictionary objectForKey:@"items"];//[[self.items mutableCopy] arrayByAddingObjectsFromArray:[dictionary objectForKey:@"items"]]; + self.items = [dictionary objectForKey:@"items"]; + if (self.items.count > 0) { + self.hasMorePages = true; + } } } } -(NSDictionary*)getParameters { NSMutableDictionary *syncParams = [NSMutableDictionary dictionary]; - if (self.syncToken != nil) { + if (self.seqId != nil) { + [syncParams setValue:self.seqId forKey:@"seq_id"]; + } else if (self.syncToken != nil) { [syncParams setValue:self.syncToken forKey:@"sync_token"]; - }else if (self.paginationToken != nil) { + } else if (self.paginationToken != nil) { [syncParams setValue:self.paginationToken forKey:@"pagination_token"]; - }else { + } else { syncParams = [NSMutableDictionary dictionaryWithDictionary:self.params]; + [syncParams setValue:@"true" forKey:@"seq_id"]; [syncParams setValue:@"true" forKey:@"init"]; } return syncParams; } + +-(NSString*)generateSeqId:(NSString*) eventAt { + return @"65c217c4c1ac8a3f43218d2f"; +} @end diff --git a/ContentstackInternal/CSIOInternalHeaders.h b/ContentstackInternal/CSIOInternalHeaders.h index 95f1125..7aeee8a 100644 --- a/ContentstackInternal/CSIOInternalHeaders.h +++ b/ContentstackInternal/CSIOInternalHeaders.h @@ -100,6 +100,7 @@ @property (nonatomic, copy) NSArray *items; @property (nonatomic, copy) NSString *paginationToken; @property (nonatomic, copy) NSString *syncToken; +@property (nonatomic, copy) NSString *seqId; @property (nonatomic, assign) BOOL hasMorePages; @property (nonatomic, assign) unsigned int skip; From a2f87854f0c0a2232d653b39a52f5126a70e6ea1 Mon Sep 17 00:00:00 2001 From: Vikram Kalta Date: Mon, 12 Feb 2024 16:21:45 +0000 Subject: [PATCH 2/6] feat: added generate seq id support --- .gitignore | 2 +- Contentstack.xcodeproj/project.pbxproj | 6 ++ Contentstack/Stack.m | 2 - Contentstack/SyncStack.m | 62 ++++++++--- ContentstackInternal/BSONObjectIdGenerator.h | 24 +++++ ContentstackInternal/BSONObjectIdGenerator.m | 102 +++++++++++++++++++ 6 files changed, 181 insertions(+), 17 deletions(-) create mode 100644 ContentstackInternal/BSONObjectIdGenerator.h create mode 100644 ContentstackInternal/BSONObjectIdGenerator.m diff --git a/.gitignore b/.gitignore index 66ea262..25e6328 100755 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - +.vscode/ ## Build generated Build/ diff --git a/Contentstack.xcodeproj/project.pbxproj b/Contentstack.xcodeproj/project.pbxproj index 7b88da3..a097be9 100644 --- a/Contentstack.xcodeproj/project.pbxproj +++ b/Contentstack.xcodeproj/project.pbxproj @@ -95,6 +95,7 @@ 23A53F501E277BBE001DBE35 /* NSObject+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 23A53F331E276BA5001DBE35 /* NSObject+Extensions.m */; }; 23A53F5A1E277CD3001DBE35 /* Contentstack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 230B38C01C16E98B00444A14 /* Contentstack.framework */; }; 23B6F12A1B5662EE00A9E983 /* ISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 23B6F1281B5662EE00A9E983 /* ISO8601DateFormatter.m */; }; + 47CD50872B7625B90032747B /* BSONObjectIdGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 47CD50862B7625B90032747B /* BSONObjectIdGenerator.m */; }; 565E11BB1BD76654005AD47F /* MMDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 565E11AA1BD76654005AD47F /* MMDocument.m */; }; 565E11BC1BD76654005AD47F /* MMElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 565E11AD1BD76654005AD47F /* MMElement.m */; }; 565E11BD1BD76654005AD47F /* MMGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 565E11AF1BD76654005AD47F /* MMGenerator.m */; }; @@ -205,6 +206,8 @@ 23B6F1281B5662EE00A9E983 /* ISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ISO8601DateFormatter.m; sourceTree = ""; }; 23C545FB1C1976FE007BBD27 /* ios-build-framework-script.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "ios-build-framework-script.sh"; sourceTree = ""; }; 3CF581B9F7526EDA48ED5C6F /* Pods-ContentstackTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ContentstackTest.debug.xcconfig"; path = "Target Support Files/Pods-ContentstackTest/Pods-ContentstackTest.debug.xcconfig"; sourceTree = ""; }; + 47CD50852B76243E0032747B /* BSONObjectIdGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSONObjectIdGenerator.h; sourceTree = ""; }; + 47CD50862B7625B90032747B /* BSONObjectIdGenerator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSONObjectIdGenerator.m; sourceTree = ""; }; 565E11A91BD76654005AD47F /* MMDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDocument.h; sourceTree = ""; }; 565E11AA1BD76654005AD47F /* MMDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMDocument.m; sourceTree = ""; }; 565E11AB1BD76654005AD47F /* MMDocument_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDocument_Private.h; sourceTree = ""; }; @@ -383,6 +386,8 @@ 23A53F311E276BA5001DBE35 /* CSIOURLCache.m */, 23A53F321E276BA5001DBE35 /* NSObject+Extensions.h */, 23A53F331E276BA5001DBE35 /* NSObject+Extensions.m */, + 47CD50852B76243E0032747B /* BSONObjectIdGenerator.h */, + 47CD50862B7625B90032747B /* BSONObjectIdGenerator.m */, ); path = ContentstackInternal; sourceTree = ""; @@ -790,6 +795,7 @@ 0FEAEF2A2361A18600985FF9 /* CSURLSessionManager.m in Sources */, 0FEAEF2C2361A18600985FF9 /* CSError.m in Sources */, 23B6F12A1B5662EE00A9E983 /* ISO8601DateFormatter.m in Sources */, + 47CD50872B7625B90032747B /* BSONObjectIdGenerator.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Contentstack/Stack.m b/Contentstack/Stack.m index 83db934..2025b2a 100644 --- a/Contentstack/Stack.m +++ b/Contentstack/Stack.m @@ -234,8 +234,6 @@ -(void)syncOnly:(NSString *)contentType locale:(NSString*)locale from:(NSDate *) [self sync:syncStack completion:^(SyncStack * _Nullable syncResult, NSError * _Nullable error) { completionBlock(syncResult, error); }]; - - } -(void)syncOnly:(NSString *)contentType locale:(NSString*)locale from:(NSDate *)date publishType:(PublishType)publishType completion:(void (^)(SyncStack * _Nullable, NSError * _Nullable))completionBlock { diff --git a/Contentstack/SyncStack.m b/Contentstack/SyncStack.m index 78ea01b..b060bcc 100644 --- a/Contentstack/SyncStack.m +++ b/Contentstack/SyncStack.m @@ -8,6 +8,8 @@ #import "SyncStack.h" #import "CSIOInternalHeaders.h" +#import "BSONObjectIdGenerator.h" + @implementation SyncStack -(instancetype)initWithParmas:(NSDictionary*) parmas { @@ -17,6 +19,11 @@ -(instancetype)initWithParmas:(NSDictionary*) parmas { if ([[self.params objectForKey:@"sync_token"] isKindOfClass:[NSString class]]) { self.syncToken = [self.params objectForKey:@"sync_token"]; } + if ([[self.params objectForKey:@"seq_id"] + isKindOfClass:[NSString + class]]) { + self.seqId = [self.params objectForKey:@"seq_id"]; + } self.items = [NSArray array]; } return self; @@ -29,20 +36,9 @@ -(void)parseSyncResult:(NSDictionary *)dictionary { self.hasMorePages = false; if ([dictionary objectForKey:@"sync_token"]) { - /* For existing persisted data sync token will be deleted. Seq Id will be generated for sync with the help of event_at field. + /* For existing persisted data sync token should be set to nil. Seq Id will be generated for sync with the help of event_at field. */ - if ([dictionary objectForKey:@"items"] && [[dictionary objectForKey:@"items"] isKindOfClass:[NSArray class]]) { - NSMutableArray * items = [NSMutableArray array]; - items = [dictionary objectForKey:@"items"]; - if (items.count > 0) { - self.syncToken = nil; - self.seqId = [self generateSeqId:[dictionary objectForKey:@"event_at"]]; - } else { - self.syncToken = [dictionary objectForKey:@"sync_token"]; - } - } else { - self.syncToken = [dictionary objectForKey:@"sync_token"]; - } + [self setExistingTokensNilAndGenerateSeqId:@"sync_token" dict:dictionary]; } if ([dictionary objectForKey:@"last_seq_id"]) { @@ -87,6 +83,44 @@ -(NSDictionary*)getParameters { } -(NSString*)generateSeqId:(NSString*) eventAt { - return @"65c217c4c1ac8a3f43218d2f"; + // Create a date formatter to parse the date string + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + NSDate *date = [dateFormatter dateFromString:eventAt]; + if (date) { + // Convert the NSDate object to an NSTimeInterval + NSTimeInterval timeInterval = [date timeIntervalSince1970]; + NSInteger timeIntervalInSeconds = (NSInteger)timeInterval; + + return [BSONObjectIdGenerator generate:timeIntervalInSeconds]; + } else { + // Handle case where date conversion failed. + [NSException raise:@"Unable to parse date string" format:@"Invalid date format %@", eventAt]; + return nil; + } } + +-(void)setExistingTokensNilAndGenerateSeqId:(NSString *) key dict:(NSDictionary *)dictionary { + NSMutableArray * items = [NSMutableArray array]; + items = [dictionary objectForKey: @"items"]; + if ([items isKindOfClass:[NSArray class]] && items.count > 0) { + // Get the last object's event_at + NSDictionary *lastObject = nil; + for (NSInteger i = items.count - 1; i >= 0; i--) { + id object = items[i]; + if ([object isKindOfClass:[NSDictionary class]]) { + lastObject = object; + break; + } + } + self.seqId = [self generateSeqId:[lastObject objectForKey:@"event_at"]]; + } else { + if ([key isEqual: @"sync_token"]) { + self.syncToken = [dictionary objectForKey:@"sync_token"]; + } else { + self.paginationToken = [dictionary objectForKey:@"pagination_token"]; + } + } +} + @end diff --git a/ContentstackInternal/BSONObjectIdGenerator.h b/ContentstackInternal/BSONObjectIdGenerator.h new file mode 100644 index 0000000..dfe3abc --- /dev/null +++ b/ContentstackInternal/BSONObjectIdGenerator.h @@ -0,0 +1,24 @@ +// +// BSONObjectIdGenerator.h +// Contentstack +// +// Created by Vikram Kalta on 09/02/2024. +// Copyright © 2024 Contentstack. All rights reserved. +// +#ifndef BSONObjectIdGenerator_h +#define BSONObjectIdGenerator_h + +#import + +typedef union { + char bytes[12]; + int ints[3]; +} bson_oid_t; + +@interface BSONObjectIdGenerator : NSObject + ++ (NSString *)generate:(NSInteger)timestamp; + +@end + +#endif /* BSONObjectIdGenerator_h */ diff --git a/ContentstackInternal/BSONObjectIdGenerator.m b/ContentstackInternal/BSONObjectIdGenerator.m new file mode 100644 index 0000000..5dc9407 --- /dev/null +++ b/ContentstackInternal/BSONObjectIdGenerator.m @@ -0,0 +1,102 @@ +// +// BSONObjectIdGenerator.m +// ThirdPartyExtension +// +// Created by Vikram Kalta on 09/02/2024. +// Copyright © 2024 Contentstack. All rights reserved. +// + +#import +#import +#import "BSONObjectIdGenerator.h" + +@implementation BSONObjectIdGenerator +static int _incr = 0; + ++ (NSString *) generate:(NSInteger)timestamp { + int i = _incr++; + bson_oid_t *oid = malloc(sizeof(bson_oid_t)); + time_t t = time(NULL); + + // Grab the PID + int pid = [NSProcessInfo processInfo].processIdentifier; + + // Get a device identifier. The specification usually has this as the MAC address + // or hostname but we already have a unique device identifier. + // + NSString *identifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; + + // MD5 hash the device identifier + NSString *md5HashOfIdentifier = [self md5HashFromString:identifier]; + const char *cIdentifier = [md5HashOfIdentifier cStringUsingEncoding:NSUTF8StringEncoding]; + + // Copy bytes over to our object id. Specification taken from http://www.mongodb.org/display/DOCS/Object+IDs + bson_swap_endian_len(&oid->bytes[0], ×tamp, 4); + bson_swap_endian_len(&oid->bytes[4], &cIdentifier, 3); + bson_swap_endian_len(&oid->bytes[7], &pid, 2); + bson_swap_endian_len(&oid->bytes[9], &i, 3); + NSString *str = [self bson_oid_to_string:oid]; + + free(oid); + + return str; +} + +/** + @discussion Given an NSString, returns the MD5 hash of it. Taken from + http://stackoverflow.com/questions/1524604/md5-algorithm-in-objective-c + @param source The source string + @return MD5 hash as a string + */ ++ (NSString *) md5HashFromString:(NSString *)source { + const char *cStr = [source UTF8String]; + unsigned char result[16]; + CC_MD5(cStr, strlen(cStr), result); + return [NSString stringWithFormat: + @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + result[0], result[1], result[2], result[3], + result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], + result[12], result[13], result[14], result[15] + ]; +} + +/** + @discussion Converts a bson_oid_t to an NSString. Mostly taken from https://github.com/mongodb/mongo-c-driver/blob/master/src/bson.c + @param oid The bson_oid_t to convert + @return Autoreleased NSString of 24 hex characters + */ ++ (NSString *) bson_oid_to_string:(bson_oid_t *)oid { + char *str = malloc(sizeof(char) * 25); + static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + int i; + for ( i=0; i<12; i++ ) { + str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4]; + str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ]; + } + str[24] = '\0'; + NSString *string = [NSString stringWithCString:str encoding:NSUTF8StringEncoding]; + free(str); + return string; +} + + +/** + @discussion The ARM architecture is little endian while intel macs are big Endian, so we need to swap endianness if we're compiling on a big Endian architecture. + @param outp The destination pointer + @param inp The source pointer + @param len The length to copy + */ +void bson_swap_endian_len(void *outp, const void *inp, int len) { + const char *in = (const char *)inp; + char *out = (char *)outp; + for (int i = 0; i < len; i ++) { + #if __DARWIN_BIG_ENDIAN + out[i] = in[len - 1 - i]; + #elif __DARWIN_LITTLE_ENDIAN + out[i] = in[i]; + #endif + } +} + +@end From 01820e41f15ccf3cfebf8e0ac30fee2c5fa8c1aa Mon Sep 17 00:00:00 2001 From: Vikram Kalta Date: Wed, 14 Feb 2024 11:30:15 +0000 Subject: [PATCH 3/6] fix: additional changes for seq id --- .github/workflows/sca-scan.yml | 2 + Contentstack/Stack.h | 24 ++++- Contentstack/Stack.m | 39 ++++++- Contentstack/SyncStack.m | 69 ++++--------- ContentstackInternal/BSONObjectIdGenerator.h | 24 ----- ContentstackInternal/BSONObjectIdGenerator.m | 102 ------------------- ContentstackInternal/CSIOInternalHeaders.h | 1 + 7 files changed, 78 insertions(+), 183 deletions(-) delete mode 100644 ContentstackInternal/BSONObjectIdGenerator.h delete mode 100644 ContentstackInternal/BSONObjectIdGenerator.m diff --git a/.github/workflows/sca-scan.yml b/.github/workflows/sca-scan.yml index f1d6cf0..4b28827 100644 --- a/.github/workflows/sca-scan.yml +++ b/.github/workflows/sca-scan.yml @@ -11,3 +11,5 @@ jobs: uses: snyk/actions/cocoapods@master env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --fail-on=all diff --git a/Contentstack/Stack.h b/Contentstack/Stack.h index e0097d9..f0cdd2e 100644 --- a/Contentstack/Stack.h +++ b/Contentstack/Stack.h @@ -229,6 +229,26 @@ BUILT_ASSUME_NONNULL_BEGIN */ - (void)sync:(void (^)(SyncStack * BUILT_NULLABLE_P syncStack, NSError * BUILT_NULLABLE_P error))completionBlock; +/** + The Initial Sync request performs a complete sync of your app data. This is done for seq id. + It returns all the published entries and assets of the specified stack in response. + The response also contains a sync token, which you need to store, + since this token is used to get subsequent delta updates later. + + //Obj-C + [stack syncSeqIdInit:^(SyncStack * _Nullable syncStack, NSError * _Nullable error) { + + }]; + + //Swift + stack.syncSeqIdInit({ ( syncStack:SyncStack, error: NSError) in + + }) + +@param completionBlock called synchronization is done. +*/ +- (void)syncSeqIdInit:(void (^)(SyncStack * BUILT_NULLABLE_P syncStack, NSError * BUILT_NULLABLE_P error))completionBlock; + /** You can use the seq_id (that you receive after every sync call) to get the updated content next time. The seq id is alternative to pagination token where it fetches all the events data in sequence, and the details of the content that was deleted or updated. @@ -246,10 +266,10 @@ BUILT_ASSUME_NONNULL_BEGIN }) - @param token Sync token from where to perform sync + @param seqId Sync token from where to perform sync @param completionBlock called synchronization is done. */ --(void)syncSeqId:(NSString *)seqId completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock; +-(void)syncSeqId:(NSString *)seqId syncToken:(NSString *)syncToken completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock; /** If the result of the initial sync (or subsequent sync) contains more than 100 records, the response would be paginated. It provides pagination token in the response. However, diff --git a/Contentstack/Stack.m b/Contentstack/Stack.m index 2025b2a..d6cd8d2 100644 --- a/Contentstack/Stack.m +++ b/Contentstack/Stack.m @@ -160,9 +160,22 @@ -(void)sync:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_N }]; } --(void)syncSeqId:(NSString *)seqId completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { - SyncStack *syncStack = [self getCurrentSyncStack:@{@"seq_id": seqId}]; - [self sync:syncStack completion:^(SyncStack * _Nullable syncResult, NSError * _Nullable error) { +-(void)syncSeqIdInit:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { + SyncStack *syncStack = [self getCurrentSyncStack:nil]; + [self syncWithSeqId:syncStack completion:^(SyncStack * _Nullable syncResult, NSError * _Nullable error) { + completionBlock(syncResult, error); + }]; +} + +-(void)syncSeqId:(NSString *)seqId syncToken:(NSString *)syncToken completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { + SyncStack *syncStack = nil; + if (syncToken) { + syncStack = [self getCurrentSyncStack:@{@"sync_token": syncToken}]; + } else { + syncStack = [self getCurrentSyncStack:@{@"seq_id": seqId}]; + } + + [self syncWithSeqId:syncStack completion:^(SyncStack * _Nullable syncResult, NSError * _Nullable error) { completionBlock(syncResult, error); }]; } @@ -252,7 +265,7 @@ -(void)syncOnly:(NSString *)contentType locale:(NSString*)locale from:(NSDate *) - (void)sync:(SyncStack *)syncResult completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:[syncResult getParameters]]; - if (syncResult.seqId == nil && syncResult.syncToken == nil) { + if (syncResult.paginationToken == nil && syncResult.syncToken == nil) { [params setValue:self.environment forKey:@"environment"]; } [self syncCallWithParams:params withCompletion:^(NSDictionary *responseDictionary, NSError *error) { @@ -266,6 +279,24 @@ - (void)sync:(SyncStack *)syncResult completion:(void (^)(SyncStack * BUILT_NULL }]; } +- (void)syncWithSeqId:(SyncStack *)syncResult completion:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { + NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:[syncResult getParametersSeqId]]; + if (syncResult.seqId == nil && syncResult.syncToken == nil) { + [params setValue:self.environment forKey:@"environment"]; + } + [self syncCallWithParams:params withCompletion:^(NSDictionary *responseDictionary, NSError *error) { + if (error == nil) { + [syncResult parseSyncResult:responseDictionary]; + completionBlock(syncResult, error); + if (syncResult.hasMorePages) { + [self syncWithSeqId:syncResult completion:completionBlock]; + } + } else { + completionBlock(syncResult, error); + } + }]; +} + - (SyncStack*) getCurrentSyncStack:(NSDictionary*) params { SyncStack *syncResult = [[SyncStack alloc] initWithParmas:params]; return syncResult; diff --git a/Contentstack/SyncStack.m b/Contentstack/SyncStack.m index b060bcc..6a6fb07 100644 --- a/Contentstack/SyncStack.m +++ b/Contentstack/SyncStack.m @@ -8,7 +8,6 @@ #import "SyncStack.h" #import "CSIOInternalHeaders.h" -#import "BSONObjectIdGenerator.h" @implementation SyncStack @@ -33,21 +32,18 @@ -(void)parseSyncResult:(NSDictionary *)dictionary { if (![dictionary isKindOfClass:[NSNull class]]) { self.syncToken = nil; self.paginationToken = nil; + self.seqId = nil; self.hasMorePages = false; - if ([dictionary objectForKey:@"sync_token"]) { - /* For existing persisted data sync token should be set to nil. Seq Id will be generated for sync with the help of event_at field. - */ - [self setExistingTokensNilAndGenerateSeqId:@"sync_token" dict:dictionary]; - } - - if ([dictionary objectForKey:@"last_seq_id"]) { - self.seqId = [dictionary objectForKey:@"last_seq_id"]; + self.syncToken = [dictionary objectForKey:@"sync_token"]; } if ([dictionary objectForKey:@"pagination_token"]) { self.hasMorePages = true; self.paginationToken = [dictionary objectForKey:@"pagination_token"]; } + if ([dictionary objectForKey:@"last_seq_id"]) { + self.seqId = [dictionary objectForKey:@"last_seq_id"]; + } if ([dictionary objectForKey:@"total_count"]) { self.totalCount = [[dictionary objectForKey:@"total_count"] unsignedIntValue]; } @@ -68,59 +64,30 @@ -(void)parseSyncResult:(NSDictionary *)dictionary { -(NSDictionary*)getParameters { NSMutableDictionary *syncParams = [NSMutableDictionary dictionary]; - if (self.seqId != nil) { - [syncParams setValue:self.seqId forKey:@"seq_id"]; - } else if (self.syncToken != nil) { + if (self.syncToken != nil) { [syncParams setValue:self.syncToken forKey:@"sync_token"]; - } else if (self.paginationToken != nil) { + }else if (self.paginationToken != nil) { [syncParams setValue:self.paginationToken forKey:@"pagination_token"]; - } else { + }else { syncParams = [NSMutableDictionary dictionaryWithDictionary:self.params]; - [syncParams setValue:@"true" forKey:@"seq_id"]; [syncParams setValue:@"true" forKey:@"init"]; } return syncParams; } --(NSString*)generateSeqId:(NSString*) eventAt { - // Create a date formatter to parse the date string - NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; - dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; - NSDate *date = [dateFormatter dateFromString:eventAt]; - if (date) { - // Convert the NSDate object to an NSTimeInterval - NSTimeInterval timeInterval = [date timeIntervalSince1970]; - NSInteger timeIntervalInSeconds = (NSInteger)timeInterval; - - return [BSONObjectIdGenerator generate:timeIntervalInSeconds]; +-(NSDictionary*)getParametersSeqId { + NSMutableDictionary *syncParams = [NSMutableDictionary dictionary]; + if (self.seqId != nil) { + [syncParams setValue:self.seqId forKey:@"seq_id"]; + } else if (self.syncToken != nil) { + [syncParams setValue:self.syncToken forKey:@"sync_token"]; } else { - // Handle case where date conversion failed. - [NSException raise:@"Unable to parse date string" format:@"Invalid date format %@", eventAt]; - return nil; + syncParams = [NSMutableDictionary dictionaryWithDictionary:self.params]; + [syncParams setValue:@"true" forKey:@"seq_id"]; + [syncParams setValue:@"true" forKey:@"init"]; } + return syncParams; } --(void)setExistingTokensNilAndGenerateSeqId:(NSString *) key dict:(NSDictionary *)dictionary { - NSMutableArray * items = [NSMutableArray array]; - items = [dictionary objectForKey: @"items"]; - if ([items isKindOfClass:[NSArray class]] && items.count > 0) { - // Get the last object's event_at - NSDictionary *lastObject = nil; - for (NSInteger i = items.count - 1; i >= 0; i--) { - id object = items[i]; - if ([object isKindOfClass:[NSDictionary class]]) { - lastObject = object; - break; - } - } - self.seqId = [self generateSeqId:[lastObject objectForKey:@"event_at"]]; - } else { - if ([key isEqual: @"sync_token"]) { - self.syncToken = [dictionary objectForKey:@"sync_token"]; - } else { - self.paginationToken = [dictionary objectForKey:@"pagination_token"]; - } - } -} @end diff --git a/ContentstackInternal/BSONObjectIdGenerator.h b/ContentstackInternal/BSONObjectIdGenerator.h deleted file mode 100644 index dfe3abc..0000000 --- a/ContentstackInternal/BSONObjectIdGenerator.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// BSONObjectIdGenerator.h -// Contentstack -// -// Created by Vikram Kalta on 09/02/2024. -// Copyright © 2024 Contentstack. All rights reserved. -// -#ifndef BSONObjectIdGenerator_h -#define BSONObjectIdGenerator_h - -#import - -typedef union { - char bytes[12]; - int ints[3]; -} bson_oid_t; - -@interface BSONObjectIdGenerator : NSObject - -+ (NSString *)generate:(NSInteger)timestamp; - -@end - -#endif /* BSONObjectIdGenerator_h */ diff --git a/ContentstackInternal/BSONObjectIdGenerator.m b/ContentstackInternal/BSONObjectIdGenerator.m deleted file mode 100644 index 5dc9407..0000000 --- a/ContentstackInternal/BSONObjectIdGenerator.m +++ /dev/null @@ -1,102 +0,0 @@ -// -// BSONObjectIdGenerator.m -// ThirdPartyExtension -// -// Created by Vikram Kalta on 09/02/2024. -// Copyright © 2024 Contentstack. All rights reserved. -// - -#import -#import -#import "BSONObjectIdGenerator.h" - -@implementation BSONObjectIdGenerator -static int _incr = 0; - -+ (NSString *) generate:(NSInteger)timestamp { - int i = _incr++; - bson_oid_t *oid = malloc(sizeof(bson_oid_t)); - time_t t = time(NULL); - - // Grab the PID - int pid = [NSProcessInfo processInfo].processIdentifier; - - // Get a device identifier. The specification usually has this as the MAC address - // or hostname but we already have a unique device identifier. - // - NSString *identifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; - - // MD5 hash the device identifier - NSString *md5HashOfIdentifier = [self md5HashFromString:identifier]; - const char *cIdentifier = [md5HashOfIdentifier cStringUsingEncoding:NSUTF8StringEncoding]; - - // Copy bytes over to our object id. Specification taken from http://www.mongodb.org/display/DOCS/Object+IDs - bson_swap_endian_len(&oid->bytes[0], ×tamp, 4); - bson_swap_endian_len(&oid->bytes[4], &cIdentifier, 3); - bson_swap_endian_len(&oid->bytes[7], &pid, 2); - bson_swap_endian_len(&oid->bytes[9], &i, 3); - NSString *str = [self bson_oid_to_string:oid]; - - free(oid); - - return str; -} - -/** - @discussion Given an NSString, returns the MD5 hash of it. Taken from - http://stackoverflow.com/questions/1524604/md5-algorithm-in-objective-c - @param source The source string - @return MD5 hash as a string - */ -+ (NSString *) md5HashFromString:(NSString *)source { - const char *cStr = [source UTF8String]; - unsigned char result[16]; - CC_MD5(cStr, strlen(cStr), result); - return [NSString stringWithFormat: - @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - result[0], result[1], result[2], result[3], - result[4], result[5], result[6], result[7], - result[8], result[9], result[10], result[11], - result[12], result[13], result[14], result[15] - ]; -} - -/** - @discussion Converts a bson_oid_t to an NSString. Mostly taken from https://github.com/mongodb/mongo-c-driver/blob/master/src/bson.c - @param oid The bson_oid_t to convert - @return Autoreleased NSString of 24 hex characters - */ -+ (NSString *) bson_oid_to_string:(bson_oid_t *)oid { - char *str = malloc(sizeof(char) * 25); - static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; - int i; - for ( i=0; i<12; i++ ) { - str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4]; - str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ]; - } - str[24] = '\0'; - NSString *string = [NSString stringWithCString:str encoding:NSUTF8StringEncoding]; - free(str); - return string; -} - - -/** - @discussion The ARM architecture is little endian while intel macs are big Endian, so we need to swap endianness if we're compiling on a big Endian architecture. - @param outp The destination pointer - @param inp The source pointer - @param len The length to copy - */ -void bson_swap_endian_len(void *outp, const void *inp, int len) { - const char *in = (const char *)inp; - char *out = (char *)outp; - for (int i = 0; i < len; i ++) { - #if __DARWIN_BIG_ENDIAN - out[i] = in[len - 1 - i]; - #elif __DARWIN_LITTLE_ENDIAN - out[i] = in[i]; - #endif - } -} - -@end diff --git a/ContentstackInternal/CSIOInternalHeaders.h b/ContentstackInternal/CSIOInternalHeaders.h index 7aeee8a..28ca166 100644 --- a/ContentstackInternal/CSIOInternalHeaders.h +++ b/ContentstackInternal/CSIOInternalHeaders.h @@ -97,6 +97,7 @@ -(instancetype)initWithParmas:(NSDictionary*) parmas; -(void)parseSyncResult:(NSDictionary*) dictionary; -(NSDictionary*)getParameters; +-(NSDictionary*)getParametersSeqId; @property (nonatomic, copy) NSArray *items; @property (nonatomic, copy) NSString *paginationToken; @property (nonatomic, copy) NSString *syncToken; From fd3b57e43677914bf5d571b26e4ad6994b77de87 Mon Sep 17 00:00:00 2001 From: Vikram Kalta Date: Wed, 14 Feb 2024 12:39:46 +0000 Subject: [PATCH 4/6] fix: minor changes --- .github/workflows/sca-scan.yml | 15 --------------- Contentstack/Stack.h | 2 +- Contentstack/Stack.m | 2 +- 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 .github/workflows/sca-scan.yml diff --git a/.github/workflows/sca-scan.yml b/.github/workflows/sca-scan.yml deleted file mode 100644 index 4b28827..0000000 --- a/.github/workflows/sca-scan.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Source Composition Analysis Scan -on: - pull_request: - types: [opened, synchronize, reopened] -jobs: - security: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/cocoapods@master - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - args: --fail-on=all diff --git a/Contentstack/Stack.h b/Contentstack/Stack.h index f0cdd2e..a3b6084 100644 --- a/Contentstack/Stack.h +++ b/Contentstack/Stack.h @@ -247,7 +247,7 @@ BUILT_ASSUME_NONNULL_BEGIN @param completionBlock called synchronization is done. */ -- (void)syncSeqIdInit:(void (^)(SyncStack * BUILT_NULLABLE_P syncStack, NSError * BUILT_NULLABLE_P error))completionBlock; +- (void)initSeqSync:(void (^)(SyncStack * BUILT_NULLABLE_P syncStack, NSError * BUILT_NULLABLE_P error))completionBlock; /** You can use the seq_id (that you receive after every sync call) to get the updated content next time. diff --git a/Contentstack/Stack.m b/Contentstack/Stack.m index d6cd8d2..e2737ba 100644 --- a/Contentstack/Stack.m +++ b/Contentstack/Stack.m @@ -160,7 +160,7 @@ -(void)sync:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_N }]; } --(void)syncSeqIdInit:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { +-(void)initSeqSync:(void (^)(SyncStack * BUILT_NULLABLE_P syncResult, NSError * BUILT_NULLABLE_P error))completionBlock { SyncStack *syncStack = [self getCurrentSyncStack:nil]; [self syncWithSeqId:syncStack completion:^(SyncStack * _Nullable syncResult, NSError * _Nullable error) { completionBlock(syncResult, error); From f3617c991565363953e7ec528557776a16362623 Mon Sep 17 00:00:00 2001 From: Vikram Kalta Date: Tue, 20 Feb 2024 11:36:23 +0000 Subject: [PATCH 5/6] feat: added git action for release --- .github/workflows/release-package.yml | 44 ++++++++++++++++++++++++++ Contentstack.xcodeproj/project.pbxproj | 6 ---- 2 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/release-package.yml diff --git a/.github/workflows/release-package.yml b/.github/workflows/release-package.yml new file mode 100644 index 0000000..a6deba0 --- /dev/null +++ b/.github/workflows/release-package.yml @@ -0,0 +1,44 @@ +name: Contentstack iOS Delivery SDK Release + +on: + push: + branches: + - main + +jobs: + release: + name: Release Contentstack iOS Delivery SDK Release + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Select Xcode + run: sudo xcode-select -switch /Applications/Xcode.app && /usr/bin/xcodebuild -version + + - name: Install dependencies + run: | + pod install --repo-update + + - name: Build + run: | + # Add commands to build and test your package + xcodebuild -workspace Contentstack.xcworkspace -scheme 'Contentstack' -destination 'platform=iOS Simulator,name=iPhone 13 Pro' + + - name: Tag release + id: tag + run: | + git tag v3.12.2 # Replace with your desired version number + echo "::set-output name=tag::v3.12.2" # Replace with the same version number as above + + - name: Push tag + uses: ad-m/github-push-action@v0.6.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ steps.tag.outputs.tag }} + + - name: CocoaPods trunk push + run: pod trunk push + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} \ No newline at end of file diff --git a/Contentstack.xcodeproj/project.pbxproj b/Contentstack.xcodeproj/project.pbxproj index a097be9..7b88da3 100644 --- a/Contentstack.xcodeproj/project.pbxproj +++ b/Contentstack.xcodeproj/project.pbxproj @@ -95,7 +95,6 @@ 23A53F501E277BBE001DBE35 /* NSObject+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 23A53F331E276BA5001DBE35 /* NSObject+Extensions.m */; }; 23A53F5A1E277CD3001DBE35 /* Contentstack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 230B38C01C16E98B00444A14 /* Contentstack.framework */; }; 23B6F12A1B5662EE00A9E983 /* ISO8601DateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 23B6F1281B5662EE00A9E983 /* ISO8601DateFormatter.m */; }; - 47CD50872B7625B90032747B /* BSONObjectIdGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 47CD50862B7625B90032747B /* BSONObjectIdGenerator.m */; }; 565E11BB1BD76654005AD47F /* MMDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = 565E11AA1BD76654005AD47F /* MMDocument.m */; }; 565E11BC1BD76654005AD47F /* MMElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 565E11AD1BD76654005AD47F /* MMElement.m */; }; 565E11BD1BD76654005AD47F /* MMGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 565E11AF1BD76654005AD47F /* MMGenerator.m */; }; @@ -206,8 +205,6 @@ 23B6F1281B5662EE00A9E983 /* ISO8601DateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ISO8601DateFormatter.m; sourceTree = ""; }; 23C545FB1C1976FE007BBD27 /* ios-build-framework-script.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "ios-build-framework-script.sh"; sourceTree = ""; }; 3CF581B9F7526EDA48ED5C6F /* Pods-ContentstackTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ContentstackTest.debug.xcconfig"; path = "Target Support Files/Pods-ContentstackTest/Pods-ContentstackTest.debug.xcconfig"; sourceTree = ""; }; - 47CD50852B76243E0032747B /* BSONObjectIdGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSONObjectIdGenerator.h; sourceTree = ""; }; - 47CD50862B7625B90032747B /* BSONObjectIdGenerator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSONObjectIdGenerator.m; sourceTree = ""; }; 565E11A91BD76654005AD47F /* MMDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDocument.h; sourceTree = ""; }; 565E11AA1BD76654005AD47F /* MMDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMDocument.m; sourceTree = ""; }; 565E11AB1BD76654005AD47F /* MMDocument_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDocument_Private.h; sourceTree = ""; }; @@ -386,8 +383,6 @@ 23A53F311E276BA5001DBE35 /* CSIOURLCache.m */, 23A53F321E276BA5001DBE35 /* NSObject+Extensions.h */, 23A53F331E276BA5001DBE35 /* NSObject+Extensions.m */, - 47CD50852B76243E0032747B /* BSONObjectIdGenerator.h */, - 47CD50862B7625B90032747B /* BSONObjectIdGenerator.m */, ); path = ContentstackInternal; sourceTree = ""; @@ -795,7 +790,6 @@ 0FEAEF2A2361A18600985FF9 /* CSURLSessionManager.m in Sources */, 0FEAEF2C2361A18600985FF9 /* CSError.m in Sources */, 23B6F12A1B5662EE00A9E983 /* ISO8601DateFormatter.m in Sources */, - 47CD50872B7625B90032747B /* BSONObjectIdGenerator.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From e1066dc4b5ec5c53433af793a15c63ad63a0a364 Mon Sep 17 00:00:00 2001 From: Vikram Kalta Date: Fri, 23 Feb 2024 08:41:52 +0000 Subject: [PATCH 6/6] fix: updated license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index af16eb4..e17cc92 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2012-2023 Contentstack +Copyright (c) 2012-2024 Contentstack Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal