Skip to content

Commit

Permalink
Version 2.11.0 (#128)
Browse files Browse the repository at this point in the history
* SSE Enhanced Events (#58)

* adds enhanced sse event handling
# Conflicts:
#	LaunchDarkly.podspec
* moves stream url method out of LDConfig and into LDClientManager
* update versions
* cleanup 2 static analyzer issues
* cleanup hello-ios static analyzer warnings

* updates version to 2.11.0

* Updated CHANGELOG

* adds date to change log
  • Loading branch information
markpokornycos authored Mar 16, 2018
1 parent dd6a0b7 commit 4c921d9
Show file tree
Hide file tree
Showing 125 changed files with 2,932 additions and 615 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to the LaunchDarkly iOS SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).

## [2.11.0] - 2018-03-15
### Added
- Support for enhanced feature streams, facilitating reduced SDK initialization times.

### Changed
- The `streamUrl` property on `LDConfig` now expects a path-less base URI.

## [2.10.1] - 2018-02-15
### Changed
- The minimum polling interval is now 5 minutes.
Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "launchdarkly/ios-eventsource"
github "launchdarkly/ios-eventsource" >= 3.2
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "launchdarkly/ios-eventsource" "3.1.2"
github "launchdarkly/ios-eventsource" "3.2.0"
236 changes: 200 additions & 36 deletions Darkly.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
LastUpgradeVersion = "0920"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
1 change: 1 addition & 0 deletions Darkly/DarklyConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ extern NSString *const kLDFlagConfigChangedNotification;
extern NSString *const kLDServerConnectionUnavailableNotification;
extern NSString *const kLDClientUnauthorizedNotification;
extern NSString *const kLDBackgroundFetchInitiated;
extern NSString *const kHTTPMethodReport;
extern int const kCapacity;
extern int const kConnectionTimeout;
extern int const kDefaultFlushInterval;
Expand Down
5 changes: 3 additions & 2 deletions Darkly/DarklyConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

#import "DarklyConstants.h"

NSString * const kClientVersion = @"2.10.1";
NSString * const kClientVersion = @"2.11.0";
NSString * const kBaseUrl = @"https://app.launchdarkly.com";
NSString * const kEventsUrl = @"https://mobile.launchdarkly.com";
NSString * const kStreamUrl = @"https://clientstream.launchdarkly.com/mping";
NSString * const kStreamUrl = @"https://clientstream.launchdarkly.com";
NSString * const kNoMobileKeyExceptionName = @"NoMobileKeyDefinedException";
NSString * const kNoMobileKeyExceptionReason = @"A valid MobileKey must be provided";
NSString * const kNilConfigExceptionName = @"NilConfigException";
Expand All @@ -29,6 +29,7 @@
NSString * const kLDFlagConfigChangedNotification = @"Darkly.FlagConfigChangedNotification";
NSString * const kLDServerConnectionUnavailableNotification = @"Darkly.ServerConnectionUnavailableNotification";
NSString * const kLDClientUnauthorizedNotification = @"Darkly.LDClientUnauthorizedNotification";
NSString * const kHTTPMethodReport = @"REPORT";
int const kCapacity = 100;
int const kConnectionTimeout = 10;
int const kDefaultFlushInterval = 30;
Expand Down
134 changes: 130 additions & 4 deletions Darkly/LDClientManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
#import "NSDictionary+JSON.h"
#import <DarklyEventSource/LDEventSource.h>
#import "LDEvent+Unauthorized.h"
#import "LDEvent+EventTypes.h"

NSString * const kLDClientManagerStreamMethod = @"meval";

@interface LDClientManager()

Expand Down Expand Up @@ -135,11 +138,14 @@ - (void)configureEventSource {
DEBUG_LOGX(@"ClientManager aborting event source creation - event source running");
return;
}
eventSource = [LDEventSource eventSourceWithURL:[NSURL URLWithString:[LDClient sharedInstance].ldConfig.streamUrl] httpHeaders:[self httpHeadersForEventSource]];


eventSource = [self eventSourceForUser:[LDClient sharedInstance].ldUser config:[LDClient sharedInstance].ldConfig httpHeaders:[self httpHeadersForEventSource]];

[eventSource onMessage:^(LDEvent *event) {
if (![event.event isEqualToString:@"ping"]) { return; }
[self syncWithServerForConfig];
[self handlePingEvent:event];
[self handlePutEvent:event];
[self handlePatchEvent:event];
[self handleDeleteEvent:event];
}];

[eventSource onError:^(LDEvent *event) {
Expand All @@ -150,6 +156,126 @@ - (void)configureEventSource {
}
}

- (LDEventSource*)eventSourceForUser:(LDUserModel*)user config:(LDConfig*)config httpHeaders:(NSDictionary*)httpHeaders {
LDEventSource *eventSource;
if (config.useReport) {
eventSource = [LDEventSource eventSourceWithURL:[self eventSourceUrlForUser:user config:config]
httpHeaders:httpHeaders
connectMethod:kHTTPMethodReport
connectBody:[[[user dictionaryValueWithPrivateAttributesAndFlagConfig:NO] jsonString] dataUsingEncoding:NSUTF8StringEncoding]];
} else {
eventSource = [LDEventSource eventSourceWithURL:[self eventSourceUrlForUser:user config:config] httpHeaders:httpHeaders connectMethod:nil connectBody:nil];
}
return eventSource;
}

- (NSURL*)eventSourceUrlForUser:(LDUserModel *)user config:(LDConfig*)config {
NSString *eventStreamUrl = [config.streamUrl stringByAppendingPathComponent:kLDClientManagerStreamMethod];
if (!config.useReport) {
NSString *encodedUser = [LDUtil base64UrlEncodeString:[[user dictionaryValueWithPrivateAttributesAndFlagConfig:NO] jsonString]];
eventStreamUrl = [eventStreamUrl stringByAppendingPathComponent:encodedUser];
}
return [NSURL URLWithString:eventStreamUrl];
}

- (void)handlePingEvent:(LDEvent*)event {
if (![event.event isEqualToString:kLDEventTypePing]) { return; }
[self syncWithServerForConfig];
}

- (void)handlePutEvent:(LDEvent*)event {
if (![event.event isEqualToString:kLDEventTypePut]) { return; }
if (event.data.length == 0) {
DEBUG_LOGX(@"ClientManager aborted handlePutEvent - event contains no data");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}
NSDictionary *newConfigDictionary = [NSJSONSerialization JSONObjectWithData:[event.data dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];
if (!newConfigDictionary) {
DEBUG_LOGX(@"ClientManager aborted handlePutEvent - event contains json data could not be read");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}

LDFlagConfigModel *newConfig = [[LDFlagConfigModel alloc] initWithDictionary:newConfigDictionary];
LDUserModel *user = [[LDClient sharedInstance] ldUser];

if ([user.config isEqualToConfig:newConfig]) {
DEBUG_LOGX(@"ClientManager handlePutEvent resulted in no change to the flag config");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}

user.config = newConfig;
[[LDDataManager sharedManager] saveUser:user];
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserUpdatedNotification object:nil];
DEBUG_LOGX(@"ClientManager posted Darkly.UserUpdatedNotification following user config update from SSE put event");
}

- (void)handlePatchEvent:(LDEvent*)event {
if (![event.event isEqualToString:kLDEventTypePatch]) { return; }
if (event.data.length == 0) {
DEBUG_LOGX(@"ClientManager aborted handlePatchEvent - event contains no data");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}
NSDictionary *patchDictionary = [NSJSONSerialization JSONObjectWithData:[event.data dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];
if (!patchDictionary) {
DEBUG_LOGX(@"ClientManager aborted handlePatchEvent - event json data could not be read");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}

LDUserModel *user = [[LDClient sharedInstance] ldUser];
NSDictionary *originalFlagConfig = user.config.featuresJsonDictionary;

[user.config addOrReplaceFromDictionary:patchDictionary];

if ([user.config hasFeaturesEqualToDictionary:originalFlagConfig]) {
DEBUG_LOGX(@"ClientManager handlePatchEvent resulted in no change to the flag config");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}

[[LDDataManager sharedManager] saveUser:user];
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserUpdatedNotification object:nil];
DEBUG_LOGX(@"ClientManager posted Darkly.UserUpdatedNotification following user config update from SSE patch event");
}

- (void)handleDeleteEvent:(LDEvent*)event {
if (![event.event isEqualToString:kLDEventTypeDelete]) { return; }
if (event.data.length == 0) {
DEBUG_LOGX(@"ClientManager aborted handleDeleteEvent - event contains no data");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}
NSDictionary *deleteDictionary = [NSJSONSerialization JSONObjectWithData:[event.data dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];
if (!deleteDictionary) {
DEBUG_LOGX(@"ClientManager aborted handleDeleteEvent - event json data could not be read");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}

LDUserModel *user = [[LDClient sharedInstance] ldUser];
NSDictionary *originalFlagConfig = user.config.featuresJsonDictionary;

[user.config deleteFromDictionary:deleteDictionary];

if ([user.config hasFeaturesEqualToDictionary:originalFlagConfig]) {
DEBUG_LOGX(@"ClientManager handleDeleteEvent resulted in no change to the flag config");
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil];
return;
}

[[LDDataManager sharedManager] saveUser:user];
[[NSNotificationCenter defaultCenter] postNotificationName:kLDUserUpdatedNotification object:nil];
DEBUG_LOGX(@"ClientManager posted Darkly.UserUpdatedNotification following user config update from SSE delete event");
}

- (void)postClientUnauthorizedNotification {
[[NSNotificationCenter defaultCenter] postNotificationName:kLDClientUnauthorizedNotification object:nil];
}

- (void)stopEventSource {
@synchronized (self) {
[eventSource close];
Expand Down
10 changes: 5 additions & 5 deletions Darkly/LDConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ - (void)setEventsUrl:(NSString *)eventsUrl {
}

- (void)setCapacity:(NSNumber *)capacity {
if (capacity) {
if (capacity != nil) {
DEBUG_LOG(@"Set LDConfig capacity: %@", capacity);
_capacity = capacity;
} else {
Expand All @@ -73,7 +73,7 @@ - (void)setCapacity:(NSNumber *)capacity {
}

- (void)setConnectionTimeout:(NSNumber *)connectionTimeout {
if (connectionTimeout) {
if (connectionTimeout != nil) {
DEBUG_LOG(@"Set LDConfig timeout: %@", connectionTimeout);
_connectionTimeout = connectionTimeout;
} else {
Expand All @@ -83,7 +83,7 @@ - (void)setConnectionTimeout:(NSNumber *)connectionTimeout {
}

- (void)setFlushInterval:(NSNumber *)flushInterval {
if (flushInterval) {
if (flushInterval != nil) {
DEBUG_LOG(@"Set LDConfig flush interval: %@", flushInterval);
_flushInterval = flushInterval;
} else {
Expand All @@ -93,7 +93,7 @@ - (void)setFlushInterval:(NSNumber *)flushInterval {
}

- (void)setPollingInterval:(NSNumber *)pollingInterval {
if (pollingInterval) {
if (pollingInterval != nil) {
DEBUG_LOG(@"Set LDConfig polling interval: %@", pollingInterval);
_pollingInterval = [NSNumber numberWithInt:MAX(pollingInterval.intValue, kMinimumPollingInterval)];
} else {
Expand All @@ -103,7 +103,7 @@ - (void)setPollingInterval:(NSNumber *)pollingInterval {
}

- (void)setBackgroundFetchInterval:(NSNumber *)backgroundFetchInterval {
if (backgroundFetchInterval) {
if (backgroundFetchInterval != nil) {
DEBUG_LOG(@"Set LDConfig background fetch interval: %@", backgroundFetchInterval);
_backgroundFetchInterval = backgroundFetchInterval;
} else {
Expand Down
2 changes: 1 addition & 1 deletion Darkly/LDDataManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ -(LDUserModel *)findUserWithkey: (NSString *)key {
}

- (void)compareConfigForUser:(LDUserModel *)user withNewUser:(LDUserModel *)newUser {
for (NSString *key in [[newUser.config dictionaryValue] objectForKey:kFeaturesJsonDictionaryKey]) {
for (NSString *key in [newUser.config dictionaryValueIncludeNulls:NO]) {
if(user == nil || ![[newUser.config configFlagValue:key] isEqual:[user.config configFlagValue:key]]) {
[[NSNotificationCenter defaultCenter] postNotificationName:kLDFlagConfigChangedNotification object:nil userInfo:[NSDictionary dictionaryWithObject:key forKey:kFlagKey]];
}
Expand Down
18 changes: 18 additions & 0 deletions Darkly/LDEvent+EventTypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// LDEvent+EventTypes.h
// Darkly
//
// Created by Mark Pokorny on 2/5/18.
// Copyright © 2018 LaunchDarkly. All rights reserved.
//

#import <DarklyEventSource/LDEventSource.h>

extern NSString * const kLDEventTypePing;
extern NSString * const kLDEventTypePut;
extern NSString * const kLDEventTypePatch;
extern NSString * const kLDEventTypeDelete;

@interface LDEvent (EventTypes)

@end
18 changes: 18 additions & 0 deletions Darkly/LDEvent+EventTypes.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// LDEvent+EventTypes.m
// Darkly
//
// Created by Mark Pokorny on 2/5/18. +JMJ
// Copyright © 2018 LaunchDarkly. All rights reserved.
//

#import "LDEvent+EventTypes.h"

NSString * const kLDEventTypePing = @"ping";
NSString * const kLDEventTypePut = @"put";
NSString * const kLDEventTypePatch = @"patch";
NSString * const kLDEventTypeDelete = @"delete";

@implementation LDEvent (EventTypes)

@end
19 changes: 12 additions & 7 deletions Darkly/LDFlagConfigModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@
//

#import <Foundation/Foundation.h>
#import "LDFlagConfigValue.h"

@interface LDFlagConfigModel : NSObject <NSCoding>

extern NSString * _Nullable const kFeaturesJsonDictionaryKey;
@property (nullable, nonatomic, strong) NSDictionary<NSString*, LDFlagConfigValue*> *featuresJsonDictionary;

@property (nullable, nonatomic, strong) NSDictionary *featuresJsonDictionary;
-(nullable id)initWithDictionary:(nullable NSDictionary*)dictionary;
-(nullable NSDictionary*)dictionaryValue;
-(nullable NSDictionary*)dictionaryValueIncludeNulls:(BOOL)includeNulls;

- (nonnull id)initWithDictionary:(nonnull NSDictionary *)dictionary;
-(nonnull NSDictionary *)dictionaryValue;
-(BOOL)doesConfigFlagExist:(nonnull NSString*)keyName;
-(nullable id)configFlagValue:(nonnull NSString*)keyName;
-(NSInteger)configFlagVersion:(nonnull NSString*)keyName;

-(nonnull NSObject*) configFlagValue: ( NSString * __nonnull )keyName;
-(BOOL) doesConfigFlagExist: ( NSString * __nonnull )keyName;
-(void)addOrReplaceFromDictionary:(nullable NSDictionary*)patch;
-(void)deleteFromDictionary:(nullable NSDictionary*)delete;

-(BOOL)isEqualToConfig:(nullable LDFlagConfigModel *)otherConfig;
-(BOOL)isEqualToConfig:(nullable LDFlagConfigModel*)otherConfig;
-(BOOL)hasFeaturesEqualToDictionary:(nullable NSDictionary*)otherDictionary;
@end
Loading

0 comments on commit 4c921d9

Please sign in to comment.