Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

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

## [2.9.0] - 2017-11-28
### Changed
- `LDClientManager` no longer extends `UIApplicationDelegate`. The framework is now marked as extension-safe. Thanks @atlassian-gaustin!

### Added
- Detect 401 Unauthorized response on flag & event requests, and take the client offline when detected.
- Detect LDEventSource report of 401 Unauthorized response on connection requests, and take the client offline when detected.
- LDClient delegate method `userUnchanged` called when the client receives a feature flag update that does not change any flag keys or values. Thanks @atlassian-gaustin!
- Xcode 9 support

### Fixed
- LDPollingManager now reads the config set at the time of the startPolling message and configures polling timers accordingly.
- LDRequestManager now reads the config set at the time of the performRequest message to configure the API request.
- Removes duplicate LDEventSource libraries linked warning
- `updateUser` now updates the `LDUser` `anonymous` property when using a default user key.

## [2.8.0] - 2017-10-13
### Added
- `useReport` property on `LDConfig` to allow switching the request verb from `GET` to `REPORT`. Do not use unless advised by LaunchDarkly.
Expand Down
135 changes: 113 additions & 22 deletions Darkly.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Darkly.xcodeproj/xcshareddata/xcschemes/Darkly_iOS.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand Down
3 changes: 3 additions & 0 deletions Darkly/DarklyConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ extern NSString *const kLDUserUpdatedNotification;
extern NSString *const kLDUserNoChangeNotification;
extern NSString *const kLDFlagConfigChangedNotification;
extern NSString *const kLDServerConnectionUnavailableNotification;
extern NSString *const kLDClientUnauthorizedNotification;
extern NSString *const kLDBackgroundFetchInitiated;
extern int const kCapacity;
extern int const kConnectionTimeout;
Expand All @@ -51,5 +52,7 @@ extern int const kDefaultBackgroundFetchInterval;
extern int const kMinimumBackgroundFetchInterval;
extern int const kMillisInSecs;
extern NSInteger const kHTTPStatusCodeBadRequest;
extern NSInteger const kHTTPStatusCodeUnauthorized;
extern NSInteger const kHTTPStatusCodeMethodNotAllowed;
extern NSInteger const kHTTPStatusCodeNotImplemented;
extern NSInteger const kErrorCodeUnauthorized;
5 changes: 4 additions & 1 deletion Darkly/DarklyConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#import "DarklyConstants.h"

NSString * const kClientVersion = @"2.8.0";
NSString * const kClientVersion = @"2.9.0";
NSString * const kBaseUrl = @"https://app.launchdarkly.com";
NSString * const kEventsUrl = @"https://mobile.launchdarkly.com";
NSString * const kStreamUrl = @"https://clientstream.launchdarkly.com/mping";
Expand All @@ -28,6 +28,7 @@
NSString * const kLDBackgroundFetchInitiated = @"Darkly.BackgroundFetchInitiated";
NSString * const kLDFlagConfigChangedNotification = @"Darkly.FlagConfigChangedNotification";
NSString * const kLDServerConnectionUnavailableNotification = @"Darkly.ServerConnectionUnavailableNotification";
NSString * const kLDClientUnauthorizedNotification = @"Darkly.LDClientUnauthorizedNotification";
int const kCapacity = 100;
int const kConnectionTimeout = 10;
int const kDefaultFlushInterval = 30;
Expand All @@ -38,5 +39,7 @@
int const kMinimumBackgroundFetchInterval = 900;
int const kMillisInSecs = 1000;
NSInteger const kHTTPStatusCodeBadRequest = 400;
NSInteger const kHTTPStatusCodeUnauthorized = 401;
NSInteger const kHTTPStatusCodeMethodNotAllowed = 405;
NSInteger const kHTTPStatusCodeNotImplemented = 501;
NSInteger const kErrorCodeUnauthorized = -kHTTPStatusCodeUnauthorized;
103 changes: 54 additions & 49 deletions Darkly/LDClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@
#import "DarklyConstants.h"
#import "NSThread+MainExecutable.h"

@interface LDClient() {
BOOL clientStarted;
}
@interface LDClient()
@property(nonatomic, strong) LDUserModel *ldUser;
@property(nonatomic, strong) LDConfig *ldConfig;
@property (nonatomic, assign) BOOL clientStarted;
@end

@implementation LDClient

@synthesize ldUser, ldConfig;

+(LDClient *)sharedInstance
{
static LDClient *sharedLDClient = nil;
Expand All @@ -38,6 +37,9 @@ +(LDClient *)sharedInstance
[[NSNotificationCenter defaultCenter] addObserver: sharedLDClient
selector:@selector(configFlagUpdated:)
name:kLDFlagConfigChangedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver: sharedLDClient
selector:@selector(handleClientUnauthorizedNotification)
name:kLDClientUnauthorizedNotification object:nil];
});
return sharedLDClient;
}
Expand All @@ -48,34 +50,33 @@ -(BOOL)start:(LDConfigBuilder *)inputConfigBuilder userBuilder:(LDUserBuilder *)

- (BOOL)start:(LDConfig *)inputConfig withUserBuilder:(LDUserBuilder *)inputUserBuilder {
DEBUG_LOGX(@"LDClient start method called");
if (clientStarted) {
if (self.clientStarted) {
DEBUG_LOGX(@"LDClient client already started");
return NO;
}
if (!inputConfig) {
DEBUG_LOGX(@"LDClient client requires a config to start");
return NO;
}
ldConfig = inputConfig;
self.ldConfig = inputConfig;

[LDUtil setLogLevel:[ldConfig debugEnabled] ? DarklyLogLevelDebug : DarklyLogLevelCriticalOnly];
[LDUtil setLogLevel:[self.ldConfig debugEnabled] ? DarklyLogLevelDebug : DarklyLogLevelCriticalOnly];

clientStarted = YES;
self.clientStarted = YES;
DEBUG_LOGX(@"LDClient started");
inputUserBuilder = inputUserBuilder ?: [[LDUserBuilder alloc] init];
ldUser = [inputUserBuilder build];
self.ldUser = [inputUserBuilder build];

[LDClientManager sharedInstance].offlineEnabled = NO;
[[LDClientManager sharedInstance] startPolling];
[LDClientManager sharedInstance].online = YES;

return YES;
}

- (BOOL)updateUser:(LDUserBuilder *)builder {
DEBUG_LOGX(@"LDClient updateUser method called");
if (clientStarted) {
if (self.clientStarted) {
if (builder) {
ldUser = [LDUserBuilder compareNewBuilder:builder withUser:ldUser];
self.ldUser = [LDUserBuilder compareNewBuilder:builder withUser:self.ldUser];
LDClientManager *clientManager = [LDClientManager sharedInstance];
[clientManager syncWithServerForConfig];
return YES;
Expand All @@ -91,8 +92,8 @@ - (BOOL)updateUser:(LDUserBuilder *)builder {

- (LDUserBuilder *)currentUserBuilder {
DEBUG_LOGX(@"LDClient currentUserBuilder method called");
if (clientStarted) {
return [LDUserBuilder currentBuilder:ldUser];
if (self.clientStarted) {
return [LDUserBuilder currentBuilder:self.ldUser];
} else {
DEBUG_LOGX(@"LDClient not started yet!");
return nil;
Expand All @@ -105,9 +106,9 @@ - (BOOL)boolVariation:(NSString *)featureKey fallback:(BOOL)fallback{
NSLog(@"featureKey should be an NSString. Returning fallback value");
return fallback;
}
if (clientStarted) {
BOOL flagExists = [ldUser doesFlagExist: featureKey];
NSObject *flagValue = [ldUser flagValue: featureKey];
if (self.clientStarted) {
BOOL flagExists = [self.ldUser doesFlagExist: featureKey];
NSObject *flagValue = [self.ldUser flagValue: featureKey];
BOOL returnValue = fallback;
if ([flagValue isKindOfClass:[NSNumber class]] && flagExists) {
returnValue = [(NSNumber *)flagValue boolValue];
Expand All @@ -127,9 +128,9 @@ - (NSNumber*)numberVariation:(NSString *)featureKey fallback:(NSNumber*)fallback
NSLog(@"featureKey should be an NSString. Returning fallback value");
return fallback;
}
if (clientStarted) {
BOOL flagExists = [ldUser doesFlagExist: featureKey];
NSObject *flagValue = [ldUser flagValue: featureKey];
if (self.clientStarted) {
BOOL flagExists = [self.ldUser doesFlagExist: featureKey];
NSObject *flagValue = [self.ldUser flagValue: featureKey];
NSNumber *returnValue = fallback;
if ([flagValue isKindOfClass:[NSNumber class]] && flagExists) {
returnValue = (NSNumber *)flagValue;
Expand All @@ -149,12 +150,12 @@ - (double)doubleVariation:(NSString *)featureKey fallback:(double)fallback {
NSLog(@"featureKey should be an NSString. Returning fallback value");
return fallback;
}
if (!clientStarted) {
if (!self.clientStarted) {
DEBUG_LOGX(@"LDClient not started yet!");
return fallback;
}
BOOL flagExists = [ldUser doesFlagExist: featureKey];
id flagValue = [ldUser flagValue: featureKey];
BOOL flagExists = [self.ldUser doesFlagExist: featureKey];
id flagValue = [self.ldUser flagValue: featureKey];
double returnValue = fallback;
if (flagExists && [flagValue isKindOfClass:[NSNumber class]]) {
returnValue = [((NSNumber *)flagValue) doubleValue];
Expand All @@ -170,9 +171,9 @@ - (NSString*)stringVariation:(NSString *)featureKey fallback:(NSString*)fallback
NSLog(@"featureKey should be an NSString. Returning fallback value");
return fallback;
}
if (clientStarted) {
BOOL flagExists = [ldUser doesFlagExist: featureKey];
NSObject *flagValue = [ldUser flagValue: featureKey];
if (self.clientStarted) {
BOOL flagExists = [self.ldUser doesFlagExist: featureKey];
NSObject *flagValue = [self.ldUser flagValue: featureKey];
NSString *returnValue = fallback;
if ([flagValue isKindOfClass:[NSString class]] && flagExists) {
returnValue = (NSString *)flagValue;
Expand All @@ -192,12 +193,12 @@ - (NSArray*)arrayVariation:(NSString *)featureKey fallback:(NSArray*)fallback{
NSLog(@"featureKey should be an NSString. Returning fallback value");
return fallback;
}
if (!clientStarted) {
if (!self.clientStarted) {
DEBUG_LOGX(@"LDClient not started yet!");
return fallback;
}
BOOL flagExists = [ldUser doesFlagExist: featureKey];
id flagValue = [ldUser flagValue: featureKey];
BOOL flagExists = [self.ldUser doesFlagExist: featureKey];
id flagValue = [self.ldUser flagValue: featureKey];
NSArray *returnValue = fallback;
if (flagExists && [flagValue isKindOfClass:[NSArray class]]) {
returnValue = (NSArray *)flagValue;
Expand All @@ -213,12 +214,12 @@ - (NSDictionary*)dictionaryVariation:(NSString *)featureKey fallback:(NSDictiona
NSLog(@"featureKey should be an NSString. Returning fallback value");
return fallback;
}
if (!clientStarted) {
if (!self.clientStarted) {
DEBUG_LOGX(@"LDClient not started yet!");
return fallback;
}
BOOL flagExists = [ldUser doesFlagExist: featureKey];
id flagValue = [ldUser flagValue: featureKey];
BOOL flagExists = [self.ldUser doesFlagExist: featureKey];
id flagValue = [self.ldUser flagValue: featureKey];
NSDictionary *returnValue = fallback;
if (flagExists && [flagValue isKindOfClass:[NSDictionary class]]) {
returnValue = (NSDictionary *)flagValue;
Expand All @@ -231,7 +232,7 @@ - (NSDictionary*)dictionaryVariation:(NSString *)featureKey fallback:(NSDictiona
- (BOOL)track:(NSString *)eventName data:(NSDictionary *)dataDictionary
{
DEBUG_LOG(@"LDClient track method called for event=%@ and data=%@", eventName, dataDictionary);
if (clientStarted) {
if (self.clientStarted) {
[[LDDataManager sharedManager] createCustomEvent:eventName
withCustomValuesDictionary: dataDictionary];
return YES;
Expand All @@ -244,10 +245,9 @@ - (BOOL)track:(NSString *)eventName data:(NSDictionary *)dataDictionary
- (BOOL)offline
{
DEBUG_LOGX(@"LDClient offline method called");
if (clientStarted) {
if (self.clientStarted) {
LDClientManager *clientManager = [LDClientManager sharedInstance];
[clientManager stopPolling];
[clientManager setOfflineEnabled:YES];
[clientManager setOnline:NO];
return YES;
} else {
DEBUG_LOGX(@"LDClient not started yet!");
Expand All @@ -258,10 +258,9 @@ - (BOOL)offline
- (BOOL)online
{
DEBUG_LOGX(@"LDClient online method called");
if (clientStarted) {
if (self.clientStarted) {
LDClientManager *clientManager = [LDClientManager sharedInstance];
[clientManager setOfflineEnabled:NO];
[clientManager startPolling];
[clientManager setOnline:YES];
return YES;
} else {
DEBUG_LOGX(@"LDClient not started yet!");
Expand All @@ -271,7 +270,7 @@ - (BOOL)online

- (BOOL)flush {
DEBUG_LOGX(@"LDClient flush method called");
if (clientStarted) {
if (self.clientStarted) {
LDClientManager *clientManager = [LDClientManager sharedInstance];
[clientManager flushEvents];
return YES;
Expand All @@ -283,16 +282,14 @@ - (BOOL)flush {

- (BOOL)stopClient {
DEBUG_LOGX(@"LDClient stop method called");
if (clientStarted) {
LDClientManager *clientManager = [LDClientManager sharedInstance];
[clientManager stopPolling];

clientStarted = NO;
return YES;
} else {
if (!self.clientStarted) {
DEBUG_LOGX(@"LDClient not started yet!");
return NO;
}

[self offline];
self.clientStarted = NO;
return YES;
}

// Notification handler for ClientManager user updated
Expand Down Expand Up @@ -327,6 +324,14 @@ -(void)configFlagUpdated:(NSNotification *)notification {
}];
}

//Notification handler for Client Unauthorized notification
-(void)handleClientUnauthorizedNotification {
[NSThread performOnMainThread:^{
DEBUG_LOGX(@"LDClient received Client Unauthorized notification. Taking LDClient offline.");
[self offline];
}];
}

-(void)dealloc {
self.delegate = nil;
}
Expand Down
2 changes: 1 addition & 1 deletion Darkly/LDClientManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
}
#endif

@property (nonatomic) BOOL offlineEnabled;
@property (nonatomic, assign, getter=isOnline) BOOL online;

+(LDClientManager *)sharedInstance;

Expand Down
Loading