Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ContentstackPersistence.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
0F449A752AE0EAD400693DE1 /* PersistenceModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F449A732AE0EAD400693DE1 /* PersistenceModel.h */; };
0F449A762AE0EAD400693DE1 /* PersistenceModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F449A742AE0EAD400693DE1 /* PersistenceModel.m */; };
44D20D49439592B697575ADF /* libPods-ContentstackPersistenceTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0181A1F1F3118F9E9E07F420 /* libPods-ContentstackPersistenceTests.a */; };
477F8B052B7B9EB600455CB3 /* BSONOIDGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 477F8B042B7B9EB600455CB3 /* BSONOIDGenerator.m */; };
4E38FC5F2DF0928B8F15875A /* libPods-ContentstackPersistence.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 554203F0D46030984D06105D /* libPods-ContentstackPersistence.a */; };
FA629A6A2186C80E00440BA5 /* ContentstackPersistence.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA629A602186C80E00440BA5 /* ContentstackPersistence.framework */; };
FA629A6F2186C80F00440BA5 /* ContentstackPersistenceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA629A6E2186C80F00440BA5 /* ContentstackPersistenceTests.m */; };
Expand Down Expand Up @@ -45,6 +46,8 @@
0FA9B00A22651307003AAE25 /* ContentstackPersistenceRealm.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = ContentstackPersistenceRealm.podspec; sourceTree = SOURCE_ROOT; };
18AA084F9E0C06DDAB20E086 /* Pods-ContentstackPersistenceTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ContentstackPersistenceTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ContentstackPersistenceTests/Pods-ContentstackPersistenceTests.release.xcconfig"; sourceTree = "<group>"; };
2C54B17E607841EF4F9652D3 /* Pods-ContentstackPersistence.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ContentstackPersistence.release.xcconfig"; path = "Pods/Target Support Files/Pods-ContentstackPersistence/Pods-ContentstackPersistence.release.xcconfig"; sourceTree = "<group>"; };
477F8B032B7B9E2C00455CB3 /* BSONOIDGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSONOIDGenerator.h; sourceTree = "<group>"; };
477F8B042B7B9EB600455CB3 /* BSONOIDGenerator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSONOIDGenerator.m; sourceTree = "<group>"; };
554203F0D46030984D06105D /* libPods-ContentstackPersistence.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ContentstackPersistence.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DA6A5F28D3C66575D458C1FA /* Pods-ContentstackPersistenceTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ContentstackPersistenceTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ContentstackPersistenceTests/Pods-ContentstackPersistenceTests.debug.xcconfig"; sourceTree = "<group>"; };
FA629A602186C80E00440BA5 /* ContentstackPersistence.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ContentstackPersistence.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -144,6 +147,8 @@
FA629A7D2186C88600440BA5 /* SyncProtocol.h */,
FA629A632186C80E00440BA5 /* ContentstackPersistence.h */,
FA629A642186C80E00440BA5 /* Info.plist */,
477F8B032B7B9E2C00455CB3 /* BSONOIDGenerator.h */,
477F8B042B7B9EB600455CB3 /* BSONOIDGenerator.m */,
);
path = ContentstackPersistence;
sourceTree = "<group>";
Expand Down Expand Up @@ -338,6 +343,7 @@
FA629A832186C88600440BA5 /* SyncManager.m in Sources */,
FA629A8A2186C8BC00440BA5 /* RealmStore.m in Sources */,
0F449A762AE0EAD400693DE1 /* PersistenceModel.m in Sources */,
477F8B052B7B9EB600455CB3 /* BSONOIDGenerator.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
25 changes: 25 additions & 0 deletions ContentstackPersistence/BSONOIDGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// BSONOIDGenerator.h
// ContentstackPersistence
//
// Created by Vikram Kalta on 13/02/2024.
// Copyright © 2024 Contentstack. All rights reserved.
//

#ifndef BSONOIDGenerator_h
#define BSONOIDGenerator_h

#import <Foundation/Foundation.h>

typedef union {
char bytes[12];
int ints[3];
} bson_oid_t;

@interface BSONOIDGenerator : NSObject

+ (NSString *)generate:(NSInteger)timestamp;

@end

#endif /* BSONOIDGenerator_h */
103 changes: 103 additions & 0 deletions ContentstackPersistence/BSONOIDGenerator.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//
// BSONOIDGenerator.m
// ContentstackPersistence
//
// Created by Vikram Kalta on 13/02/2024.
// Copyright © 2024 Contentstack. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>
#import "BSONOIDGenerator.h"
#import <UIKit/UIKit.h>

@implementation BSONOIDGenerator
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], &timestamp, 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
134 changes: 128 additions & 6 deletions ContentstackPersistence/SyncManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#import "PersistenceModel.h"
#import <CoreData/CoreData.h>
#import <objc/runtime.h>
#import "CSIOInternalHeaders.h"
#import "BSONOIDGenerator.h"

@implementation SyncManageSwiftSupport
+ (BOOL)isSwiftClassName:(NSString *)className {
Expand Down Expand Up @@ -92,24 +94,36 @@ -(NSString*) getSyncToken {
return syncToken;
}

-(NSString*) getSeqId {
__block NSString *seqId;
[self.persistanceDelegate performBlockAndWait:^{
id<SyncStoreProtocol> syncStack = [self findOrCreate:self->_syncStack predicate:nil];
seqId = syncStack.seqId;
}];
// return [seqId stringByAppendingString: @"abc"];
return seqId;
}

-(void)updateSyncStack:(SyncStack*)syncStack {
id<SyncStoreProtocol> syncStore = (id<SyncStoreProtocol>)[self findOrCreate:_syncStack predicate:nil];
syncStore.syncToken = syncStack.syncToken;
syncStore.paginationToken = syncStack.paginationToken;
syncStore.seqId = syncStack.seqId;
}

-(void)syncWithInit:(BOOL) shouldInit onCompletion:(void (^)(double, BOOL, NSError * _Nullable))completion {
NSString *paginationToken = [self getPaginationToken];//csb0e8704474a9624785098d233edd2715`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented tokens should be removed

NSString *syncToken = [self getSyncToken];//cse053899d15e9e94cd3751df26f719c87
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented tokens should be removed

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ishaileshmishra , yes they are removed.

/* The following method will be deprecated soon */
-(void)syncWithInit:(BOOL) shouldInit syncToken:(NSString *)syncToken onCompletion:(void (^)(double, BOOL, NSError * _Nullable))completion {
__weak typeof (self) weakSelf = self;
NSString *paginationToken = [self getPaginationToken];

id completionBlock = ^(SyncStack * _Nullable syncStack, NSError * _Nullable error) {
if (error != nil) {
//Init the sync API on pagination and sync token errors
if (error.code == 422) {
if (error.userInfo && error.userInfo[@"errors"]) {
NSDictionary *errors = error.userInfo[@"errors"];
if (errors[@"pagination_token"] || errors[@"sync_token"]) {
[weakSelf syncWithInit:true onCompletion:completion];
[weakSelf syncWithInit:true syncToken:nil onCompletion:completion];
return;
}
}
Expand Down Expand Up @@ -153,7 +167,6 @@ -(void)syncWithInit:(BOOL) shouldInit onCompletion:(void (^)(double, BOOL, NSErr
}];
completion(self.percentageComplete, isSyncCompleted, error);
}

};
if (shouldInit) {
self.percentageComplete = 0;
Expand All @@ -168,8 +181,117 @@ -(void)syncWithInit:(BOOL) shouldInit onCompletion:(void (^)(double, BOOL, NSErr
}
}

-(void)syncWithSeqId:(NSString *)seqId syncToken:(NSString *)syncToken onCompletion:(void (^)(double, BOOL, NSError * _Nullable))completion {
__weak typeof (self) weakSelf = self;
id completionBlock = ^(SyncStack * _Nullable syncStack, NSError * _Nullable error) {
if (error != nil) {
//Init the sync API on pagination and sync token errors
if (error.code == 422) {
if (error.userInfo && error.userInfo[@"errors"]) {
NSDictionary *errors = error.userInfo[@"errors"];
if (errors[@"seq_id"]) {
[weakSelf syncWithSeqId:nil syncToken:nil onCompletion:completion];
return;
}
}
}
completion(self.percentageComplete, false, error);
} else {
__block BOOL isSyncCompleted = false;
[self.persistanceDelegate performBlockAndWait:^{

if (syncStack.items) {
self.percentageComplete = ((double)(syncStack.skip) + (double)syncStack.items.count) / (double)syncStack.totalCount;

if (syncToken && syncStack.seqId == nil && syncStack.items.count > 0) {
// Generate seq id.
[self generateAndPersistSeqId:syncStack];
}
}

//entry_unpublished || entry_deleted
NSArray *deletedEntryArray = [syncStack.items filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"type = 'entry_unpublished' || type = 'entry_deleted' "]];
[self deleteEntries:deletedEntryArray];

//asset_unpublished || asset_deleted
if (self->_asset != nil) {
NSArray *deletedAssetsArray = [syncStack.items filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"type = 'asset_unpublished' || type = 'asset_deleted'"]];
[self.persistanceDelegate delete:self->_asset inUid:[deletedAssetsArray valueForKeyPath:@"data.uid"]];
}

//asset_published
NSArray *publishAssetArray = [syncStack.items filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"type = 'asset_published'"]];
[self createAssets:publishAssetArray];

//entry_published
NSArray *publishEntryArray = [syncStack.items filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"type = 'entry_published'"]];
[self createEntries:publishEntryArray];

//Sync toke Update
if (syncStack.seqId != nil) {
isSyncCompleted = true;
[self updateSyncStack:syncStack];
}
//Save context
if ([self.persistanceDelegate respondsToSelector:@selector(save)]) {
[self.persistanceDelegate save];
}
}];
completion(self.percentageComplete, isSyncCompleted, error);
}
};
if (seqId != nil) {
[_stack syncSeqId:seqId syncToken:syncToken completion:completionBlock];
} else if (syncToken != nil) {
[_stack syncSeqId:seqId syncToken:syncToken completion:completionBlock];
} else {
self.percentageComplete = 0;
[_stack syncSeqIdInit:completionBlock];
}
}

-(void)generateAndPersistSeqId:(SyncStack * _Nullable)syncStack {
// Get the last object's event_at
NSDictionary *lastObject = nil;
for (NSInteger i = syncStack.items.count - 1; i >= 0; i--) {
id object = syncStack.items[i];
if ([object isKindOfClass:[NSDictionary class]]) {
lastObject = object;
break;
}
}
syncStack.seqId = [self generateSeqId:[lastObject objectForKey:@"event_at"]];
syncStack.syncToken = nil;

}

-(NSString *)generateSeqId:(NSString *)eventAt {
// Create a date formatter to parse the date string
NSDateFormatter *dateFormater = [[NSDateFormatter alloc] init];
dateFormater.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
NSDate *date = [dateFormater dateFromString:eventAt];
if (date) {
// Convert the NSDate object to an NSTimeInterval
NSTimeInterval timeInterval = [date timeIntervalSince1970];
NSInteger timeIntervalInSeconds = (NSInteger)timeInterval;
return [BSONOIDGenerator generate:timeIntervalInSeconds];
} else {
// Handle case where date conversion failed.
[NSException raise:@"Unable to parse date string" format:@"Invalid date format %@", eventAt];
return nil;
}
}

-(void)sync:(void (^)(double, BOOL, NSError * _Nullable))completion {
[self syncWithInit:false onCompletion:completion];
NSString *syncToken = [self getSyncToken];
NSString *seqId = [self getSeqId];
if (syncToken) {
[self syncWithSeqId:nil syncToken:syncToken onCompletion:completion];
} else if (seqId) {
[self syncWithSeqId:seqId syncToken:nil onCompletion:completion];
} else {
[self syncWithSeqId:nil syncToken:nil onCompletion:completion];
}
}

-(void)createAssets:(NSArray*)assetsArray {
Expand Down
1 change: 1 addition & 0 deletions ContentstackPersistence/SyncProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
@required
@property (nonatomic)NSString* paginationToken;
@property (nonatomic)NSString* syncToken;
@property (nonatomic)NSString* seqId;
@end

/**
Expand Down