Skip to content

Commit

Permalink
Version 2.14.0 (#156)
Browse files Browse the repository at this point in the history
* Adds support for multiple projects/environments
* Added allFlags property to LDClient that provides a dictionary of feature flag keys and values
* LDUserBuilder build method no longer restores cached user attributes
* Fixed defect preventing SDK from calling userUpdated or featureFlagDidUpdate when deleting a feature flag under certain conditions
  • Loading branch information
arun251 authored Dec 6, 2018
1 parent efbb41f commit b590c58
Show file tree
Hide file tree
Showing 168 changed files with 9,852 additions and 4,701 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
FL_OUTPUT_DIR: output

macos:
xcode: '10.0.0'
xcode: '10.1.0'

steps:
- checkout
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

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

## [2.14.0] - 2018-12-05
### Added
- Added `allFlags` property to `LDClient` that provides a dictionary of feature flag keys and values. Accessing feature flags via `allFlags` does not record any analytics events.
- Support for multiple LaunchDarkly projects or environments. Each set of feature flags associated with a mobile key is called an `environment`.
• Added `secondaryMobileKeys` to LDConfig. LDConfig `mobileKey` refers to the *primary* environment, and must be present. All entries in `secondaryMobileKeys` refer to optional *secondary* environments.
NOTE: See `LDClient.h` for the requirements to add `secondaryMobileKeys`. The SDK will throw an `NSInvalidArgumentException` if an attempt is made to set mobile keys that do not meet these requirements.
• Installed `LDClientInterface` protocol used to access secondary environment feature flags. May also be used on the primary environment to provide normalized access to feature flags.
• Adds `environmentForMobileKeyNamed:` to vend an environment (primary or secondary) object conforming to `LDClientInterface`. Use the vended object to access feature flags for the requested environment.
• Adds new constant `kLDPrimaryEnvironmentName` used to vend the primary environment's `LDClientInterface` from `environmentForMobileKeyNamed:`.

### Changed
- `LDUserBuilder build` method no longer restores cached user attributes. The SDK sets into the `LDUserModel` object only the attributes in the `LDUserBuilder` at the time of the build message. On start, the SDK restores the last cached feature flags, which the SDK will use until the first feature flag update from the server.
- Changed the format for caching feature flags to associate a set of feature flags with a mobile key. Downgrading to an earlier version will be able to store feature flags, but without the environment association. As a result, the SDK will not restore cached feature flags from 2.14.0 if the SDK is downgraded to a version before 2.14.0.
- Installed a URL cache that does not use the `[NSURLSession defaultSession]` or the `[NSURLCache sharedURLCache]`, precluding conflicts with custom client app URL caching.

### Fixed
- Fixed defect preventing SDK from calling `userUpdated` or `featureFlagDidUpdate` when deleting a feature flag under certain conditions.
- Fixed defect preventing URL caching for feature flag requests using the `REPORT` verb.
- Fixed defect causing the loss of some analytics events when changing users.

## [2.13.9] - 2018-11-05
### Fixed
- Fixed defect causing a crash when unknown data exists in a feature flag cache.
Expand Down
902 changes: 603 additions & 299 deletions Darkly.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

12 changes: 2 additions & 10 deletions Darkly/Darkly.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,8 @@

#import <Foundation/Foundation.h>

#import "LDDataManager.h"
#import "DarklyConstants.h"
#import "LDClient.h"
#import "LDClientManager.h"
#import "LDConfig.h"
#import "LDEventModel.h"
#import "LDFlagConfigModel.h"
#import "LDPollingManager.h"
#import "LDRequestManager.h"
#import "LDUserBuilder.h"
#import "LDUserModel.h"
#import "LDUtil.h"
#import "NSDictionary+JSON.h"
#import "LDClientInterface.h"
#import "LDClient.h"
18 changes: 15 additions & 3 deletions Darkly/DarklyConstants.h → Darkly/DataModels/DarklyConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ typedef enum {
} DarklyLogLevel;

extern NSString * const kClientVersion;
extern NSString * const kLDPrimaryEnvironmentName;
extern NSString * const kBaseUrl;
extern NSString * const kEventsUrl;
extern NSString * const kStreamUrl;
Expand All @@ -36,25 +37,36 @@ extern NSString * const kAppleTV;
extern NSString * const kMacOS;
extern NSString * const kUserDictionaryStorageKey;
extern NSString * const kDeviceIdentifierKey;
extern NSString * const kHeaderMobileKey;
extern NSString * const kHTTPMethodReport;

extern NSString *const kLDUserUpdatedNotification;
extern NSString *const kLDUserNoChangeNotification;
extern NSString *const kLDFlagConfigChangedNotification;
extern NSString *const kLDFeatureFlagsChangedNotification;
extern NSString *const kLDServerConnectionUnavailableNotification;
extern NSString *const kLDClientUnauthorizedNotification;
extern NSString *const kLDFlagConfigTimerFiredNotification;
extern NSString *const kLDEventTimerFiredNotification;
extern NSString *const kLDBackgroundFetchInitiated;
extern NSString *const kHTTPMethodReport;
extern NSString *const kLDNotificationUserInfoKeyMobileKey;
extern NSString *const kLDNotificationUserInfoKeyFlagKeys;

extern int const kCapacity;
extern int const kConnectionTimeout;
extern int const kDefaultFlushInterval;
extern int const kMinimumFlushIntervalMillis;
extern int const kMinimumFlushInterval;
extern int const kDefaultPollingInterval;
extern int const kMinimumPollingInterval;
extern int const kDefaultBackgroundFetchInterval;
extern int const kMinimumBackgroundFetchInterval;
extern int const kMillisInSecs;
extern NSInteger const kHTTPStatusCodeOk;
extern NSInteger const kHTTPStatusCodeNotModified;
extern NSInteger const kHTTPStatusCodeBadRequest;
extern NSInteger const kHTTPStatusCodeUnauthorized;
extern NSInteger const kHTTPStatusCodeMethodNotAllowed;
extern NSInteger const kHTTPStatusCodeNotImplemented;
extern NSInteger const kErrorCodeUnauthorized;
extern NSUInteger const kNSURLCacheMemoryCapacity;
extern NSUInteger const kNSURLCacheDiskCapacity;
extern NSTimeInterval const kMaxThrottlingDelayInterval;
21 changes: 17 additions & 4 deletions Darkly/DarklyConstants.m → Darkly/DataModels/DarklyConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

#import "DarklyConstants.h"

NSString * const kClientVersion = @"2.13.9";
NSString * const kClientVersion = @"2.14.0";
NSString * const kLDPrimaryEnvironmentName = @"LaunchDarkly.EnvironmentName.Primary";
NSString * const kBaseUrl = @"https://app.launchdarkly.com";
NSString * const kEventsUrl = @"https://mobile.launchdarkly.com";
NSString * const kStreamUrl = @"https://clientstream.launchdarkly.com";
Expand All @@ -23,17 +24,24 @@
NSString * const kMacOS = @"macOS";
NSString * const kUserDictionaryStorageKey = @"ldUserModelDictionary";
NSString * const kDeviceIdentifierKey = @"ldDeviceIdentifier";
NSString * const kHeaderMobileKey = @"api_key ";
NSString * const kHTTPMethodReport = @"REPORT";

NSString * const kLDUserUpdatedNotification = @"Darkly.UserUpdatedNotification";
NSString * const kLDUserNoChangeNotification = @"Darkly.UserNoChangeNotification";
NSString * const kLDBackgroundFetchInitiated = @"Darkly.BackgroundFetchInitiated";
NSString * const kLDFlagConfigChangedNotification = @"Darkly.FlagConfigChangedNotification";
NSString * const kLDFeatureFlagsChangedNotification = @"Darkly.FeatureFlagsChangedNotification";
NSString * const kLDServerConnectionUnavailableNotification = @"Darkly.ServerConnectionUnavailableNotification";
NSString * const kLDClientUnauthorizedNotification = @"Darkly.LDClientUnauthorizedNotification";
NSString * const kHTTPMethodReport = @"REPORT";
NSString * const kLDFlagConfigTimerFiredNotification = @"Darkly.FlagConfigTimerFiredNotification";
NSString * const kLDEventTimerFiredNotification = @"Darkly.EventTimerFiredNotification";
NSString * const kLDNotificationUserInfoKeyMobileKey = @"Darkly.Notification.UserInfo.MobileKey";
NSString * const kLDNotificationUserInfoKeyFlagKeys = @"Darkly.Notification.UserInfo.FlagKeys";

int const kCapacity = 100;
int const kConnectionTimeout = 10;
int const kDefaultFlushInterval = 30;
int const kMinimumFlushIntervalMillis = 0;
int const kMinimumFlushInterval = 0;
int const kDefaultPollingInterval = 300;
#if DEBUG
int const kMinimumPollingInterval = 30;
Expand All @@ -43,9 +51,14 @@
int const kDefaultBackgroundFetchInterval = 3600;
int const kMinimumBackgroundFetchInterval = 900;
int const kMillisInSecs = 1000;
NSInteger const kHTTPStatusCodeOk = 200;
NSInteger const kHTTPStatusCodeNotModified = 304;
NSInteger const kHTTPStatusCodeBadRequest = 400;
NSInteger const kHTTPStatusCodeUnauthorized = 401;
NSInteger const kHTTPStatusCodeMethodNotAllowed = 405;
NSInteger const kHTTPStatusCodeNotImplemented = 501;
NSInteger const kErrorCodeUnauthorized = -kHTTPStatusCodeUnauthorized;
NSUInteger const kNSURLCacheMemoryCapacity = 512000;
NSUInteger const kNSURLCacheDiskCapacity = 0;

NSTimeInterval const kMaxThrottlingDelayInterval = 600.0;
11 changes: 10 additions & 1 deletion Darkly/LDConfig.h → Darkly/DataModels/LDConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
*/
@property (nonatomic, readonly, nonnull) NSString* mobileKey;

/**
These are the names and mobile keys for secondary environments to use in the SDK. The
property must specify a 1:1 mapping of environment name to mobile key. Neither
kLDPrimaryEnvironmentName nor the value in mobileKey may appear in secondaryMobileKeys.
Neither the names nor mobile keys may be empty. If any of these conditions are not met
the SDK will throw an NSInvalidArgumentException. Optional. The default is nil.
*/
@property (nonatomic, strong, nullable) NSDictionary<NSString*, NSString*> *secondaryMobileKeys;

/**
The base URL of the LaunchDarkly service, should you need to override
the default.
Expand Down Expand Up @@ -113,7 +122,7 @@
*/
- (instancetype _Nonnull)initWithMobileKey:(nonnull NSString *)mobileKey NS_DESIGNATED_INITIALIZER;
- (BOOL)isFlagRetryStatusCode:(NSInteger)statusCode;

-(NSString*)secondaryMobileKeysDescription;
- (instancetype _Nonnull )init NS_UNAVAILABLE;

@end
Expand Down
67 changes: 67 additions & 0 deletions Darkly/LDConfig.m → Darkly/DataModels/LDConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

#import "LDConfig.h"
#import "LDUtil.h"
#import "LDClient.h"
#import "NSString+LaunchDarkly.h"
#import "NSDictionary+LaunchDarkly.h"

@interface LDConfig()
@property (nonatomic, copy, nonnull) NSString* mobileKey;
Expand Down Expand Up @@ -42,6 +45,35 @@ - (void)setMobileKey:(NSString *)mobileKey {
DEBUG_LOG(@"Set LDConfig mobileKey: %@", mobileKey);
}

- (void)setSecondaryMobileKeys:(NSDictionary<NSString *,NSString *> *)secondaryMobileKeys {
if ([secondaryMobileKeys.allKeys containsObject:kLDPrimaryEnvironmentName]) {
NSException *invalidConfigException =
[NSException exceptionWithName:NSInvalidArgumentException reason:@"Illegal LDConfig secondaryMobileKeys: May not contain the primary environment name." userInfo:nil];
@throw invalidConfigException;
}
if ([secondaryMobileKeys.allValues containsObject:self.mobileKey]) {
NSException *invalidConfigException =
[NSException exceptionWithName:NSInvalidArgumentException reason:@"Illegal LDConfig secondaryMobileKeys: May not contain the primary mobile key." userInfo:nil];
@throw invalidConfigException;
}
if ([NSSet setWithArray:secondaryMobileKeys.allValues].count != secondaryMobileKeys.allValues.count) {
NSException *invalidConfigException =
[NSException exceptionWithName:NSInvalidArgumentException reason:@"Illegal LDConfig secondaryMobileKeys: mobile keys must all be unique" userInfo:nil];
@throw invalidConfigException;
}
if ([secondaryMobileKeys.allKeys containsObject:@""]) {
NSException *invalidConfigException =
[NSException exceptionWithName:NSInvalidArgumentException reason:@"Illegal LDConfig secondaryMobileKeys: May not contain an empty environment name." userInfo:nil];
@throw invalidConfigException;
}
if ([secondaryMobileKeys.allValues containsObject:@""]) {
NSException *invalidConfigException =
[NSException exceptionWithName:NSInvalidArgumentException reason:@"Illegal LDConfig secondaryMobileKeys: May not contain an empty mobile key." userInfo:nil];
@throw invalidConfigException;
}
_secondaryMobileKeys = secondaryMobileKeys;
}

- (void)setBaseUrl:(NSString *)baseUrl {
if (baseUrl) {
DEBUG_LOG(@"Set LDConfig baseUrl: %@", baseUrl);
Expand Down Expand Up @@ -136,6 +168,41 @@ - (BOOL)isFlagRetryStatusCode:(NSInteger)statusCode {
return [self.flagRetryStatusCodes containsObject:@(statusCode)];
}

-(NSString*)description {
NSString *description = [NSString stringWithFormat:@"<LDConfig:%p mobileKey:%@", self, self.mobileKey];
description = [NSString stringWithFormat:@"%@ secondaryKeys:%@", description, [self secondaryMobileKeysDescription]];
description = [NSString stringWithFormat:@"%@ baseUrl:%@", description, self.baseUrl];
description = [NSString stringWithFormat:@"%@ streamUrl:%@", description, self.streamUrl];
description = [NSString stringWithFormat:@"%@ eventsUrl:%@", description, self.eventsUrl];
description = [NSString stringWithFormat:@"%@ capacity:%ld connectionTimeout:%ld", description, (long)[self.capacity integerValue], (long)[self.connectionTimeout integerValue]];
description = [NSString stringWithFormat:@"%@ capacity:%ld connectionTimeout:%ld", description, (long)[self.capacity integerValue], (long)[self.connectionTimeout integerValue]];
description = [NSString stringWithFormat:@"%@ flushInterval:%ld pollingInterval:%ld backgroundFetchInterval:%ld", description,
(long)[self.flushInterval integerValue], (long)[self.pollingInterval integerValue], (long)[self.backgroundFetchInterval integerValue]];
description = [NSString stringWithFormat:@"%@ capacity:%ld connectionTimeout:%ld", description, (long)[self.capacity integerValue], (long)[self.connectionTimeout integerValue]];
description = [NSString stringWithFormat:@"%@ streaming:%@", description, [NSString stringWithBool:self.streaming]];
description = [NSString stringWithFormat:@"%@ privateUserAttributes:%@", description, [self.privateUserAttributes componentsJoinedByString:@","]];
description = [NSString stringWithFormat:@"%@ allUserAttributesPrivate:%@", description, [NSString stringWithBool:self.allUserAttributesPrivate]];
description = [NSString stringWithFormat:@"%@ useReport:%@", description, [NSString stringWithBool:self.useReport]];
description = [NSString stringWithFormat:@"%@ useReport:%@", description, [NSString stringWithBool:self.useReport]];
description = [NSString stringWithFormat:@"%@ inlineUserInEvents:%@", description, [NSString stringWithBool:self.inlineUserInEvents]];
description = [NSString stringWithFormat:@"%@ debugEnabled:%@", description, [NSString stringWithBool:self.debugEnabled]];
description = [NSString stringWithFormat:@"%@>", description];
return description;
}

-(NSString*)secondaryMobileKeysDescription {
if (self.secondaryMobileKeys.count == 0) {
return @"{}";
}
NSString *secondaryKeysDescription = @"{";
NSString *separator = @"";
for (NSString *environmentName in self.secondaryMobileKeys.allKeys) {
secondaryKeysDescription = [NSString stringWithFormat:@"%@%@%@:%@", secondaryKeysDescription, separator, environmentName, self.secondaryMobileKeys[environmentName]];
separator = @",";
}
secondaryKeysDescription = [NSString stringWithFormat:@"%@}", secondaryKeysDescription];
return secondaryKeysDescription;
}
@end

#pragma clang diagnostic push
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
@interface LDFlagConfigModel : NSObject <NSCoding>

@property (nullable, nonatomic, strong) NSDictionary<NSString*, LDFlagConfigValue*> *featuresJsonDictionary;
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString*, id> *allFlagValues;

-(nullable id)initWithDictionary:(nullable NSDictionary*)dictionary;
-(nullable NSDictionary*)dictionaryValue;
-(nullable NSDictionary*)dictionaryValueIncludeNulls:(BOOL)includeNulls;

-(BOOL)doesFlagConfigValueExistForFlagKey:(nonnull NSString*)flagKey;
-(BOOL)containsFlagKey:(nonnull NSString*)flagKey;
-(nullable LDFlagConfigValue*)flagConfigValueForFlagKey:(nonnull NSString*)flagKey;
-(nullable id)flagValueForFlagKey:(nonnull NSString*)flagKey;
-(NSInteger)flagModelVersionForFlagKey:(nonnull NSString*)flagKey;
Expand All @@ -27,9 +28,11 @@
-(void)deleteFromDictionary:(nullable NSDictionary*)eventDictionary;

-(BOOL)isEqualToConfig:(nullable LDFlagConfigModel*)otherConfig;
-(NSArray<NSString*>*)differingFlagKeysFromConfig:(nullable LDFlagConfigModel*)otherConfig;
-(BOOL)hasFeaturesEqualToDictionary:(nullable NSDictionary*)otherDictionary;

-(void)updateEventTrackingContextFromConfig:(nullable LDFlagConfigModel*)otherConfig;

-(LDFlagConfigModel*)copy;
-(nonnull NSString*)description;
@end
Loading

0 comments on commit b590c58

Please sign in to comment.