Skip to content

Commit 4336f63

Browse files
First pass at datafile download by checking the 'If-Modified-Since' in the HTTP header.
1 parent a5361df commit 4336f63

File tree

10 files changed

+147
-47
lines changed

10 files changed

+147
-47
lines changed

OptimizelySDKCore/OptimizelySDKCore/OptimizelySDKCore.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@
2323
#import "OPTLYEventBuilder.h"
2424
#import "OPTLYEventDispatcher.h"
2525
#import "OPTLYExperiment.h"
26+
#import "OPTLYLog.h"
2627
#import "OPTLYLogger.h"
2728
#import "OPTLYLoggerMessages.h"
2829
#import "OPTLYProjectConfig.h"
2930
#import "OPTLYProjectConfigBuilder.h"
30-
#import "OPTLYVariable.h"
31-
#import "OPTLYVariation.h"
32-
#import "OPTLYLog.h"
3331
#import "OPTLYQueue.h"
3432
#import "OPTLYUserProfile.h"
33+
#import "OPTLYVariable.h"
34+
#import "OPTLYVariation.h"
3535

3636
FOUNDATION_EXPORT double OptimizelySDKCoreVersionNumber;
3737
FOUNDATION_EXPORT const unsigned char OptimizelySDKCoreVersionString[];

OptimizelySDKDatafileManager/OptimizelySDKDatafileManager/OPTLYDatafileManager.m

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import <UIKit/UIKit.h>
1818
#import <OptimizelySDKCore/OPTLYErrorHandler.h>
1919
#import <OptimizelySDKCore/OPTLYLog.h>
20+
#import <OptimizelySDKCore/OPTLYLogger.h>
2021
#import <OptimizelySDKShared/OPTLYDataStore.h>
2122
#import <OptimizelySDKShared/OPTLYNetworkService.h>
2223
#import "OPTLYDatafileManager.h"
@@ -62,14 +63,23 @@ - (instancetype)initWithBuilder:(OPTLYDatafileManagerBuilder *)builder {
6263

6364
- (void)downloadDatafile:(NSString *)projectId completionHandler:(OPTLYHTTPRequestManagerResponse)completion {
6465
OPTLYLogInfo(@"Downloading datafile: %@", projectId);
66+
67+
NSString *lastSavedModifiedDate = [self getLastModifiedDate:projectId];
6568
[self.networkService downloadProjectConfig:self.projectId
69+
lastModified:lastSavedModifiedDate
6670
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
6771
if (error != nil) {
6872
[self.errorHandler handleError:error];
6973
}
7074
else if ([(NSHTTPURLResponse *)response statusCode] == 200) { // got datafile OK
7175
[self saveDatafile:data];
72-
OPTLYLogInfo(@"Datafile for project ID %@ downloaded. Saving datafile.");
76+
77+
// save the last modified date
78+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
79+
NSDictionary *dictionary = [httpResponse allHeaderFields];
80+
NSString *lastModifiedDate = [dictionary valueForKey:@"Last-Modified"];
81+
[self saveLastModifiedDate:lastModifiedDate project:projectId];
82+
OPTLYLogInfo(@"Datafile for project ID %@ downloaded. Saving datafile and last modified date: %@", lastModifiedDate);
7383
}
7484
else {
7585
// TODO: Josh W. handle bad response
@@ -99,6 +109,33 @@ - (BOOL)isDatafileCached {
99109
return isCached;
100110
}
101111

112+
# pragma mark - Persistence for Last Modified Date
113+
- (void)saveLastModifiedDate:(nonnull NSString *)lastModifiedDate
114+
project:(nonnull NSString *)projectId {
115+
116+
NSDictionary *userProfileData = [self.dataStore getUserDataForType:OPTLYDataStoreDataTypeDatafile];
117+
NSMutableDictionary *userProfileDataMutable = userProfileData ? [userProfileData mutableCopy] : [NSMutableDictionary new];
118+
userProfileDataMutable[projectId] = lastModifiedDate;
119+
[self.dataStore saveUserData:userProfileDataMutable
120+
type:OPTLYDataStoreDataTypeDatafile];
121+
}
122+
123+
- (nullable NSString *)getLastModifiedDate:(nonnull NSString *)projectId {
124+
NSDictionary *userData = [self.dataStore getUserDataForType:OPTLYDataStoreDataTypeDatafile];
125+
NSString *lastModifiedDate = [userData objectForKey:projectId];
126+
127+
NSString *logMessage = @"";
128+
if ([lastModifiedDate length]) {
129+
logMessage = [NSString stringWithFormat:@"[DATAFILE MANAGER] Last modified date %@ found for project %@.", lastModifiedDate, projectId];
130+
} else {
131+
logMessage = [NSString stringWithFormat:@"[DATAFILE MANAGER] Last modified date not found for project %@.", projectId];
132+
}
133+
[self.logger logMessage:logMessage withLevel:OptimizelyLogLevelDebug];
134+
135+
return lastModifiedDate;
136+
}
137+
138+
102139
#pragma mark - Application Lifecycle Handlers
103140
- (void)setupApplicationNotificationHandlers {
104141
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];

OptimizelySDKShared/OptimizelySDKShared/OPTLYHTTPRequestManager.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,16 @@ typedef void (^OPTLYHTTPRequestManagerResponse)(NSData * _Nullable data, NSURLRe
5757
- (void)POSTWithParameters:(nullable NSDictionary *)parameters
5858
completionHandler:(nullable OPTLYHTTPRequestManagerResponse)completion;
5959

60+
/**
61+
* A GET response with the following condition:
62+
* If the requested variant has not been modified since the time specified in the
63+
* "If-Modified-Since" field, an entity will not be returned from the server; instead,
64+
* a 304 (not modified) response will be returned without any message-body.
65+
*
66+
* @param lastModifiedDate - The date since the URL request was last modified
67+
* @param completion - The completion block of type OPTLYHTTPRequestManagerResponse
68+
*/
69+
- (void)GETIfModifiedSince:(nonnull NSString *)lastModifiedDate
70+
completionHandler:(nullable OPTLYHTTPRequestManagerResponse)completion;
71+
6072
@end

OptimizelySDKShared/OptimizelySDKShared/OPTLYHTTPRequestManager.m

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License. *
1515
***************************************************************************/
1616

17+
#import <OptimizelySDKCore/OPTLYLog.h>
1718
#import "OPTLYHTTPRequestManager.h"
1819

1920
static NSString * const kHTTPRequestMethodGet = @"GET";
@@ -91,8 +92,28 @@ - (void)POSTWithParameters:(NSDictionary *)parameters
9192

9293
[uploadTask resume];
9394
}
95+
}
96+
97+
- (void)GETIfModifiedSince:(nonnull NSString *)lastModifiedDate
98+
completionHandler:(nullable OPTLYHTTPRequestManagerResponse)completion
99+
{
100+
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self url]];
101+
[request setValue:lastModifiedDate forHTTPHeaderField:@"If-Modified-Since"];
102+
103+
NSURLSession *ephemeralSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
104+
NSURLSessionDataTask *dataTask = [ephemeralSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
105+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
106+
107+
int responseCode = (int)[httpResponse statusCode];
108+
OPTLYLogInfo(@"All headers: %@", [httpResponse allHeaderFields]);
109+
OPTLYLogInfo(@"Status code:: %d", responseCode);
110+
111+
if (completion) {
112+
completion(data, response, error);
113+
}
114+
}];
94115

95-
// TODO (Alda) - Log NSJSONSerializationnerror when the logger class is implemented
116+
[dataTask resume];
96117
}
97118

98119
# pragma mark - Helper Methods

OptimizelySDKShared/OptimizelySDKShared/OPTLYManager.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
*/
6060
- (void)initializeClientWithCallback:(void(^ _Nullable)(NSError * _Nullable error, OPTLYClient * _Nullable client))callback;
6161

62+
/*
63+
* Gets the cached Optimizely client.
64+
*/
6265
- (nullable OPTLYClient *)getOptimizely;
6366

6467
@end

OptimizelySDKShared/OptimizelySDKShared/OPTLYManager.m

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@
1414
* limitations under the License. *
1515
***************************************************************************/
1616

17-
#import "OPTLYManager.h"
18-
#import "OPTLYClient.h"
19-
#import "OPTLYNetworkService.h"
17+
#import <OptimizelySDKCore/OPTLYDatafileManager.h>
2018
#import <OptimizelySDKCore/OPTLYErrorHandler.h>
2119
#import <OptimizelySDKCore/OPTLYErrorHandlerMessages.h>
2220
#import <OptimizelySDKCore/OPTLYLogger.h>
2321
#import <OptimizelySDKCore/OPTLYLoggerMessages.h>
22+
#import <OptimizelySDKShared/OPTLYManagerBuilder.h>
23+
#import "OPTLYClient.h"
24+
#import "OPTLYManager.h"
25+
#import "OPTLYManagerBuilder.h"
26+
#import "OPTLYNetworkService.h"
2427

25-
@interface OPTLYManager ()
26-
28+
@interface OPTLYManager()
2729
@property (strong, readwrite, nonatomic, nullable) OPTLYClient *optimizelyClient;
28-
2930
@end
3031

3132
@implementation OPTLYManager
@@ -103,22 +104,23 @@ - (OPTLYClient *)initializeClientWithDatafile:(NSData *)datafile {
103104
}
104105

105106
- (void)initializeClientWithCallback:(void (^)(NSError * _Nullable, OPTLYClient * _Nullable))callback {
106-
OPTLYNetworkService *networkService = [[OPTLYNetworkService alloc] init];
107-
[networkService downloadProjectConfig:self.projectId
108-
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
109-
if (error != nil) {
110-
callback(error, nil);
111-
}
112-
else {
113-
OPTLYClient *client = [OPTLYClient initWithBuilderBlock:^(OPTLYClientBuilder * _Nonnull builder) {
114-
builder.datafile = data;
115-
}];
116-
if (client.optimizely != nil) {
117-
self.optimizelyClient = client;
118-
}
119-
callback(nil, client);
120-
}
121-
}];
107+
[self.datafileManager downloadDatafile:self.projectId completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
108+
OPTLYClient *client = nil;
109+
if (!error) {
110+
client = [OPTLYClient initWithBuilderBlock:^(OPTLYClientBuilder * _Nonnull builder) {
111+
builder.datafile = data;
112+
}];
113+
if (client.optimizely) {
114+
self.optimizelyClient = client;
115+
}
116+
} else {
117+
// TODO - log error
118+
}
119+
120+
if (callback) {
121+
callback(error, client);
122+
}
123+
}];
122124
}
123125

124126
- (OPTLYClient *)getOptimizely {

OptimizelySDKShared/OptimizelySDKShared/OPTLYManagerBuilder.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#import <Foundation/Foundation.h>
1818

1919
@protocol OPTLYDatafileManager, OPTLYErrorHandler, OPTLYEventDispatcher, OPTLYLogger;
20-
2120
@class OPTLYManagerBuilder;
2221

2322
typedef void (^OPTLYManagerBuilderBlock)(OPTLYManagerBuilder * _Nullable builder);

OptimizelySDKShared/OptimizelySDKShared/OPTLYNetworkService.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,22 @@ NS_ASSUME_NONNULL_END
2626
/**
2727
* Download the project config file from remote server
2828
*
29-
* @param projectId - projectId of the project config to download
30-
* @param completion - The completion block of type OPTLYHTTPRequestManagerResponse
29+
* @param projectId The project ID of the datafile to download.
30+
* @param completion The completion block of type OPTLYHTTPRequestManagerResponse
3131
*/
3232
- (void)downloadProjectConfig:(nonnull NSString *)projectId
3333
completionHandler:(nullable OPTLYHTTPRequestManagerResponse)completion;
3434

35+
/**
36+
* Download the project config file from remote server only if it
37+
* has been modified.
38+
*
39+
* @param projectId The project ID of the datafile to download.
40+
* @param lastModifiedDate The date the datafile was last modified.
41+
* @param completion The completion block of type OPTLYHTTPRequestManagerResponse
42+
*/
43+
- (void)downloadProjectConfig:(nonnull NSString *)projectId
44+
lastModified:(nonnull NSString *)lastModifiedDate
45+
completionHandler:(nullable OPTLYHTTPRequestManagerResponse)completion;
46+
3547
@end

OptimizelySDKShared/OptimizelySDKShared/OPTLYNetworkService.m

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,42 @@
1717
#import "OPTLYNetworkService.h"
1818

1919
// ---- Datafile Download URLs ----
20-
NSString * const OPTLYNetworkServiceCDNServerURL = @"https://cdn.optimizely.com/";
21-
NSString * const OPTLYNetworkServiceS3ServerURL = @"https://optimizely.s3.amazonaws.com/";
20+
NSString * const OPTLYNetworkServiceCDNServerURL = @"https://cdn.optimizely.com/";
21+
NSString * const OPTLYNetworkServiceS3ServerURL = @"https://optimizely.s3.amazonaws.com/";
2222

2323
@implementation OPTLYNetworkService
2424

25-
- (void)downloadProjectConfig:(NSString *)projectId completionHandler:(OPTLYHTTPRequestManagerResponse)completion
25+
- (void)downloadProjectConfig:(nonnull NSString *)projectId
26+
lastModified:(nonnull NSString *)lastModifiedDate
27+
completionHandler:(nullable OPTLYHTTPRequestManagerResponse)completion
2628
{
2729
NSURL *cdnURL = [NSURL URLWithString:OPTLYNetworkServiceCDNServerURL];
2830
NSURL *cdnConfigFilePathURL = [self projectConfigURLPath:cdnURL withProjectId:projectId];
2931

3032
OPTLYHTTPRequestManager *requestManager = [[OPTLYHTTPRequestManager alloc] initWithURL:cdnConfigFilePathURL];
31-
[requestManager GET:^(NSData *data, NSURLResponse *response, NSError *error) {
32-
if (error)
33-
{
34-
// TODO (Alda) - Handle error. Possible ways of handling error:
35-
// 1. Log GET error when the logger class is implemented
36-
// 2. Retry download with backoff
37-
// 3. Return error to users
38-
// 4. Telemetry error log?
39-
}
33+
34+
[requestManager GETIfModifiedSince:lastModifiedDate
35+
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
4036

4137
if (completion) {
4238
completion(data, response, error);
4339
}
4440
}];
4541
}
4642

43+
- (void)downloadProjectConfig:(NSString *)projectId completionHandler:(OPTLYHTTPRequestManagerResponse)completion
44+
{
45+
NSURL *cdnURL = [NSURL URLWithString:OPTLYNetworkServiceCDNServerURL];
46+
NSURL *cdnConfigFilePathURL = [self projectConfigURLPath:cdnURL withProjectId:projectId];
47+
48+
OPTLYHTTPRequestManager *requestManager = [[OPTLYHTTPRequestManager alloc] initWithURL:cdnConfigFilePathURL];
49+
[requestManager GET:^(NSData *data, NSURLResponse *response, NSError *error) {
50+
if (completion) {
51+
completion(data, response, error);
52+
}
53+
}];
54+
}
55+
4756
# pragma mark - Helper Methods
4857

4958
- (NSURL *)projectConfigURLPath:(NSURL *)url

OptimizelySDKShared/OptimizelySDKShared/OptimizelySDKShared.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,21 @@
1515
***************************************************************************/
1616

1717
#import <OptimizelySDKCore/OptimizelySDKCore.h>
18-
#import "OPTLYManager.h"
19-
#import "OPTLYHTTPRequestManager.h"
20-
#import "OPTLYNetworkService.h"
21-
#import "OPTLYDataStore.h"
2218
#import "OPTLYClient.h"
23-
#import "OPTLYFileManager.h"
19+
#import "OPTLYClientBuilder.h"
20+
#import "OPTLYDataStore.h"
2421
#if TARGET_OS_IOS
2522
#import "OPTLYDatabase.h"
2623
#import "OPTLYDatabaseEntity.h"
2724
#endif
25+
#import "OPTLYFileManager.h"
26+
#import "OPTLYHTTPRequestManager.h"
27+
#import "OPTLYManager.h"
28+
#import "OPTLYManagerBuilder.h"
29+
#import "OPTLYNetworkService.h"
30+
31+
32+
2833

2934
//! Project version number for OptimizelySDKShared.
3035
FOUNDATION_EXPORT double OptimizelySDKSharedVersionNumber;

0 commit comments

Comments
 (0)