diff --git a/CHANGELOG.md b/CHANGELOG.md index 9551a832..b6991321 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to the LaunchDarkly iOS SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org). +## [2.13.0] - 2018-06-01 +### Added +- New property `inlineUserInEvents` in `LDConfig`. When `YES` includes the full user (excluding private attributes) in analytics `feature` and `custom` events. When `NO` includes only the `userKey`. Default: `NO`. +- Calling `start` or `updateUser` (when started) on `LDClient` logs an analytics `identify` event. `identify` events contain the full user (excluding private attributes) regardless of `inlineUserInEvents`. +- Adds analytics `summary` event used to track feature flag requests to the SDK. +- Adds analytics `debug` event available to assist with debugging when requested from the website Debugger. + +### Changed +- Changes analytics `feature` events so that they are only sent when requested via the website Dashboard. +- Fixed a defect preventing the SDK from updating correctly on a `put` streaming event when there are no flag changes. +- Fixed a defect on `watchOS` causing the SDK to report analytics dates incorrectly. + ## [2.12.1] - 2018-04-23 ### Changed - Clears selected warnings in CocoaPods project diff --git a/Darkly.xcodeproj/project.pbxproj b/Darkly.xcodeproj/project.pbxproj index 03e83917..259de211 100644 --- a/Darkly.xcodeproj/project.pbxproj +++ b/Darkly.xcodeproj/project.pbxproj @@ -18,9 +18,9 @@ 690346FA1E68990000E45133 /* LDClientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346DE1E68990000E45133 /* LDClientManager.m */; }; 690346FB1E68990000E45133 /* LDConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346DF1E68990000E45133 /* LDConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; 690346FC1E68990000E45133 /* LDConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E01E68990000E45133 /* LDConfig.m */; }; - 690346FD1E68990000E45133 /* LDEventModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E11E68990000E45133 /* LDEventModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 690346FD1E68990000E45133 /* LDEventModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E11E68990000E45133 /* LDEventModel.h */; settings = {ATTRIBUTES = (Private, ); }; }; 690346FE1E68990000E45133 /* LDEventModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E21E68990000E45133 /* LDEventModel.m */; }; - 690346FF1E68990000E45133 /* LDFlagConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E31E68990000E45133 /* LDFlagConfigModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 690346FF1E68990000E45133 /* LDFlagConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E31E68990000E45133 /* LDFlagConfigModel.h */; }; 690347001E68990000E45133 /* LDFlagConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E41E68990000E45133 /* LDFlagConfigModel.m */; }; 690347011E68990000E45133 /* LDPollingManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E51E68990000E45133 /* LDPollingManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 690347021E68990000E45133 /* LDPollingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E61E68990000E45133 /* LDPollingManager.m */; }; @@ -43,11 +43,11 @@ 6903472C1E689B9F00E45133 /* LDClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6903471A1E689B9F00E45133 /* LDClientTest.m */; }; 6903472D1E689B9F00E45133 /* LDUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6903471B1E689B9F00E45133 /* LDUtilTest.m */; }; 6903472E1E689B9F00E45133 /* LDClientManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6903471C1E689B9F00E45133 /* LDClientManagerTest.m */; }; - 6903472F1E689B9F00E45133 /* featureFlags-withVersions.json in Resources */ = {isa = PBXBuildFile; fileRef = 6903471E1E689B9F00E45133 /* featureFlags-withVersions.json */; }; + 6903472F1E689B9F00E45133 /* featureFlags.json in Resources */ = {isa = PBXBuildFile; fileRef = 6903471E1E689B9F00E45133 /* featureFlags.json */; }; 690347301E689B9F00E45133 /* LDConfigTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 6903471F1E689B9F00E45133 /* LDConfigTest.m */; }; 690347311E689B9F00E45133 /* LDFlagConfigModelTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 690347211E689B9F00E45133 /* LDFlagConfigModelTest.m */; }; 690347321E689B9F00E45133 /* DarklyXCTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 690347221E689B9F00E45133 /* DarklyXCTestCase.m */; }; - 690347331E689B9F00E45133 /* NSArray+UnitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 690347251E689B9F00E45133 /* NSArray+UnitTests.m */; }; + 690347331E689B9F00E45133 /* NSArray+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 690347251E689B9F00E45133 /* NSArray+Testable.m */; }; 69071F7E1EA2A7CA00497F93 /* Darkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 69BAF40B1E9AAB4800747613 /* Darkly.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69071F7F1EA2A7CB00497F93 /* Darkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 69BAF40B1E9AAB4800747613 /* Darkly.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69071F801EA2A7CC00497F93 /* Darkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 69BAF40B1E9AAB4800747613 /* Darkly.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -62,9 +62,9 @@ 69A87E9F1E74712800B88B23 /* LDClientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346DE1E68990000E45133 /* LDClientManager.m */; }; 69A87EA01E74712800B88B23 /* LDConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346DF1E68990000E45133 /* LDConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69A87EA11E74712800B88B23 /* LDConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E01E68990000E45133 /* LDConfig.m */; }; - 69A87EA21E74712800B88B23 /* LDEventModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E11E68990000E45133 /* LDEventModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69A87EA21E74712800B88B23 /* LDEventModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E11E68990000E45133 /* LDEventModel.h */; settings = {ATTRIBUTES = (Private, ); }; }; 69A87EA31E74712800B88B23 /* LDEventModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E21E68990000E45133 /* LDEventModel.m */; }; - 69A87EA41E74712800B88B23 /* LDFlagConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E31E68990000E45133 /* LDFlagConfigModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69A87EA41E74712800B88B23 /* LDFlagConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E31E68990000E45133 /* LDFlagConfigModel.h */; }; 69A87EA51E74712800B88B23 /* LDFlagConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E41E68990000E45133 /* LDFlagConfigModel.m */; }; 69A87EA61E74712800B88B23 /* LDPollingManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E51E68990000E45133 /* LDPollingManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69A87EA71E74712800B88B23 /* LDPollingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E61E68990000E45133 /* LDPollingManager.m */; }; @@ -91,9 +91,9 @@ 69BD7E201E6C79910056D70F /* LDClientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346DE1E68990000E45133 /* LDClientManager.m */; }; 69BD7E211E6C79910056D70F /* LDConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346DF1E68990000E45133 /* LDConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69BD7E221E6C79910056D70F /* LDConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E01E68990000E45133 /* LDConfig.m */; }; - 69BD7E231E6C79910056D70F /* LDEventModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E11E68990000E45133 /* LDEventModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69BD7E231E6C79910056D70F /* LDEventModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E11E68990000E45133 /* LDEventModel.h */; settings = {ATTRIBUTES = (Private, ); }; }; 69BD7E241E6C79910056D70F /* LDEventModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E21E68990000E45133 /* LDEventModel.m */; }; - 69BD7E251E6C79910056D70F /* LDFlagConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E31E68990000E45133 /* LDFlagConfigModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69BD7E251E6C79910056D70F /* LDFlagConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E31E68990000E45133 /* LDFlagConfigModel.h */; }; 69BD7E261E6C79910056D70F /* LDFlagConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E41E68990000E45133 /* LDFlagConfigModel.m */; }; 69BD7E271E6C79910056D70F /* LDPollingManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E51E68990000E45133 /* LDPollingManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69BD7E281E6C79910056D70F /* LDPollingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E61E68990000E45133 /* LDPollingManager.m */; }; @@ -118,9 +118,9 @@ 69F3F69A1E6BF82C00079A09 /* LDClientManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346DE1E68990000E45133 /* LDClientManager.m */; }; 69F3F69B1E6BF82C00079A09 /* LDConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346DF1E68990000E45133 /* LDConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69F3F69C1E6BF82C00079A09 /* LDConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E01E68990000E45133 /* LDConfig.m */; }; - 69F3F69D1E6BF82C00079A09 /* LDEventModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E11E68990000E45133 /* LDEventModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69F3F69D1E6BF82C00079A09 /* LDEventModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E11E68990000E45133 /* LDEventModel.h */; settings = {ATTRIBUTES = (Private, ); }; }; 69F3F69E1E6BF82C00079A09 /* LDEventModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E21E68990000E45133 /* LDEventModel.m */; }; - 69F3F69F1E6BF82C00079A09 /* LDFlagConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E31E68990000E45133 /* LDFlagConfigModel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69F3F69F1E6BF82C00079A09 /* LDFlagConfigModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E31E68990000E45133 /* LDFlagConfigModel.h */; }; 69F3F6A01E6BF82C00079A09 /* LDFlagConfigModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E41E68990000E45133 /* LDFlagConfigModel.m */; }; 69F3F6A11E6BF82C00079A09 /* LDPollingManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346E51E68990000E45133 /* LDPollingManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69F3F6A21E6BF82C00079A09 /* LDPollingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346E61E68990000E45133 /* LDPollingManager.m */; }; @@ -135,33 +135,16 @@ 69F3F6AB1E6BF82C00079A09 /* NSDictionary+JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 690346F21E68990000E45133 /* NSDictionary+JSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; 69F3F6AC1E6BF82C00079A09 /* NSDictionary+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 690346F31E68990000E45133 /* NSDictionary+JSON.m */; }; 69F3F6AE1E6BF84B00079A09 /* Darkly-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 690346D81E68990000E45133 /* Darkly-Prefix.pch */; settings = {ATTRIBUTES = (Public, ); }; }; - 8305EC6720221973002F20DB /* LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6520221973002F20DB /* LDFlagConfigValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8305EC6820221973002F20DB /* LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6520221973002F20DB /* LDFlagConfigValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8305EC6920221973002F20DB /* LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6520221973002F20DB /* LDFlagConfigValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8305EC6A20221973002F20DB /* LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6520221973002F20DB /* LDFlagConfigValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8305EC6720221973002F20DB /* LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6520221973002F20DB /* LDFlagConfigValue.h */; }; + 8305EC6820221973002F20DB /* LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6520221973002F20DB /* LDFlagConfigValue.h */; }; + 8305EC6920221973002F20DB /* LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6520221973002F20DB /* LDFlagConfigValue.h */; }; + 8305EC6A20221973002F20DB /* LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6520221973002F20DB /* LDFlagConfigValue.h */; }; 8305EC6B20221973002F20DB /* LDFlagConfigValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC6620221973002F20DB /* LDFlagConfigValue.m */; }; 8305EC6C20221973002F20DB /* LDFlagConfigValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC6620221973002F20DB /* LDFlagConfigValue.m */; }; 8305EC6D20221973002F20DB /* LDFlagConfigValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC6620221973002F20DB /* LDFlagConfigValue.m */; }; 8305EC6E20221973002F20DB /* LDFlagConfigValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC6620221973002F20DB /* LDFlagConfigValue.m */; }; - 8305EC7120222078002F20DB /* NSObject+LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6F20222078002F20DB /* NSObject+LDFlagConfigValue.h */; }; - 8305EC7220222078002F20DB /* NSObject+LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6F20222078002F20DB /* NSObject+LDFlagConfigValue.h */; }; - 8305EC7320222078002F20DB /* NSObject+LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6F20222078002F20DB /* NSObject+LDFlagConfigValue.h */; }; - 8305EC7420222078002F20DB /* NSObject+LDFlagConfigValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8305EC6F20222078002F20DB /* NSObject+LDFlagConfigValue.h */; }; - 8305EC7520222078002F20DB /* NSObject+LDFlagConfigValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC7020222078002F20DB /* NSObject+LDFlagConfigValue.m */; }; - 8305EC7620222078002F20DB /* NSObject+LDFlagConfigValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC7020222078002F20DB /* NSObject+LDFlagConfigValue.m */; }; - 8305EC7720222078002F20DB /* NSObject+LDFlagConfigValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC7020222078002F20DB /* NSObject+LDFlagConfigValue.m */; }; - 8305EC7820222078002F20DB /* NSObject+LDFlagConfigValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC7020222078002F20DB /* NSObject+LDFlagConfigValue.m */; }; - 8305EC7A20222B5E002F20DB /* NSObject+LDFlagConfigValueTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC7920222B5E002F20DB /* NSObject+LDFlagConfigValueTest.m */; }; 8305EC7C2022336C002F20DB /* LDFlagConfigValueTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8305EC7B2022336C002F20DB /* LDFlagConfigValueTest.m */; }; - 8305EC7E202238FC002F20DB /* boolConfigIsABool-true-withoutVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC7D202238FC002F20DB /* boolConfigIsABool-true-withoutVersion.json */; }; - 8305EC8020223A2E002F20DB /* numberConfigIsANumber-2-withoutVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC7F20223A2E002F20DB /* numberConfigIsANumber-2-withoutVersion.json */; }; - 8305EC8220223BC3002F20DB /* doubleConfigIsADouble-Pi-withoutVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC8120223BC2002F20DB /* doubleConfigIsADouble-Pi-withoutVersion.json */; }; - 8305EC8420223D65002F20DB /* stringConfigIsAString-someString-withoutVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC8320223D65002F20DB /* stringConfigIsAString-someString-withoutVersion.json */; }; - 8305EC8620223EBA002F20DB /* arrayConfigIsAnArray-123-withoutVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC8520223EBA002F20DB /* arrayConfigIsAnArray-123-withoutVersion.json */; }; - 8305EC8820224156002F20DB /* dictionaryConfigIsADictionary-3Key-withoutVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC8720224156002F20DB /* dictionaryConfigIsADictionary-3Key-withoutVersion.json */; }; - 8305EC8A202243D6002F20DB /* nullConfigIsANull-null-withVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC89202243D6002F20DB /* nullConfigIsANull-null-withVersion.json */; }; - 8305EC8C2022440D002F20DB /* nullConfigIsANull-null-withoutVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC8B2022440D002F20DB /* nullConfigIsANull-null-withoutVersion.json */; }; - 8305EC8E20227365002F20DB /* featureFlags-withoutVersions.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC8D20227365002F20DB /* featureFlags-withoutVersions.json */; }; + 8305EC8A202243D6002F20DB /* nullConfigIsANull-null.json in Resources */ = {isa = PBXBuildFile; fileRef = 8305EC89202243D6002F20DB /* nullConfigIsANull-null.json */; }; 830C2AC5207579AC001D645D /* LDThrottler.h in Headers */ = {isa = PBXBuildFile; fileRef = 830C2AC3207579AC001D645D /* LDThrottler.h */; }; 830C2AC6207579AC001D645D /* LDThrottler.h in Headers */ = {isa = PBXBuildFile; fileRef = 830C2AC3207579AC001D645D /* LDThrottler.h */; }; 830C2AC7207579AC001D645D /* LDThrottler.h in Headers */ = {isa = PBXBuildFile; fileRef = 830C2AC3207579AC001D645D /* LDThrottler.h */; }; @@ -175,7 +158,7 @@ 83258A3D1F323049008C2133 /* LDClientManager+EventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 83258A3C1F323049008C2133 /* LDClientManager+EventSource.m */; }; 83258A401F3244D0008C2133 /* LDUserBuilder+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83258A3F1F3244D0008C2133 /* LDUserBuilder+Testable.m */; }; 83258A421F32721A008C2133 /* emptyConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = 83258A411F32721A008C2133 /* emptyConfig.json */; }; - 83258A441F329EFB008C2133 /* doubleConfigIsADouble-Pi-withVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 83258A431F329EFB008C2133 /* doubleConfigIsADouble-Pi-withVersion.json */; }; + 83258A441F329EFB008C2133 /* doubleConfigIsADouble-Pi.json in Resources */ = {isa = PBXBuildFile; fileRef = 83258A431F329EFB008C2133 /* doubleConfigIsADouble-Pi.json */; }; 832C78831F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h in Headers */ = {isa = PBXBuildFile; fileRef = 832C78811F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h */; }; 832C78841F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h in Headers */ = {isa = PBXBuildFile; fileRef = 832C78811F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h */; }; 832C78851F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h in Headers */ = {isa = PBXBuildFile; fileRef = 832C78811F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h */; }; @@ -185,7 +168,6 @@ 832C78891F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m in Sources */ = {isa = PBXBuildFile; fileRef = 832C78821F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m */; }; 832C788A1F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m in Sources */ = {isa = PBXBuildFile; fileRef = 832C78821F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m */; }; 832C788D1F2977B800E334A2 /* NSString+RemoveWhitespace.m in Sources */ = {isa = PBXBuildFile; fileRef = 832C788C1F2977B800E334A2 /* NSString+RemoveWhitespace.m */; }; - 832C78901F2A8DF600E334A2 /* LDUserModel+JsonDecodeable.m in Sources */ = {isa = PBXBuildFile; fileRef = 832C788F1F2A8DF600E334A2 /* LDUserModel+JsonDecodeable.m */; }; 833D08CB1F3B97EB00BEED83 /* NSThread+MainExecutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 833D08C91F3B97EB00BEED83 /* NSThread+MainExecutable.h */; }; 833D08CC1F3B97EB00BEED83 /* NSThread+MainExecutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 833D08C91F3B97EB00BEED83 /* NSThread+MainExecutable.h */; }; 833D08CD1F3B97EB00BEED83 /* NSThread+MainExecutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 833D08C91F3B97EB00BEED83 /* NSThread+MainExecutable.h */; }; @@ -195,9 +177,16 @@ 833D08D11F3B97EB00BEED83 /* NSThread+MainExecutable.m in Sources */ = {isa = PBXBuildFile; fileRef = 833D08CA1F3B97EB00BEED83 /* NSThread+MainExecutable.m */; }; 833D08D21F3B97EB00BEED83 /* NSThread+MainExecutable.m in Sources */ = {isa = PBXBuildFile; fileRef = 833D08CA1F3B97EB00BEED83 /* NSThread+MainExecutable.m */; }; 8349F51E1F19352300B1F3DB /* NSDictionary+StringKey_Matchable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8349F51D1F19352300B1F3DB /* NSDictionary+StringKey_Matchable.m */; }; - 8349F5211F195BCF00B1F3DB /* LDUserModel+Equatable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8349F5201F195BCF00B1F3DB /* LDUserModel+Equatable.m */; }; 8358F25A1F4202A300ECE1AF /* LDConfig+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 8358F2591F4202A300ECE1AF /* LDConfig+Testable.m */; }; 835B421B1FD5AF7300709384 /* DarklyEventSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 835B421A1FD5AF7300709384 /* DarklyEventSource.framework */; }; + 83620B8520BDF36B00F1F28E /* NSNumber+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83620B8320BDF36B00F1F28E /* NSNumber+LaunchDarkly.h */; }; + 83620B8620BDF36B00F1F28E /* NSNumber+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83620B8320BDF36B00F1F28E /* NSNumber+LaunchDarkly.h */; }; + 83620B8720BDF36B00F1F28E /* NSNumber+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83620B8320BDF36B00F1F28E /* NSNumber+LaunchDarkly.h */; }; + 83620B8820BDF36B00F1F28E /* NSNumber+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83620B8320BDF36B00F1F28E /* NSNumber+LaunchDarkly.h */; }; + 83620B8920BDF36B00F1F28E /* NSNumber+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83620B8420BDF36B00F1F28E /* NSNumber+LaunchDarkly.m */; }; + 83620B8A20BDF36B00F1F28E /* NSNumber+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83620B8420BDF36B00F1F28E /* NSNumber+LaunchDarkly.m */; }; + 83620B8B20BDF36B00F1F28E /* NSNumber+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83620B8420BDF36B00F1F28E /* NSNumber+LaunchDarkly.m */; }; + 83620B8C20BDF36B00F1F28E /* NSNumber+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83620B8420BDF36B00F1F28E /* NSNumber+LaunchDarkly.m */; }; 8365E5082028F39E00DE8E2B /* LDEvent+EventTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8365E5062028F39E00DE8E2B /* LDEvent+EventTypes.h */; }; 8365E5092028F39E00DE8E2B /* LDEvent+EventTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8365E5062028F39E00DE8E2B /* LDEvent+EventTypes.h */; }; 8365E50A2028F39E00DE8E2B /* LDEvent+EventTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8365E5062028F39E00DE8E2B /* LDEvent+EventTypes.h */; }; @@ -207,19 +196,19 @@ 8365E50E2028F39E00DE8E2B /* LDEvent+EventTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 8365E5072028F39E00DE8E2B /* LDEvent+EventTypes.m */; }; 8365E50F2028F39E00DE8E2B /* LDEvent+EventTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 8365E5072028F39E00DE8E2B /* LDEvent+EventTypes.m */; }; 8369475C1F1FED400047697C /* boolConfigIsABool-false.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947591F1FED400047697C /* boolConfigIsABool-false.json */; }; - 8369475D1F1FED400047697C /* boolConfigIsABool-true-withVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369475A1F1FED400047697C /* boolConfigIsABool-true-withVersion.json */; }; + 8369475D1F1FED400047697C /* boolConfigIsABool-true.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369475A1F1FED400047697C /* boolConfigIsABool-true.json */; }; 8369475E1F1FED400047697C /* boolConfigIsABool2-true.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369475B1F1FED400047697C /* boolConfigIsABool2-true.json */; }; 836947621F1FEEB40047697C /* numberConfigIsANumber-1.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369475F1F1FEEB40047697C /* numberConfigIsANumber-1.json */; }; - 836947631F1FEEB40047697C /* numberConfigIsANumber-2-withVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947601F1FEEB40047697C /* numberConfigIsANumber-2-withVersion.json */; }; + 836947631F1FEEB40047697C /* numberConfigIsANumber-2.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947601F1FEEB40047697C /* numberConfigIsANumber-2.json */; }; 836947641F1FEEB40047697C /* numberConfigIsANumber2-1.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947611F1FEEB40047697C /* numberConfigIsANumber2-1.json */; }; - 836947681F1FF0A00047697C /* stringConfigIsAString-someString-withVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947651F1FF0A00047697C /* stringConfigIsAString-someString-withVersion.json */; }; + 836947681F1FF0A00047697C /* stringConfigIsAString-someString.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947651F1FF0A00047697C /* stringConfigIsAString-someString.json */; }; 836947691F1FF0A00047697C /* stringConfigIsAString-someStringA.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947661F1FF0A00047697C /* stringConfigIsAString-someStringA.json */; }; 8369476A1F1FF0A00047697C /* stringConfigIsAStringA-someString.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947671F1FF0A00047697C /* stringConfigIsAStringA-someString.json */; }; 8369476F1F1FF45B0047697C /* arrayConfigIsAnArray-1.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369476B1F1FF45B0047697C /* arrayConfigIsAnArray-1.json */; }; - 836947701F1FF45B0047697C /* arrayConfigIsAnArray-123-withVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369476C1F1FF45B0047697C /* arrayConfigIsAnArray-123-withVersion.json */; }; + 836947701F1FF45B0047697C /* arrayConfigIsAnArray-123.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369476C1F1FF45B0047697C /* arrayConfigIsAnArray-123.json */; }; 836947711F1FF45B0047697C /* arrayConfigIsAnArray-Empty.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369476D1F1FF45B0047697C /* arrayConfigIsAnArray-Empty.json */; }; 836947721F1FF45B0047697C /* arrayConfigIsAnArrayA-123.json in Resources */ = {isa = PBXBuildFile; fileRef = 8369476E1F1FF45B0047697C /* arrayConfigIsAnArrayA-123.json */; }; - 8369477A1F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key-withVersion.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947731F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key-withVersion.json */; }; + 8369477A1F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947731F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key.json */; }; 8369477B1F1FF80A0047697C /* dictionaryConfigIsADictionary-Empty.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947741F1FF80A0047697C /* dictionaryConfigIsADictionary-Empty.json */; }; 8369477C1F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyA.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947751F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyA.json */; }; 8369477D1F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyB-124.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947761F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyB-124.json */; }; @@ -228,21 +217,49 @@ 836947801F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyC.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947791F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyC.json */; }; 836947831F20125F0047697C /* ldClientManagerTestConfigA.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947811F20125F0047697C /* ldClientManagerTestConfigA.json */; }; 836947841F20125F0047697C /* ldClientManagerTestConfigB.json in Resources */ = {isa = PBXBuildFile; fileRef = 836947821F20125F0047697C /* ldClientManagerTestConfigB.json */; }; + 8375839A209CCE71004329DD /* LDEventTrackingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 83758398209CCE71004329DD /* LDEventTrackingContext.h */; }; + 8375839B209CCE71004329DD /* LDEventTrackingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 83758398209CCE71004329DD /* LDEventTrackingContext.h */; }; + 8375839C209CCE71004329DD /* LDEventTrackingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 83758398209CCE71004329DD /* LDEventTrackingContext.h */; }; + 8375839D209CCE71004329DD /* LDEventTrackingContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 83758398209CCE71004329DD /* LDEventTrackingContext.h */; }; + 8375839E209CCE71004329DD /* LDEventTrackingContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 83758399209CCE71004329DD /* LDEventTrackingContext.m */; }; + 8375839F209CCE71004329DD /* LDEventTrackingContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 83758399209CCE71004329DD /* LDEventTrackingContext.m */; }; + 837583A0209CCE71004329DD /* LDEventTrackingContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 83758399209CCE71004329DD /* LDEventTrackingContext.m */; }; + 837583A1209CCE71004329DD /* LDEventTrackingContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 83758399209CCE71004329DD /* LDEventTrackingContext.m */; }; + 837583A3209CD0A7004329DD /* LDEventTrackingContextTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 837583A2209CD0A7004329DD /* LDEventTrackingContextTest.m */; }; + 837583A7209CD187004329DD /* LDEventTrackingContext+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 837583A6209CD187004329DD /* LDEventTrackingContext+Testable.m */; }; 83889B141F8E93A100A4EF69 /* LDEvent+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B131F8E93A100A4EF69 /* LDEvent+Testable.m */; }; - 83889B171F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83889B151F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h */; }; - 83889B181F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83889B151F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h */; }; - 83889B191F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83889B151F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h */; }; - 83889B1A1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83889B151F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h */; }; - 83889B1B1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B161F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m */; }; - 83889B1C1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B161F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m */; }; - 83889B1D1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B161F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m */; }; - 83889B1E1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B161F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m */; }; + 83889B171F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83889B151F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h */; }; + 83889B181F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83889B151F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h */; }; + 83889B191F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83889B151F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h */; }; + 83889B1A1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83889B151F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h */; }; + 83889B1B1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B161F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m */; }; + 83889B1C1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B161F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m */; }; + 83889B1D1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B161F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m */; }; + 83889B1E1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83889B161F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m */; }; 839956E820053081009707D1 /* LDUserModel+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 839956E720053081009707D1 /* LDUserModel+Testable.m */; }; 839D6D271FD1B57B000BE6BD /* DarklyEventSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 839D6D261FD1B57B000BE6BD /* DarklyEventSource.framework */; }; 839D6D291FD1B58E000BE6BD /* DarklyEventSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 839D6D281FD1B58E000BE6BD /* DarklyEventSource.framework */; }; 839E0A16201F97E900DB8CD1 /* ldClientManagerTestPatchIsANumber.json in Resources */ = {isa = PBXBuildFile; fileRef = 839E0A15201F97E800DB8CD1 /* ldClientManagerTestPatchIsANumber.json */; }; 839E0A18201FB8D900DB8CD1 /* ldClientManagerTestDeleteIsANumber.json in Resources */ = {isa = PBXBuildFile; fileRef = 839E0A17201FB8D900DB8CD1 /* ldClientManagerTestDeleteIsANumber.json */; }; - 83B8C24C1FEB1CD20082B8A9 /* LDUserModel+Stub.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B8C24B1FEB1CD20082B8A9 /* LDUserModel+Stub.m */; }; + 83B62E1720A249A200F2E656 /* NSDateFormatter+JsonHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B62E1520A249A200F2E656 /* NSDateFormatter+JsonHeader.h */; }; + 83B62E1820A249A200F2E656 /* NSDateFormatter+JsonHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B62E1520A249A200F2E656 /* NSDateFormatter+JsonHeader.h */; }; + 83B62E1920A249A200F2E656 /* NSDateFormatter+JsonHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B62E1520A249A200F2E656 /* NSDateFormatter+JsonHeader.h */; }; + 83B62E1A20A249A200F2E656 /* NSDateFormatter+JsonHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B62E1520A249A200F2E656 /* NSDateFormatter+JsonHeader.h */; }; + 83B62E1B20A249A200F2E656 /* NSDateFormatter+JsonHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B62E1620A249A200F2E656 /* NSDateFormatter+JsonHeader.m */; }; + 83B62E1C20A249A200F2E656 /* NSDateFormatter+JsonHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B62E1620A249A200F2E656 /* NSDateFormatter+JsonHeader.m */; }; + 83B62E1D20A249A200F2E656 /* NSDateFormatter+JsonHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B62E1620A249A200F2E656 /* NSDateFormatter+JsonHeader.m */; }; + 83B62E1E20A249A200F2E656 /* NSDateFormatter+JsonHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B62E1620A249A200F2E656 /* NSDateFormatter+JsonHeader.m */; }; + 83B62E2020A2517500F2E656 /* NSDateFormatter+JsonHeaderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B62E1F20A2517500F2E656 /* NSDateFormatter+JsonHeaderTest.m */; }; + 83B62E2320A258D400F2E656 /* NSDateFormatter+JsonHeader+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B62E2220A258D400F2E656 /* NSDateFormatter+JsonHeader+Testable.m */; }; + 83B6FC1D207E60B6002DBA7B /* NSDate+ReferencedDate.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B6FC1B207E60B6002DBA7B /* NSDate+ReferencedDate.h */; }; + 83B6FC1E207E60B6002DBA7B /* NSDate+ReferencedDate.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B6FC1B207E60B6002DBA7B /* NSDate+ReferencedDate.h */; }; + 83B6FC1F207E60B6002DBA7B /* NSDate+ReferencedDate.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B6FC1B207E60B6002DBA7B /* NSDate+ReferencedDate.h */; }; + 83B6FC20207E60B6002DBA7B /* NSDate+ReferencedDate.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B6FC1B207E60B6002DBA7B /* NSDate+ReferencedDate.h */; }; + 83B6FC21207E60B6002DBA7B /* NSDate+ReferencedDate.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B6FC1C207E60B6002DBA7B /* NSDate+ReferencedDate.m */; }; + 83B6FC22207E60B6002DBA7B /* NSDate+ReferencedDate.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B6FC1C207E60B6002DBA7B /* NSDate+ReferencedDate.m */; }; + 83B6FC23207E60B6002DBA7B /* NSDate+ReferencedDate.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B6FC1C207E60B6002DBA7B /* NSDate+ReferencedDate.m */; }; + 83B6FC24207E60B6002DBA7B /* NSDate+ReferencedDate.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B6FC1C207E60B6002DBA7B /* NSDate+ReferencedDate.m */; }; + 83B6FC2B208127B2002DBA7B /* LDEventModel+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B6FC2A208127B2002DBA7B /* LDEventModel+Testable.m */; }; 83B8C24F1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B8C24D1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h */; }; 83B8C2501FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B8C24D1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h */; }; 83B8C2511FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 83B8C24D1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h */; }; @@ -251,9 +268,8 @@ 83B8C2541FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B8C24E1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m */; }; 83B8C2551FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B8C24E1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m */; }; 83B8C2561FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B8C24E1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m */; }; - 83B8C2581FEC4C3B0082B8A9 /* featureFlags-excludeNulls-withVersions.json in Resources */ = {isa = PBXBuildFile; fileRef = 83B8C2571FEC4C3B0082B8A9 /* featureFlags-excludeNulls-withVersions.json */; }; + 83B8C2581FEC4C3B0082B8A9 /* featureFlags-excludeNulls.json in Resources */ = {isa = PBXBuildFile; fileRef = 83B8C2571FEC4C3B0082B8A9 /* featureFlags-excludeNulls.json */; }; 83B975D31FD1CA6000A4EF4E /* DarklyEventSource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83B975D21FD1CA6000A4EF4E /* DarklyEventSource.framework */; }; - 83BE9385201A63B400DD1ED9 /* ldDataManagerTestConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = 83BE9384201A63B400DD1ED9 /* ldDataManagerTestConfig.json */; }; 83BE9387201A6AA400DD1ED9 /* ldFlagConfigModelTest.json in Resources */ = {isa = PBXBuildFile; fileRef = 83BE9386201A6AA400DD1ED9 /* ldFlagConfigModelTest.json */; }; 83BE9389201A781200DD1ED9 /* ldFlagConfigModelPatchVersion1Flag.json in Resources */ = {isa = PBXBuildFile; fileRef = 83BE9388201A781200DD1ED9 /* ldFlagConfigModelPatchVersion1Flag.json */; }; 83BE938C201A797B00DD1ED9 /* NSJSONSerialization+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BE938B201A797B00DD1ED9 /* NSJSONSerialization+Testable.m */; }; @@ -264,7 +280,39 @@ 83BE9398201AA53100DD1ED9 /* ldFlagConfigModelPatchVersion2FlagWithNull.json in Resources */ = {isa = PBXBuildFile; fileRef = 83BE9397201AA53100DD1ED9 /* ldFlagConfigModelPatchVersion2FlagWithNull.json */; }; 83BE939A201B6EBA00DD1ED9 /* ldFlagConfigModelDeleteVersion2Flag.json in Resources */ = {isa = PBXBuildFile; fileRef = 83BE9399201B6EBA00DD1ED9 /* ldFlagConfigModelDeleteVersion2Flag.json */; }; 83BE939C201B80F800DD1ED9 /* ldFlagConfigModelDeleteNewFlag.json in Resources */ = {isa = PBXBuildFile; fileRef = 83BE939B201B80F800DD1ED9 /* ldFlagConfigModelDeleteNewFlag.json */; }; - 83C28CF92023872C00394693 /* featureFlags-excludeNulls-withoutVersions.json in Resources */ = {isa = PBXBuildFile; fileRef = 83C28CF82023872B00394693 /* featureFlags-excludeNulls-withoutVersions.json */; }; + 83C8867D2087A786004AC82F /* LDFlagValueCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C8867B2087A786004AC82F /* LDFlagValueCounter.h */; }; + 83C8867E2087A786004AC82F /* LDFlagValueCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C8867B2087A786004AC82F /* LDFlagValueCounter.h */; }; + 83C8867F2087A786004AC82F /* LDFlagValueCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C8867B2087A786004AC82F /* LDFlagValueCounter.h */; }; + 83C886802087A786004AC82F /* LDFlagValueCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C8867B2087A786004AC82F /* LDFlagValueCounter.h */; }; + 83C886812087A786004AC82F /* LDFlagValueCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C8867C2087A786004AC82F /* LDFlagValueCounter.m */; }; + 83C886822087A786004AC82F /* LDFlagValueCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C8867C2087A786004AC82F /* LDFlagValueCounter.m */; }; + 83C886832087A786004AC82F /* LDFlagValueCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C8867C2087A786004AC82F /* LDFlagValueCounter.m */; }; + 83C886842087A786004AC82F /* LDFlagValueCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C8867C2087A786004AC82F /* LDFlagValueCounter.m */; }; + 83ECCC882087BF860086F879 /* LDFlagValueCounterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCC872087BF860086F879 /* LDFlagValueCounterTest.m */; }; + 83ECCC8B2087CA280086F879 /* LDFlagValueCounter+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCC8A2087CA280086F879 /* LDFlagValueCounter+Testable.m */; }; + 83ECCC8E2087D67D0086F879 /* LDFlagCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83ECCC8C2087D67D0086F879 /* LDFlagCounter.h */; }; + 83ECCC8F2087D67D0086F879 /* LDFlagCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83ECCC8C2087D67D0086F879 /* LDFlagCounter.h */; }; + 83ECCC902087D67D0086F879 /* LDFlagCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83ECCC8C2087D67D0086F879 /* LDFlagCounter.h */; }; + 83ECCC912087D67D0086F879 /* LDFlagCounter.h in Headers */ = {isa = PBXBuildFile; fileRef = 83ECCC8C2087D67D0086F879 /* LDFlagCounter.h */; }; + 83ECCC922087D67D0086F879 /* LDFlagCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCC8D2087D67D0086F879 /* LDFlagCounter.m */; }; + 83ECCC932087D67D0086F879 /* LDFlagCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCC8D2087D67D0086F879 /* LDFlagCounter.m */; }; + 83ECCC942087D67D0086F879 /* LDFlagCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCC8D2087D67D0086F879 /* LDFlagCounter.m */; }; + 83ECCC952087D67D0086F879 /* LDFlagCounter.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCC8D2087D67D0086F879 /* LDFlagCounter.m */; }; + 83ECCC982087DF040086F879 /* LDFlagCounterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCC972087DF040086F879 /* LDFlagCounterTest.m */; }; + 83ECCC9B2087EF4A0086F879 /* LDFlagConfigValue+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCC9A2087EF4A0086F879 /* LDFlagConfigValue+Testable.m */; }; + 83ECCC9D208800F20086F879 /* doubleConfigIsADouble-e.json in Resources */ = {isa = PBXBuildFile; fileRef = 83ECCC9C208800F10086F879 /* doubleConfigIsADouble-e.json */; }; + 83ECCCA32088E4E90086F879 /* LDFlagCounter+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCCA22088E4E90086F879 /* LDFlagCounter+Testable.m */; }; + 83ECCCA62088FBA80086F879 /* LDFlagConfigTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 83ECCCA42088FBA80086F879 /* LDFlagConfigTracker.h */; }; + 83ECCCA72088FBA80086F879 /* LDFlagConfigTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 83ECCCA42088FBA80086F879 /* LDFlagConfigTracker.h */; }; + 83ECCCA82088FBA80086F879 /* LDFlagConfigTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 83ECCCA42088FBA80086F879 /* LDFlagConfigTracker.h */; }; + 83ECCCA92088FBA80086F879 /* LDFlagConfigTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 83ECCCA42088FBA80086F879 /* LDFlagConfigTracker.h */; }; + 83ECCCAA2088FBA80086F879 /* LDFlagConfigTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCCA52088FBA80086F879 /* LDFlagConfigTracker.m */; }; + 83ECCCAB2088FBA80086F879 /* LDFlagConfigTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCCA52088FBA80086F879 /* LDFlagConfigTracker.m */; }; + 83ECCCAC2088FBA80086F879 /* LDFlagConfigTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCCA52088FBA80086F879 /* LDFlagConfigTracker.m */; }; + 83ECCCAD2088FBA80086F879 /* LDFlagConfigTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCCA52088FBA80086F879 /* LDFlagConfigTracker.m */; }; + 83ECCCAF2088FF420086F879 /* LDFlagConfigTrackerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCCAE2088FF420086F879 /* LDFlagConfigTrackerTest.m */; }; + 83ECCCB220890A430086F879 /* LDFlagConfigTracker+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCCB120890A430086F879 /* LDFlagConfigTracker+Testable.m */; }; + 83ECCCB520891E8E0086F879 /* NSDate+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83ECCCB420891E8E0086F879 /* NSDate+Testable.m */; }; 83EF67811F979B4100403126 /* LDEvent+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83EF677F1F979B4100403126 /* LDEvent+Unauthorized.h */; }; 83EF67821F979B4100403126 /* LDEvent+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83EF677F1F979B4100403126 /* LDEvent+Unauthorized.h */; }; 83EF67831F979B4100403126 /* LDEvent+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83EF677F1F979B4100403126 /* LDEvent+Unauthorized.h */; }; @@ -276,14 +324,14 @@ 83EF678D1F98FC9200403126 /* LDFlagConfigModel+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF678C1F98FC9200403126 /* LDFlagConfigModel+Testable.m */; }; 83EF67901F99365600403126 /* LDClient+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF678F1F99365600403126 /* LDClient+Testable.m */; }; 83F5B4751F91560300174DF7 /* LDDataManager+Testable.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4741F91560300174DF7 /* LDDataManager+Testable.m */; }; - 83F5B4781F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h */; }; - 83F5B4791F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h */; }; - 83F5B47A1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h */; }; - 83F5B47B1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h */; }; - 83F5B47C1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m */; }; - 83F5B47D1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m */; }; - 83F5B47E1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m */; }; - 83F5B47F1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m */; }; + 83F5B4781F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h */; }; + 83F5B4791F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h */; }; + 83F5B47A1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h */; }; + 83F5B47B1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h in Headers */ = {isa = PBXBuildFile; fileRef = 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h */; }; + 83F5B47C1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m */; }; + 83F5B47D1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m */; }; + 83F5B47E1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m */; }; + 83F5B47F1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m in Sources */ = {isa = PBXBuildFile; fileRef = 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m */; }; 8BB91CDBFE302A67F0F1403A /* Pods_Darkly_watchOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 119B5C495711E2F896661BBA /* Pods_Darkly_watchOS.framework */; }; D2E446FE87A4037E841D12CC /* Pods_Darkly_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 875461696618AE6C4F4B4AC6 /* Pods_Darkly_iOS.framework */; }; E0D956853DFFDC3A33332F76 /* Pods_DarklyTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C604EED857FC066033CAB2CB /* Pods_DarklyTests.framework */; }; @@ -348,13 +396,13 @@ 6903471A1E689B9F00E45133 /* LDClientTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDClientTest.m; sourceTree = ""; }; 6903471B1E689B9F00E45133 /* LDUtilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDUtilTest.m; sourceTree = ""; }; 6903471C1E689B9F00E45133 /* LDClientManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDClientManagerTest.m; sourceTree = ""; }; - 6903471E1E689B9F00E45133 /* featureFlags-withVersions.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "featureFlags-withVersions.json"; sourceTree = ""; }; + 6903471E1E689B9F00E45133 /* featureFlags.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = featureFlags.json; sourceTree = ""; }; 6903471F1E689B9F00E45133 /* LDConfigTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDConfigTest.m; sourceTree = ""; }; 690347201E689B9F00E45133 /* DarklyXCTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DarklyXCTestCase.h; sourceTree = ""; }; 690347211E689B9F00E45133 /* LDFlagConfigModelTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LDFlagConfigModelTest.m; sourceTree = ""; }; 690347221E689B9F00E45133 /* DarklyXCTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DarklyXCTestCase.m; sourceTree = ""; }; - 690347241E689B9F00E45133 /* NSArray+UnitTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+UnitTests.h"; sourceTree = ""; }; - 690347251E689B9F00E45133 /* NSArray+UnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+UnitTests.m"; sourceTree = ""; }; + 690347241E689B9F00E45133 /* NSArray+Testable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+Testable.h"; sourceTree = ""; }; + 690347251E689B9F00E45133 /* NSArray+Testable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Testable.m"; sourceTree = ""; }; 69A87E8F1E74458900B88B23 /* Darkly.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Darkly.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69BAF40B1E9AAB4800747613 /* Darkly.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Darkly.h; path = Framework/Darkly.h; sourceTree = SOURCE_ROOT; }; 69BAF40C1E9AAB4800747613 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = SOURCE_ROOT; }; @@ -363,21 +411,8 @@ 7587BE78E11BC6EC851CA413 /* Pods-Darkly_osx.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Darkly_osx.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Darkly_osx/Pods-Darkly_osx.debug.xcconfig"; sourceTree = ""; }; 8305EC6520221973002F20DB /* LDFlagConfigValue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDFlagConfigValue.h; sourceTree = ""; }; 8305EC6620221973002F20DB /* LDFlagConfigValue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDFlagConfigValue.m; sourceTree = ""; }; - 8305EC6F20222078002F20DB /* NSObject+LDFlagConfigValue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+LDFlagConfigValue.h"; sourceTree = ""; }; - 8305EC7020222078002F20DB /* NSObject+LDFlagConfigValue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+LDFlagConfigValue.m"; sourceTree = ""; }; - 8305EC7920222B5E002F20DB /* NSObject+LDFlagConfigValueTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+LDFlagConfigValueTest.m"; sourceTree = ""; }; 8305EC7B2022336C002F20DB /* LDFlagConfigValueTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDFlagConfigValueTest.m; sourceTree = ""; }; - 8305EC7D202238FC002F20DB /* boolConfigIsABool-true-withoutVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "boolConfigIsABool-true-withoutVersion.json"; sourceTree = ""; }; - 8305EC7F20223A2E002F20DB /* numberConfigIsANumber-2-withoutVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "numberConfigIsANumber-2-withoutVersion.json"; sourceTree = ""; }; - 8305EC8120223BC2002F20DB /* doubleConfigIsADouble-Pi-withoutVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "doubleConfigIsADouble-Pi-withoutVersion.json"; sourceTree = ""; }; - 8305EC8320223D65002F20DB /* stringConfigIsAString-someString-withoutVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "stringConfigIsAString-someString-withoutVersion.json"; sourceTree = ""; }; - 8305EC8520223EBA002F20DB /* arrayConfigIsAnArray-123-withoutVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "arrayConfigIsAnArray-123-withoutVersion.json"; sourceTree = ""; }; - 8305EC8720224156002F20DB /* dictionaryConfigIsADictionary-3Key-withoutVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dictionaryConfigIsADictionary-3Key-withoutVersion.json"; sourceTree = ""; }; - 8305EC89202243D6002F20DB /* nullConfigIsANull-null-withVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "nullConfigIsANull-null-withVersion.json"; sourceTree = ""; }; - 8305EC8B2022440D002F20DB /* nullConfigIsANull-null-withoutVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "nullConfigIsANull-null-withoutVersion.json"; sourceTree = ""; }; - 8305EC8D20227365002F20DB /* featureFlags-withoutVersions.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "featureFlags-withoutVersions.json"; sourceTree = ""; }; - 830BF92F202A8854006DF9B1 /* NSJSONSerialization+Testable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSJSONSerialization+Testable.h"; sourceTree = ""; }; - 830BF930202A8855006DF9B1 /* NSJSONSerialization+Testable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSJSONSerialization+Testable.m"; sourceTree = ""; }; + 8305EC89202243D6002F20DB /* nullConfigIsANull-null.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "nullConfigIsANull-null.json"; sourceTree = ""; }; 830C2AC3207579AC001D645D /* LDThrottler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDThrottler.h; sourceTree = ""; }; 830C2AC4207579AC001D645D /* LDThrottler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDThrottler.m; sourceTree = ""; }; 830C2ACE20757CE9001D645D /* LDThrottlerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDThrottlerTest.m; sourceTree = ""; }; @@ -388,38 +423,36 @@ 83258A3E1F3244D0008C2133 /* LDUserBuilder+Testable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "LDUserBuilder+Testable.h"; sourceTree = ""; }; 83258A3F1F3244D0008C2133 /* LDUserBuilder+Testable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "LDUserBuilder+Testable.m"; sourceTree = ""; }; 83258A411F32721A008C2133 /* emptyConfig.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = emptyConfig.json; sourceTree = ""; }; - 83258A431F329EFB008C2133 /* doubleConfigIsADouble-Pi-withVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "doubleConfigIsADouble-Pi-withVersion.json"; sourceTree = ""; }; + 83258A431F329EFB008C2133 /* doubleConfigIsADouble-Pi.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "doubleConfigIsADouble-Pi.json"; sourceTree = ""; }; 832C78811F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableDictionary+NullRemovable.h"; sourceTree = ""; }; 832C78821F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableDictionary+NullRemovable.m"; sourceTree = ""; }; 832C788B1F2977B800E334A2 /* NSString+RemoveWhitespace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+RemoveWhitespace.h"; path = "../../Darkly/NSString+RemoveWhitespace.h"; sourceTree = ""; }; 832C788C1F2977B800E334A2 /* NSString+RemoveWhitespace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+RemoveWhitespace.m"; path = "../../Darkly/NSString+RemoveWhitespace.m"; sourceTree = ""; }; - 832C788E1F2A8DF600E334A2 /* LDUserModel+JsonDecodeable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "LDUserModel+JsonDecodeable.h"; sourceTree = ""; }; - 832C788F1F2A8DF600E334A2 /* LDUserModel+JsonDecodeable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "LDUserModel+JsonDecodeable.m"; sourceTree = ""; }; 833D08C91F3B97EB00BEED83 /* NSThread+MainExecutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSThread+MainExecutable.h"; sourceTree = ""; }; 833D08CA1F3B97EB00BEED83 /* NSThread+MainExecutable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSThread+MainExecutable.m"; sourceTree = ""; }; 8349F51B1F1934A000B1F3DB /* NSDictionary+StringKey_Matchable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+StringKey_Matchable.h"; sourceTree = ""; }; 8349F51D1F19352300B1F3DB /* NSDictionary+StringKey_Matchable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+StringKey_Matchable.m"; sourceTree = ""; }; - 8349F51F1F195BCF00B1F3DB /* LDUserModel+Equatable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "LDUserModel+Equatable.h"; sourceTree = ""; }; - 8349F5201F195BCF00B1F3DB /* LDUserModel+Equatable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "LDUserModel+Equatable.m"; sourceTree = ""; }; 8358F2581F4202A300ECE1AF /* LDConfig+Testable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "LDConfig+Testable.h"; sourceTree = ""; }; 8358F2591F4202A300ECE1AF /* LDConfig+Testable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "LDConfig+Testable.m"; sourceTree = ""; }; 835B421A1FD5AF7300709384 /* DarklyEventSource.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DarklyEventSource.framework; path = Carthage/Build/iOS/DarklyEventSource.framework; sourceTree = ""; }; + 83620B8320BDF36B00F1F28E /* NSNumber+LaunchDarkly.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSNumber+LaunchDarkly.h"; sourceTree = ""; }; + 83620B8420BDF36B00F1F28E /* NSNumber+LaunchDarkly.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSNumber+LaunchDarkly.m"; sourceTree = ""; }; 8365E5062028F39E00DE8E2B /* LDEvent+EventTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDEvent+EventTypes.h"; sourceTree = ""; }; 8365E5072028F39E00DE8E2B /* LDEvent+EventTypes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDEvent+EventTypes.m"; sourceTree = ""; }; 836947591F1FED400047697C /* boolConfigIsABool-false.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "boolConfigIsABool-false.json"; sourceTree = ""; }; - 8369475A1F1FED400047697C /* boolConfigIsABool-true-withVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "boolConfigIsABool-true-withVersion.json"; sourceTree = ""; }; + 8369475A1F1FED400047697C /* boolConfigIsABool-true.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "boolConfigIsABool-true.json"; sourceTree = ""; }; 8369475B1F1FED400047697C /* boolConfigIsABool2-true.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "boolConfigIsABool2-true.json"; sourceTree = ""; }; 8369475F1F1FEEB40047697C /* numberConfigIsANumber-1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "numberConfigIsANumber-1.json"; sourceTree = ""; }; - 836947601F1FEEB40047697C /* numberConfigIsANumber-2-withVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "numberConfigIsANumber-2-withVersion.json"; sourceTree = ""; }; + 836947601F1FEEB40047697C /* numberConfigIsANumber-2.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "numberConfigIsANumber-2.json"; sourceTree = ""; }; 836947611F1FEEB40047697C /* numberConfigIsANumber2-1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "numberConfigIsANumber2-1.json"; sourceTree = ""; }; - 836947651F1FF0A00047697C /* stringConfigIsAString-someString-withVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "stringConfigIsAString-someString-withVersion.json"; sourceTree = ""; }; + 836947651F1FF0A00047697C /* stringConfigIsAString-someString.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "stringConfigIsAString-someString.json"; sourceTree = ""; }; 836947661F1FF0A00047697C /* stringConfigIsAString-someStringA.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "stringConfigIsAString-someStringA.json"; sourceTree = ""; }; 836947671F1FF0A00047697C /* stringConfigIsAStringA-someString.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "stringConfigIsAStringA-someString.json"; sourceTree = ""; }; 8369476B1F1FF45B0047697C /* arrayConfigIsAnArray-1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "arrayConfigIsAnArray-1.json"; sourceTree = ""; }; - 8369476C1F1FF45B0047697C /* arrayConfigIsAnArray-123-withVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "arrayConfigIsAnArray-123-withVersion.json"; sourceTree = ""; }; + 8369476C1F1FF45B0047697C /* arrayConfigIsAnArray-123.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "arrayConfigIsAnArray-123.json"; sourceTree = ""; }; 8369476D1F1FF45B0047697C /* arrayConfigIsAnArray-Empty.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "arrayConfigIsAnArray-Empty.json"; sourceTree = ""; }; 8369476E1F1FF45B0047697C /* arrayConfigIsAnArrayA-123.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "arrayConfigIsAnArrayA-123.json"; sourceTree = ""; }; - 836947731F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key-withVersion.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dictionaryConfigIsADictionary-3Key-withVersion.json"; sourceTree = ""; }; + 836947731F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dictionaryConfigIsADictionary-3Key.json"; sourceTree = ""; }; 836947741F1FF80A0047697C /* dictionaryConfigIsADictionary-Empty.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dictionaryConfigIsADictionary-Empty.json"; sourceTree = ""; }; 836947751F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyA.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dictionaryConfigIsADictionary-KeyA.json"; sourceTree = ""; }; 836947761F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyB-124.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dictionaryConfigIsADictionary-KeyB-124.json"; sourceTree = ""; }; @@ -428,23 +461,35 @@ 836947791F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyC.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "dictionaryConfigIsADictionary-KeyC.json"; sourceTree = ""; }; 836947811F20125F0047697C /* ldClientManagerTestConfigA.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldClientManagerTestConfigA.json; sourceTree = ""; }; 836947821F20125F0047697C /* ldClientManagerTestConfigB.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldClientManagerTestConfigB.json; sourceTree = ""; }; + 83758398209CCE71004329DD /* LDEventTrackingContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDEventTrackingContext.h; sourceTree = ""; }; + 83758399209CCE71004329DD /* LDEventTrackingContext.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDEventTrackingContext.m; sourceTree = ""; }; + 837583A2209CD0A7004329DD /* LDEventTrackingContextTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDEventTrackingContextTest.m; sourceTree = ""; }; + 837583A5209CD187004329DD /* LDEventTrackingContext+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDEventTrackingContext+Testable.h"; sourceTree = ""; }; + 837583A6209CD187004329DD /* LDEventTrackingContext+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDEventTrackingContext+Testable.m"; sourceTree = ""; }; 83889B121F8E93A100A4EF69 /* LDEvent+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDEvent+Testable.h"; sourceTree = ""; }; 83889B131F8E93A100A4EF69 /* LDEvent+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDEvent+Testable.m"; sourceTree = ""; }; - 83889B151F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSURLResponse+Unauthorized.h"; sourceTree = ""; }; - 83889B161F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSURLResponse+Unauthorized.m"; sourceTree = ""; }; + 83889B151F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSURLResponse+LaunchDarkly.h"; sourceTree = ""; }; + 83889B161F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSURLResponse+LaunchDarkly.m"; sourceTree = ""; }; 839956E620053081009707D1 /* LDUserModel+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDUserModel+Testable.h"; sourceTree = ""; }; 839956E720053081009707D1 /* LDUserModel+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDUserModel+Testable.m"; sourceTree = ""; }; 839D6D261FD1B57B000BE6BD /* DarklyEventSource.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DarklyEventSource.framework; path = Carthage/Build/watchOS/DarklyEventSource.framework; sourceTree = ""; }; 839D6D281FD1B58E000BE6BD /* DarklyEventSource.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DarklyEventSource.framework; path = Carthage/Build/tvOS/DarklyEventSource.framework; sourceTree = ""; }; 839E0A15201F97E800DB8CD1 /* ldClientManagerTestPatchIsANumber.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldClientManagerTestPatchIsANumber.json; sourceTree = ""; }; 839E0A17201FB8D900DB8CD1 /* ldClientManagerTestDeleteIsANumber.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldClientManagerTestDeleteIsANumber.json; sourceTree = ""; }; - 83B8C24A1FEB1CD20082B8A9 /* LDUserModel+Stub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDUserModel+Stub.h"; sourceTree = ""; }; - 83B8C24B1FEB1CD20082B8A9 /* LDUserModel+Stub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDUserModel+Stub.m"; sourceTree = ""; }; + 83B62E1520A249A200F2E656 /* NSDateFormatter+JsonHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+JsonHeader.h"; sourceTree = ""; }; + 83B62E1620A249A200F2E656 /* NSDateFormatter+JsonHeader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+JsonHeader.m"; sourceTree = ""; }; + 83B62E1F20A2517500F2E656 /* NSDateFormatter+JsonHeaderTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+JsonHeaderTest.m"; sourceTree = ""; }; + 83B62E2120A258D400F2E656 /* NSDateFormatter+JsonHeader+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+JsonHeader+Testable.h"; sourceTree = ""; }; + 83B62E2220A258D400F2E656 /* NSDateFormatter+JsonHeader+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+JsonHeader+Testable.m"; sourceTree = ""; }; + 83B62E2420A275A100F2E656 /* NSHTTPURLResponse+LaunchDarkly+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSHTTPURLResponse+LaunchDarkly+Testable.h"; sourceTree = ""; }; + 83B6FC1B207E60B6002DBA7B /* NSDate+ReferencedDate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDate+ReferencedDate.h"; sourceTree = ""; }; + 83B6FC1C207E60B6002DBA7B /* NSDate+ReferencedDate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDate+ReferencedDate.m"; sourceTree = ""; }; + 83B6FC29208127B2002DBA7B /* LDEventModel+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDEventModel+Testable.h"; sourceTree = ""; }; + 83B6FC2A208127B2002DBA7B /* LDEventModel+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDEventModel+Testable.m"; sourceTree = ""; }; 83B8C24D1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+LDUserModel.h"; sourceTree = ""; }; 83B8C24E1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+LDUserModel.m"; sourceTree = ""; }; - 83B8C2571FEC4C3B0082B8A9 /* featureFlags-excludeNulls-withVersions.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "featureFlags-excludeNulls-withVersions.json"; sourceTree = ""; }; + 83B8C2571FEC4C3B0082B8A9 /* featureFlags-excludeNulls.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "featureFlags-excludeNulls.json"; sourceTree = ""; }; 83B975D21FD1CA6000A4EF4E /* DarklyEventSource.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DarklyEventSource.framework; path = Carthage/Build/Mac/DarklyEventSource.framework; sourceTree = ""; }; - 83BE9384201A63B400DD1ED9 /* ldDataManagerTestConfig.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldDataManagerTestConfig.json; sourceTree = ""; }; 83BE9386201A6AA400DD1ED9 /* ldFlagConfigModelTest.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldFlagConfigModelTest.json; sourceTree = ""; }; 83BE9388201A781200DD1ED9 /* ldFlagConfigModelPatchVersion1Flag.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldFlagConfigModelPatchVersion1Flag.json; sourceTree = ""; }; 83BE938A201A797B00DD1ED9 /* NSJSONSerialization+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSJSONSerialization+Testable.h"; sourceTree = ""; }; @@ -458,7 +503,26 @@ 83BE9397201AA53100DD1ED9 /* ldFlagConfigModelPatchVersion2FlagWithNull.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldFlagConfigModelPatchVersion2FlagWithNull.json; sourceTree = ""; }; 83BE9399201B6EBA00DD1ED9 /* ldFlagConfigModelDeleteVersion2Flag.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldFlagConfigModelDeleteVersion2Flag.json; sourceTree = ""; }; 83BE939B201B80F800DD1ED9 /* ldFlagConfigModelDeleteNewFlag.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ldFlagConfigModelDeleteNewFlag.json; sourceTree = ""; }; - 83C28CF82023872B00394693 /* featureFlags-excludeNulls-withoutVersions.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "featureFlags-excludeNulls-withoutVersions.json"; sourceTree = ""; }; + 83C8867B2087A786004AC82F /* LDFlagValueCounter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDFlagValueCounter.h; sourceTree = ""; }; + 83C8867C2087A786004AC82F /* LDFlagValueCounter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDFlagValueCounter.m; sourceTree = ""; }; + 83ECCC872087BF860086F879 /* LDFlagValueCounterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDFlagValueCounterTest.m; sourceTree = ""; }; + 83ECCC892087CA280086F879 /* LDFlagValueCounter+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDFlagValueCounter+Testable.h"; sourceTree = ""; }; + 83ECCC8A2087CA280086F879 /* LDFlagValueCounter+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDFlagValueCounter+Testable.m"; sourceTree = ""; }; + 83ECCC8C2087D67D0086F879 /* LDFlagCounter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDFlagCounter.h; sourceTree = ""; }; + 83ECCC8D2087D67D0086F879 /* LDFlagCounter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDFlagCounter.m; sourceTree = ""; }; + 83ECCC972087DF040086F879 /* LDFlagCounterTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDFlagCounterTest.m; sourceTree = ""; }; + 83ECCC992087EF4A0086F879 /* LDFlagConfigValue+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDFlagConfigValue+Testable.h"; sourceTree = ""; }; + 83ECCC9A2087EF4A0086F879 /* LDFlagConfigValue+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDFlagConfigValue+Testable.m"; sourceTree = ""; }; + 83ECCC9C208800F10086F879 /* doubleConfigIsADouble-e.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "doubleConfigIsADouble-e.json"; sourceTree = ""; }; + 83ECCCA12088E4E90086F879 /* LDFlagCounter+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDFlagCounter+Testable.h"; sourceTree = ""; }; + 83ECCCA22088E4E90086F879 /* LDFlagCounter+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDFlagCounter+Testable.m"; sourceTree = ""; }; + 83ECCCA42088FBA80086F879 /* LDFlagConfigTracker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LDFlagConfigTracker.h; sourceTree = ""; }; + 83ECCCA52088FBA80086F879 /* LDFlagConfigTracker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDFlagConfigTracker.m; sourceTree = ""; }; + 83ECCCAE2088FF420086F879 /* LDFlagConfigTrackerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LDFlagConfigTrackerTest.m; sourceTree = ""; }; + 83ECCCB020890A430086F879 /* LDFlagConfigTracker+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDFlagConfigTracker+Testable.h"; sourceTree = ""; }; + 83ECCCB120890A430086F879 /* LDFlagConfigTracker+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDFlagConfigTracker+Testable.m"; sourceTree = ""; }; + 83ECCCB320891E8E0086F879 /* NSDate+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDate+Testable.h"; sourceTree = ""; }; + 83ECCCB420891E8E0086F879 /* NSDate+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDate+Testable.m"; sourceTree = ""; }; 83EF677F1F979B4100403126 /* LDEvent+Unauthorized.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDEvent+Unauthorized.h"; sourceTree = ""; }; 83EF67801F979B4100403126 /* LDEvent+Unauthorized.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDEvent+Unauthorized.m"; sourceTree = ""; }; 83EF678B1F98FC9200403126 /* LDFlagConfigModel+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDFlagConfigModel+Testable.h"; sourceTree = ""; }; @@ -467,8 +531,8 @@ 83EF678F1F99365600403126 /* LDClient+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDClient+Testable.m"; sourceTree = ""; }; 83F5B4731F91560300174DF7 /* LDDataManager+Testable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LDDataManager+Testable.h"; sourceTree = ""; }; 83F5B4741F91560300174DF7 /* LDDataManager+Testable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "LDDataManager+Testable.m"; sourceTree = ""; }; - 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSHTTPURLResponse+Unauthorized.h"; sourceTree = ""; }; - 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSHTTPURLResponse+Unauthorized.m"; sourceTree = ""; }; + 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSHTTPURLResponse+LaunchDarkly.h"; sourceTree = ""; }; + 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSHTTPURLResponse+LaunchDarkly.m"; sourceTree = ""; }; 83FE9DD643D01669FB16BBEF /* Pods-Darkly_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Darkly_iOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Darkly_iOS/Pods-Darkly_iOS.release.xcconfig"; sourceTree = ""; }; 875461696618AE6C4F4B4AC6 /* Pods_Darkly_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Darkly_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9EBBFAE744BB0BB1176E84CC /* Pods-Darkly_watchOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Darkly_watchOS.release.xcconfig"; path = "Pods/Target Support Files/Pods-Darkly_watchOS/Pods-Darkly_watchOS.release.xcconfig"; sourceTree = ""; }; @@ -596,16 +660,13 @@ 690346E01E68990000E45133 /* LDConfig.m */, 690346E11E68990000E45133 /* LDEventModel.h */, 690346E21E68990000E45133 /* LDEventModel.m */, - 690346E31E68990000E45133 /* LDFlagConfigModel.h */, - 690346E41E68990000E45133 /* LDFlagConfigModel.m */, - 8305EC6520221973002F20DB /* LDFlagConfigValue.h */, - 8305EC6620221973002F20DB /* LDFlagConfigValue.m */, 690346E51E68990000E45133 /* LDPollingManager.h */, 690346E61E68990000E45133 /* LDPollingManager.m */, 690346E71E68990000E45133 /* LDRequestManager.h */, 690346E81E68990000E45133 /* LDRequestManager.m */, 830C2AC3207579AC001D645D /* LDThrottler.h */, 830C2AC4207579AC001D645D /* LDThrottler.m */, + 83C886862087A7D4004AC82F /* LDFlagConfig */, 690346E91E68990000E45133 /* LDUserBuilder.h */, 690346EA1E68990000E45133 /* LDUserBuilder.m */, 690346EB1E68990000E45133 /* LDUserModel.h */, @@ -644,10 +705,9 @@ children = ( 690347161E689B9F00E45133 /* LDDataManagerTest.m */, 690347171E689B9F00E45133 /* LDEventModelTest.m */, + 83ECCC962087DE960086F879 /* LDFlagConfig */, 690347181E689B9F00E45133 /* LDUserModelTest.m */, - 690347211E689B9F00E45133 /* LDFlagConfigModelTest.m */, - 8305EC7920222B5E002F20DB /* NSObject+LDFlagConfigValueTest.m */, - 8305EC7B2022336C002F20DB /* LDFlagConfigValueTest.m */, + 83B62E1F20A2517500F2E656 /* NSDateFormatter+JsonHeaderTest.m */, ); path = Models; sourceTree = ""; @@ -655,43 +715,34 @@ 6903471D1E689B9F00E45133 /* Fixtures */ = { isa = PBXGroup; children = ( - 6903471E1E689B9F00E45133 /* featureFlags-withVersions.json */, - 8305EC8D20227365002F20DB /* featureFlags-withoutVersions.json */, - 83B8C2571FEC4C3B0082B8A9 /* featureFlags-excludeNulls-withVersions.json */, - 83C28CF82023872B00394693 /* featureFlags-excludeNulls-withoutVersions.json */, + 6903471E1E689B9F00E45133 /* featureFlags.json */, + 83B8C2571FEC4C3B0082B8A9 /* featureFlags-excludeNulls.json */, 83258A411F32721A008C2133 /* emptyConfig.json */, 836947591F1FED400047697C /* boolConfigIsABool-false.json */, - 8369475A1F1FED400047697C /* boolConfigIsABool-true-withVersion.json */, - 8305EC7D202238FC002F20DB /* boolConfigIsABool-true-withoutVersion.json */, + 8369475A1F1FED400047697C /* boolConfigIsABool-true.json */, 8369475B1F1FED400047697C /* boolConfigIsABool2-true.json */, - 83258A431F329EFB008C2133 /* doubleConfigIsADouble-Pi-withVersion.json */, - 8305EC8120223BC2002F20DB /* doubleConfigIsADouble-Pi-withoutVersion.json */, + 83258A431F329EFB008C2133 /* doubleConfigIsADouble-Pi.json */, + 83ECCC9C208800F10086F879 /* doubleConfigIsADouble-e.json */, 8369475F1F1FEEB40047697C /* numberConfigIsANumber-1.json */, - 836947601F1FEEB40047697C /* numberConfigIsANumber-2-withVersion.json */, - 8305EC7F20223A2E002F20DB /* numberConfigIsANumber-2-withoutVersion.json */, + 836947601F1FEEB40047697C /* numberConfigIsANumber-2.json */, 836947611F1FEEB40047697C /* numberConfigIsANumber2-1.json */, - 836947651F1FF0A00047697C /* stringConfigIsAString-someString-withVersion.json */, - 8305EC8320223D65002F20DB /* stringConfigIsAString-someString-withoutVersion.json */, + 836947651F1FF0A00047697C /* stringConfigIsAString-someString.json */, 836947661F1FF0A00047697C /* stringConfigIsAString-someStringA.json */, 836947671F1FF0A00047697C /* stringConfigIsAStringA-someString.json */, 8369476B1F1FF45B0047697C /* arrayConfigIsAnArray-1.json */, - 8369476C1F1FF45B0047697C /* arrayConfigIsAnArray-123-withVersion.json */, - 8305EC8520223EBA002F20DB /* arrayConfigIsAnArray-123-withoutVersion.json */, + 8369476C1F1FF45B0047697C /* arrayConfigIsAnArray-123.json */, 8369476D1F1FF45B0047697C /* arrayConfigIsAnArray-Empty.json */, 8369476E1F1FF45B0047697C /* arrayConfigIsAnArrayA-123.json */, - 836947731F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key-withVersion.json */, - 8305EC8720224156002F20DB /* dictionaryConfigIsADictionary-3Key-withoutVersion.json */, + 836947731F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key.json */, 836947741F1FF80A0047697C /* dictionaryConfigIsADictionary-Empty.json */, 836947751F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyA.json */, 836947761F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyB-124.json */, 836947771F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyB.json */, 836947781F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyC-keyDValueDiffers.json */, 836947791F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyC.json */, - 8305EC89202243D6002F20DB /* nullConfigIsANull-null-withVersion.json */, - 8305EC8B2022440D002F20DB /* nullConfigIsANull-null-withoutVersion.json */, + 8305EC89202243D6002F20DB /* nullConfigIsANull-null.json */, 836947811F20125F0047697C /* ldClientManagerTestConfigA.json */, 836947821F20125F0047697C /* ldClientManagerTestConfigB.json */, - 83BE9384201A63B400DD1ED9 /* ldDataManagerTestConfig.json */, 83BE9386201A6AA400DD1ED9 /* ldFlagConfigModelTest.json */, 83BE9388201A781200DD1ED9 /* ldFlagConfigModelPatchVersion1Flag.json */, 83BE9395201A93AC00DD1ED9 /* ldFlagConfigModelPatchVersion2Flag.json */, @@ -710,8 +761,8 @@ children = ( 83BE9392201A8DEE00DD1ED9 /* NSObject+Testable.h */, 83BE9393201A8DEE00DD1ED9 /* NSObject+Testable.m */, - 690347241E689B9F00E45133 /* NSArray+UnitTests.h */, - 690347251E689B9F00E45133 /* NSArray+UnitTests.m */, + 690347241E689B9F00E45133 /* NSArray+Testable.h */, + 690347251E689B9F00E45133 /* NSArray+Testable.m */, 8349F51B1F1934A000B1F3DB /* NSDictionary+StringKey_Matchable.h */, 8349F51D1F19352300B1F3DB /* NSDictionary+StringKey_Matchable.m */, 83BE938F201A8AD100DD1ED9 /* NSDictionary+Testable.h */, @@ -720,14 +771,8 @@ 832C788C1F2977B800E334A2 /* NSString+RemoveWhitespace.m */, 83BE938A201A797B00DD1ED9 /* NSJSONSerialization+Testable.h */, 83BE938B201A797B00DD1ED9 /* NSJSONSerialization+Testable.m */, - 83B8C24A1FEB1CD20082B8A9 /* LDUserModel+Stub.h */, - 83B8C24B1FEB1CD20082B8A9 /* LDUserModel+Stub.m */, 839956E620053081009707D1 /* LDUserModel+Testable.h */, 839956E720053081009707D1 /* LDUserModel+Testable.m */, - 8349F51F1F195BCF00B1F3DB /* LDUserModel+Equatable.h */, - 8349F5201F195BCF00B1F3DB /* LDUserModel+Equatable.m */, - 832C788E1F2A8DF600E334A2 /* LDUserModel+JsonDecodeable.h */, - 832C788F1F2A8DF600E334A2 /* LDUserModel+JsonDecodeable.m */, 83258A3B1F323049008C2133 /* LDClientManager+EventSource.h */, 83258A3C1F323049008C2133 /* LDClientManager+EventSource.m */, 83258A3E1F3244D0008C2133 /* LDUserBuilder+Testable.h */, @@ -740,12 +785,16 @@ 83F5B4741F91560300174DF7 /* LDDataManager+Testable.m */, 83EF678E1F99365600403126 /* LDClient+Testable.h */, 83EF678F1F99365600403126 /* LDClient+Testable.m */, - 83EF678B1F98FC9200403126 /* LDFlagConfigModel+Testable.h */, - 83EF678C1F98FC9200403126 /* LDFlagConfigModel+Testable.m */, - 830BF92F202A8854006DF9B1 /* NSJSONSerialization+Testable.h */, - 830BF930202A8855006DF9B1 /* NSJSONSerialization+Testable.m */, + 837583A4209CD0E0004329DD /* LDFlagConfig */, 830C2AD020768697001D645D /* LDThrottler+Testable.h */, 830C2AD120768697001D645D /* LDThrottler+Testable.m */, + 83B6FC29208127B2002DBA7B /* LDEventModel+Testable.h */, + 83B6FC2A208127B2002DBA7B /* LDEventModel+Testable.m */, + 83ECCCB320891E8E0086F879 /* NSDate+Testable.h */, + 83ECCCB420891E8E0086F879 /* NSDate+Testable.m */, + 83B62E2120A258D400F2E656 /* NSDateFormatter+JsonHeader+Testable.h */, + 83B62E2220A258D400F2E656 /* NSDateFormatter+JsonHeader+Testable.m */, + 83B62E2420A275A100F2E656 /* NSHTTPURLResponse+LaunchDarkly+Testable.h */, ); path = Categories; sourceTree = ""; @@ -759,22 +808,45 @@ 832C78821F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m */, 833D08C91F3B97EB00BEED83 /* NSThread+MainExecutable.h */, 833D08CA1F3B97EB00BEED83 /* NSThread+MainExecutable.m */, - 83889B151F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h */, - 83889B161F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m */, - 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h */, - 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m */, + 83889B151F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h */, + 83889B161F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m */, + 83F5B4761F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h */, + 83F5B4771F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m */, 83EF677F1F979B4100403126 /* LDEvent+Unauthorized.h */, 83EF67801F979B4100403126 /* LDEvent+Unauthorized.m */, 8365E5062028F39E00DE8E2B /* LDEvent+EventTypes.h */, 8365E5072028F39E00DE8E2B /* LDEvent+EventTypes.m */, 83B8C24D1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h */, 83B8C24E1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m */, - 8305EC6F20222078002F20DB /* NSObject+LDFlagConfigValue.h */, - 8305EC7020222078002F20DB /* NSObject+LDFlagConfigValue.m */, + 83B62E1520A249A200F2E656 /* NSDateFormatter+JsonHeader.h */, + 83B62E1620A249A200F2E656 /* NSDateFormatter+JsonHeader.m */, + 83B6FC1B207E60B6002DBA7B /* NSDate+ReferencedDate.h */, + 83B6FC1C207E60B6002DBA7B /* NSDate+ReferencedDate.m */, + 83620B8320BDF36B00F1F28E /* NSNumber+LaunchDarkly.h */, + 83620B8420BDF36B00F1F28E /* NSNumber+LaunchDarkly.m */, ); name = Categories; sourceTree = ""; }; + 837583A4209CD0E0004329DD /* LDFlagConfig */ = { + isa = PBXGroup; + children = ( + 83EF678B1F98FC9200403126 /* LDFlagConfigModel+Testable.h */, + 83EF678C1F98FC9200403126 /* LDFlagConfigModel+Testable.m */, + 83ECCC992087EF4A0086F879 /* LDFlagConfigValue+Testable.h */, + 83ECCC9A2087EF4A0086F879 /* LDFlagConfigValue+Testable.m */, + 83ECCCB020890A430086F879 /* LDFlagConfigTracker+Testable.h */, + 83ECCCB120890A430086F879 /* LDFlagConfigTracker+Testable.m */, + 83ECCCA12088E4E90086F879 /* LDFlagCounter+Testable.h */, + 83ECCCA22088E4E90086F879 /* LDFlagCounter+Testable.m */, + 83ECCC892087CA280086F879 /* LDFlagValueCounter+Testable.h */, + 83ECCC8A2087CA280086F879 /* LDFlagValueCounter+Testable.m */, + 837583A5209CD187004329DD /* LDEventTrackingContext+Testable.h */, + 837583A6209CD187004329DD /* LDEventTrackingContext+Testable.m */, + ); + path = LDFlagConfig; + sourceTree = ""; + }; 839D6D2D1FD1B6BF000BE6BD /* CarthageFrameworks */ = { isa = PBXGroup; children = ( @@ -786,6 +858,38 @@ name = CarthageFrameworks; sourceTree = ""; }; + 83C886862087A7D4004AC82F /* LDFlagConfig */ = { + isa = PBXGroup; + children = ( + 690346E31E68990000E45133 /* LDFlagConfigModel.h */, + 690346E41E68990000E45133 /* LDFlagConfigModel.m */, + 8305EC6520221973002F20DB /* LDFlagConfigValue.h */, + 8305EC6620221973002F20DB /* LDFlagConfigValue.m */, + 83ECCCA42088FBA80086F879 /* LDFlagConfigTracker.h */, + 83ECCCA52088FBA80086F879 /* LDFlagConfigTracker.m */, + 83ECCC8C2087D67D0086F879 /* LDFlagCounter.h */, + 83ECCC8D2087D67D0086F879 /* LDFlagCounter.m */, + 83C8867B2087A786004AC82F /* LDFlagValueCounter.h */, + 83C8867C2087A786004AC82F /* LDFlagValueCounter.m */, + 83758398209CCE71004329DD /* LDEventTrackingContext.h */, + 83758399209CCE71004329DD /* LDEventTrackingContext.m */, + ); + path = LDFlagConfig; + sourceTree = ""; + }; + 83ECCC962087DE960086F879 /* LDFlagConfig */ = { + isa = PBXGroup; + children = ( + 690347211E689B9F00E45133 /* LDFlagConfigModelTest.m */, + 8305EC7B2022336C002F20DB /* LDFlagConfigValueTest.m */, + 83ECCCAE2088FF420086F879 /* LDFlagConfigTrackerTest.m */, + 83ECCC972087DF040086F879 /* LDFlagCounterTest.m */, + 83ECCC872087BF860086F879 /* LDFlagValueCounterTest.m */, + 837583A2209CD0A7004329DD /* LDEventTrackingContextTest.m */, + ); + path = LDFlagConfig; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -795,11 +899,13 @@ files = ( 690346FD1E68990000E45133 /* LDEventModel.h in Headers */, 690346F91E68990000E45133 /* LDClientManager.h in Headers */, - 8305EC7120222078002F20DB /* NSObject+LDFlagConfigValue.h in Headers */, + 83B62E1720A249A200F2E656 /* NSDateFormatter+JsonHeader.h in Headers */, 690346FF1E68990000E45133 /* LDFlagConfigModel.h in Headers */, + 83ECCC8E2087D67D0086F879 /* LDFlagCounter.h in Headers */, 83EF67811F979B4100403126 /* LDEvent+Unauthorized.h in Headers */, 83B8C24F1FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h in Headers */, 690346F51E68990000E45133 /* DarklyConstants.h in Headers */, + 83620B8520BDF36B00F1F28E /* NSNumber+LaunchDarkly.h in Headers */, 690346FB1E68990000E45133 /* LDConfig.h in Headers */, 690347011E68990000E45133 /* LDPollingManager.h in Headers */, 690346F41E68990000E45133 /* Darkly-Prefix.pch in Headers */, @@ -807,14 +913,18 @@ 690347031E68990000E45133 /* LDRequestManager.h in Headers */, 690347051E68990000E45133 /* LDUserBuilder.h in Headers */, 8305EC6720221973002F20DB /* LDFlagConfigValue.h in Headers */, - 83F5B4781F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h in Headers */, + 83F5B4781F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h in Headers */, 6903470D1E68990000E45133 /* NSDictionary+JSON.h in Headers */, + 83B6FC1D207E60B6002DBA7B /* NSDate+ReferencedDate.h in Headers */, 690347091E68990000E45133 /* LDUtil.h in Headers */, 69BAF40D1E9AAB4800747613 /* Darkly.h in Headers */, 830C2AC5207579AC001D645D /* LDThrottler.h in Headers */, 690346F71E68990000E45133 /* LDClient.h in Headers */, 833D08CB1F3B97EB00BEED83 /* NSThread+MainExecutable.h in Headers */, - 83889B171F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h in Headers */, + 83C8867D2087A786004AC82F /* LDFlagValueCounter.h in Headers */, + 8375839A209CCE71004329DD /* LDEventTrackingContext.h in Headers */, + 83ECCCA62088FBA80086F879 /* LDFlagConfigTracker.h in Headers */, + 83889B171F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h in Headers */, 690347071E68990000E45133 /* LDUserModel.h in Headers */, 8365E5082028F39E00DE8E2B /* LDEvent+EventTypes.h in Headers */, 832C78831F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h in Headers */, @@ -827,11 +937,13 @@ files = ( 69A87EA21E74712800B88B23 /* LDEventModel.h in Headers */, 69A87E9E1E74712800B88B23 /* LDClientManager.h in Headers */, - 8305EC7420222078002F20DB /* NSObject+LDFlagConfigValue.h in Headers */, + 83B62E1A20A249A200F2E656 /* NSDateFormatter+JsonHeader.h in Headers */, 69A87EA41E74712800B88B23 /* LDFlagConfigModel.h in Headers */, + 83ECCC912087D67D0086F879 /* LDFlagCounter.h in Headers */, 83EF67841F979B4100403126 /* LDEvent+Unauthorized.h in Headers */, 83B8C2521FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h in Headers */, 69A87E9A1E74712800B88B23 /* DarklyConstants.h in Headers */, + 83620B8820BDF36B00F1F28E /* NSNumber+LaunchDarkly.h in Headers */, 69A87EA01E74712800B88B23 /* LDConfig.h in Headers */, 69A87EA61E74712800B88B23 /* LDPollingManager.h in Headers */, 69A87E991E74712800B88B23 /* Darkly-Prefix.pch in Headers */, @@ -839,14 +951,18 @@ 69A87EA81E74712800B88B23 /* LDRequestManager.h in Headers */, 69A87EB01E74712800B88B23 /* NSDictionary+JSON.h in Headers */, 8305EC6A20221973002F20DB /* LDFlagConfigValue.h in Headers */, - 83F5B47B1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h in Headers */, + 83F5B47B1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h in Headers */, 69A87EAA1E74712800B88B23 /* LDUserBuilder.h in Headers */, + 83B6FC20207E60B6002DBA7B /* NSDate+ReferencedDate.h in Headers */, 69A87EAE1E74712800B88B23 /* LDUtil.h in Headers */, 69071F801EA2A7CC00497F93 /* Darkly.h in Headers */, 830C2AC8207579AC001D645D /* LDThrottler.h in Headers */, 69A87E9C1E74712800B88B23 /* LDClient.h in Headers */, 833D08CE1F3B97EB00BEED83 /* NSThread+MainExecutable.h in Headers */, - 83889B1A1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h in Headers */, + 83C886802087A786004AC82F /* LDFlagValueCounter.h in Headers */, + 8375839D209CCE71004329DD /* LDEventTrackingContext.h in Headers */, + 83ECCCA92088FBA80086F879 /* LDFlagConfigTracker.h in Headers */, + 83889B1A1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h in Headers */, 69A87EAC1E74712800B88B23 /* LDUserModel.h in Headers */, 8365E50B2028F39E00DE8E2B /* LDEvent+EventTypes.h in Headers */, 832C78861F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h in Headers */, @@ -859,11 +975,13 @@ files = ( 69BD7E231E6C79910056D70F /* LDEventModel.h in Headers */, 69BD7E1F1E6C79910056D70F /* LDClientManager.h in Headers */, - 8305EC7320222078002F20DB /* NSObject+LDFlagConfigValue.h in Headers */, + 83B62E1920A249A200F2E656 /* NSDateFormatter+JsonHeader.h in Headers */, 69BD7E251E6C79910056D70F /* LDFlagConfigModel.h in Headers */, + 83ECCC902087D67D0086F879 /* LDFlagCounter.h in Headers */, 83EF67831F979B4100403126 /* LDEvent+Unauthorized.h in Headers */, 83B8C2511FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h in Headers */, 69BD7E1B1E6C79910056D70F /* DarklyConstants.h in Headers */, + 83620B8720BDF36B00F1F28E /* NSNumber+LaunchDarkly.h in Headers */, 69BD7E211E6C79910056D70F /* LDConfig.h in Headers */, 69BD7E271E6C79910056D70F /* LDPollingManager.h in Headers */, 69BD7E1A1E6C79910056D70F /* Darkly-Prefix.pch in Headers */, @@ -871,14 +989,18 @@ 69BD7E291E6C79910056D70F /* LDRequestManager.h in Headers */, 69BD7E311E6C79910056D70F /* NSDictionary+JSON.h in Headers */, 8305EC6920221973002F20DB /* LDFlagConfigValue.h in Headers */, - 83F5B47A1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h in Headers */, + 83F5B47A1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h in Headers */, 69BD7E2B1E6C79910056D70F /* LDUserBuilder.h in Headers */, + 83B6FC1F207E60B6002DBA7B /* NSDate+ReferencedDate.h in Headers */, 69BD7E2F1E6C79910056D70F /* LDUtil.h in Headers */, 69071F7F1EA2A7CB00497F93 /* Darkly.h in Headers */, 830C2AC7207579AC001D645D /* LDThrottler.h in Headers */, 69BD7E1D1E6C79910056D70F /* LDClient.h in Headers */, 833D08CD1F3B97EB00BEED83 /* NSThread+MainExecutable.h in Headers */, - 83889B191F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h in Headers */, + 83C8867F2087A786004AC82F /* LDFlagValueCounter.h in Headers */, + 8375839C209CCE71004329DD /* LDEventTrackingContext.h in Headers */, + 83ECCCA82088FBA80086F879 /* LDFlagConfigTracker.h in Headers */, + 83889B191F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h in Headers */, 69BD7E2D1E6C79910056D70F /* LDUserModel.h in Headers */, 8365E50A2028F39E00DE8E2B /* LDEvent+EventTypes.h in Headers */, 832C78851F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h in Headers */, @@ -891,11 +1013,13 @@ files = ( 69F3F6AE1E6BF84B00079A09 /* Darkly-Prefix.pch in Headers */, 69F3F6A11E6BF82C00079A09 /* LDPollingManager.h in Headers */, - 8305EC7220222078002F20DB /* NSObject+LDFlagConfigValue.h in Headers */, + 83B62E1820A249A200F2E656 /* NSDateFormatter+JsonHeader.h in Headers */, 69F3F6941E6BF80800079A09 /* LDDataManager.h in Headers */, + 83ECCC8F2087D67D0086F879 /* LDFlagCounter.h in Headers */, 83EF67821F979B4100403126 /* LDEvent+Unauthorized.h in Headers */, 83B8C2501FEC19500082B8A9 /* NSDateFormatter+LDUserModel.h in Headers */, 69F3F6991E6BF82C00079A09 /* LDClientManager.h in Headers */, + 83620B8620BDF36B00F1F28E /* NSNumber+LaunchDarkly.h in Headers */, 69F3F6A51E6BF82C00079A09 /* LDUserBuilder.h in Headers */, 69F3F6A91E6BF82C00079A09 /* LDUtil.h in Headers */, 69F3F69B1E6BF82C00079A09 /* LDConfig.h in Headers */, @@ -903,14 +1027,18 @@ 69F3F6A71E6BF82C00079A09 /* LDUserModel.h in Headers */, 69F3F6951E6BF82100079A09 /* DarklyConstants.h in Headers */, 8305EC6820221973002F20DB /* LDFlagConfigValue.h in Headers */, - 83F5B4791F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.h in Headers */, + 83F5B4791F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.h in Headers */, 69F3F6971E6BF82C00079A09 /* LDClient.h in Headers */, + 83B6FC1E207E60B6002DBA7B /* NSDate+ReferencedDate.h in Headers */, 69F3F6AB1E6BF82C00079A09 /* NSDictionary+JSON.h in Headers */, 69071F7E1EA2A7CA00497F93 /* Darkly.h in Headers */, 830C2AC6207579AC001D645D /* LDThrottler.h in Headers */, 69F3F6A31E6BF82C00079A09 /* LDRequestManager.h in Headers */, 833D08CC1F3B97EB00BEED83 /* NSThread+MainExecutable.h in Headers */, - 83889B181F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.h in Headers */, + 83C8867E2087A786004AC82F /* LDFlagValueCounter.h in Headers */, + 8375839B209CCE71004329DD /* LDEventTrackingContext.h in Headers */, + 83ECCCA72088FBA80086F879 /* LDFlagConfigTracker.h in Headers */, + 83889B181F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.h in Headers */, 69F3F69D1E6BF82C00079A09 /* LDEventModel.h in Headers */, 8365E5092028F39E00DE8E2B /* LDEvent+EventTypes.h in Headers */, 832C78841F296F3400E334A2 /* NSMutableDictionary+NullRemovable.h in Headers */, @@ -1086,51 +1214,42 @@ buildActionMask = 2147483647; files = ( 8369477D1F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyB-124.json in Resources */, - 8305EC8820224156002F20DB /* dictionaryConfigIsADictionary-3Key-withoutVersion.json in Resources */, - 8305EC8C2022440D002F20DB /* nullConfigIsANull-null-withoutVersion.json in Resources */, - 8305EC8220223BC3002F20DB /* doubleConfigIsADouble-Pi-withoutVersion.json in Resources */, 83BE9398201AA53100DD1ED9 /* ldFlagConfigModelPatchVersion2FlagWithNull.json in Resources */, - 8305EC8A202243D6002F20DB /* nullConfigIsANull-null-withVersion.json in Resources */, + 8305EC8A202243D6002F20DB /* nullConfigIsANull-null.json in Resources */, 83BE9387201A6AA400DD1ED9 /* ldFlagConfigModelTest.json in Resources */, + 83ECCC9D208800F20086F879 /* doubleConfigIsADouble-e.json in Resources */, 836947801F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyC.json in Resources */, 836947841F20125F0047697C /* ldClientManagerTestConfigB.json in Resources */, 83BE939C201B80F800DD1ED9 /* ldFlagConfigModelDeleteNewFlag.json in Resources */, - 83C28CF92023872C00394693 /* featureFlags-excludeNulls-withoutVersions.json in Resources */, 836947711F1FF45B0047697C /* arrayConfigIsAnArray-Empty.json in Resources */, 8369476A1F1FF0A00047697C /* stringConfigIsAStringA-someString.json in Resources */, 83258A421F32721A008C2133 /* emptyConfig.json in Resources */, 836947691F1FF0A00047697C /* stringConfigIsAString-someStringA.json in Resources */, - 8369477A1F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key-withVersion.json in Resources */, - 8305EC8420223D65002F20DB /* stringConfigIsAString-someString-withoutVersion.json in Resources */, + 8369477A1F1FF80A0047697C /* dictionaryConfigIsADictionary-3Key.json in Resources */, 836947621F1FEEB40047697C /* numberConfigIsANumber-1.json in Resources */, - 6903472F1E689B9F00E45133 /* featureFlags-withVersions.json in Resources */, + 6903472F1E689B9F00E45133 /* featureFlags.json in Resources */, 83BE9389201A781200DD1ED9 /* ldFlagConfigModelPatchVersion1Flag.json in Resources */, - 836947631F1FEEB40047697C /* numberConfigIsANumber-2-withVersion.json in Resources */, + 836947631F1FEEB40047697C /* numberConfigIsANumber-2.json in Resources */, 8369475C1F1FED400047697C /* boolConfigIsABool-false.json in Resources */, - 8305EC8020223A2E002F20DB /* numberConfigIsANumber-2-withoutVersion.json in Resources */, - 83B8C2581FEC4C3B0082B8A9 /* featureFlags-excludeNulls-withVersions.json in Resources */, - 8305EC7E202238FC002F20DB /* boolConfigIsABool-true-withoutVersion.json in Resources */, + 83B8C2581FEC4C3B0082B8A9 /* featureFlags-excludeNulls.json in Resources */, 83BE9396201A93AC00DD1ED9 /* ldFlagConfigModelPatchVersion2Flag.json in Resources */, 836947831F20125F0047697C /* ldClientManagerTestConfigA.json in Resources */, 839E0A16201F97E900DB8CD1 /* ldClientManagerTestPatchIsANumber.json in Resources */, 836947721F1FF45B0047697C /* arrayConfigIsAnArrayA-123.json in Resources */, 8369477C1F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyA.json in Resources */, 8369475E1F1FED400047697C /* boolConfigIsABool2-true.json in Resources */, - 83258A441F329EFB008C2133 /* doubleConfigIsADouble-Pi-withVersion.json in Resources */, - 83BE9385201A63B400DD1ED9 /* ldDataManagerTestConfig.json in Resources */, + 83258A441F329EFB008C2133 /* doubleConfigIsADouble-Pi.json in Resources */, 836947641F1FEEB40047697C /* numberConfigIsANumber2-1.json in Resources */, 839E0A18201FB8D900DB8CD1 /* ldClientManagerTestDeleteIsANumber.json in Resources */, - 8305EC8E20227365002F20DB /* featureFlags-withoutVersions.json in Resources */, 8369477B1F1FF80A0047697C /* dictionaryConfigIsADictionary-Empty.json in Resources */, - 836947681F1FF0A00047697C /* stringConfigIsAString-someString-withVersion.json in Resources */, + 836947681F1FF0A00047697C /* stringConfigIsAString-someString.json in Resources */, 8369477E1F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyB.json in Resources */, 8369476F1F1FF45B0047697C /* arrayConfigIsAnArray-1.json in Resources */, - 8369475D1F1FED400047697C /* boolConfigIsABool-true-withVersion.json in Resources */, + 8369475D1F1FED400047697C /* boolConfigIsABool-true.json in Resources */, 8369477F1F1FF80A0047697C /* dictionaryConfigIsADictionary-KeyC-keyDValueDiffers.json in Resources */, - 836947701F1FF45B0047697C /* arrayConfigIsAnArray-123-withVersion.json in Resources */, + 836947701F1FF45B0047697C /* arrayConfigIsAnArray-123.json in Resources */, 83BE939A201B6EBA00DD1ED9 /* ldFlagConfigModelDeleteVersion2Flag.json in Resources */, 83BE938E201A863D00DD1ED9 /* ldFlagConfigModelPatchNewFlag.json in Resources */, - 8305EC8620223EBA002F20DB /* arrayConfigIsAnArray-123-withoutVersion.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1352,15 +1471,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 83889B1B1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m in Sources */, + 83889B1B1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m in Sources */, + 8375839E209CCE71004329DD /* LDEventTrackingContext.m in Sources */, 8365E50C2028F39E00DE8E2B /* LDEvent+EventTypes.m in Sources */, 690346FE1E68990000E45133 /* LDEventModel.m in Sources */, + 83B62E1B20A249A200F2E656 /* NSDateFormatter+JsonHeader.m in Sources */, 690347121E68994500E45133 /* LDDataManager.m in Sources */, 832C78871F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m in Sources */, 690347041E68990000E45133 /* LDRequestManager.m in Sources */, + 83ECCCAA2088FBA80086F879 /* LDFlagConfigTracker.m in Sources */, 833D08CF1F3B97EB00BEED83 /* NSThread+MainExecutable.m in Sources */, 690346FA1E68990000E45133 /* LDClientManager.m in Sources */, - 83F5B47C1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m in Sources */, + 83F5B47C1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m in Sources */, 690346F81E68990000E45133 /* LDClient.m in Sources */, 690346F61E68990000E45133 /* DarklyConstants.m in Sources */, 83B8C2531FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m in Sources */, @@ -1371,9 +1493,12 @@ 690347021E68990000E45133 /* LDPollingManager.m in Sources */, 690346FC1E68990000E45133 /* LDConfig.m in Sources */, 690347061E68990000E45133 /* LDUserBuilder.m in Sources */, - 8305EC7520222078002F20DB /* NSObject+LDFlagConfigValue.m in Sources */, + 83620B8920BDF36B00F1F28E /* NSNumber+LaunchDarkly.m in Sources */, 690347081E68990000E45133 /* LDUserModel.m in Sources */, 6903470A1E68990000E45133 /* LDUtil.m in Sources */, + 83B6FC21207E60B6002DBA7B /* NSDate+ReferencedDate.m in Sources */, + 83C886812087A786004AC82F /* LDFlagValueCounter.m in Sources */, + 83ECCC922087D67D0086F879 /* LDFlagCounter.m in Sources */, 690347001E68990000E45133 /* LDFlagConfigModel.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1382,38 +1507,47 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 837583A7209CD187004329DD /* LDEventTrackingContext+Testable.m in Sources */, + 83ECCCAF2088FF420086F879 /* LDFlagConfigTrackerTest.m in Sources */, 83EF678D1F98FC9200403126 /* LDFlagConfigModel+Testable.m in Sources */, 8349F51E1F19352300B1F3DB /* NSDictionary+StringKey_Matchable.m in Sources */, + 83B6FC2B208127B2002DBA7B /* LDEventModel+Testable.m in Sources */, 839956E820053081009707D1 /* LDUserModel+Testable.m in Sources */, 690347291E689B9F00E45133 /* LDEventModelTest.m in Sources */, 830C2ACF20757CE9001D645D /* LDThrottlerTest.m in Sources */, 690347301E689B9F00E45133 /* LDConfigTest.m in Sources */, 690347261E689B9F00E45133 /* LDUserBuilderTest.m in Sources */, 832C788D1F2977B800E334A2 /* NSString+RemoveWhitespace.m in Sources */, + 83ECCC982087DF040086F879 /* LDFlagCounterTest.m in Sources */, 6903472A1E689B9F00E45133 /* LDUserModelTest.m in Sources */, - 8349F5211F195BCF00B1F3DB /* LDUserModel+Equatable.m in Sources */, + 83ECCCB220890A430086F879 /* LDFlagConfigTracker+Testable.m in Sources */, 6903472B1E689B9F00E45133 /* LDPollingManagerTest.m in Sources */, 83EF67901F99365600403126 /* LDClient+Testable.m in Sources */, 83258A3D1F323049008C2133 /* LDClientManager+EventSource.m in Sources */, + 83ECCC9B2087EF4A0086F879 /* LDFlagConfigValue+Testable.m in Sources */, + 837583A3209CD0A7004329DD /* LDEventTrackingContextTest.m in Sources */, + 83B62E2320A258D400F2E656 /* NSDateFormatter+JsonHeader+Testable.m in Sources */, 830C2AD220768697001D645D /* LDThrottler+Testable.m in Sources */, - 8305EC7A20222B5E002F20DB /* NSObject+LDFlagConfigValueTest.m in Sources */, 83BE938C201A797B00DD1ED9 /* NSJSONSerialization+Testable.m in Sources */, 6903472E1E689B9F00E45133 /* LDClientManagerTest.m in Sources */, 69E5275E1E6E948F00E4B63B /* LDDataManagerTest.m in Sources */, 83BE9394201A8DEE00DD1ED9 /* NSObject+Testable.m in Sources */, + 83ECCC882087BF860086F879 /* LDFlagValueCounterTest.m in Sources */, 690347321E689B9F00E45133 /* DarklyXCTestCase.m in Sources */, 83889B141F8E93A100A4EF69 /* LDEvent+Testable.m in Sources */, 69B205D31EA92ECD00487CA3 /* LDRequestManagerTest.m in Sources */, 6903472D1E689B9F00E45133 /* LDUtilTest.m in Sources */, + 83B62E2020A2517500F2E656 /* NSDateFormatter+JsonHeaderTest.m in Sources */, + 83ECCC8B2087CA280086F879 /* LDFlagValueCounter+Testable.m in Sources */, 690347311E689B9F00E45133 /* LDFlagConfigModelTest.m in Sources */, 8305EC7C2022336C002F20DB /* LDFlagConfigValueTest.m in Sources */, 83BE9391201A8AD100DD1ED9 /* NSDictionary+Testable.m in Sources */, 83258A401F3244D0008C2133 /* LDUserBuilder+Testable.m in Sources */, - 690347331E689B9F00E45133 /* NSArray+UnitTests.m in Sources */, + 690347331E689B9F00E45133 /* NSArray+Testable.m in Sources */, 6903472C1E689B9F00E45133 /* LDClientTest.m in Sources */, - 83B8C24C1FEB1CD20082B8A9 /* LDUserModel+Stub.m in Sources */, - 832C78901F2A8DF600E334A2 /* LDUserModel+JsonDecodeable.m in Sources */, 8358F25A1F4202A300ECE1AF /* LDConfig+Testable.m in Sources */, + 83ECCCA32088E4E90086F879 /* LDFlagCounter+Testable.m in Sources */, + 83ECCCB520891E8E0086F879 /* NSDate+Testable.m in Sources */, 83F5B4751F91560300174DF7 /* LDDataManager+Testable.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1422,15 +1556,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 83889B1E1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m in Sources */, + 83889B1E1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m in Sources */, + 837583A1209CCE71004329DD /* LDEventTrackingContext.m in Sources */, 8365E50F2028F39E00DE8E2B /* LDEvent+EventTypes.m in Sources */, 69A87EA31E74712800B88B23 /* LDEventModel.m in Sources */, + 83B62E1E20A249A200F2E656 /* NSDateFormatter+JsonHeader.m in Sources */, 69A87E9D1E74712800B88B23 /* LDClient.m in Sources */, 832C788A1F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m in Sources */, 69A87E9B1E74712800B88B23 /* DarklyConstants.m in Sources */, + 83ECCCAD2088FBA80086F879 /* LDFlagConfigTracker.m in Sources */, 833D08D21F3B97EB00BEED83 /* NSThread+MainExecutable.m in Sources */, 69A87EA11E74712800B88B23 /* LDConfig.m in Sources */, - 83F5B47F1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m in Sources */, + 83F5B47F1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m in Sources */, 69A87EA51E74712800B88B23 /* LDFlagConfigModel.m in Sources */, 69A87EB11E74712800B88B23 /* NSDictionary+JSON.m in Sources */, 83B8C2561FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m in Sources */, @@ -1441,9 +1578,12 @@ 69A87EA71E74712800B88B23 /* LDPollingManager.m in Sources */, 69A87E9F1E74712800B88B23 /* LDClientManager.m in Sources */, 69A87EAD1E74712800B88B23 /* LDUserModel.m in Sources */, - 8305EC7820222078002F20DB /* NSObject+LDFlagConfigValue.m in Sources */, + 83620B8C20BDF36B00F1F28E /* NSNumber+LaunchDarkly.m in Sources */, 69A87E981E74712800B88B23 /* LDDataManager.m in Sources */, 69A87EAB1E74712800B88B23 /* LDUserBuilder.m in Sources */, + 83B6FC24207E60B6002DBA7B /* NSDate+ReferencedDate.m in Sources */, + 83C886842087A786004AC82F /* LDFlagValueCounter.m in Sources */, + 83ECCC952087D67D0086F879 /* LDFlagCounter.m in Sources */, 69A87EA91E74712800B88B23 /* LDRequestManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1452,15 +1592,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 83889B1D1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m in Sources */, + 83889B1D1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m in Sources */, + 837583A0209CCE71004329DD /* LDEventTrackingContext.m in Sources */, 8365E50E2028F39E00DE8E2B /* LDEvent+EventTypes.m in Sources */, 69BD7E241E6C79910056D70F /* LDEventModel.m in Sources */, + 83B62E1D20A249A200F2E656 /* NSDateFormatter+JsonHeader.m in Sources */, 69BD7E1E1E6C79910056D70F /* LDClient.m in Sources */, 832C78891F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m in Sources */, 69BD7E1C1E6C79910056D70F /* DarklyConstants.m in Sources */, + 83ECCCAC2088FBA80086F879 /* LDFlagConfigTracker.m in Sources */, 833D08D11F3B97EB00BEED83 /* NSThread+MainExecutable.m in Sources */, 69BD7E221E6C79910056D70F /* LDConfig.m in Sources */, - 83F5B47E1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m in Sources */, + 83F5B47E1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m in Sources */, 69BD7E261E6C79910056D70F /* LDFlagConfigModel.m in Sources */, 69BD7E321E6C79910056D70F /* NSDictionary+JSON.m in Sources */, 83B8C2551FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m in Sources */, @@ -1471,9 +1614,12 @@ 69BD7E281E6C79910056D70F /* LDPollingManager.m in Sources */, 69BD7E201E6C79910056D70F /* LDClientManager.m in Sources */, 69BD7E2E1E6C79910056D70F /* LDUserModel.m in Sources */, - 8305EC7720222078002F20DB /* NSObject+LDFlagConfigValue.m in Sources */, + 83620B8B20BDF36B00F1F28E /* NSNumber+LaunchDarkly.m in Sources */, 69BD7E191E6C79910056D70F /* LDDataManager.m in Sources */, 69BD7E2C1E6C79910056D70F /* LDUserBuilder.m in Sources */, + 83B6FC23207E60B6002DBA7B /* NSDate+ReferencedDate.m in Sources */, + 83C886832087A786004AC82F /* LDFlagValueCounter.m in Sources */, + 83ECCC942087D67D0086F879 /* LDFlagCounter.m in Sources */, 69BD7E2A1E6C79910056D70F /* LDRequestManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1482,15 +1628,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 83889B1C1F8F28AB00A4EF69 /* NSURLResponse+Unauthorized.m in Sources */, + 83889B1C1F8F28AB00A4EF69 /* NSURLResponse+LaunchDarkly.m in Sources */, + 8375839F209CCE71004329DD /* LDEventTrackingContext.m in Sources */, 8365E50D2028F39E00DE8E2B /* LDEvent+EventTypes.m in Sources */, 69F3F69E1E6BF82C00079A09 /* LDEventModel.m in Sources */, + 83B62E1C20A249A200F2E656 /* NSDateFormatter+JsonHeader.m in Sources */, 69F3F6981E6BF82C00079A09 /* LDClient.m in Sources */, 832C78881F296F3400E334A2 /* NSMutableDictionary+NullRemovable.m in Sources */, 69F3F6961E6BF82100079A09 /* DarklyConstants.m in Sources */, + 83ECCCAB2088FBA80086F879 /* LDFlagConfigTracker.m in Sources */, 833D08D01F3B97EB00BEED83 /* NSThread+MainExecutable.m in Sources */, 69F3F69C1E6BF82C00079A09 /* LDConfig.m in Sources */, - 83F5B47D1F95096A00174DF7 /* NSHTTPURLResponse+Unauthorized.m in Sources */, + 83F5B47D1F95096A00174DF7 /* NSHTTPURLResponse+LaunchDarkly.m in Sources */, 69F3F6A01E6BF82C00079A09 /* LDFlagConfigModel.m in Sources */, 69F3F6AC1E6BF82C00079A09 /* NSDictionary+JSON.m in Sources */, 83B8C2541FEC19500082B8A9 /* NSDateFormatter+LDUserModel.m in Sources */, @@ -1501,9 +1650,12 @@ 69F3F6A21E6BF82C00079A09 /* LDPollingManager.m in Sources */, 69F3F69A1E6BF82C00079A09 /* LDClientManager.m in Sources */, 69F3F6A81E6BF82C00079A09 /* LDUserModel.m in Sources */, - 8305EC7620222078002F20DB /* NSObject+LDFlagConfigValue.m in Sources */, + 83620B8A20BDF36B00F1F28E /* NSNumber+LaunchDarkly.m in Sources */, 69F3F6921E6BF7F600079A09 /* LDDataManager.m in Sources */, 69F3F6A61E6BF82C00079A09 /* LDUserBuilder.m in Sources */, + 83B6FC22207E60B6002DBA7B /* NSDate+ReferencedDate.m in Sources */, + 83C886822087A786004AC82F /* LDFlagValueCounter.m in Sources */, + 83ECCC932087D67D0086F879 /* LDFlagCounter.m in Sources */, 69F3F6A41E6BF82C00079A09 /* LDRequestManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Darkly/DarklyConstants.m b/Darkly/DarklyConstants.m index d6b6ef46..cf4e278d 100644 --- a/Darkly/DarklyConstants.m +++ b/Darkly/DarklyConstants.m @@ -4,7 +4,7 @@ #import "DarklyConstants.h" -NSString * const kClientVersion = @"2.12.1"; +NSString * const kClientVersion = @"2.13.0"; NSString * const kBaseUrl = @"https://app.launchdarkly.com"; NSString * const kEventsUrl = @"https://mobile.launchdarkly.com"; NSString * const kStreamUrl = @"https://clientstream.launchdarkly.com"; @@ -35,7 +35,11 @@ int const kDefaultFlushInterval = 30; int const kMinimumFlushIntervalMillis = 0; int const kDefaultPollingInterval = 300; +#if DEBUG +int const kMinimumPollingInterval = 30; +#else int const kMinimumPollingInterval = 300; +#endif int const kDefaultBackgroundFetchInterval = 3600; int const kMinimumBackgroundFetchInterval = 900; int const kMillisInSecs = 1000; diff --git a/Darkly/LDClient.m b/Darkly/LDClient.m index 22e11ece..c521511c 100644 --- a/Darkly/LDClient.m +++ b/Darkly/LDClient.m @@ -11,6 +11,9 @@ #import "DarklyConstants.h" #import "NSThread+MainExecutable.h" #import "LDThrottler.h" +#import "LDFlagConfigModel.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigTracker.h" @interface LDClient() @property (nonatomic, assign) BOOL isOnline; @@ -49,6 +52,11 @@ +(LDClient *)sharedInstance return sharedLDClient; } +-(void)setLdUser:(LDUserModel*)user { + _ldUser = user; + [[LDDataManager sharedManager] createIdentifyEventWithUser:_ldUser config:self.ldConfig]; +} + -(BOOL)start:(LDConfigBuilder *)inputConfigBuilder userBuilder:(LDUserBuilder *)inputUserBuilder { return [self start:[inputConfigBuilder build] withUserBuilder:inputUserBuilder]; } @@ -103,132 +111,141 @@ - (LDUserBuilder *)currentUserBuilder { } } -- (BOOL)boolVariation:(NSString *)featureKey fallback:(BOOL)fallback{ - DEBUG_LOG(@"LDClient boolVariation method called for feature=%@ and fallback=%d", featureKey, fallback); - if (![featureKey isKindOfClass:[NSString class]]) { - NSLog(@"featureKey should be an NSString. Returning fallback value"); +- (BOOL)boolVariation:(NSString *)flagKey fallback:(BOOL)fallback{ + DEBUG_LOG(@"LDClient boolVariation method called for flagKey=%@ and fallback=%d", flagKey, fallback); + if (![flagKey isKindOfClass:[NSString class]]) { + NSLog(@"flagKey should be an NSString. Returning fallback value"); return fallback; } - 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]; - } - - [[LDDataManager sharedManager] createFeatureEvent: featureKey keyValue:[NSNumber numberWithBool:returnValue] defaultKeyValue:[NSNumber numberWithBool:fallback] user:self.ldUser config:self.ldConfig]; - return returnValue; - } else { + if (!self.clientStarted) { DEBUG_LOGX(@"LDClient not started yet!"); + return fallback; } - return fallback; + + LDFlagConfigValue *flagConfigValue = [self.ldUser.flagConfig flagConfigValueForFlagKey:flagKey]; + BOOL returnValue = flagConfigValue.value && [flagConfigValue.value isKindOfClass:[NSNumber class]] ? [flagConfigValue.value boolValue] : fallback; + + [[LDDataManager sharedManager] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:@(returnValue) + flagConfigValue:flagConfigValue + defaultFlagValue:@(fallback) + user:self.ldUser + config:self.ldConfig]; + return returnValue; } -- (NSNumber*)numberVariation:(NSString *)featureKey fallback:(NSNumber*)fallback{ - DEBUG_LOG(@"LDClient numberVariation method called for feature=%@ and fallback=%@", featureKey, fallback); - if (![featureKey isKindOfClass:[NSString class]]) { - NSLog(@"featureKey should be an NSString. Returning fallback value"); +- (NSNumber*)numberVariation:(NSString *)flagKey fallback:(NSNumber*)fallback{ + DEBUG_LOG(@"LDClient numberVariation method called for flagKey=%@ and fallback=%@", flagKey, fallback); + if (![flagKey isKindOfClass:[NSString class]]) { + NSLog(@"flagKey should be an NSString. Returning fallback value"); return fallback; } - 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; - } - - [[LDDataManager sharedManager] createFeatureEvent: featureKey keyValue:returnValue defaultKeyValue:fallback user:self.ldUser config:self.ldConfig]; - return returnValue; - } else { + if (!self.clientStarted) { DEBUG_LOGX(@"LDClient not started yet!"); + return fallback; } - return fallback; + + LDFlagConfigValue *flagConfigValue = [self.ldUser.flagConfig flagConfigValueForFlagKey:flagKey]; + NSNumber *returnValue = flagConfigValue.value && [flagConfigValue.value isKindOfClass:[NSNumber class]] ? flagConfigValue.value : fallback; + + [[LDDataManager sharedManager] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:returnValue + flagConfigValue:flagConfigValue + defaultFlagValue:fallback + user:self.ldUser + config:self.ldConfig]; + return returnValue; } -- (double)doubleVariation:(NSString *)featureKey fallback:(double)fallback { - DEBUG_LOG(@"LDClient doubleVariation method called for feature=%@ and fallback=%f", featureKey, fallback); - if (![featureKey isKindOfClass:[NSString class]]) { - NSLog(@"featureKey should be an NSString. Returning fallback value"); +- (double)doubleVariation:(NSString *)flagKey fallback:(double)fallback { + DEBUG_LOG(@"LDClient doubleVariation method called for flagKey=%@ and fallback=%f", flagKey, fallback); + if (![flagKey isKindOfClass:[NSString class]]) { + NSLog(@"flagKey should be an NSString. Returning fallback value"); return fallback; } if (!self.clientStarted) { DEBUG_LOGX(@"LDClient not started yet!"); return fallback; } - 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]; - } - - [[LDDataManager sharedManager] createFeatureEvent:featureKey keyValue:[NSNumber numberWithDouble:returnValue] defaultKeyValue:[NSNumber numberWithDouble:fallback] user:self.ldUser config:self.ldConfig]; + + LDFlagConfigValue *flagConfigValue = [self.ldUser.flagConfig flagConfigValueForFlagKey:flagKey]; + double returnValue = flagConfigValue.value && [flagConfigValue.value isKindOfClass:[NSNumber class]] ? [flagConfigValue.value doubleValue] : fallback; + + [[LDDataManager sharedManager] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:@(returnValue) + flagConfigValue:flagConfigValue + defaultFlagValue:@(fallback) + user:self.ldUser + config:self.ldConfig]; return returnValue; } -- (NSString*)stringVariation:(NSString *)featureKey fallback:(NSString*)fallback{ - DEBUG_LOG(@"LDClient stringVariation method called for feature=%@ and fallback=%@", featureKey, fallback); - if (![featureKey isKindOfClass:[NSString class]]) { - NSLog(@"featureKey should be an NSString. Returning fallback value"); +- (NSString*)stringVariation:(NSString *)flagKey fallback:(NSString*)fallback{ + DEBUG_LOG(@"LDClient stringVariation method called for flagKey=%@ and fallback=%@", flagKey, fallback); + if (![flagKey isKindOfClass:[NSString class]]) { + NSLog(@"flagKey should be an NSString. Returning fallback value"); return fallback; } - 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; - } - - [[LDDataManager sharedManager] createFeatureEvent: featureKey keyValue:returnValue defaultKeyValue:fallback user:self.ldUser config:self.ldConfig]; - return returnValue; - } else { + if (!self.clientStarted) { DEBUG_LOGX(@"LDClient not started yet!"); + return fallback; } - return fallback; + + LDFlagConfigValue *flagConfigValue = [self.ldUser.flagConfig flagConfigValueForFlagKey:flagKey]; + NSString *returnValue = flagConfigValue.value && [flagConfigValue.value isKindOfClass:[NSString class]] ? flagConfigValue.value : fallback; + + [[LDDataManager sharedManager] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:returnValue + flagConfigValue:flagConfigValue + defaultFlagValue:fallback + user:self.ldUser + config:self.ldConfig]; + return returnValue; } -- (NSArray*)arrayVariation:(NSString *)featureKey fallback:(NSArray*)fallback{ - DEBUG_LOG(@"LDClient arrayVariation method called for feature=%@ and fallback=%@", featureKey, fallback); - if (![featureKey isKindOfClass:[NSString class]]) { - NSLog(@"featureKey should be an NSString. Returning fallback value"); +- (NSArray*)arrayVariation:(NSString *)flagKey fallback:(NSArray*)fallback{ + DEBUG_LOG(@"LDClient arrayVariation method called for flagKey=%@ and fallback=%@", flagKey, fallback); + if (![flagKey isKindOfClass:[NSString class]]) { + NSLog(@"flagKey should be an NSString. Returning fallback value"); return fallback; } if (!self.clientStarted) { DEBUG_LOGX(@"LDClient not started yet!"); return fallback; } - BOOL flagExists = [self.ldUser doesFlagExist: featureKey]; - id flagValue = [self.ldUser flagValue: featureKey]; - NSArray *returnValue = fallback; - if (flagExists && [flagValue isKindOfClass:[NSArray class]]) { - returnValue = (NSArray *)flagValue; - } - - [[LDDataManager sharedManager] createFeatureEvent: featureKey keyValue:returnValue defaultKeyValue:fallback user:self.ldUser config:self.ldConfig]; + + LDFlagConfigValue *flagConfigValue = [self.ldUser.flagConfig flagConfigValueForFlagKey:flagKey]; + NSArray *returnValue = flagConfigValue.value && [flagConfigValue.value isKindOfClass:[NSArray class]] ? flagConfigValue.value : fallback; + + [[LDDataManager sharedManager] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:returnValue + flagConfigValue:flagConfigValue + defaultFlagValue:fallback + user:self.ldUser + config:self.ldConfig]; return returnValue; } -- (NSDictionary*)dictionaryVariation:(NSString *)featureKey fallback:(NSDictionary*)fallback{ - DEBUG_LOG(@"LDClient dictionaryVariation method called for feature=%@ and fallback=%@", featureKey, fallback); - if (![featureKey isKindOfClass:[NSString class]]) { - NSLog(@"featureKey should be an NSString. Returning fallback value"); +- (NSDictionary*)dictionaryVariation:(NSString *)flagKey fallback:(NSDictionary*)fallback{ + DEBUG_LOG(@"LDClient dictionaryVariation method called for flagKey=%@ and fallback=%@", flagKey, fallback); + if (![flagKey isKindOfClass:[NSString class]]) { + NSLog(@"flagKey should be an NSString. Returning fallback value"); return fallback; } if (!self.clientStarted) { DEBUG_LOGX(@"LDClient not started yet!"); return fallback; } - BOOL flagExists = [self.ldUser doesFlagExist: featureKey]; - id flagValue = [self.ldUser flagValue: featureKey]; - NSDictionary *returnValue = fallback; - if (flagExists && [flagValue isKindOfClass:[NSDictionary class]]) { - returnValue = (NSDictionary *)flagValue; - } - - [[LDDataManager sharedManager] createFeatureEvent: featureKey keyValue:returnValue defaultKeyValue:fallback user:self.ldUser config:self.ldConfig]; + + LDFlagConfigValue *flagConfigValue = [self.ldUser.flagConfig flagConfigValueForFlagKey:flagKey]; + NSDictionary *returnValue = flagConfigValue.value && [flagConfigValue.value isKindOfClass:[NSDictionary class]] ? flagConfigValue.value : fallback; + + [[LDDataManager sharedManager] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:returnValue + flagConfigValue:flagConfigValue + defaultFlagValue:fallback + user:self.ldUser + config:self.ldConfig]; return returnValue; } @@ -236,7 +253,7 @@ - (BOOL)track:(NSString *)eventName data:(NSDictionary *)dataDictionary { DEBUG_LOG(@"LDClient track method called for event=%@ and data=%@", eventName, dataDictionary); if (self.clientStarted) { - [[LDDataManager sharedManager] createCustomEvent:eventName withCustomValuesDictionary: dataDictionary user:self.ldUser config:self.ldConfig]; + [[LDDataManager sharedManager] createCustomEventWithKey:eventName customData: dataDictionary user:self.ldUser config:self.ldConfig]; return YES; } else { DEBUG_LOGX(@"LDClient not started yet!"); diff --git a/Darkly/LDClientManager.h b/Darkly/LDClientManager.h index 84278fa2..a5cdad2e 100644 --- a/Darkly/LDClientManager.h +++ b/Darkly/LDClientManager.h @@ -27,7 +27,7 @@ - (void)syncWithServerForEvents; - (void)syncWithServerForConfig; -- (void)processedEvents:(BOOL)success jsonEventArray:(NSArray *)jsonEventArray; +- (void)processedEvents:(BOOL)success jsonEventArray:(NSArray*)jsonEventArray responseDate:(NSDate*)responseDate; - (void)processedConfig:(BOOL)success jsonConfigDictionary:(NSDictionary *)jsonConfigDictionary; - (void)startPolling; - (void)stopPolling; diff --git a/Darkly/LDClientManager.m b/Darkly/LDClientManager.m index ea843fed..1a9c2ec4 100644 --- a/Darkly/LDClientManager.m +++ b/Darkly/LDClientManager.m @@ -3,6 +3,7 @@ // #import "LDClientManager.h" +#import "LDClient.h" #import "LDPollingManager.h" #import "LDDataManager.h" #import "LDUtil.h" @@ -220,16 +221,11 @@ - (void)handlePutEvent:(LDEvent*)event { 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; + NSString *updateResultNotificationName = [user.flagConfig isEqualToConfig:newConfig] ? kLDUserNoChangeNotification : kLDUserUpdatedNotification; + user.flagConfig = 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"); + [[NSNotificationCenter defaultCenter] postNotificationName:updateResultNotificationName object:nil]; + DEBUG_LOG(@"ClientManager posted %@ following user config update from SSE put event", updateResultNotificationName); } - (void)handlePatchEvent:(LDEvent*)event { @@ -247,11 +243,11 @@ - (void)handlePatchEvent:(LDEvent*)event { } LDUserModel *user = [[LDClient sharedInstance] ldUser]; - NSDictionary *originalFlagConfig = user.config.featuresJsonDictionary; + NSDictionary *originalFlagConfig = user.flagConfig.featuresJsonDictionary; - [user.config addOrReplaceFromDictionary:patchDictionary]; + [user.flagConfig addOrReplaceFromDictionary:patchDictionary]; - if ([user.config hasFeaturesEqualToDictionary:originalFlagConfig]) { + if ([user.flagConfig hasFeaturesEqualToDictionary:originalFlagConfig]) { DEBUG_LOGX(@"ClientManager handlePatchEvent resulted in no change to the flag config"); [[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil]; return; @@ -277,11 +273,11 @@ - (void)handleDeleteEvent:(LDEvent*)event { } LDUserModel *user = [[LDClient sharedInstance] ldUser]; - NSDictionary *originalFlagConfig = user.config.featuresJsonDictionary; + NSDictionary *originalFlagConfig = user.flagConfig.featuresJsonDictionary; - [user.config deleteFromDictionary:deleteDictionary]; + [user.flagConfig deleteFromDictionary:deleteDictionary]; - if ([user.config hasFeaturesEqualToDictionary:originalFlagConfig]) { + if ([user.flagConfig hasFeaturesEqualToDictionary:originalFlagConfig]) { DEBUG_LOGX(@"ClientManager handleDeleteEvent resulted in no change to the flag config"); [[NSNotificationCenter defaultCenter] postNotificationName:kLDUserNoChangeNotification object:nil]; return; @@ -320,7 +316,10 @@ -(void)syncWithServerForEvents { DEBUG_LOGX(@"ClientManager syncing events with server"); + [[LDDataManager sharedManager] createSummaryEventWithTracker:[LDClient sharedInstance].ldUser.flagConfigTracker config:[LDClient sharedInstance].ldConfig]; + [[LDDataManager sharedManager] allEventDictionaries:^(NSArray *eventDictionaries) { + [[LDClient sharedInstance].ldUser resetTracker]; if (eventDictionaries) { [[LDRequestManager sharedInstance] performEventRequest:eventDictionaries]; } else { @@ -351,14 +350,15 @@ - (void)flushEvents { [self syncWithServerForEvents]; } -- (void)processedEvents:(BOOL)success jsonEventArray:(NSArray *)jsonEventArray { +- (void)processedEvents:(BOOL)success jsonEventArray:(NSArray *)jsonEventArray responseDate:(NSDate*)responseDate { // If Success if (success) { DEBUG_LOGX(@"ClientManager processedEvents method called after receiving successful response from server"); // Audit cached events versus processed Events and only keep difference if (jsonEventArray) { - [[LDDataManager sharedManager] deleteProcessedEvents: jsonEventArray]; + [[LDDataManager sharedManager] deleteProcessedEvents:jsonEventArray]; } + [LDDataManager sharedManager].lastEventResponseDate = responseDate; } } @@ -373,7 +373,8 @@ - (void)processedConfig:(BOOL)success jsonConfigDictionary:(NSDictionary *)jsonC DEBUG_LOGX(@"ClientManager processedConfig method called after receiving successful response from server"); LDFlagConfigModel *newConfig = [[LDFlagConfigModel alloc] initWithDictionary:jsonConfigDictionary]; - if (!newConfig || [[LDClient sharedInstance].ldUser.config isEqualToConfig:newConfig]) { + if (!newConfig || [[LDClient sharedInstance].ldUser.flagConfig isEqualToConfig:newConfig]) { + [[LDClient sharedInstance].ldUser.flagConfig updateEventTrackingContextFromConfig:newConfig]; //Notify interested clients and bail out if no new config, or the new config equals the existing config [[NSNotificationCenter defaultCenter] postNotificationName: kLDUserNoChangeNotification object: nil]; @@ -382,7 +383,7 @@ - (void)processedConfig:(BOOL)success jsonConfigDictionary:(NSDictionary *)jsonC } LDUserModel *user = [LDClient sharedInstance].ldUser; - user.config = newConfig; + user.flagConfig = newConfig; [[LDDataManager sharedManager] saveUser:user]; // Save context [[NSNotificationCenter defaultCenter] postNotificationName: kLDUserUpdatedNotification diff --git a/Darkly/LDConfig.h b/Darkly/LDConfig.h index 16edf0f9..9a615bc4 100644 --- a/Darkly/LDConfig.h +++ b/Darkly/LDConfig.h @@ -94,6 +94,13 @@ */ @property (nonatomic, assign) BOOL useReport; +/** + Flag that tells the SDK to include the user attributes in analytics event reports. When set to YES, event reports will + contain the user attributes, except attributes marked as private. When set to NO, event reports will contain the user's key + only, reducing the size of event reports. The default is NO. + */ +@property (nonatomic, assign) BOOL inlineUserInEvents; + /** Flag that enables debug mode to allow things such as logging. */ diff --git a/Darkly/LDConfig.m b/Darkly/LDConfig.m index c915a5f4..329ac01e 100644 --- a/Darkly/LDConfig.m +++ b/Darkly/LDConfig.m @@ -127,6 +127,11 @@ - (void)setDebugEnabled:(BOOL)debugEnabled { DEBUG_LOG(@"Set LDConfig debug enabled: %d", debugEnabled); } +- (void)setInlineUserInEvents:(BOOL)inlineUserInEvents { + _inlineUserInEvents = inlineUserInEvents; + DEBUG_LOG(@"Set LDConfig inlineUserInEvents: %d", inlineUserInEvents); +} + - (BOOL)isFlagRetryStatusCode:(NSInteger)statusCode { return [self.flagRetryStatusCodes containsObject:@(statusCode)]; } diff --git a/Darkly/LDDataManager.h b/Darkly/LDDataManager.h index 88e8201b..bb73562a 100644 --- a/Darkly/LDDataManager.h +++ b/Darkly/LDDataManager.h @@ -2,26 +2,49 @@ // Copyright © 2015 Catamorphic Co. All rights reserved. // - -#import +#import #import "LDUserModel.h" +@class LDFlagConfigValue; +@class LDFlagConfigTracker; + extern int const kUserCacheSize; @interface LDDataManager : NSObject +@property (nonatomic, strong) NSDate *lastEventResponseDate; + +(LDDataManager *)sharedManager; -(void) allEventDictionaries:(void (^)(NSArray *eventDictionaries))completion; --(NSMutableDictionary *)retrieveUserDictionary; --(NSMutableArray *)retrieveEventsArray; --(LDUserModel *)findUserWithkey: (NSString *)key; --(void) createFeatureEvent: (NSString *)featureKey keyValue:(NSObject*)keyValue defaultKeyValue:(NSObject*)defaultKeyValue user:(LDUserModel*)user config:(LDConfig*)config; --(void) createCustomEvent: (NSString *)eventKey withCustomValuesDictionary: (NSDictionary *)customDict user:(LDUserModel*)user config:(LDConfig*)config; --(void) purgeOldUser: (NSMutableDictionary *)dictionary; --(void) saveUser: (LDUserModel *) user; --(void) saveUserDeprecated:(LDUserModel *)user __deprecated_msg("Use saveUser: instead"); --(void) deleteProcessedEvents: (NSArray *) processedJsonArray; +-(NSMutableDictionary*)retrieveUserDictionary; +-(NSMutableArray*)retrieveEventsArray; +-(LDUserModel*)findUserWithkey: (NSString *)key; +-(void)createFlagEvaluationEventsWithFlagKey:(NSString*)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + config:(LDConfig*)config; +-(void)createFeatureEventWithFlagKey:(NSString*)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + config:(LDConfig*)config; +-(void)createCustomEventWithKey:(NSString*)eventKey customData:(NSDictionary*)customData user:(LDUserModel*)user config:(LDConfig*)config; +-(void)createIdentifyEventWithUser:(LDUserModel*)user config:(LDConfig*)config; +-(void)createSummaryEventWithTracker:(LDFlagConfigTracker*)tracker config:(LDConfig*)config; +-(void)createDebugEventWithFlagKey:(NSString *)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + config:(LDConfig*)config; +-(void)purgeOldUser: (NSMutableDictionary *)dictionary; +-(void)saveUser: (LDUserModel *) user; +-(void)saveUserDeprecated:(LDUserModel *)user __deprecated_msg("Use saveUser: instead"); +-(void)deleteProcessedEvents: (NSArray *) processedJsonArray; -(void)flushEventsDictionary; @end diff --git a/Darkly/LDDataManager.m b/Darkly/LDDataManager.m index 9100a74b..52560f9e 100644 --- a/Darkly/LDDataManager.m +++ b/Darkly/LDDataManager.m @@ -7,6 +7,10 @@ #import "LDEventModel.h" #import "LDUtil.h" #import "LDFlagConfigModel.h" +#import "LDFlagConfigValue.h" +#import "LDEventTrackingContext.h" +#import "LDFlagConfigTracker.h" +#import "NSDate+ReferencedDate.h" int const kUserCacheSize = 5; @@ -98,8 +102,8 @@ -(LDUserModel *)findUserWithkey: (NSString *)key { } - (void)compareConfigForUser:(LDUserModel *)user withNewUser:(LDUserModel *)newUser { - for (NSString *key in [newUser.config dictionaryValueIncludeNulls:NO]) { - if(user == nil || ![[newUser.config configFlagValue:key] isEqual:[user.config configFlagValue:key]]) { + for (NSString *key in [newUser.flagConfig dictionaryValueIncludeNulls:NO]) { + if(user == nil || ![[newUser.flagConfig flagValueForFlagKey:key] isEqual:[user.flagConfig flagValueForFlagKey:key]]) { [[NSNotificationCenter defaultCenter] postNotificationName:kLDFlagConfigChangedNotification object:nil userInfo:[NSDictionary dictionaryWithObject:key forKey:kFlagKey]]; } } @@ -147,37 +151,114 @@ - (NSMutableDictionary *)retrieveUserDictionary { #pragma mark - events --(void) createFeatureEvent: (NSString *)featureKey keyValue:(NSObject*)keyValue defaultKeyValue:(NSObject*)defaultKeyValue user:(LDUserModel*)user config:(LDConfig*)config { - if(![self isAtEventCapacity:_eventsArray]) { - DEBUG_LOG(@"Creating event for feature:%@ with value:%@ and fallback:%@", featureKey, keyValue, defaultKeyValue); - [self addEventDictionary:[[[LDEventModel alloc] initFeatureEventWithKey:featureKey keyValue:keyValue defaultKeyValue:defaultKeyValue userValue:user] dictionaryValueUsingConfig:config]]; +-(void)createFlagEvaluationEventsWithFlagKey:(NSString*)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + config:(LDConfig*)config { + [self createFeatureEventWithFlagKey:flagKey reportedFlagValue:reportedFlagValue flagConfigValue:flagConfigValue defaultFlagValue:defaultFlagValue user:user config:config]; + [self createDebugEventWithFlagKey:flagKey reportedFlagValue:reportedFlagValue flagConfigValue:flagConfigValue defaultFlagValue:defaultFlagValue user:user config:config]; + [user.flagConfigTracker logRequestForFlagKey:flagKey reportedFlagValue:reportedFlagValue flagConfigValue:flagConfigValue defaultValue:defaultFlagValue]; +} + +-(void)createFeatureEventWithFlagKey:(NSString*)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + config:(LDConfig*)config { + if ([self isAtEventCapacity:self.eventsArray]) { + DEBUG_LOG(@"Events have surpassed capacity. Discarding feature event %@", flagKey); + return; + } + if (!flagConfigValue.eventTrackingContext || (flagConfigValue.eventTrackingContext && !flagConfigValue.eventTrackingContext.trackEvents)) { + DEBUG_LOG(@"Tracking is off. Discarding feature event %@", flagKey); + return; + } + DEBUG_LOG(@"Creating feature event for feature:%@ with flagConfigValue:%@ and fallback:%@", flagKey, flagConfigValue, defaultFlagValue); + [self addEventDictionary:[[LDEventModel featureEventWithFlagKey:flagKey + reportedFlagValue:reportedFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:user + inlineUser:config.inlineUserInEvents] + dictionaryValueUsingConfig:config]]; +} + +-(void)createCustomEventWithKey:(NSString *)eventKey customData:(NSDictionary *)customData user:(LDUserModel*)user config:(LDConfig*)config { + if ([self isAtEventCapacity:self.eventsArray]) { + DEBUG_LOG(@"Events have surpassed capacity. Discarding custom event %@ with customData %@", eventKey, customData); + return; + } + DEBUG_LOG(@"Creating custom event for custom key:%@ and customData:%@", eventKey, customData); + [self addEventDictionary:[[LDEventModel customEventWithKey:eventKey customData:customData userValue:user inlineUser:config.inlineUserInEvents] dictionaryValueUsingConfig:config]]; +} + +-(void)createIdentifyEventWithUser:(LDUserModel*)user config:(LDConfig*)config { + if ([self isAtEventCapacity:self.eventsArray]) { + DEBUG_LOG(@"Events have surpassed capacity. Discarding identify event for user key:%@", user.key); + return; } - else { - DEBUG_LOG(@"Events have surpassed capacity. Discarding feature event %@", featureKey); + DEBUG_LOG(@"Creating identify event for user key:%@", user.key); + [self addEventDictionary:[[LDEventModel identifyEventWithUser:user] dictionaryValueUsingConfig:config]]; +} + +-(void)createSummaryEventWithTracker:(LDFlagConfigTracker*)tracker config:(LDConfig*)config { + if ([self isAtEventCapacity:self.eventsArray]) { + DEBUG_LOGX(@"Events have surpassed capacity. Discarding summary event."); + return; + } + if (tracker.flagCounters.count == 0) { + DEBUG_LOGX(@"Tracker has no flag counters. Discarding summary event."); + return; } + DEBUG_LOGX(@"Creating summary event"); + [self addEventDictionary:[[LDEventModel summaryEventWithTracker:tracker] dictionaryValueUsingConfig:config]]; } --(void) createCustomEvent: (NSString *)eventKey withCustomValuesDictionary: (NSDictionary *)customDict user:(LDUserModel*)user config:(LDConfig*)config { - if(![self isAtEventCapacity:_eventsArray]) { - DEBUG_LOG(@"Creating event for custom key:%@ and value:%@", eventKey, customDict); - [self addEventDictionary:[[[LDEventModel alloc] initCustomEventWithKey:eventKey andDataDictionary: customDict userValue:user] dictionaryValueUsingConfig:config]]; +-(void)createDebugEventWithFlagKey:(NSString *)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + config:(LDConfig*)config { + if ([self isAtEventCapacity:self.eventsArray]) { + DEBUG_LOG(@"Events have surpassed capacity. Discarding debug event %@", flagKey); + return; } - else { - DEBUG_LOG(@"Events have surpassed capacity. Discarding event %@ with dictionary %@", eventKey, customDict); + if (![self shouldCreateDebugEventForContext:flagConfigValue.eventTrackingContext lastEventResponseDate:self.lastEventResponseDate]) { + DEBUG_LOG(@"LDDataManager createDebugEventWithFlagKey aborting, debug events are turned off. Discarding debug event %@", flagKey); + return; } + DEBUG_LOG(@"Creating debug event for feature:%@ with flagConfigValue:%@ and fallback:%@", flagKey, flagConfigValue, defaultFlagValue); + [self addEventDictionary:[[LDEventModel debugEventWithFlagKey:flagKey + reportedFlagValue:reportedFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:user] + dictionaryValueUsingConfig:config]]; +} + +-(BOOL)shouldCreateDebugEventForContext:(LDEventTrackingContext*)eventTrackingContext lastEventResponseDate:(NSDate*)lastEventResponseDate { + if (!eventTrackingContext || !eventTrackingContext.debugEventsUntilDate) { return NO; } + if ([lastEventResponseDate isLaterThan:eventTrackingContext.debugEventsUntilDate]) { return NO; } + if ([[NSDate date] isLaterThan:eventTrackingContext.debugEventsUntilDate]) { return NO; } + + return YES; } -(void)addEventDictionary:(NSDictionary*)eventDictionary { + if (!eventDictionary || eventDictionary.allKeys.count == 0) { + DEBUG_LOGX(@"LDDataManager addEventDictionary aborting. Event dictionary is missing or empty."); + return; + } dispatch_async(eventsQueue, ^{ - if (!self.eventsArray) { - self.eventsArray = [[NSMutableArray alloc] init]; - } - if(![self isAtEventCapacity:self.eventsArray]) { - [self.eventsArray addObject:eventDictionary]; - } - else { + if([self isAtEventCapacity:self.eventsArray]) { DEBUG_LOG(@"Events have surpassed capacity. Discarding event %@", eventDictionary[@"key"]); + return; } + [self.eventsArray addObject:eventDictionary]; }); } @@ -206,7 +287,7 @@ -(void) allEventDictionaries:(void (^)(NSArray *))completion { } -(void)flushEventsDictionary { - [_eventsArray removeAllObjects]; + [self.eventsArray removeAllObjects]; } - (NSMutableArray *)retrieveEventsArray { diff --git a/Darkly/LDEventModel.h b/Darkly/LDEventModel.h index b91e9b9d..40e1c24a 100644 --- a/Darkly/LDEventModel.h +++ b/Darkly/LDEventModel.h @@ -7,24 +7,76 @@ // #import +#import "LDUserModel.h" +#import "NSDate+ReferencedDate.h" -@class LDUserModel; @class LDConfig; +@class LDFlagConfigValue; +@class LDFlagConfigTracker; @interface LDEventModel : NSObject -@property (nullable, nonatomic, strong) NSString *key; +//all event kinds @property (nullable, nonatomic, strong) NSString *kind; -@property (atomic, assign) NSInteger creationDate; -@property (nullable, nonatomic, strong) NSDictionary *data; + +//feature, debug, custom, & identify events +@property (nullable, nonatomic, strong) NSString *key; +@property (nonatomic, assign) LDMillisecond creationDate; @property (nullable, nonatomic, strong) LDUserModel *user; +@property (nonatomic, assign) BOOL inlineUser; + +//feature & debug events only +@property (nullable, nonatomic, strong) LDFlagConfigValue *flagConfigValue; +@property (nullable, nonatomic, strong) id reportedValue; +@property (nullable, nonatomic, strong) id defaultValue; + +//custom events only +@property (nullable, nonatomic, strong) NSDictionary *data; -@property (nonnull, nonatomic, strong) NSObject *value; -@property (nonnull, nonatomic, strong) NSObject *isDefault; +//summary events only +@property (nonatomic, assign) LDMillisecond startDateMillis; +@property (nonatomic, assign) LDMillisecond endDateMillis; +@property (nullable, nonatomic, strong) NSDictionary *flagRequestSummary; --(nonnull id)initWithDictionary:(nonnull NSDictionary *)dictionary; -(nonnull NSDictionary *)dictionaryValueUsingConfig:(nonnull LDConfig*)config; --(nonnull instancetype)initFeatureEventWithKey:(nonnull NSString *)featureKey keyValue:(NSObject * _Nullable)keyValue defaultKeyValue:(NSObject * _Nullable)defaultKeyValue userValue:(nonnull LDUserModel *)userValue; --(nonnull instancetype)initCustomEventWithKey: (nonnull NSString *)featureKey - andDataDictionary: (nonnull NSDictionary *)customData userValue:(nonnull LDUserModel *)userValue; ++(nullable instancetype)featureEventWithFlagKey:(nonnull NSString *)flagKey + reportedFlagValue:(nonnull id)reportedFlagValue + flagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(nullable id)defaultflagValue + user:(nonnull LDUserModel*)user + inlineUser:(BOOL)inlineUser; +-(nullable instancetype)initFeatureEventWithFlagKey:(nonnull NSString *)flagKey + reportedFlagValue:(nonnull id)reportedFlagValue + flagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(nullable id)defaultFlagValue + user:(nonnull LDUserModel*)user + inlineUser:(BOOL)inlineUser; + ++(nullable instancetype)customEventWithKey:(nonnull NSString*)featureKey + customData:(nonnull NSDictionary*)customData + userValue:(nonnull LDUserModel*)userValue + inlineUser:(BOOL)inlineUser; +-(nullable instancetype)initCustomEventWithKey:(nonnull NSString*)featureKey + customData:(nonnull NSDictionary*)customData + userValue:(nonnull LDUserModel*)userValue + inlineUser:(BOOL)inlineUser; + ++(nullable instancetype)identifyEventWithUser:(nonnull LDUserModel*)user; +-(nullable instancetype)initIdentifyEventWithUser:(nonnull LDUserModel*)user; + ++(nullable instancetype)summaryEventWithTracker:(nonnull LDFlagConfigTracker*)tracker; +-(nullable instancetype)initSummaryEventWithTracker:(nonnull LDFlagConfigTracker*)tracker; + ++(nullable instancetype)debugEventWithFlagKey:(nonnull NSString*)flagKey + reportedFlagValue:(nonnull id)reportedFlagValue + flagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(nullable id)defaultFlagValue + user:(nonnull LDUserModel*)user; +-(nullable instancetype)initDebugEventWithFlagKey:(nonnull NSString*)flagKey + reportedFlagValue:(nonnull id)reportedFlagValue + flagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(nullable id)defaultFlagValue + user:(nonnull LDUserModel*)user; + +-(nonnull NSString*)description; @end diff --git a/Darkly/LDEventModel.m b/Darkly/LDEventModel.m index 11407dbb..1d6fceaf 100644 --- a/Darkly/LDEventModel.m +++ b/Darkly/LDEventModel.m @@ -8,92 +8,185 @@ #import "LDEventModel.h" #import "LDUserModel.h" +#import "NSDate+ReferencedDate.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigTracker.h" +#import "LDFlagCounter.h" -static NSString * const kKeyKey = @"key"; -static NSString * const kKindKey = @"kind"; -static NSString * const kCreationDateKey = @"creationDate"; -static NSString * const kDataKey = @"data"; -static NSString * const kFeatureKeyValueKey = @"value"; -static NSString * const kIsDefaultKey = @"isDefault"; -static NSString * const kUserKey = @"user"; +NSString * const kEventModelKindFeature = @"feature"; +NSString * const kEventModelKindCustom = @"custom"; +NSString * const kEventModelKindIdentify = @"identify"; +NSString * const kEventModelKindFeatureSummary = @"summary"; +NSString * const kEventModelKindDebug = @"debug"; -static NSString * const kFeatureKeyValueServerKey = @"value"; -static NSString * const kIsDefaultServerKey = @"default"; - -static NSString * const kFeatureEventName = @"feature"; -static NSString * const kCustomEventName = @"custom"; +NSString * const kEventModelKeyKey = @"key"; +NSString * const kEventModelKeyKind = @"kind"; +NSString * const kEventModelKeyCreationDate = @"creationDate"; +NSString * const kEventModelKeyData = @"data"; +NSString * const kEventModelKeyFlagConfigValue = @"flagConfigValue"; +NSString * const kEventModelKeyValue = @"value"; +NSString * const kEventModelKeyVersion = @"version"; +NSString * const kEventModelKeyVariation = @"variation"; +NSString * const kEventModelKeyIsDefault = @"isDefault"; +NSString * const kEventModelKeyDefault = @"default"; +NSString * const kEventModelKeyUser = @"user"; +NSString * const kEventModelKeyUserKey = @"userKey"; +NSString * const kEventModelKeyInlineUser = @"inlineUser"; +NSString * const kEventModelKeyStartDate = @"startDate"; +NSString * const kEventModelKeyEndDate = @"endDate"; +NSString * const kEventModelKeyFeatures = @"features"; @implementation LDEventModel - (void)encodeWithCoder:(NSCoder *)encoder { - //Encode properties, other class variables, etc - [encoder encodeObject:self.key forKey:kKeyKey]; - [encoder encodeObject:self.kind forKey:kKindKey]; - [encoder encodeInteger:self.creationDate forKey:kCreationDateKey]; - [encoder encodeObject:self.data forKey:kDataKey]; - [encoder encodeObject:self.value forKey:kFeatureKeyValueKey]; - [encoder encodeObject:self.isDefault forKey:kIsDefaultKey]; - [encoder encodeObject:self.user forKey:kUserKey]; + [encoder encodeObject:self.key forKey:kEventModelKeyKey]; + [encoder encodeObject:self.kind forKey:kEventModelKeyKind]; + [encoder encodeInt64:self.creationDate forKey:kEventModelKeyCreationDate]; + [encoder encodeObject:self.data forKey:kEventModelKeyData]; + [encoder encodeObject:self.flagConfigValue forKey:kEventModelKeyFlagConfigValue]; + [encoder encodeObject:self.defaultValue forKey:kEventModelKeyDefault]; + [encoder encodeObject:self.user forKey:kEventModelKeyUser]; + [encoder encodeBool:self.inlineUser forKey:kEventModelKeyInlineUser]; + [encoder encodeInt64:self.startDateMillis forKey:kEventModelKeyStartDate]; + [encoder encodeInt64:self.endDateMillis forKey:kEventModelKeyEndDate]; + [encoder encodeObject:self.flagRequestSummary forKey:kEventModelKeyFeatures]; } - (id)initWithCoder:(NSCoder *)decoder { - if((self = [super init])) { - //Decode properties, other class vars - self.key = [decoder decodeObjectForKey:kKeyKey]; - self.kind = [decoder decodeObjectForKey:kKindKey]; - self.creationDate = [decoder decodeIntegerForKey:kCreationDateKey]; - self.data = [decoder decodeObjectForKey:kDataKey]; - self.value = [decoder decodeObjectForKey:kFeatureKeyValueKey]; - self.isDefault = [decoder decodeObjectForKey:kIsDefaultKey]; - self.user = [decoder decodeObjectForKey:kUserKey]; + if(!(self = [super init])) { return nil; } + + self.key = [decoder decodeObjectForKey:kEventModelKeyKey]; + self.kind = [decoder decodeObjectForKey:kEventModelKeyKind]; + self.creationDate = [decoder decodeInt64ForKey:kEventModelKeyCreationDate]; + self.data = [decoder decodeObjectForKey:kEventModelKeyData]; + self.flagConfigValue = [decoder decodeObjectForKey:kEventModelKeyFlagConfigValue]; + self.defaultValue = [decoder decodeObjectForKey:kEventModelKeyDefault]; + if (!self.defaultValue) { + self.defaultValue = [decoder decodeObjectForKey:kEventModelKeyIsDefault]; } + self.user = [decoder decodeObjectForKey:kEventModelKeyUser]; + self.inlineUser = [decoder decodeBoolForKey:kEventModelKeyInlineUser]; + self.startDateMillis = [decoder decodeInt64ForKey:kEventModelKeyStartDate]; + self.endDateMillis = [decoder decodeInt64ForKey:kEventModelKeyEndDate]; + self.flagRequestSummary = [decoder decodeObjectForKey:kEventModelKeyFeatures]; + return self; } -- (id)initWithDictionary:(NSDictionary *)dictionary { - if((self = [super init])) { - //Process json that comes down from server - self.key = [dictionary objectForKey: kKeyKey]; - self.kind = [dictionary objectForKey: kKindKey]; - NSNumber *creationDateValue = [dictionary objectForKey:kCreationDateKey]; - self.creationDate = [creationDateValue longValue]; - self.value = [dictionary objectForKey: kFeatureKeyValueServerKey]; - self.isDefault = [dictionary objectForKey: kIsDefaultServerKey]; - self.user = [[LDUserModel alloc] initWithDictionary:[dictionary objectForKey:kUserKey]]; - } ++(instancetype)featureEventWithFlagKey:(NSString*)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + inlineUser:(BOOL)inlineUser { + return [[LDEventModel alloc] initFeatureEventWithFlagKey:flagKey + reportedFlagValue:reportedFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:user + inlineUser:inlineUser]; +} + +-(instancetype)initFeatureEventWithFlagKey:(NSString*)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + inlineUser:(BOOL)inlineUser { + if (!(self = [self init])) { return nil; } + + self.key = flagKey; + self.kind = kEventModelKindFeature; + self.reportedValue = reportedFlagValue; + self.flagConfigValue = flagConfigValue; + self.defaultValue = defaultFlagValue; + self.user = user; + self.inlineUser = inlineUser; + return self; } --(instancetype)initFeatureEventWithKey:(nonnull NSString *)featureKey keyValue:(NSObject*)keyValue defaultKeyValue:(NSObject*)defaultKeyValue userValue:(LDUserModel *)userValue { - if((self = [self init])) { - self.key = featureKey; - self.kind = kFeatureEventName; - self.value = keyValue; - self.isDefault = defaultKeyValue; - self.user = userValue; - } - ++(instancetype)customEventWithKey:(NSString*)featureKey + customData:(NSDictionary*)customData + userValue:(LDUserModel*)userValue + inlineUser:(BOOL)inlineUser { + return [[LDEventModel alloc] initCustomEventWithKey:featureKey customData:customData userValue:userValue inlineUser:inlineUser]; +} + +-(instancetype)initCustomEventWithKey:(NSString*)featureKey + customData:(NSDictionary*)customData + userValue:(LDUserModel*)userValue + inlineUser:(BOOL)inlineUser { + if(!(self = [self init])) { return nil; } + + self.key = featureKey; + self.kind = kEventModelKindCustom; + self.data = customData; + self.user = userValue; + self.inlineUser = inlineUser; + return self; } --(instancetype)initCustomEventWithKey: (NSString *)featureKey - andDataDictionary: (NSDictionary *)customData userValue:(LDUserModel *)userValue { - if((self = [self init])) { - self.key = featureKey; - self.kind = kCustomEventName; - self.data = customData; - self.user = userValue; ++(instancetype)identifyEventWithUser:(LDUserModel*)user { + return [[LDEventModel alloc] initIdentifyEventWithUser:user]; +} + +-(instancetype)initIdentifyEventWithUser:(LDUserModel*)user { + if(!(self = [self init])) { return nil; } + + self.key = user.key; + self.kind = kEventModelKindIdentify; + self.user = user; + self.inlineUser = YES; + + return self; +} + ++(instancetype)summaryEventWithTracker:(LDFlagConfigTracker*)tracker { + return [[LDEventModel alloc] initSummaryEventWithTracker:tracker]; +} + +-(instancetype)initSummaryEventWithTracker:(LDFlagConfigTracker*)tracker { + if(!(self = [self init])) { return nil; } + + self.kind = kEventModelKindFeatureSummary; + self.startDateMillis = tracker.startDateMillis; + self.endDateMillis = [[NSDate date] millisSince1970]; + NSMutableDictionary *flagRequestSummary = [NSMutableDictionary dictionaryWithCapacity:tracker.flagCounters.count]; + for (NSString *flagKey in [tracker.flagCounters.allKeys copy]) { + flagRequestSummary[flagKey] = [tracker.flagCounters[flagKey] dictionaryValue]; } - + self.flagRequestSummary = [NSDictionary dictionaryWithDictionary:flagRequestSummary]; + return self; } -- (instancetype)init { ++(instancetype)debugEventWithFlagKey:(NSString*)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user { + return [[LDEventModel alloc] initDebugEventWithFlagKey:flagKey reportedFlagValue:reportedFlagValue flagConfigValue:flagConfigValue defaultFlagValue:defaultFlagValue user:user]; +} + +-(instancetype)initDebugEventWithFlagKey:(NSString*)flagKey + reportedFlagValue:(id)reportedFlagValue + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user { + self = [self initFeatureEventWithFlagKey:flagKey reportedFlagValue:reportedFlagValue flagConfigValue:flagConfigValue defaultFlagValue:defaultFlagValue user:user inlineUser:YES]; + self.kind = kEventModelKindDebug; + + return self; +} + +-(instancetype)init { self = [super init]; if(self != nil) { // Need to set creationDate - self.creationDate = [@(floor([[NSDate date] timeIntervalSince1970]*1000)) longValue]; + self.creationDate = [[NSDate date] millisSince1970]; } return self; @@ -101,17 +194,72 @@ - (instancetype)init { -(NSDictionary *)dictionaryValueUsingConfig:(LDConfig*)config { + if ([self.kind isEqualToString:kEventModelKindFeatureSummary]) { + return [self featuresSummaryDictionaryValue]; + } + NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; - self.key ? [dictionary setObject:self.key forKey: kKeyKey] : nil; - self.kind ? [dictionary setObject:self.kind forKey: kKindKey] : nil; - self.creationDate ? [dictionary setObject:[NSNumber numberWithInteger: self.creationDate] forKey: kCreationDateKey] : nil; - self.data ? [dictionary setObject:self.data forKey: kDataKey] : nil; - self.value ? [dictionary setObject:self.value forKey: kFeatureKeyValueServerKey] : nil; - self.isDefault ? [dictionary setObject:self.isDefault forKey: kIsDefaultServerKey] : nil; - self.user ? [dictionary setObject:[self.user dictionaryValueWithFlagConfig:NO includePrivateAttributes:NO config:config] forKey: kUserKey] : nil; - - return dictionary; + if (self.key) { + dictionary[kEventModelKeyKey] = self.key; + } + if (self.kind) { + dictionary[kEventModelKeyKind] = self.kind; + } + if (self.creationDate) { + dictionary[kEventModelKeyCreationDate] = @(self.creationDate); + } + if (self.data) { + dictionary[kEventModelKeyData] = self.data; + } + if ([self.kind isEqualToString:kEventModelKindFeature] || [self.kind isEqualToString:kEventModelKindDebug]) { + if (self.flagConfigValue) { + [dictionary addEntriesFromDictionary:[self.flagConfigValue dictionaryValueUseFlagVersionForVersion:YES includeEventTrackingContext:NO]]; + } + dictionary[kEventModelKeyValue] = self.reportedValue ?: [NSNull null]; + } + if (self.defaultValue) { + dictionary[kEventModelKeyDefault] = self.defaultValue; + } + if (self.user) { + if (self.inlineUser || [self.kind isEqualToString:kEventModelKindIdentify]) { + dictionary[kEventModelKeyUser] = [self.user dictionaryValueWithFlagConfig:NO includePrivateAttributes:NO config:config]; + } else { + dictionary[kEventModelKeyUserKey] = self.user.key; + } + } + + return [NSDictionary dictionaryWithDictionary:dictionary]; +} + +-(NSDictionary*)featuresSummaryDictionaryValue; { + NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; + if (self.kind) { + dictionary[kEventModelKeyKind] = self.kind; + } + dictionary[kEventModelKeyStartDate] = @(self.startDateMillis); + dictionary[kEventModelKeyEndDate] = @(self.endDateMillis); + dictionary[kEventModelKeyFeatures] = self.flagRequestSummary; + + return [NSDictionary dictionaryWithDictionary:dictionary]; +} + +-(nonnull NSString*)description { + NSString *details; + + if ([self.kind isEqualToString:kEventModelKindFeature] || [self.kind isEqualToString:kEventModelKindDebug]) { + details = [NSString stringWithFormat:@"key: %@,\n\treportedFlagValue: %@,\n\tflagConfigValue: %@,\n\tdefaultValue: %@,\n\tuser: %@,\n\tinlineUser: %@, creationDate: %ld", + self.key, self.reportedValue ?: @"", self.flagConfigValue ?: @"", self.defaultValue ?: @"", self.user, self.inlineUser ? @"YES" : @"NO", (long)self.creationDate]; + } else if ([self.kind isEqualToString:kEventModelKindCustom]) { + details = [NSString stringWithFormat:@"key: %@, data: %@, user: %@, inlineUser: %@, creationDate: %ld", + self.key, self.data ?: @"", self.user, self.inlineUser ? @"YES" : @"NO", (long)self.creationDate]; + } else if ([self.kind isEqualToString:kEventModelKindIdentify]) { + details = [NSString stringWithFormat:@"key: %@, user: %@, inlineUser: %@, creationDate: %ld", self.key, self.user, self.inlineUser ? @"YES" : @"NO", (long)self.creationDate]; + } else if ([self.kind isEqualToString:kEventModelKindFeatureSummary]) { + details = [NSString stringWithFormat:@"startDateMillis: %ld, endDateMillis: %ld, flagRequestSummary: %@", (long)self.startDateMillis, (long)self.endDateMillis, self.flagRequestSummary]; + } + + return [NSString stringWithFormat:@"", self, self.kind, details]; } @end diff --git a/Darkly/LDFlagConfig/LDEventTrackingContext.h b/Darkly/LDFlagConfig/LDEventTrackingContext.h new file mode 100644 index 00000000..62574de7 --- /dev/null +++ b/Darkly/LDFlagConfig/LDEventTrackingContext.h @@ -0,0 +1,21 @@ +// +// LDEventTrackingContext.h +// Darkly +// +// Created by Mark Pokorny on 5/4/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import + +@interface LDEventTrackingContext : NSObject +@property (nonatomic, assign) BOOL trackEvents; +@property (nullable, nonatomic, strong) NSDate *debugEventsUntilDate; + ++(nullable instancetype)contextWithObject:(nullable id)object; +-(nullable instancetype)initWithObject:(nullable id)object; +-(nonnull NSDictionary*)dictionaryValue; +-(void)encodeWithCoder:(nonnull NSCoder*)aCoder; +-(nullable instancetype)initWithCoder:(nonnull NSCoder*)aDecoder; +-(nonnull NSString*)description; +@end diff --git a/Darkly/LDFlagConfig/LDEventTrackingContext.m b/Darkly/LDFlagConfig/LDEventTrackingContext.m new file mode 100644 index 00000000..3137bdd7 --- /dev/null +++ b/Darkly/LDFlagConfig/LDEventTrackingContext.m @@ -0,0 +1,62 @@ +// +// LDEventTrackingContext.m +// Darkly +// +// Created by Mark Pokorny on 5/4/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDEventTrackingContext.h" +#import "NSDate+ReferencedDate.h" +#import "NSNumber+LaunchDarkly.h" + +NSString * const kLDEventTrackingContextKeyTrackEvents = @"trackEvents"; +NSString * const kLDEventTrackingContextKeyDebugEventsUntilDate = @"debugEventsUntilDate"; + +@implementation LDEventTrackingContext ++(instancetype)contextWithObject:(id)object { + return [[LDEventTrackingContext alloc] initWithObject:object]; +} + +-(instancetype)initWithObject:(id)object { + if (!object || ![object isKindOfClass:[NSDictionary class]]) { return nil; } + NSDictionary *dictionary = object; + if (!dictionary[kLDEventTrackingContextKeyTrackEvents]) { return nil; } + if (![dictionary[kLDEventTrackingContextKeyTrackEvents] isKindOfClass:[NSNumber class]]) { return nil; } + if (!(self = [super init])) { return nil; } + + self.trackEvents = [dictionary[kLDEventTrackingContextKeyTrackEvents] boolValue]; + if (dictionary[kLDEventTrackingContextKeyDebugEventsUntilDate] && [dictionary[kLDEventTrackingContextKeyDebugEventsUntilDate] isKindOfClass:[NSNumber class]]) { + self.debugEventsUntilDate = [NSDate dateFromMillisSince1970:[dictionary[kLDEventTrackingContextKeyDebugEventsUntilDate] ldMillisecondValue]]; + } + + return self; +} + +-(NSDictionary*)dictionaryValue { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:2]; + dictionary[kLDEventTrackingContextKeyTrackEvents] = @(self.trackEvents); + if (self.debugEventsUntilDate) { + dictionary[kLDEventTrackingContextKeyDebugEventsUntilDate] = @([self.debugEventsUntilDate millisSince1970]); + } + return [NSDictionary dictionaryWithDictionary:dictionary]; +} + +-(void)encodeWithCoder:(NSCoder*)encoder { + [encoder encodeBool:self.trackEvents forKey:kLDEventTrackingContextKeyTrackEvents]; + [encoder encodeObject:self.debugEventsUntilDate forKey:kLDEventTrackingContextKeyDebugEventsUntilDate]; +} + +-(instancetype)initWithCoder:(NSCoder*)decoder { + if (!(self = [super init])) { return nil; } + + self.trackEvents = [decoder decodeBoolForKey:kLDEventTrackingContextKeyTrackEvents]; + self.debugEventsUntilDate = [decoder decodeObjectForKey:kLDEventTrackingContextKeyDebugEventsUntilDate]; + + return self; +} + +-(NSString*)description { + return [NSString stringWithFormat:@"", self, self.trackEvents ? @"YES" : @"NO", self.debugEventsUntilDate ?: @"nil"]; +} +@end diff --git a/Darkly/LDFlagConfigModel.h b/Darkly/LDFlagConfig/LDFlagConfigModel.h similarity index 64% rename from Darkly/LDFlagConfigModel.h rename to Darkly/LDFlagConfig/LDFlagConfigModel.h index 2a37f5b1..076cd985 100644 --- a/Darkly/LDFlagConfigModel.h +++ b/Darkly/LDFlagConfig/LDFlagConfigModel.h @@ -7,7 +7,8 @@ // #import -#import "LDFlagConfigValue.h" + +@class LDFlagConfigValue; @interface LDFlagConfigModel : NSObject @@ -17,13 +18,18 @@ -(nullable NSDictionary*)dictionaryValue; -(nullable NSDictionary*)dictionaryValueIncludeNulls:(BOOL)includeNulls; --(BOOL)doesConfigFlagExist:(nonnull NSString*)keyName; --(nullable id)configFlagValue:(nonnull NSString*)keyName; --(NSInteger)configFlagVersion:(nonnull NSString*)keyName; +-(BOOL)doesFlagConfigValueExistForFlagKey:(nonnull NSString*)flagKey; +-(nullable LDFlagConfigValue*)flagConfigValueForFlagKey:(nonnull NSString*)flagKey; +-(nullable id)flagValueForFlagKey:(nonnull NSString*)flagKey; +-(NSInteger)flagModelVersionForFlagKey:(nonnull NSString*)flagKey; -(void)addOrReplaceFromDictionary:(nullable NSDictionary*)patch; -(void)deleteFromDictionary:(nullable NSDictionary*)delete; -(BOOL)isEqualToConfig:(nullable LDFlagConfigModel*)otherConfig; -(BOOL)hasFeaturesEqualToDictionary:(nullable NSDictionary*)otherDictionary; + +-(void)updateEventTrackingContextFromConfig:(nullable LDFlagConfigModel*)otherConfig; + +-(nonnull NSString*)description; @end diff --git a/Darkly/LDFlagConfig/LDFlagConfigModel.m b/Darkly/LDFlagConfig/LDFlagConfigModel.m new file mode 100644 index 00000000..23f064a9 --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagConfigModel.m @@ -0,0 +1,170 @@ +// +// LDFlagConfigModel.m +// Darkly +// +// Created by Jeffrey Byrnes on 1/18/16. +// Copyright © 2016 Darkly. All rights reserved. +// + +#import "LDFlagConfigModel.h" +#import "LDFlagConfigValue.h" +#import "LDUtil.h" +#import "NSMutableDictionary+NullRemovable.h" + +NSString * const kFeaturesJsonDictionaryKey = @"featuresJsonDictionary"; +NSString * const kLDFlagConfigModelKeyKey = @"key"; + +@implementation LDFlagConfigModel + +- (void)encodeWithCoder:(NSCoder *)encoder { + [encoder encodeObject:self.featuresJsonDictionary forKey:kFeaturesJsonDictionaryKey]; +} + +- (id)initWithCoder:(NSCoder *)decoder { + if (!(self = [self init])) { return nil; } + + self.featuresJsonDictionary = [decoder decodeObjectForKey:kFeaturesJsonDictionaryKey]; + + return self; +} + +- (id)initWithDictionary:(NSDictionary *)dictionary { + if (!(self = [self init])) { return nil; } + + NSMutableDictionary *flagConfigValues = [NSMutableDictionary dictionaryWithCapacity:dictionary.count]; + + for (NSString *key in [dictionary.allKeys copy]) { + flagConfigValues[key] = [LDFlagConfigValue flagConfigValueWithObject:dictionary[key]]; + } + + self.featuresJsonDictionary = [NSDictionary dictionaryWithDictionary:[flagConfigValues copy]]; + + return self; +} + +-(instancetype)init { + if (!(self = [super init])) { return nil; } + + self.featuresJsonDictionary = @{}; + + return self; +} + +-(NSDictionary *)dictionaryValue { + return [self dictionaryValueIncludeNulls:YES]; +} + +-(NSDictionary*)dictionaryValueIncludeNulls:(BOOL)includeNulls { + if (!self.featuresJsonDictionary) return nil; + + NSMutableDictionary *flagConfigDictionaryValues = [NSMutableDictionary dictionaryWithCapacity:self.featuresJsonDictionary.count]; + for (NSString *key in [self.featuresJsonDictionary.allKeys copy]) { + if (!includeNulls && [self.featuresJsonDictionary[key].value isKindOfClass:[NSNull class]]) { continue; } + flagConfigDictionaryValues[key] = [self.featuresJsonDictionary[key] dictionaryValue]; + } + if (!includeNulls) { + [flagConfigDictionaryValues removeNullValues]; //Redact nulls out of values that are dictionaries + } + + return [NSDictionary dictionaryWithDictionary:[flagConfigDictionaryValues copy]]; +} + +-(BOOL)doesFlagConfigValueExistForFlagKey:(NSString*)flagKey { + if (!self.featuresJsonDictionary) { return NO; } + + return [[self.featuresJsonDictionary allKeys] containsObject: flagKey]; +} + +-(LDFlagConfigValue*)flagConfigValueForFlagKey:(NSString*)flagKey { + return self.featuresJsonDictionary[flagKey]; +} + +-(id)flagValueForFlagKey:(NSString*)flagKey { + LDFlagConfigValue *featureValue = self.featuresJsonDictionary[flagKey]; + if (!featureValue || [featureValue.value isKindOfClass:[NSNull class]]) { + return nil; + } + + return featureValue.value; +} + +-(NSInteger)flagModelVersionForFlagKey:(NSString*)flagKey { + LDFlagConfigValue *featureValue = self.featuresJsonDictionary[flagKey]; + if (!featureValue) { return kLDFlagConfigValueItemDoesNotExist; } + + return featureValue.modelVersion; +} + +-(void)addOrReplaceFromDictionary:(NSDictionary*)patch { + NSString *flagKey = patch[kLDFlagConfigModelKeyKey]; + LDFlagConfigValue *patchedFlagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:patch]; + if (![self shouldApplyPatch:patchedFlagConfigValue forFlagKey:flagKey]) { return; } + + NSMutableDictionary *updatedFlagConfig = [NSMutableDictionary dictionaryWithDictionary:self.featuresJsonDictionary]; + updatedFlagConfig[flagKey] = patchedFlagConfigValue; + self.featuresJsonDictionary = [updatedFlagConfig copy]; +} + +-(BOOL)shouldApplyPatch:(LDFlagConfigValue*)patchedFlagConfigValue forFlagKey:(NSString*)flagKey { + if (flagKey.length == 0) { return NO; } + if (!patchedFlagConfigValue) { return NO; } + if (patchedFlagConfigValue.modelVersion == kLDFlagConfigValueItemDoesNotExist) { return YES; } + LDFlagConfigValue *currentFlagConfigValue = [self flagConfigValueForFlagKey:flagKey]; + if (!currentFlagConfigValue) { return YES; } + if (currentFlagConfigValue.modelVersion == kLDFlagConfigValueItemDoesNotExist) { return YES; } + return patchedFlagConfigValue.modelVersion > [self flagModelVersionForFlagKey:flagKey]; +} + +-(void)deleteFromDictionary:(NSDictionary*)delete { + NSString *flagKey = delete[kLDFlagConfigModelKeyKey]; + if (flagKey.length == 0) { return; } + + id flagVersionObject = delete[kLDFlagConfigValueKeyVersion]; + if (!flagVersionObject || ![flagVersionObject isKindOfClass:[NSNumber class]]) { return; } + NSInteger flagVersion = [(NSNumber*)flagVersionObject integerValue]; + if ([self doesFlagConfigValueExistForFlagKey:flagKey] && flagVersion <= [self flagModelVersionForFlagKey:flagKey]) { return; } + + NSMutableDictionary *updatedFlagConfig = [NSMutableDictionary dictionaryWithDictionary:self.featuresJsonDictionary]; + updatedFlagConfig[flagKey] = nil; + + self.featuresJsonDictionary = [updatedFlagConfig copy]; +} + +-(BOOL)isEqualToConfig:(LDFlagConfigModel *)otherConfig { + return [self.featuresJsonDictionary isEqualToDictionary:otherConfig.featuresJsonDictionary]; +} + +-(BOOL)hasFeaturesEqualToDictionary:(NSDictionary*)otherDictionary { + NSArray *flagKeys = self.featuresJsonDictionary.allKeys; + if (flagKeys.count != otherDictionary.allKeys.count) { return NO; } + for (NSString *flagKey in flagKeys) { + LDFlagConfigValue *flagConfigValue = self.featuresJsonDictionary[flagKey]; + if (!otherDictionary[flagKey] || ![otherDictionary[flagKey] isKindOfClass:[NSDictionary class]]) { return NO; } + NSDictionary *otherFlagConfigValueDictionary = otherDictionary[flagKey]; + + if (![flagConfigValue hasPropertiesMatchingDictionary:otherFlagConfigValueDictionary]) { + return NO; + } + } + return YES; +} + +-(void)updateEventTrackingContextFromConfig:(LDFlagConfigModel*)otherConfig { + if (!otherConfig || otherConfig.featuresJsonDictionary.count == 0) { + DEBUG_LOGX(@"LDFlagConfigModel updateEventTrackingContext found missing or empty otherConfig. Aborting."); + return; + } + for (NSString *flagKey in [self.featuresJsonDictionary.allKeys copy]) { + LDFlagConfigValue *otherFlagConfigValue = otherConfig.featuresJsonDictionary[flagKey]; + if (!otherFlagConfigValue) { continue; } + LDFlagConfigValue *flagConfigValue = self.featuresJsonDictionary[flagKey]; + if (!flagConfigValue) { continue; } + flagConfigValue.eventTrackingContext = otherFlagConfigValue.eventTrackingContext; + } +} + +-(NSString*)description { + return [NSString stringWithFormat:@"", self, [self.featuresJsonDictionary description]]; +} + +@end diff --git a/Darkly/LDFlagConfig/LDFlagConfigTracker.h b/Darkly/LDFlagConfig/LDFlagConfigTracker.h new file mode 100644 index 00000000..e7c6445c --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagConfigTracker.h @@ -0,0 +1,28 @@ +// +// LDFlagConfigTracker.h +// Darkly +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "NSDate+ReferencedDate.h" + +@class LDFlagCounter; +@class LDFlagConfigValue; + +@interface LDFlagConfigTracker : NSObject +@property (nonatomic, assign, readonly) LDMillisecond startDateMillis; +@property (nonnull, nonatomic, strong, readonly) NSDictionary *flagCounters; + ++(nonnull instancetype)tracker; +-(nonnull instancetype)init; + +-(void)logRequestForFlagKey:(nonnull NSString*)flagKey + reportedFlagValue:(nonnull id)reportedFlagValue + flagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue + defaultValue:(nullable id)defaultValue; + +-(nonnull NSString*)description; +@end diff --git a/Darkly/LDFlagConfig/LDFlagConfigTracker.m b/Darkly/LDFlagConfig/LDFlagConfigTracker.m new file mode 100644 index 00000000..3ac42a0e --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagConfigTracker.m @@ -0,0 +1,50 @@ +// +// LDFlagConfigTracker.m +// Darkly +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDFlagConfigTracker.h" +#import "LDFlagCounter.h" +#import "LDFlagConfigValue.h" +#import "NSDate+ReferencedDate.h" + +@interface LDFlagConfigTracker() +@property (nonatomic, assign) LDMillisecond startDateMillis; +@property (nonatomic, strong) NSMutableDictionary *mutableFlagCounters; +@end + +@implementation LDFlagConfigTracker + ++(instancetype)tracker { + return [[LDFlagConfigTracker alloc] init]; +} + +-(instancetype)init { + if (!(self = [super init])) { return nil; } + + self.startDateMillis = [[NSDate date] millisSince1970]; + self.mutableFlagCounters = [NSMutableDictionary dictionary]; + + return self; +} + +-(NSDictionary*)flagCounters { + return [NSDictionary dictionaryWithDictionary:self.mutableFlagCounters]; +} + +-(void)logRequestForFlagKey:(NSString*)flagKey reportedFlagValue:(id)reportedFlagValue flagConfigValue:(LDFlagConfigValue*)flagConfigValue defaultValue:(id)defaultValue { + if (!self.mutableFlagCounters[flagKey]) { + self.mutableFlagCounters[flagKey] = [LDFlagCounter counterWithFlagKey:flagKey defaultValue:defaultValue]; + } + + [self.mutableFlagCounters[flagKey] logRequestWithFlagConfigValue:flagConfigValue reportedFlagValue:reportedFlagValue defaultValue:defaultValue]; +} + +-(NSString*)description { + return [NSString stringWithFormat:@"", self, [self.mutableFlagCounters description], (long)self.startDateMillis]; +} +@end diff --git a/Darkly/LDFlagConfig/LDFlagConfigValue.h b/Darkly/LDFlagConfig/LDFlagConfigValue.h new file mode 100644 index 00000000..0fe60453 --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagConfigValue.h @@ -0,0 +1,46 @@ +// +// LDFlagConfigValue.h +// Darkly +// +// Created by Mark Pokorny on 1/31/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import + +@class LDEventTrackingContext; + +extern NSString * _Nonnull const kLDFlagConfigValueKeyValue; +extern NSString * _Nonnull const kLDFlagConfigValueKeyVersion; +extern NSString * _Nonnull const kLDFlagConfigValueKeyFlagVersion; +extern NSString * _Nonnull const kLDFlagConfigValueKeyVariation; + +extern NSInteger const kLDFlagConfigValueItemDoesNotExist; + +@interface LDFlagConfigValue: NSObject +//Core Items +@property (nullable, nonatomic, strong) id value; +@property (nonatomic, assign) NSInteger modelVersion; +@property (nonatomic, assign) NSInteger variation; +//Optional Items, excluded from equality tests +@property (nullable, nonatomic, strong) NSNumber *flagVersion; +@property (nullable, nonatomic, strong) LDEventTrackingContext *eventTrackingContext; + ++(nullable instancetype)flagConfigValueWithObject:(nullable id)object; +-(nullable instancetype)initWithObject:(nullable id)object; + +-(void)encodeWithCoder:(nonnull NSCoder*)encoder; +-(nullable id)initWithCoder:(nonnull NSCoder*)decoder; +-(nonnull NSDictionary*)dictionaryValue; +-(nonnull NSDictionary*)dictionaryValueUseFlagVersionForVersion:(BOOL)useFlagVersion includeEventTrackingContext:(BOOL)includeEventTrackingContext; + +///Returns true when the core items of both flagConfigValues are the same. +///Ignores the optional items +-(BOOL)isEqualToFlagConfigValue:(nullable LDFlagConfigValue*)other; +-(BOOL)isEqual:(nullable id)object; +///Returns true when the dictionary has the same core item values as the flagConfigValue +///NOTE: Will return false if the dictionary was created with UseFlagVersionForVersion = YES and the flagVersion differs from the modelVersion +-(BOOL)hasPropertiesMatchingDictionary:(nullable NSDictionary*)dictionary; + +-(nonnull NSString*)description; +@end diff --git a/Darkly/LDFlagConfig/LDFlagConfigValue.m b/Darkly/LDFlagConfig/LDFlagConfigValue.m new file mode 100644 index 00000000..1458940b --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagConfigValue.m @@ -0,0 +1,155 @@ +// +// LDFlagValue.m +// Darkly +// +// Created by Mark Pokorny on 1/31/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDFlagConfigValue.h" +#import "LDEventTrackingContext.h" +#import "LDUtil.h" + +NSString * const kLDFlagConfigValueKeyValue = @"value"; +NSString * const kLDFlagConfigValueKeyVersion = @"version"; +NSString * const kLDFlagConfigValueKeyVariation = @"variation"; +NSString * const kLDFlagConfigValueKeyFlagVersion = @"flagVersion"; +NSString * const kLDFlagConfigValueKeyEventTrackingContext = @"eventTrackingContext"; + +NSInteger const kLDFlagConfigValueItemDoesNotExist = -1; + +@implementation LDFlagConfigValue + ++(instancetype)flagConfigValueWithObject:(id)object { + return [[LDFlagConfigValue alloc] initWithObject:object]; +} + +-(instancetype)initWithObject:(id)object { + if (!object) { return nil; } + if (![object isKindOfClass:[NSDictionary class]]) { return nil; } + if (!(self = [super init])) { return nil; } + NSDictionary *flagConfigValueDictionary = object; + self.value = flagConfigValueDictionary[kLDFlagConfigValueKeyValue] ?: [NSNull null]; + self.modelVersion = kLDFlagConfigValueItemDoesNotExist; + if (flagConfigValueDictionary[kLDFlagConfigValueKeyVersion] != nil && [flagConfigValueDictionary[kLDFlagConfigValueKeyVersion] isKindOfClass:[NSNumber class]]) { + self.modelVersion = [flagConfigValueDictionary[kLDFlagConfigValueKeyVersion] integerValue]; + } + self.flagVersion = nil; + if (flagConfigValueDictionary[kLDFlagConfigValueKeyFlagVersion] != nil && [flagConfigValueDictionary[kLDFlagConfigValueKeyFlagVersion] isKindOfClass:[NSNumber class]]) { + self.flagVersion = flagConfigValueDictionary[kLDFlagConfigValueKeyFlagVersion]; + } + self.variation = kLDFlagConfigValueItemDoesNotExist; + if (flagConfigValueDictionary[kLDFlagConfigValueKeyVariation] != nil && [flagConfigValueDictionary[kLDFlagConfigValueKeyVariation] isKindOfClass:[NSNumber class]]) { + self.variation = [flagConfigValueDictionary[kLDFlagConfigValueKeyVariation] integerValue]; + } + self.eventTrackingContext = [LDEventTrackingContext contextWithObject:object]; + + return self; +} + +-(void)encodeWithCoder:(NSCoder *)encoder { + [encoder encodeObject:self.value forKey:kLDFlagConfigValueKeyValue]; + [encoder encodeInteger:self.modelVersion forKey:kLDFlagConfigValueKeyVersion]; + [encoder encodeInteger:self.variation forKey:kLDFlagConfigValueKeyVariation]; + [encoder encodeObject:self.flagVersion forKey:kLDFlagConfigValueKeyFlagVersion]; + [encoder encodeObject:self.eventTrackingContext forKey:kLDFlagConfigValueKeyEventTrackingContext]; +} + +-(id)initWithCoder:(NSCoder *)decoder { + if (!(self = [super init])) { return nil; } + + self.value = [decoder decodeObjectForKey:kLDFlagConfigValueKeyValue]; + self.modelVersion = [decoder decodeIntegerForKey:kLDFlagConfigValueKeyVersion]; + self.variation = [decoder decodeIntegerForKey:kLDFlagConfigValueKeyVariation]; + self.flagVersion = [decoder decodeObjectForKey:kLDFlagConfigValueKeyFlagVersion]; + self.eventTrackingContext = [decoder decodeObjectForKey:kLDFlagConfigValueKeyEventTrackingContext]; + + return self; +} + +-(NSDictionary*)dictionaryValue { + return [self dictionaryValueUseFlagVersionForVersion:NO includeEventTrackingContext:YES]; +} + +-(NSDictionary*)dictionaryValueUseFlagVersionForVersion:(BOOL)useFlagVersion includeEventTrackingContext:(BOOL)includeEventTrackingContext { + NSMutableDictionary *dictionaryValue = [NSMutableDictionary dictionaryWithCapacity:5]; + dictionaryValue[kLDFlagConfigValueKeyValue] = self.value ?: [NSNull null]; + if (self.modelVersion != kLDFlagConfigValueItemDoesNotExist) { + dictionaryValue[kLDFlagConfigValueKeyVersion] = @(self.modelVersion); + } + if (self.flagVersion) { + NSString *versionKey = useFlagVersion ? kLDFlagConfigValueKeyVersion : kLDFlagConfigValueKeyFlagVersion; + dictionaryValue[versionKey] = self.flagVersion; + } + if (self.variation != kLDFlagConfigValueItemDoesNotExist) { + dictionaryValue[kLDFlagConfigValueKeyVariation] = @(self.variation); + } + if (self.eventTrackingContext && includeEventTrackingContext) { + [dictionaryValue addEntriesFromDictionary:[self.eventTrackingContext dictionaryValue]]; + } + return dictionaryValue; +} + +-(BOOL)isEqualToFlagConfigValue:(LDFlagConfigValue*)other { + if (!other) { return NO; } + if (self == other) { return YES; } + + return self.variation == other.variation && self.modelVersion == other.modelVersion; +} + +-(BOOL)isEqual:(id)object { + if (!object || ![object isKindOfClass:[LDFlagConfigValue class]]) { return NO; } + LDFlagConfigValue *other = object; + + return [self isEqualToFlagConfigValue:other]; +} + +-(NSUInteger)hash { + return labs(self.variation) ^ labs(self.modelVersion); +} + +-(BOOL)hasPropertiesMatchingDictionary:(NSDictionary*)dictionary { + NSMutableArray *mismatchedProperties = [NSMutableArray array]; + + if (self.value) { + if (![self.value isEqual:dictionary[kLDFlagConfigValueKeyValue]]) { + [mismatchedProperties addObject:kLDFlagConfigValueKeyValue]; + } + } else { + if (dictionary[kLDFlagConfigValueKeyValue] && ![dictionary[kLDFlagConfigValueKeyValue] isKindOfClass:[NSNull class]]) { + [mismatchedProperties addObject:kLDFlagConfigValueKeyValue]; + } + } + + if (self.variation == kLDFlagConfigValueItemDoesNotExist) { + if (dictionary[kLDFlagConfigValueKeyVariation]) { + [mismatchedProperties addObject:kLDFlagConfigValueKeyVariation]; + } + } else { + if (!dictionary[kLDFlagConfigValueKeyVariation] || self.variation != [dictionary[kLDFlagConfigValueKeyVariation] integerValue]) { + [mismatchedProperties addObject:kLDFlagConfigValueKeyVariation]; + } + } + + if (self.modelVersion == kLDFlagConfigValueItemDoesNotExist) { + if (dictionary[kLDFlagConfigValueKeyVersion]) { + [mismatchedProperties addObject:kLDFlagConfigValueKeyVersion]; + } + } else { + if (!dictionary[kLDFlagConfigValueKeyVersion] || self.modelVersion != [dictionary[kLDFlagConfigValueKeyVersion] integerValue]) { + [mismatchedProperties addObject:kLDFlagConfigValueKeyVersion]; + } + } + + if (mismatchedProperties.count > 0) { + DEBUG_LOG(@"[%@ %@] unequal fields %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [mismatchedProperties componentsJoinedByString:@", "]); + return NO; + } + return YES; +} + +-(NSString*)description { + return [NSString stringWithFormat:@"", self, [self.value description], (long)self.modelVersion, (long)self.variation, self.flagVersion ? [self.flagVersion description] : @"nil", self.eventTrackingContext ?: @"nil"]; +} +@end diff --git a/Darkly/LDFlagConfig/LDFlagCounter.h b/Darkly/LDFlagConfig/LDFlagCounter.h new file mode 100644 index 00000000..af2b42ce --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagCounter.h @@ -0,0 +1,27 @@ +// +// LDFlagCounter.h +// Darkly +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import + +@class LDFlagValueCounter; +@class LDFlagConfigValue; + +@interface LDFlagCounter : NSObject +@property (nonatomic, strong, nonnull, readonly) NSString *flagKey; +@property (nonatomic, strong, nonnull) id defaultValue; +@property (nonatomic, strong, nonnull, readonly) NSArray *valueCounters; + ++(nonnull instancetype)counterWithFlagKey:(nonnull NSString*)flagKey defaultValue:(nonnull id)defaultValue; +-(nonnull instancetype)initWithFlagKey:(nonnull NSString*)flagKey defaultValue:(nonnull id)defaultValue; + +-(void)logRequestWithFlagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue reportedFlagValue:(nonnull id)reportedFlagValue defaultValue:(nullable id)defaultValue; + +-(nonnull NSDictionary*)dictionaryValue; + +-(nonnull NSString*)description; +@end diff --git a/Darkly/LDFlagConfig/LDFlagCounter.m b/Darkly/LDFlagConfig/LDFlagCounter.m new file mode 100644 index 00000000..e92bd436 --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagCounter.m @@ -0,0 +1,89 @@ +// +// LDFlagCounter.m +// Darkly +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDFlagCounter.h" +#import "LDFlagValueCounter.h" +#import "LDFlagConfigValue.h" + +NSString * const kLDFlagCounterKeyDefaultValue = @"default"; +NSString * const kLDFlagCounterKeyCounters = @"counters"; + +@interface LDFlagCounter() +@property (nonatomic, strong) NSString *flagKey; +@property (nonatomic, strong) NSMutableArray *flagValueCounters; +@end + +@implementation LDFlagCounter ++(instancetype)counterWithFlagKey:(NSString*)flagKey defaultValue:(id)defaultValue { + return [[LDFlagCounter alloc] initWithFlagKey:flagKey defaultValue:defaultValue]; +} + +-(instancetype)initWithFlagKey:(NSString*)flagKey defaultValue:(id)defaultValue { + if (!(self = [super init])) { return nil; } + + self.flagKey = flagKey; + self.defaultValue = defaultValue; + self.flagValueCounters = [NSMutableArray array]; + + return self; +} + +-(NSArray*)valueCounters { + return self.flagValueCounters; +} + +-(void)logRequestWithFlagConfigValue:(LDFlagConfigValue*)flagConfigValue reportedFlagValue:(id)reportedFlagValue defaultValue:(id)defaultValue { + LDFlagValueCounter *selectedFlagValueCounter = [self valueCounterForFlagConfigValue:flagConfigValue]; + if (selectedFlagValueCounter) { + selectedFlagValueCounter.count += 1; + return; + } + + [self.flagValueCounters addObject:[LDFlagValueCounter counterWithFlagConfigValue:flagConfigValue reportedFlagValue:reportedFlagValue]]; +} + +-(LDFlagValueCounter*)valueCounterForFlagConfigValue:(LDFlagConfigValue*)flagConfigValue { + NSPredicate *variationPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + if (![evaluatedObject isKindOfClass:[LDFlagValueCounter class]]) { return NO; } + LDFlagValueCounter *evaluatedFlagValueCounter = evaluatedObject; + return (!flagConfigValue && !evaluatedFlagValueCounter.isKnown) || (flagConfigValue && [evaluatedFlagValueCounter.flagConfigValue isEqual:flagConfigValue]); + }]; + return [[self.flagValueCounters filteredArrayUsingPredicate:variationPredicate] firstObject]; +} + +-(NSDictionary*)dictionaryValue { + NSMutableArray *flagValueCounterDictionaries = [NSMutableArray arrayWithCapacity:self.flagValueCounters.count]; + for (LDFlagValueCounter *flagValueCounter in self.flagValueCounters) { + NSMutableDictionary *flagValueCounterDictionary = [NSMutableDictionary dictionaryWithDictionary:[flagValueCounter dictionaryValue]]; + //If the flagConfigValue.value is nil or null, the client will have served the default value + if (!flagValueCounter.flagConfigValue.value || [flagValueCounter.flagConfigValue.value isKindOfClass:[NSNull class]]) { + flagValueCounterDictionary[kLDFlagConfigValueKeyValue] = self.defaultValue ?: [NSNull null]; + } + [flagValueCounterDictionaries addObject:[flagValueCounterDictionary copy]]; + } + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + if (self.defaultValue) { + dictionary[kLDFlagCounterKeyDefaultValue] = self.defaultValue; + } else { + dictionary[kLDFlagCounterKeyDefaultValue] = [NSNull null]; + } + dictionary[kLDFlagCounterKeyCounters] = flagValueCounterDictionaries; + + return [NSDictionary dictionaryWithDictionary:dictionary]; +} + +-(NSString*)description { + return [NSString stringWithFormat:@"", + self, + self.flagKey, + [self.defaultValue description], + [self.flagValueCounters description]]; +} + +@end diff --git a/Darkly/LDFlagConfig/LDFlagValueCounter.h b/Darkly/LDFlagConfig/LDFlagValueCounter.h new file mode 100644 index 00000000..13eb2b53 --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagValueCounter.h @@ -0,0 +1,25 @@ +// +// LDFlagValueCounter.h +// Darkly +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import + +@class LDFlagConfigValue; + +@interface LDFlagValueCounter : NSObject +@property (nonnull, nonatomic, strong) id reportedFlagValue; +@property (nullable, nonatomic, strong, readonly) LDFlagConfigValue *flagConfigValue; +@property (nonatomic, assign, readonly, getter=isKnown) BOOL known; +@property (nonatomic, assign) NSInteger count; + ++(nonnull instancetype)counterWithFlagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue reportedFlagValue:(nonnull id)reportedFlagValue; +-(nonnull instancetype)initWithFlagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue reportedFlagValue:(nonnull id)reportedFlagValue; + +-(nonnull NSDictionary*)dictionaryValue; + +-(nonnull NSString*)description; +@end diff --git a/Darkly/LDFlagConfig/LDFlagValueCounter.m b/Darkly/LDFlagConfig/LDFlagValueCounter.m new file mode 100644 index 00000000..7d25ad0d --- /dev/null +++ b/Darkly/LDFlagConfig/LDFlagValueCounter.m @@ -0,0 +1,62 @@ +// +// LDFlagValueCounter.m +// Darkly +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDFlagValueCounter.h" +#import "LDFlagConfigValue.h" + +NSString * const kLDFlagValueCounterKeyFlagConfigValue = @"flagConfigValue"; +NSString * const kLDFlagValueCounterKeyValue = @"value"; +NSString * const kLDFlagValueCounterKeyVersion = @"version"; +NSString * const kLDFlagValueCounterKeyVariation = @"variation"; +NSString * const kLDFlagValueCounterKeyCount = @"count"; +NSString * const kLDFlagValueCounterKeyUnknown = @"unknown"; + +@interface LDFlagValueCounter() +@property (nullable, nonatomic, strong) LDFlagConfigValue *flagConfigValue; +@property (nonatomic, assign, getter=isKnown) BOOL known; +@end + +@implementation LDFlagValueCounter ++(instancetype)counterWithFlagConfigValue:(LDFlagConfigValue*)flagConfigValue reportedFlagValue:(id)reportedFlagValue { + return [[LDFlagValueCounter alloc] initWithFlagConfigValue:flagConfigValue reportedFlagValue:reportedFlagValue]; +} + +-(instancetype)initWithFlagConfigValue:(LDFlagConfigValue*)flagConfigValue reportedFlagValue:(id)reportedFlagValue { + if (!(self = [super init])) { return nil; } + + self.flagConfigValue = flagConfigValue; + self.reportedFlagValue = reportedFlagValue; + self.known = self.flagConfigValue != nil; + self.count = 1; + + return self; +} + +-(NSDictionary*)dictionaryValue { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3]; + + if (self.known) { + if (self.flagConfigValue) { + [dictionary addEntriesFromDictionary:[self.flagConfigValue dictionaryValueUseFlagVersionForVersion:YES includeEventTrackingContext:NO]]; + } + } else { + dictionary[kLDFlagValueCounterKeyUnknown] = @(YES); + } + dictionary[kLDFlagValueCounterKeyValue] = self.reportedFlagValue ?: [NSNull null]; + dictionary[kLDFlagValueCounterKeyCount] = @(self.count); + + return [NSDictionary dictionaryWithDictionary:dictionary]; +} + +-(NSString*)description { + return [NSString stringWithFormat:@"", + self, [self.reportedFlagValue description], [self.flagConfigValue description], (long)self.count, self.known ? @"YES" : @"NO"]; +} + +@end diff --git a/Darkly/LDFlagConfigModel.m b/Darkly/LDFlagConfigModel.m deleted file mode 100644 index 0d476cf9..00000000 --- a/Darkly/LDFlagConfigModel.m +++ /dev/null @@ -1,125 +0,0 @@ -// -// LDFlagConfigModel.m -// Darkly -// -// Created by Jeffrey Byrnes on 1/18/16. -// Copyright © 2016 Darkly. All rights reserved. -// - -#import "LDFlagConfigModel.h" -#import "LDUtil.h" -#import "NSMutableDictionary+NullRemovable.h" - -NSString * const kFeaturesJsonDictionaryKey = @"featuresJsonDictionary"; -NSString * const kLDFlagConfigJsonDictionaryKeyKey = @"key"; - -extern NSString * const kLDFlagConfigJsonDictionaryKeyValue; -extern NSString * const kLDFlagConfigJsonDictionaryKeyVersion; -extern const NSInteger kLDFlagConfigVersionDoesNotExist; - -@implementation LDFlagConfigModel - -- (void)encodeWithCoder:(NSCoder *)encoder { - [encoder encodeObject:self.featuresJsonDictionary forKey:kFeaturesJsonDictionaryKey]; -} - -- (id)initWithCoder:(NSCoder *)decoder { - if (!(self = [super init])) { return nil; } - _featuresJsonDictionary = [decoder decodeObjectForKey:kFeaturesJsonDictionaryKey]; - return self; -} - -- (id)initWithDictionary:(NSDictionary *)dictionary { - if (!(self = [super init])) { return nil; } - - NSMutableDictionary *flagConfigValues = [NSMutableDictionary dictionaryWithCapacity:dictionary.count]; - - for (NSString *key in [dictionary.allKeys copy]) { - flagConfigValues[key] = [LDFlagConfigValue flagConfigValueWithObject:dictionary[key]]; - } - - _featuresJsonDictionary = [NSDictionary dictionaryWithDictionary:[flagConfigValues copy]]; - - return self; -} - --(NSDictionary *)dictionaryValue { - return [self dictionaryValueIncludeNulls:YES]; -} - --(NSDictionary*)dictionaryValueIncludeNulls:(BOOL)includeNulls { - if (!self.featuresJsonDictionary) return nil; - - NSMutableDictionary *flagConfigDictionaryValues = [NSMutableDictionary dictionaryWithCapacity:self.featuresJsonDictionary.count]; - for (NSString *key in [self.featuresJsonDictionary.allKeys copy]) { - if (!includeNulls && [self.featuresJsonDictionary[key].value isKindOfClass:[NSNull class]]) { continue; } - flagConfigDictionaryValues[key] = [self.featuresJsonDictionary[key] dictionaryValue]; - } - if (!includeNulls) { - [flagConfigDictionaryValues removeNullValues]; //Redact nulls out of values that are dictionaries - } - - return [NSDictionary dictionaryWithDictionary:[flagConfigDictionaryValues copy]]; -} - --(id)configFlagValue:(NSString*)keyName { - LDFlagConfigValue *featureValue = self.featuresJsonDictionary[keyName]; - if (!featureValue || [featureValue.value isKindOfClass:[NSNull class]]) { return nil; } - - return featureValue.value; -} - --(NSInteger)configFlagVersion:(NSString*)keyName { - LDFlagConfigValue *featureValue = self.featuresJsonDictionary[keyName]; - if (!featureValue) { return kLDFlagConfigVersionDoesNotExist; } - - return featureValue.version; -} - --(BOOL)doesConfigFlagExist:(NSString*)keyName { - if (!self.featuresJsonDictionary) { return NO; } - - return [[self.featuresJsonDictionary allKeys] containsObject: keyName]; -} - --(void)addOrReplaceFromDictionary:(NSDictionary*)patch { - NSString *flagKey = patch[kLDFlagConfigJsonDictionaryKeyKey]; - if (flagKey.length == 0) { return; } - - id flagValue = patch[kLDFlagConfigJsonDictionaryKeyValue]; - if (!flagValue) { return; } - - id flagVersionObject = patch[kLDFlagConfigJsonDictionaryKeyVersion]; - if (!flagVersionObject || ![flagVersionObject isKindOfClass:[NSNumber class]]) { return; } - NSInteger flagVersion = [(NSNumber*)flagVersionObject integerValue]; - if ([self doesConfigFlagExist:flagKey] && flagVersion <= [self configFlagVersion:flagKey]) { return; } - - NSMutableDictionary *updatedFlagConfig = [NSMutableDictionary dictionaryWithDictionary:self.featuresJsonDictionary]; - updatedFlagConfig[flagKey] = [LDFlagConfigValue flagConfigValueWithObject:@{kLDFlagConfigJsonDictionaryKeyValue:flagValue, kLDFlagConfigJsonDictionaryKeyVersion:@(flagVersion)}]; - self.featuresJsonDictionary = [updatedFlagConfig copy]; -} - --(void)deleteFromDictionary:(nullable NSDictionary*)delete { - NSString *flagKey = delete[kLDFlagConfigJsonDictionaryKeyKey]; - if (flagKey.length == 0) { return; } - - id flagVersionObject = delete[kLDFlagConfigJsonDictionaryKeyVersion]; - if (!flagVersionObject || ![flagVersionObject isKindOfClass:[NSNumber class]]) { return; } - NSInteger flagVersion = [(NSNumber*)flagVersionObject integerValue]; - if ([self doesConfigFlagExist:flagKey] && flagVersion <= [self configFlagVersion:flagKey]) { return; } - - NSMutableDictionary *updatedFlagConfig = [NSMutableDictionary dictionaryWithDictionary:self.featuresJsonDictionary]; - updatedFlagConfig[flagKey] = nil; - - self.featuresJsonDictionary = [updatedFlagConfig copy]; -} - --(BOOL)isEqualToConfig:(LDFlagConfigModel *)otherConfig { - return [self.featuresJsonDictionary isEqualToDictionary:otherConfig.featuresJsonDictionary]; -} - --(BOOL)hasFeaturesEqualToDictionary:(NSDictionary*)otherDictionary { - return [[self dictionaryValue] isEqualToDictionary:otherDictionary]; -} - -@end diff --git a/Darkly/LDFlagConfigValue.h b/Darkly/LDFlagConfigValue.h deleted file mode 100644 index 3f5ca4ef..00000000 --- a/Darkly/LDFlagConfigValue.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// LDFlagConfigValue.h -// Darkly -// -// Created by Mark Pokorny on 1/31/18. +JMJ -// Copyright © 2018 LaunchDarkly. All rights reserved. -// - -#import - -extern NSInteger const kLDFlagConfigVersionDoesNotExist; - -@interface LDFlagConfigValue: NSObject -@property (nonatomic, strong) id _Nullable value; -@property (nonatomic, assign) NSInteger version; - -+(nullable instancetype)flagConfigValueWithObject:(nullable id)object; --(nullable instancetype)initWithObject:(nullable id)object; - --(void)encodeWithCoder:(nonnull NSCoder*)encoder; --(nullable id)initWithCoder:(nonnull NSCoder*)decoder; --(nonnull NSDictionary*)dictionaryValue; - --(BOOL)isEqual:(nullable id)object; -@end diff --git a/Darkly/LDFlagConfigValue.m b/Darkly/LDFlagConfigValue.m deleted file mode 100644 index 07f7d8b6..00000000 --- a/Darkly/LDFlagConfigValue.m +++ /dev/null @@ -1,62 +0,0 @@ -// -// LDFlagValue.m -// Darkly -// -// Created by Mark Pokorny on 1/31/18. +JMJ -// Copyright © 2018 LaunchDarkly. All rights reserved. -// - -#import "LDFlagConfigValue.h" -#import "NSObject+LDFlagConfigValue.h" - -NSString * const kLDFlagConfigJsonDictionaryKeyValue = @"value"; -NSString * const kLDFlagConfigJsonDictionaryKeyVersion = @"version"; - -NSInteger const kLDFlagConfigVersionDoesNotExist = -1; - -@implementation LDFlagConfigValue - -+(instancetype)flagConfigValueWithObject:(id)object { - return [[LDFlagConfigValue alloc] initWithObject:object]; -} - --(instancetype)initWithObject:(id)object { - if (!object) { return nil; } - if (!(self = [super init])) { return nil; } - if ([object isValueAndVersionDictionary]) { - NSDictionary *valueAndVersionDictionary = object; - _value = valueAndVersionDictionary[kLDFlagConfigJsonDictionaryKeyValue]; - _version = [(NSNumber*)valueAndVersionDictionary[kLDFlagConfigJsonDictionaryKeyVersion] integerValue]; - } else { - _value = object; - _version = kLDFlagConfigVersionDoesNotExist; - } - - return self; -} - --(void)encodeWithCoder:(NSCoder *)encoder { - [encoder encodeObject:self.value forKey:kLDFlagConfigJsonDictionaryKeyValue]; - [encoder encodeInteger:self.version forKey:kLDFlagConfigJsonDictionaryKeyVersion]; -} - --(id)initWithCoder:(NSCoder *)decoder { - if (!(self = [super init])) { return nil; } - - _value = [decoder decodeObjectForKey:kLDFlagConfigJsonDictionaryKeyValue]; - _version = [decoder decodeIntegerForKey:kLDFlagConfigJsonDictionaryKeyVersion]; - - return self; -} - --(NSDictionary*)dictionaryValue { - return @{kLDFlagConfigJsonDictionaryKeyValue: self.value, kLDFlagConfigJsonDictionaryKeyVersion: @(self.version)}; -} - --(BOOL)isEqual:(id)object { - if (!object || ![object isKindOfClass:[LDFlagConfigValue class]]) { return NO; } - LDFlagConfigValue *other = object; - - return [self.value isEqual:other.value] && self.version == other.version; -} -@end diff --git a/Darkly/LDRequestManager.h b/Darkly/LDRequestManager.h index 164af52d..1bd03dd3 100644 --- a/Darkly/LDRequestManager.h +++ b/Darkly/LDRequestManager.h @@ -6,24 +6,25 @@ @protocol RequestManagerDelegate -- (void)processedEvents:(BOOL)success jsonEventArray:(NSArray *)jsonEventArray; -- (void)processedConfig:(BOOL)success jsonConfigDictionary:(NSDictionary *)jsonConfigDictionary; +-(void)processedEvents:(BOOL)success jsonEventArray:(nonnull NSArray*)jsonEventArray responseDate:(nullable NSDate*)responseDate; +-(void)processedConfig:(BOOL)success jsonConfigDictionary:(nonnull NSDictionary*)jsonConfigDictionary; @end +extern NSString * _Nonnull const kHeaderMobileKey; + @interface LDRequestManager : NSObject -extern NSString * const kHeaderMobileKey; -@property (nonatomic) NSString* mobileKey; -@property (nonatomic) NSString* baseUrl; -@property (nonatomic) NSString* eventsUrl; -@property (nonatomic) NSTimeInterval connectionTimeout; -@property (nonatomic, weak) id delegate; +@property (nonnull, nonatomic, copy) NSString* mobileKey; +@property (nonnull, nonatomic, copy) NSString* baseUrl; +@property (nonnull, nonatomic, copy) NSString* eventsUrl; +@property (nonatomic, assign) NSTimeInterval connectionTimeout; +@property (nullable, nonatomic, weak) id delegate; -+(LDRequestManager *)sharedInstance; ++(nonnull LDRequestManager*)sharedInstance; --(void)performFeatureFlagRequest:(LDUserModel *)user; +-(void)performFeatureFlagRequest:(nullable LDUserModel*)user; --(void)performEventRequest:(NSArray *)eventDictionaries; +-(void)performEventRequest:(nullable NSArray*)eventDictionaries; @end diff --git a/Darkly/LDRequestManager.m b/Darkly/LDRequestManager.m index 74577e8a..a8e99c4b 100644 --- a/Darkly/LDRequestManager.m +++ b/Darkly/LDRequestManager.m @@ -6,16 +6,20 @@ #import "LDUtil.h" #import "LDClientManager.h" #import "LDConfig.h" -#import "NSURLResponse+Unauthorized.h" +#import "NSURLResponse+LaunchDarkly.h" #import "NSDictionary+JSON.h" +#import "NSHTTPURLResponse+LaunchDarkly.h" -static NSString * const kFeatureFlagGetUrl = @"/msdk/eval/users/"; -static NSString * const kFeatureFlagReportUrl = @"/msdk/eval/user"; +static NSString * const kFeatureFlagGetUrl = @"/msdk/evalx/users/"; +static NSString * const kFeatureFlagReportUrl = @"/msdk/evalx/user"; static NSString * const kEventUrl = @"/mobile/events/bulk"; NSString * const kHeaderMobileKey = @"api_key "; static NSString * const kConfigRequestCompletedNotification = @"config_request_completed_notification"; static NSString * const kEventRequestCompletedNotification = @"event_request_completed_notification"; +NSString * const kEventHeaderLaunchDarklyEventSchema = @"X-LaunchDarkly-Event-Schema"; +NSString * const kEventSchema = @"3"; + @implementation LDRequestManager @synthesize mobileKey, baseUrl, eventsUrl, connectionTimeout, delegate; @@ -182,7 +186,7 @@ -(void)performEventRequest:(NSArray *)eventDictionaries { dispatch_semaphore_signal(semaphore); dispatch_async(dispatch_get_main_queue(), ^{ BOOL processedEvents = !error; - [self.delegate processedEvents:processedEvents jsonEventArray:eventDictionaries]; + [self.delegate processedEvents:processedEvents jsonEventArray:eventDictionaries responseDate:[response headerDate]]; }); }]; @@ -248,6 +252,7 @@ -(void)addEventRequestHeaders: (NSMutableURLRequest *)request { NSString *authKey = [kHeaderMobileKey stringByAppendingString:mobileKey]; [request addValue:authKey forHTTPHeaderField:@"Authorization"]; + [request addValue:kEventSchema forHTTPHeaderField:kEventHeaderLaunchDarklyEventSchema]; [request addValue:[@"iOS/" stringByAppendingString:kClientVersion] forHTTPHeaderField:@"User-Agent"]; [request addValue: @"application/json" forHTTPHeaderField:@"Content-Type"]; [request addValue:@"application/json" forHTTPHeaderField:@"Accept"]; diff --git a/Darkly/LDUserModel.h b/Darkly/LDUserModel.h index da5719c8..80007aba 100644 --- a/Darkly/LDUserModel.h +++ b/Darkly/LDUserModel.h @@ -10,6 +10,7 @@ #import "LDConfig.h" @class LDFlagConfigModel; +@class LDFlagConfigTracker; extern NSString * __nonnull const kUserAttributeIp; extern NSString * __nonnull const kUserAttributeCountry; @@ -31,7 +32,8 @@ extern NSString * __nonnull const kUserAttributeCustom; @property (nullable, nonatomic, strong) NSString *avatar; @property (nullable, nonatomic, strong) NSDictionary *custom; @property (nullable, nonatomic, strong) NSDate *updatedAt; -@property (nullable, nonatomic, strong) LDFlagConfigModel *config; +@property (nullable, nonatomic, strong) LDFlagConfigModel *flagConfig; +@property (nullable, nonatomic, strong, readonly) LDFlagConfigTracker *flagConfigTracker; @property (nonatomic, strong, nullable) NSArray* privateAttributes; @property (nonatomic, assign) BOOL anonymous; @@ -42,9 +44,8 @@ extern NSString * __nonnull const kUserAttributeCustom; -(nonnull NSDictionary *)dictionaryValueWithPrivateAttributesAndFlagConfig:(BOOL)includeFlags; -(nonnull NSDictionary *)dictionaryValueWithFlagConfig:(BOOL)includeFlags includePrivateAttributes:(BOOL)includePrivate config:(nullable LDConfig*)config; --(NSObject * __nonnull) flagValue: ( NSString * __nonnull )keyName; --(BOOL) doesFlagExist: (nonnull NSString *)keyName; - +(NSArray * __nonnull) allUserAttributes; +-(void)resetTracker; + @end diff --git a/Darkly/LDUserModel.m b/Darkly/LDUserModel.m index 64593b54..b5b33153 100644 --- a/Darkly/LDUserModel.m +++ b/Darkly/LDUserModel.m @@ -8,6 +8,7 @@ #import "LDUserModel.h" #import "LDFlagConfigModel.h" +#import "LDFlagConfigTracker.h" #import "LDUtil.h" #import "NSDateFormatter+LDUserModel.h" @@ -27,6 +28,9 @@ NSString * const kUserAttributeOs = @"os"; NSString * const kUserAttributePrivateAttributes = @"privateAttrs"; +@interface LDUserModel() +@property (nullable, nonatomic, strong) LDFlagConfigTracker *flagConfigTracker; +@end @implementation LDUserModel @@ -64,8 +68,8 @@ -(NSDictionary *)dictionaryValueWithFlagConfig:(BOOL)includeFlags includePrivate dictionary[kUserAttributePrivateAttributes] = [redactedPrivateAttributes allObjects]; } - if (includeFlags && self.config.featuresJsonDictionary) { - dictionary[kUserAttributeConfig] = [self.config dictionaryValueIncludeNulls:NO]; + if (includeFlags && self.flagConfig.featuresJsonDictionary) { + dictionary[kUserAttributeConfig] = [self.flagConfig dictionaryValueIncludeNulls:NO]; } return [dictionary copy]; @@ -113,7 +117,7 @@ - (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:self.avatar forKey:kUserAttributeAvatar]; [encoder encodeObject:self.custom forKey:kUserAttributeCustom]; [encoder encodeObject:self.updatedAt forKey:kUserAttributeUpdatedAt]; - [encoder encodeObject:self.config forKey:kUserAttributeConfig]; + [encoder encodeObject:self.flagConfig forKey:kUserAttributeConfig]; [encoder encodeBool:self.anonymous forKey:kUserAttributeAnonymous]; [encoder encodeObject:self.device forKey:kUserAttributeDevice]; [encoder encodeObject:self.os forKey:kUserAttributeOs]; @@ -133,11 +137,12 @@ - (id)initWithCoder:(NSCoder *)decoder { self.avatar = [decoder decodeObjectForKey:kUserAttributeAvatar]; self.custom = [decoder decodeObjectForKey:kUserAttributeCustom]; self.updatedAt = [decoder decodeObjectForKey:kUserAttributeUpdatedAt]; - self.config = [decoder decodeObjectForKey:kUserAttributeConfig]; + self.flagConfig = [decoder decodeObjectForKey:kUserAttributeConfig]; self.anonymous = [decoder decodeBoolForKey:kUserAttributeAnonymous]; self.device = [decoder decodeObjectForKey:kUserAttributeDevice]; self.os = [decoder decodeObjectForKey:kUserAttributeOs]; self.privateAttributes = [decoder decodeObjectForKey:kUserAttributePrivateAttributes]; + self.flagConfigTracker = [LDFlagConfigTracker tracker]; } return self; } @@ -161,7 +166,7 @@ - (id)initWithDictionary:(NSDictionary *)dictionary { } self.anonymous = [[dictionary objectForKey: kUserAttributeAnonymous] boolValue]; self.updatedAt = [[NSDateFormatter userDateFormatter] dateFromString:[dictionary objectForKey:kUserAttributeUpdatedAt]]; - self.config = [[LDFlagConfigModel alloc] initWithDictionary:[dictionary objectForKey:kUserAttributeConfig]]; + self.flagConfig = [[LDFlagConfigModel alloc] initWithDictionary:[dictionary objectForKey:kUserAttributeConfig]]; self.privateAttributes = [dictionary objectForKey:kUserAttributePrivateAttributes]; } return self; @@ -169,36 +174,32 @@ - (id)initWithDictionary:(NSDictionary *)dictionary { - (instancetype)init { self = [super init]; - - if(self != nil) { - // Need to set device - NSString *device = [LDUtil getDeviceAsString]; - DEBUG_LOG(@"User building User with device: %@", device); - [self setDevice:device]; - - // Need to set os - NSString *systemVersion = [LDUtil getSystemVersionAsString]; - DEBUG_LOG(@"User building User with system version: %@", systemVersion); - [self setOs:systemVersion]; - - // Need to set updated Date - NSDate *currentDate = [NSDate date]; - DEBUG_LOG(@"User building User with updatedAt: %@", currentDate); - [self setUpdatedAt:currentDate]; - - self.custom = @{}; - } - - return self; -} + if(self == nil) { return nil; } + + // Need to set device + NSString *device = [LDUtil getDeviceAsString]; + DEBUG_LOG(@"User building User with device: %@", device); + [self setDevice:device]; + + // Need to set os + NSString *systemVersion = [LDUtil getSystemVersionAsString]; + DEBUG_LOG(@"User building User with system version: %@", systemVersion); + [self setOs:systemVersion]; --(NSObject *) flagValue: ( NSString * __nonnull )keyName { - return [self.config configFlagValue: keyName]; + // Need to set updated Date + NSDate *currentDate = [NSDate date]; + DEBUG_LOG(@"User building User with updatedAt: %@", currentDate); + [self setUpdatedAt:currentDate]; + + self.custom = @{}; + self.flagConfig = [[LDFlagConfigModel alloc] init]; + self.flagConfigTracker = [LDFlagConfigTracker tracker]; + + return self; } --(BOOL) doesFlagExist: ( NSString * __nonnull )keyName { - BOOL value = [self.config doesConfigFlagExist: keyName]; - return value; +-(void)resetTracker { + self.flagConfigTracker = [LDFlagConfigTracker tracker]; } -(NSString*) description { diff --git a/Darkly/NSDate+ReferencedDate.h b/Darkly/NSDate+ReferencedDate.h new file mode 100644 index 00000000..614af337 --- /dev/null +++ b/Darkly/NSDate+ReferencedDate.h @@ -0,0 +1,19 @@ +// +// NSDate+ReferencedDate.h +// Darkly +// +// Created by Mark Pokorny on 4/11/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import + +typedef long long LDMillisecond; + +@interface NSDate (ReferencedDate) ++(NSDate*)dateFromMillisSince1970:(LDMillisecond)millis; +-(LDMillisecond)millisSince1970; +-(BOOL)isWithinTimeInterval:(NSTimeInterval)timeInterval ofDate:(NSDate*)otherDate; +-(BOOL)isEarlierThan:(NSDate*)otherDate; +-(BOOL)isLaterThan:(NSDate*)otherDate; +@end diff --git a/Darkly/NSDate+ReferencedDate.m b/Darkly/NSDate+ReferencedDate.m new file mode 100644 index 00000000..1075fd2d --- /dev/null +++ b/Darkly/NSDate+ReferencedDate.m @@ -0,0 +1,36 @@ +// +// NSDate+ReferencedDate.m +// Darkly +// +// Created by Mark Pokorny on 4/11/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "NSDate+ReferencedDate.h" + +@implementation NSDate (ReferencedDate) ++(NSDate*)dateFromMillisSince1970:(LDMillisecond)millis { + return [NSDate dateWithTimeIntervalSince1970:millis / 1000]; +} + +-(LDMillisecond)millisSince1970 { + return (LDMillisecond)floor([self timeIntervalSince1970] * 1000); +} + +-(BOOL)isWithinTimeInterval:(NSTimeInterval)timeInterval ofDate:(NSDate*)otherDate { + if (!otherDate) { return NO; } + if (timeInterval < 0.0) { return NO; } + NSTimeInterval difference = fabs([self timeIntervalSinceDate:otherDate]); + return difference <= timeInterval; +} + +-(BOOL)isEarlierThan:(NSDate*)otherDate { + if (!otherDate) { return NO; } + return [self compare:otherDate] == NSOrderedAscending; +} + +-(BOOL)isLaterThan:(NSDate*)otherDate { + if (!otherDate) { return NO; } + return [self compare:otherDate] == NSOrderedDescending; +} +@end diff --git a/Darkly/NSDateFormatter+JsonHeader.h b/Darkly/NSDateFormatter+JsonHeader.h new file mode 100644 index 00000000..d2e58089 --- /dev/null +++ b/Darkly/NSDateFormatter+JsonHeader.h @@ -0,0 +1,13 @@ +// +// NSDateFormatter+JsonHeader.h +// Darkly +// +// Created by Mark Pokorny on 5/8/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import + +@interface NSDateFormatter(JsonHeader) ++(instancetype)jsonHeaderDateFormatter; +@end diff --git a/Darkly/NSDateFormatter+JsonHeader.m b/Darkly/NSDateFormatter+JsonHeader.m new file mode 100644 index 00000000..3da0187f --- /dev/null +++ b/Darkly/NSDateFormatter+JsonHeader.m @@ -0,0 +1,21 @@ +// +// NSDateFormatter+JsonHeader.m +// Darkly +// +// Created by Mark Pokorny on 5/8/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "NSDateFormatter+JsonHeader.h" + +@implementation NSDateFormatter(JsonHeader) ++(instancetype)jsonHeaderDateFormatter { + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.locale = [NSLocale localeWithLocaleIdentifier: @"en_US_POSIX"]; + formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; + formatter.dateFormat = @"EEE, dd MMM yyyy HH:mm:ss zzz"; //Mon, 07 May 2018 19:46:29 GMT + + return formatter; +} +@end diff --git a/Darkly/NSHTTPURLResponse+Unauthorized.h b/Darkly/NSHTTPURLResponse+LaunchDarkly.h similarity index 66% rename from Darkly/NSHTTPURLResponse+Unauthorized.h rename to Darkly/NSHTTPURLResponse+LaunchDarkly.h index 5adc321f..cb53db69 100644 --- a/Darkly/NSHTTPURLResponse+Unauthorized.h +++ b/Darkly/NSHTTPURLResponse+LaunchDarkly.h @@ -1,5 +1,5 @@ // -// NSHTTPURLResponse+Unauthorized.h +// NSHTTPURLResponse+LaunchDarkly.h // Darkly // // Created by Mark Pokorny on 10/16/17. +JMJ @@ -8,6 +8,7 @@ #import -@interface NSHTTPURLResponse(Unauthorized) +@interface NSHTTPURLResponse(LaunchDarkly) -(BOOL)isUnauthorizedHTTPResponse; +-(NSDate*)headerDate; @end diff --git a/Darkly/NSHTTPURLResponse+LaunchDarkly.m b/Darkly/NSHTTPURLResponse+LaunchDarkly.m new file mode 100644 index 00000000..3321096c --- /dev/null +++ b/Darkly/NSHTTPURLResponse+LaunchDarkly.m @@ -0,0 +1,24 @@ +// +// NSHTTPURLResponse+LaunchDarkly.m +// Darkly +// +// Created by Mark Pokorny on 10/16/17. +JMJ +// Copyright © 2017 LaunchDarkly. All rights reserved. +// + +#import "NSHTTPURLResponse+LaunchDarkly.h" +#import "DarklyConstants.h" +#import "NSDateFormatter+JsonHeader.h" + +NSString * const kHeaderKeyDate = @"Date"; + +@implementation NSHTTPURLResponse(LaunchDarkly) +-(BOOL)isUnauthorizedHTTPResponse { + return self.statusCode == kHTTPStatusCodeUnauthorized; +} +-(NSDate*)headerDate { + NSString* headerDateString = [self allHeaderFields][kHeaderKeyDate]; + if (headerDateString.length == 0) { return nil; } + return [[NSDateFormatter jsonHeaderDateFormatter] dateFromString:headerDateString]; +} +@end diff --git a/Darkly/NSHTTPURLResponse+Unauthorized.m b/Darkly/NSHTTPURLResponse+Unauthorized.m deleted file mode 100644 index 1791cb28..00000000 --- a/Darkly/NSHTTPURLResponse+Unauthorized.m +++ /dev/null @@ -1,16 +0,0 @@ -// -// NSHTTPURLResponse+Unauthorized.m -// Darkly -// -// Created by Mark Pokorny on 10/16/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import "NSHTTPURLResponse+Unauthorized.h" -#import "DarklyConstants.h" - -@implementation NSHTTPURLResponse(Unauthorized) --(BOOL)isUnauthorizedHTTPResponse { - return self.statusCode == kHTTPStatusCodeUnauthorized; -} -@end diff --git a/Darkly/NSNumber+LaunchDarkly.h b/Darkly/NSNumber+LaunchDarkly.h new file mode 100644 index 00000000..c5c24cd2 --- /dev/null +++ b/Darkly/NSNumber+LaunchDarkly.h @@ -0,0 +1,14 @@ +// +// NSNumber+LaunchDarkly.h +// Darkly +// +// Created by Mark Pokorny on 5/29/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "NSDate+ReferencedDate.h" + +@interface NSNumber(LaunchDarkly) +-(LDMillisecond)ldMillisecondValue; +@end diff --git a/Darkly/NSNumber+LaunchDarkly.m b/Darkly/NSNumber+LaunchDarkly.m new file mode 100644 index 00000000..56938470 --- /dev/null +++ b/Darkly/NSNumber+LaunchDarkly.m @@ -0,0 +1,15 @@ +// +// NSNumber+LaunchDarkly.m +// Darkly +// +// Created by Mark Pokorny on 5/29/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "NSNumber+LaunchDarkly.h" + +@implementation NSNumber(LaunchDarkly) +-(LDMillisecond)ldMillisecondValue { + return [self longLongValue]; +} +@end diff --git a/Darkly/NSObject+LDFlagConfigValue.h b/Darkly/NSObject+LDFlagConfigValue.h deleted file mode 100644 index f9baa755..00000000 --- a/Darkly/NSObject+LDFlagConfigValue.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NSObject+LDFlagConfigValue.h -// Darkly -// -// Created by Mark Pokorny on 1/31/18. +JMJ -// Copyright © 2018 LaunchDarkly. All rights reserved. -// - -#import - -@interface NSObject(LDFlagConfigValue) - --(BOOL)isValueAndVersionDictionary; - -@end diff --git a/Darkly/NSObject+LDFlagConfigValue.m b/Darkly/NSObject+LDFlagConfigValue.m deleted file mode 100644 index 1fe0f674..00000000 --- a/Darkly/NSObject+LDFlagConfigValue.m +++ /dev/null @@ -1,26 +0,0 @@ -// -// NSObject+LDFlagConfigValue.m -// Darkly -// -// Created by Mark Pokorny on 1/31/18. +JMJ -// Copyright © 2018 LaunchDarkly. All rights reserved. -// - -#import "NSObject+LDFlagConfigValue.h" - -extern NSString * const kLDFlagConfigJsonDictionaryKeyValue; -extern NSString * const kLDFlagConfigJsonDictionaryKeyVersion; - -@implementation NSObject(LDFlagConfigValue) - --(BOOL)isValueAndVersionDictionary { - if (![self isKindOfClass:[NSDictionary class]]) { return NO; } - NSDictionary *dictionaryObject = (NSDictionary*)self; - if (![dictionaryObject.allKeys containsObject:kLDFlagConfigJsonDictionaryKeyValue]) { return NO; } - if (![dictionaryObject.allKeys containsObject:kLDFlagConfigJsonDictionaryKeyVersion]) { return NO; } - if (![dictionaryObject[kLDFlagConfigJsonDictionaryKeyVersion] isKindOfClass:[NSNumber class]]) { return NO; } - - return YES; -} - -@end diff --git a/Darkly/NSURLResponse+Unauthorized.h b/Darkly/NSURLResponse+LaunchDarkly.h similarity index 79% rename from Darkly/NSURLResponse+Unauthorized.h rename to Darkly/NSURLResponse+LaunchDarkly.h index f037349a..77a14e4d 100644 --- a/Darkly/NSURLResponse+Unauthorized.h +++ b/Darkly/NSURLResponse+LaunchDarkly.h @@ -1,5 +1,5 @@ // -// NSURLResponse+Unauthorized.h +// NSURLResponse+LaunchDarkly.h // Darkly // // Created by Mark Pokorny on 10/11/17. +JMJ @@ -9,6 +9,7 @@ #import /// Determines if this response is an unauthorized HTTP response. By default NO, but can be overridden by subclasses that can detected unuathorized response. -@interface NSURLResponse(Unauthorized) +@interface NSURLResponse(LaunchDarkly) -(BOOL)isUnauthorizedHTTPResponse; +-(NSDate*)headerDate; @end diff --git a/Darkly/NSURLResponse+LaunchDarkly.m b/Darkly/NSURLResponse+LaunchDarkly.m new file mode 100644 index 00000000..528367d5 --- /dev/null +++ b/Darkly/NSURLResponse+LaunchDarkly.m @@ -0,0 +1,20 @@ +// +// NSURLResponse+LaunchDarkly.m +// Darkly +// +// Created by Mark Pokorny on 10/11/17. +JMJ +// Copyright © 2017 LaunchDarkly. All rights reserved. +// + +#import "NSURLResponse+LaunchDarkly.h" +#import "NSHTTPURLResponse+LaunchDarkly.h" + +@implementation NSURLResponse(LaunchDarkly) +-(BOOL)isUnauthorizedHTTPResponse { + return NO; +} + +-(NSDate*)headerDate { + return nil; +} +@end diff --git a/Darkly/NSURLResponse+Unauthorized.m b/Darkly/NSURLResponse+Unauthorized.m deleted file mode 100644 index 227ee680..00000000 --- a/Darkly/NSURLResponse+Unauthorized.m +++ /dev/null @@ -1,16 +0,0 @@ -// -// NSURLResponse+Unauthorized.m -// Darkly -// -// Created by Mark Pokorny on 10/11/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import "NSURLResponse+Unauthorized.h" -#import "NSHTTPURLResponse+Unauthorized.h" - -@implementation NSURLResponse(Unauthorized) --(BOOL)isUnauthorizedHTTPResponse { - return NO; -} -@end diff --git a/DarklyTests/Categories/LDEventModel+Testable.h b/DarklyTests/Categories/LDEventModel+Testable.h new file mode 100644 index 00000000..76423435 --- /dev/null +++ b/DarklyTests/Categories/LDEventModel+Testable.h @@ -0,0 +1,65 @@ +// +// LDEventModel+Testable.h +// DarklyTests +// +// Created by Mark Pokorny on 4/13/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDEventModel.h" +#import "NSDate+ReferencedDate.h" + +@class LDUserModel; +@class LDConfig; + +extern NSString * _Nonnull const kEventModelKindFeature; +extern NSString * _Nonnull const kEventModelKindCustom; +extern NSString * _Nonnull const kEventModelKindIdentify; +extern NSString * _Nonnull const kEventModelKindFeatureSummary; +extern NSString * _Nonnull const kEventModelKindDebug; + +extern NSString * _Nonnull const kEventModelKeyKey; +extern NSString * _Nonnull const kEventModelKeyKind; +extern NSString * _Nonnull const kEventModelKeyCreationDate; +extern NSString * _Nonnull const kEventModelKeyData; +extern NSString * _Nonnull const kEventModelKeyFlagConfigValue; +extern NSString * _Nonnull const kEventModelKeyValue; +extern NSString * _Nonnull const kEventModelKeyVersion; +extern NSString * _Nonnull const kEventModelKeyVariation; +extern NSString * _Nonnull const kEventModelKeyIsDefault; +extern NSString * _Nonnull const kEventModelKeyDefault; +extern NSString * _Nonnull const kEventModelKeyUser; +extern NSString * _Nonnull const kEventModelKeyUserKey; +extern NSString * _Nonnull const kEventModelKeyInlineUser; +extern NSString * _Nonnull const kEventModelKeyStartDate; +extern NSString * _Nonnull const kEventModelKeyEndDate; +extern NSString * _Nonnull const kEventModelKeyFeatures; + +extern NSString * _Nonnull const kFeatureEventKeyStub; +extern NSString * _Nonnull const kCustomEventKeyStub; +extern NSString * _Nonnull const kCustomEventCustomDataKeyStub; +extern NSString * _Nonnull const kCustomEventCustomDataValueStub; +extern const double featureEventValueStub; +extern const double featureEventDefaultValueStub; + +@interface LDEventModel(Testable) +@property (nonatomic, assign, readonly) BOOL isFlagRequestEventKind; +@property (nonatomic, assign, readonly) BOOL hasCommonFields; +@property (nonatomic, assign, readonly) BOOL alwaysInlinesUser; + ++(nonnull NSArray*)allEventKinds; ++(nonnull NSArray*)eventKindsWithCommonFields; ++(nonnull NSArray*)eventKindsForFlagRequests; ++(nonnull NSArray*)eventKindsThatAlwaysInlineUsers; ++(nonnull instancetype)stubEventWithKind:(nonnull NSString*)eventKind user:(nullable LDUserModel*)user config:(nullable LDConfig*)config; ++(nonnull NSArray*)stubEventDictionariesForUser:(nullable LDUserModel*)user config:(nullable LDConfig*)config; +-(BOOL)isEqual:(nullable id)object; +-(BOOL)hasPropertiesMatchingFlagKey:(nonnull NSString*)flagKey + eventKind:(nonnull NSString*)eventKind + flagConfigValue:(nullable LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(nullable id)defaultFlagValue + user:(nonnull LDUserModel*)user + inlineUser:(BOOL)inlineUser + creationDateMillis:(LDMillisecond)creationDateMillis; +@end diff --git a/DarklyTests/Categories/LDEventModel+Testable.m b/DarklyTests/Categories/LDEventModel+Testable.m new file mode 100644 index 00000000..29c3c14e --- /dev/null +++ b/DarklyTests/Categories/LDEventModel+Testable.m @@ -0,0 +1,216 @@ +// +// LDEventModel+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 4/13/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDEventModel+Testable.h" +#import "LDEventModel.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDUserModel+Testable.h" +#import "NSDate+Testable.h" +#import "LDFlagConfigTracker+Testable.h" +#import "LDEventTrackingContext.h" +#import "LDEventTrackingContext+Testable.h" +#import "NSDate+ReferencedDate.h" + +extern NSString * const kLDFlagKeyIsADouble; + +NSString * const kFeatureEventKeyStub = @"LDEventModel.featureEvent.key"; +NSString * const kCustomEventKeyStub = @"LDEventModel.customEvent.key"; +NSString * const kDebugEventKeyStub = @"LDEventModel.debugEvent.key"; +NSString * const kCustomEventCustomDataKeyStub = @"LDEventModel.customEventCustomData.key"; +NSString * const kCustomEventCustomDataValueStub = @"LDEventModel.customEventCustomData.value"; +const double featureEventValueStub = 3.14159; +const double featureEventDefaultValueStub = 2.71828; + +@implementation LDEventModel (Testable) ++(NSArray*)allEventKinds { + return @[kEventModelKindFeature, kEventModelKindCustom, kEventModelKindIdentify, kEventModelKindFeatureSummary, kEventModelKindDebug]; +} + ++(NSArray*)eventKindsWithCommonFields { + return @[kEventModelKindFeature, kEventModelKindCustom, kEventModelKindIdentify, kEventModelKindDebug]; +} + +-(BOOL)hasCommonFields { + return [[LDEventModel eventKindsWithCommonFields] containsObject:self.kind]; +} + ++(NSArray*)eventKindsForFlagRequests { + return @[kEventModelKindFeature, kEventModelKindDebug]; +} + +-(BOOL)isFlagRequestEventKind { + return [[LDEventModel eventKindsForFlagRequests] containsObject:self.kind]; +} + ++(NSArray*)eventKindsThatAlwaysInlineUsers { + return @[kEventModelKindIdentify, kEventModelKindDebug]; +} + +-(BOOL)alwaysInlinesUser { + return [[LDEventModel eventKindsThatAlwaysInlineUsers] containsObject:self.kind]; +} + ++(instancetype)stubEventWithKind:(NSString*)eventKind user:(LDUserModel*)user config:(LDConfig*)config { + if (!user) { + user = [LDUserModel stubWithKey:[[NSUUID UUID] UUIDString]]; + } + BOOL inlineUser = config ? config.inlineUserInEvents : false; + if ([eventKind isEqualToString:kEventModelKindFeature]) { + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"doubleConfigIsADouble-Pi" + flagKey:kLDFlagKeyIsADouble + eventTrackingContext:[LDEventTrackingContext stub]]; + return [LDEventModel featureEventWithFlagKey:kFeatureEventKeyStub + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(featureEventDefaultValueStub) + user:user + inlineUser:inlineUser]; + } + if ([eventKind isEqualToString:kEventModelKindCustom]) { + return [LDEventModel customEventWithKey:kCustomEventKeyStub + customData:@{kCustomEventCustomDataKeyStub: kCustomEventCustomDataValueStub} + userValue:user + inlineUser:inlineUser]; + } + if ([eventKind isEqualToString:kEventModelKindFeatureSummary]) { + return [LDEventModel summaryEventWithTracker:[LDFlagConfigTracker stubTracker]]; + } + if ([eventKind isEqualToString:kEventModelKindDebug]) { + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"doubleConfigIsADouble-Pi" + flagKey:kLDFlagKeyIsADouble + eventTrackingContext:[LDEventTrackingContext stub]]; + return [LDEventModel debugEventWithFlagKey:kDebugEventKeyStub + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(featureEventDefaultValueStub) + user:user]; + } + + return [LDEventModel identifyEventWithUser:user]; +} + ++(nonnull NSArray*)stubEventDictionariesForUser:(nullable LDUserModel*)user config:(nullable LDConfig*)config { + NSDictionary *featureEventDictionary = [[LDEventModel stubEventWithKind:kEventModelKindFeature user:user config:config] dictionaryValueUsingConfig:config]; + NSDictionary *customEventDictionary = [[LDEventModel stubEventWithKind:kEventModelKindCustom user:user config:config] dictionaryValueUsingConfig:config]; + NSDictionary *identifyEventDictionary = [[LDEventModel stubEventWithKind:kEventModelKindIdentify user:user config:config] dictionaryValueUsingConfig:config]; + NSDictionary *summaryEventDictionary = [[LDEventModel summaryEventWithTracker:[LDFlagConfigTracker stubTracker]] dictionaryValueUsingConfig:config]; + NSDictionary *debugEventDictionary = [[LDEventModel stubEventWithKind:kEventModelKindDebug user:user config:config] dictionaryValueUsingConfig:config]; + return @[featureEventDictionary, customEventDictionary, identifyEventDictionary, summaryEventDictionary, debugEventDictionary]; +} + +-(BOOL)isEqual:(id)object { + if (![object isKindOfClass:[LDEventModel class]]) { + NSLog(@"[%@ %@]: object is not class %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromClass([self class])); + return NO; + } + LDEventModel *otherEvent = object; + + NSMutableArray *mismatchedProperties = [NSMutableArray array]; + if (![self.kind isEqualToString:otherEvent.kind]) { + [mismatchedProperties addObject:kEventModelKeyKind]; + } + + if (self.hasCommonFields) { + if (![self.key isEqualToString:otherEvent.key]) { + [mismatchedProperties addObject:kEventModelKeyKey]; + } + if (self.inlineUser != otherEvent.inlineUser) { + [mismatchedProperties addObject:kEventModelKeyInlineUser]; + } + if (self.inlineUser) { + if (![self.user isEqual:otherEvent.user ignoringAttributes:@[kUserAttributeConfig]]) { + [mismatchedProperties addObject:kEventModelKeyUser]; + } + } else { + if (![self.user.key isEqualToString:otherEvent.user.key]) { + [mismatchedProperties addObject:kEventModelKeyUserKey]; + } + } + if (self.creationDate != otherEvent.creationDate) { + [mismatchedProperties addObject:kEventModelKeyCreationDate]; + } + } + + if (self.isFlagRequestEventKind) { + if (![self.flagConfigValue isEqual:otherEvent.flagConfigValue]) { + [mismatchedProperties addObject:kEventModelKeyFlagConfigValue]; + } + if (![self.defaultValue isEqual:otherEvent.defaultValue]) { + [mismatchedProperties addObject:kEventModelKeyDefault]; + } + } + + if ([self.kind isEqualToString:kEventModelKindCustom]) { + if (![self.data isEqual:otherEvent.data]) { + [mismatchedProperties addObject:kEventModelKeyData]; + } + } + + if ([self.kind isEqualToString:kEventModelKindFeatureSummary]) { + if (self.startDateMillis != otherEvent.startDateMillis) { + [mismatchedProperties addObject:kEventModelKeyStartDate]; + } + if (!Approximately(self.endDateMillis, otherEvent.endDateMillis, 10)) { + [mismatchedProperties addObject:kEventModelKeyEndDate]; + } + if (![self.flagRequestSummary isEqualToDictionary:otherEvent.flagRequestSummary]) { + [mismatchedProperties addObject:kEventModelKeyFeatures]; + } + } + + //identify events have only the fields common to all events + + if (mismatchedProperties.count > 0) { + NSLog(@"[%@ %@] unequal fields %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [mismatchedProperties componentsJoinedByString:@", "]); + return NO; + } + + return YES; +} + +//This is only built for Feature and Debug events...open for modification to handle the others when needed +-(BOOL)hasPropertiesMatchingFlagKey:(NSString*)flagKey + eventKind:(NSString*)eventKind + flagConfigValue:(LDFlagConfigValue*)flagConfigValue + defaultFlagValue:(id)defaultFlagValue + user:(LDUserModel*)user + inlineUser:(BOOL)inlineUser + creationDateMillis:(LDMillisecond)creationDateMillis { + NSMutableArray *mismatchedProperties = [NSMutableArray array]; + + if (![self.key isEqualToString:flagKey]) { + [mismatchedProperties addObject:kEventModelKeyKey]; + } + if (![self.kind isEqualToString:eventKind]) { + [mismatchedProperties addObject:kEventModelKeyKind]; + } + if (![self.flagConfigValue isEqual:flagConfigValue]) { + [mismatchedProperties addObject:kEventModelKeyValue]; + } + if (![self.defaultValue isEqual:defaultFlagValue]) { + [mismatchedProperties addObject:kEventModelKeyDefault]; + } + if (![self.user isEqual:user ignoringAttributes:@[]]) { + [mismatchedProperties addObject:kEventModelKeyUser]; + } + if (self.inlineUser != inlineUser) { + [mismatchedProperties addObject:kEventModelKeyInlineUser]; + } + if (!Approximately(self.creationDate, creationDateMillis, 10)) { + [mismatchedProperties addObject:kEventModelKeyCreationDate]; + } + + if (mismatchedProperties.count > 0) { + NSLog(@"[%@ %@] unequal fields %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [mismatchedProperties componentsJoinedByString:@", "]); + return NO; + } + return YES; +} + +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDEventTrackingContext+Testable.h b/DarklyTests/Categories/LDFlagConfig/LDEventTrackingContext+Testable.h new file mode 100644 index 00000000..60f52a60 --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDEventTrackingContext+Testable.h @@ -0,0 +1,21 @@ +// +// LDEventTrackingContext+Testable.h +// DarklyTests +// +// Created by Mark Pokorny on 5/4/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDEventTrackingContext.h" + +extern NSString * const kLDEventTrackingContextKeyTrackEvents; +extern NSString * const kLDEventTrackingContextKeyDebugEventsUntilDate; + +@interface LDEventTrackingContext(Testable) ++(instancetype)stub; ++(instancetype)contextWithTrackEvents:(BOOL)trackEvents debugEventsUntilDate:(NSDate*)debugEventsUntilDate; +-(instancetype)initWithTrackEvents:(BOOL)trackEvents debugEventsUntilDate:(NSDate*)debugEventsUntilDate; +-(BOOL)isEqualToContext:(LDEventTrackingContext*)otherContext; +-(BOOL)isEqual:(id)other; +-(BOOL)hasPropertiesMatchingDictionary:(NSDictionary*)dictionary; +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDEventTrackingContext+Testable.m b/DarklyTests/Categories/LDFlagConfig/LDEventTrackingContext+Testable.m new file mode 100644 index 00000000..f2257b8e --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDEventTrackingContext+Testable.m @@ -0,0 +1,77 @@ +// +// LDEventTrackingContext+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 5/4/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDEventTrackingContext+Testable.h" +#import "NSDate+ReferencedDate.h" +#import "NSNumber+LaunchDarkly.h" + +@implementation LDEventTrackingContext(Testable) ++(instancetype)stub { + return [LDEventTrackingContext contextWithTrackEvents:YES debugEventsUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]]; +} + ++(instancetype)contextWithTrackEvents:(BOOL)trackEvents debugEventsUntilDate:(NSDate*)debugEventsUntilDate { + return [[LDEventTrackingContext alloc] initWithTrackEvents:trackEvents debugEventsUntilDate:debugEventsUntilDate]; +} + +-(instancetype)initWithTrackEvents:(BOOL)trackEvents debugEventsUntilDate:(NSDate*)debugEventsUntilDate { + if (!(self = [super init])) { return nil; } + + self.trackEvents = trackEvents; + self.debugEventsUntilDate = debugEventsUntilDate; + + return self; +} + +-(BOOL)isEqualToContext:(LDEventTrackingContext*)otherContext { + return self.trackEvents == otherContext.trackEvents + && ((!self.debugEventsUntilDate && !otherContext.debugEventsUntilDate) || ([self.debugEventsUntilDate isWithinTimeInterval:1.0 ofDate:otherContext.debugEventsUntilDate])); +} + +-(BOOL)isEqual:(id)other { + if (!other) { return NO; } + if (![other isKindOfClass:[LDEventTrackingContext class]]) { return NO; } + if (self == other) { return YES; } + return [self isEqualToContext:other]; +} + +-(BOOL)hasPropertiesMatchingDictionary:(NSDictionary*)dictionary { + NSMutableArray *mismatchedProperties = [NSMutableArray array]; + + if (self.trackEvents) { + if (!dictionary[kLDEventTrackingContextKeyTrackEvents] || (dictionary[kLDEventTrackingContextKeyTrackEvents] && ![dictionary[kLDEventTrackingContextKeyTrackEvents] boolValue])) { + [mismatchedProperties addObject:kLDEventTrackingContextKeyTrackEvents]; + } + } else { + if (dictionary[kLDEventTrackingContextKeyTrackEvents] && [dictionary[kLDEventTrackingContextKeyTrackEvents] boolValue]) { + [mismatchedProperties addObject:kLDEventTrackingContextKeyTrackEvents]; + } + } + + if (self.debugEventsUntilDate) { + if (dictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]) { + NSDate *otherDebugUntil = [NSDate dateFromMillisSince1970:[dictionary[kLDEventTrackingContextKeyDebugEventsUntilDate] ldMillisecondValue]]; + if (![self.debugEventsUntilDate isWithinTimeInterval:1.0 ofDate:otherDebugUntil]) { + [mismatchedProperties addObject:kLDEventTrackingContextKeyDebugEventsUntilDate]; + } + } else { + [mismatchedProperties addObject:kLDEventTrackingContextKeyDebugEventsUntilDate]; + } + } else { + if (dictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]) { + [mismatchedProperties addObject:kLDEventTrackingContextKeyDebugEventsUntilDate]; + } + } + + if (mismatchedProperties.count > 0) { + NSLog(@"[%@ %@] unequal fields %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [mismatchedProperties componentsJoinedByString:@", "]); + return NO; + } + return YES; +} +@end diff --git a/DarklyTests/Categories/LDFlagConfigModel+Testable.h b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigModel+Testable.h similarity index 58% rename from DarklyTests/Categories/LDFlagConfigModel+Testable.h rename to DarklyTests/Categories/LDFlagConfig/LDFlagConfigModel+Testable.h index c161d74b..f4030478 100644 --- a/DarklyTests/Categories/LDFlagConfigModel+Testable.h +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigModel+Testable.h @@ -6,10 +6,16 @@ // Copyright © 2017 LaunchDarkly. All rights reserved. // -#import +#import "LDFlagConfigModel.h" + +@class LDEventTrackingContext; @interface LDFlagConfigModel(Testable) + +(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName; ++(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName omitKey:(NSString*)key; ++(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext; ++(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext omitKey:(NSString*)key; +(NSDictionary*)patchFromJsonFileNamed:(NSString *)fileName useVersion:(NSInteger)version; +(NSDictionary*)patchFromJsonFileNamed:(NSString *)fileName omitKey:(NSString*)key; +(NSDictionary*)deleteFromJsonFileNamed:(NSString *)fileName useVersion:(NSInteger)version; diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagConfigModel+Testable.m b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigModel+Testable.m new file mode 100644 index 00000000..5c25b730 --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigModel+Testable.m @@ -0,0 +1,83 @@ +// +// LDFlagConfigModel+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 10/19/17. +JMJ +// Copyright © 2017 LaunchDarkly. All rights reserved. +// + +#import "LDFlagConfigModel.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigModel+Testable.h" +#import "LDFlagConfigTracker+Testable.h" +#import "LDEventTrackingContext.h" +#import "LDEventTrackingContext+Testable.h" +#import "NSJSONSerialization+Testable.h" + +@implementation LDFlagConfigModel(Testable) + ++(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName { + return [LDFlagConfigModel flagConfigFromJsonFileNamed:fileName eventTrackingContext:nil omitKey:nil]; +} + ++(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName omitKey:(NSString*)omitKey { + return [LDFlagConfigModel flagConfigFromJsonFileNamed:fileName eventTrackingContext:nil omitKey:omitKey]; +} + ++(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext { + return [LDFlagConfigModel flagConfigFromJsonFileNamed:fileName eventTrackingContext:eventTrackingContext omitKey:nil]; +} + ++(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext omitKey:(NSString*)omitKey { + NSMutableDictionary *flagConfigModelDictionary = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization jsonObjectFromFileNamed:fileName]]; + if (eventTrackingContext) { + for (NSString *flagKey in flagConfigModelDictionary.allKeys) { + NSMutableDictionary *flagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:flagConfigModelDictionary[flagKey]]; + [flagConfigValueDictionary addEntriesFromDictionary:[eventTrackingContext dictionaryValue]]; + flagConfigModelDictionary[flagKey] = [flagConfigValueDictionary copy]; + } + } + + LDFlagConfigModel *flagConfigModel = [[LDFlagConfigModel alloc] initWithDictionary:flagConfigModelDictionary]; + + if (omitKey.length > 0) { + NSMutableDictionary *flagConfigValues = [NSMutableDictionary dictionaryWithDictionary:flagConfigModel.featuresJsonDictionary]; + [flagConfigValues removeObjectForKey:omitKey]; + for (NSString *flagKey in flagConfigValues.allKeys) { + LDFlagConfigValue *flagConfigValue = flagConfigValues[flagKey]; + if ([omitKey isEqualToString:kLDFlagConfigValueKeyValue]) { + flagConfigValue.value = nil; + } else if ([omitKey isEqualToString:kLDFlagConfigValueKeyVersion]) { + flagConfigValue.modelVersion = kLDFlagConfigValueItemDoesNotExist; + } else if ([omitKey isEqualToString:kLDFlagConfigValueKeyVariation]) { + flagConfigValue.variation = kLDFlagConfigValueItemDoesNotExist; + } + } + flagConfigModel.featuresJsonDictionary = [flagConfigValues copy]; + } + + return flagConfigModel; +} + ++(NSDictionary*)patchFromJsonFileNamed:(NSString *)fileName useVersion:(NSInteger)version { + NSMutableDictionary *patch = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization jsonObjectFromFileNamed:fileName]]; + patch[kLDFlagConfigValueKeyVersion] = @(version); + return patch; +} + ++(NSDictionary*)patchFromJsonFileNamed:(NSString *)fileName omitKey:(NSString*)key { + NSMutableDictionary *patch = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization jsonObjectFromFileNamed:fileName]]; + if (key.length > 0) { + [patch removeObjectForKey:key]; + } + return patch; +} + ++(NSDictionary*)deleteFromJsonFileNamed:(NSString *)fileName useVersion:(NSInteger)version { + return [LDFlagConfigModel patchFromJsonFileNamed:fileName useVersion:version]; +} + ++(NSDictionary*)deleteFromJsonFileNamed:(NSString *)fileName omitKey:(NSString*)key { + return [LDFlagConfigModel patchFromJsonFileNamed:fileName omitKey:key]; +} +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagConfigTracker+Testable.h b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigTracker+Testable.h new file mode 100644 index 00000000..6ae6b7db --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigTracker+Testable.h @@ -0,0 +1,19 @@ +// +// LDFlagConfigTracker+Testable.h +// DarklyTests +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDFlagConfigTracker.h" +#import "NSDate+ReferencedDate.h" + +@interface LDFlagConfigTracker(Testable) +@property (nonatomic, assign) LDMillisecond startDateMillis; +@property (nonatomic, strong) NSMutableDictionary *mutableFlagCounters; ++(instancetype)stubTracker; ++(instancetype)stubTrackerIncludeFlagVersion:(BOOL)includeFlagVersion; ++(instancetype)stubTrackerUseKnownValues:(BOOL)useKnownValues; ++(instancetype)stubTrackerWithNullValuesInFlagConfigValue; +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagConfigTracker+Testable.m b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigTracker+Testable.m new file mode 100644 index 00000000..1b149806 --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigTracker+Testable.m @@ -0,0 +1,63 @@ +// +// LDFlagConfigTracker+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDFlagConfigTracker+Testable.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDFlagCounter+Testable.h" +#import "NSDate+ReferencedDate.h" +#import "LDEventTrackingContext+Testable.h" + +extern NSString * const kEventModelKeyFeatures; +const NSTimeInterval kLDFlagConfigTrackerTrackingInterval = -30.0; + +@implementation LDFlagConfigTracker(Testable) +@dynamic startDateMillis; +@dynamic mutableFlagCounters; + ++(instancetype)stubTracker { + return [LDFlagConfigTracker stubTrackerIncludeFlagVersion:YES]; +} + ++(instancetype)stubTrackerIncludeFlagVersion:(BOOL)includeFlagVersion { + NSDate *startStubbing = [NSDate date]; + LDFlagConfigTracker *tracker = [LDFlagConfigTracker tracker]; + tracker.startDateMillis = [[startStubbing dateByAddingTimeInterval:kLDFlagConfigTrackerTrackingInterval] millisSince1970]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + tracker.mutableFlagCounters[flagKey] = [LDFlagCounter stubForFlagKey:flagKey includeFlagVersion:includeFlagVersion]; + } + + return tracker; +} + ++(instancetype)stubTrackerUseKnownValues:(BOOL)useKnownValues { + NSDate *startStubbing = [NSDate date]; + LDFlagConfigTracker *tracker = [LDFlagConfigTracker tracker]; + tracker.startDateMillis = [[startStubbing dateByAddingTimeInterval:kLDFlagConfigTrackerTrackingInterval] millisSince1970]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + tracker.mutableFlagCounters[flagKey] = [LDFlagCounter stubForFlagKey:flagKey useKnownValues:useKnownValues]; + } + + return tracker; +} + ++(instancetype)stubTrackerWithNullValuesInFlagConfigValue { + NSDate *startStubbing = [NSDate date]; + LDFlagConfigTracker *tracker = [LDFlagConfigTracker tracker]; + tracker.startDateMillis = [[startStubbing dateByAddingTimeInterval:kLDFlagConfigTrackerTrackingInterval] millisSince1970]; + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext stub]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"nullConfigIsANull-null" flagKey:kLDFlagKeyIsANull eventTrackingContext:eventTrackingContext]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + id defaultValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + [tracker logRequestForFlagKey:flagKey reportedFlagValue:defaultValue flagConfigValue:flagConfigValue defaultValue:defaultValue]; + [tracker logRequestForFlagKey:flagKey reportedFlagValue:defaultValue flagConfigValue:flagConfigValue defaultValue:defaultValue]; + [tracker logRequestForFlagKey:flagKey reportedFlagValue:defaultValue flagConfigValue:flagConfigValue defaultValue:defaultValue]; + } + return tracker; +} + +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagConfigValue+Testable.h b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigValue+Testable.h new file mode 100644 index 00000000..ea3d4780 --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigValue+Testable.h @@ -0,0 +1,38 @@ +// +// LDFlagConfigValue+Testable.h +// DarklyTests +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDFlagConfigValue.h" + +extern NSString * const kLDFlagConfigValueKeyEventTrackingContext; + +extern NSString * const kLDFlagKeyIsABool; +extern NSString * const kLDFlagKeyIsANumber; +extern NSString * const kLDFlagKeyIsADouble; +extern NSString * const kLDFlagKeyIsAString; +extern NSString * const kLDFlagKeyIsAnArray; +extern NSString * const kLDFlagKeyIsADictionary; +extern NSString * const kLDFlagKeyIsANull; + +extern NSString * const kLDFlagConfigValueKeyValue; +extern NSString * const kLDFlagConfigValueKeyVersion; +extern NSString * const kLDFlagConfigValueKeyVariation; + +@interface LDFlagConfigValue(Testable) ++(NSDictionary*)flagConfigJsonObjectFromFileNamed:(NSString*)fileName flagKey:(NSString*)flagKey eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext; ++(instancetype)flagConfigValueFromJsonFileNamed:(NSString*)fileName flagKey:(NSString*)flagKey eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext; ++(NSArray*)stubFlagConfigValuesForFlagKey:(NSString*)flagKey; ++(NSArray*)stubFlagConfigValuesForFlagKey:(NSString*)flagKey eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext; ++(NSArray*)stubFlagConfigValuesForFlagKey:(NSString*)flagKey includeFlagVersion:(BOOL)includeFlagVersion; ++(NSArray*)stubFlagConfigValuesForFlagKey:(NSString*)flagKey eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext includeFlagVersion:(BOOL)includeFlagVersion; ++(NSArray*)fixtureFileNamesForFlagKey:(NSString*)flagKey; ++(id)defaultValueForFlagKey:(NSString*)flagKey; ++(NSArray*)flagKeys; ++(NSDictionary*>*)flagConfigValues; +-(NSDictionary*)dictionaryValueIncludeContext:(BOOL)includeContext; +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagConfigValue+Testable.m b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigValue+Testable.m new file mode 100644 index 00000000..10fc9bf4 --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagConfigValue+Testable.m @@ -0,0 +1,165 @@ +// +// LDFlagConfigValue+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDFlagConfigValue+Testable.h" +#import "NSJSONSerialization+Testable.h" +#import "LDEventTrackingContext.h" +#import "LDEventTrackingContext+Testable.h" + +NSString * const kLDFlagKeyIsABool = @"isABool"; +NSString * const kLDFlagKeyIsANumber = @"isANumber"; +NSString * const kLDFlagKeyIsADouble = @"isADouble"; +NSString * const kLDFlagKeyIsAString = @"isAString"; +NSString * const kLDFlagKeyIsAnArray = @"isAnArray"; +NSString * const kLDFlagKeyIsADictionary = @"isADictionary"; +NSString * const kLDFlagKeyIsANull = @"isANull"; + +@implementation LDFlagConfigValue(Testable) ++(NSDictionary*)flagConfigJsonObjectFromFileNamed:(NSString*)fileName flagKey:(NSString*)flagKey eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext { + NSMutableDictionary *flagConfigStub = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization jsonObjectFromFileNamed:fileName]]; + if (eventTrackingContext) { + if (flagKey.length > 0) { + NSMutableDictionary *flagConfigObject = [NSMutableDictionary dictionaryWithDictionary:flagConfigStub[flagKey]]; + [flagConfigObject addEntriesFromDictionary:[eventTrackingContext dictionaryValue]]; + flagConfigStub[flagKey] = [flagConfigObject copy]; + } else { + [flagConfigStub addEntriesFromDictionary:[eventTrackingContext dictionaryValue]]; + } + } + + return [flagConfigStub copy]; +} + ++(instancetype)flagConfigValueFromJsonFileNamed:(NSString*)fileName flagKey:(NSString*)flagKey eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext { + id flagConfigStub = [LDFlagConfigValue flagConfigJsonObjectFromFileNamed:fileName flagKey:flagKey eventTrackingContext:eventTrackingContext]; + LDFlagConfigValue *flagConfigValue = flagKey.length > 0 ? [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[flagKey]] + : [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub]; + return flagConfigValue; +} + ++(NSArray*)stubFlagConfigValuesForFlagKey:(NSString*)flagKey { + return [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey eventTrackingContext:[LDEventTrackingContext stub] includeFlagVersion:YES]; +} + ++(NSArray*)stubFlagConfigValuesForFlagKey:(NSString*)flagKey eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext { + return [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey eventTrackingContext:eventTrackingContext includeFlagVersion:YES]; +} + ++(NSArray*)stubFlagConfigValuesForFlagKey:(NSString*)flagKey includeFlagVersion:(BOOL)includeFlagVersion { + return [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey eventTrackingContext:[LDEventTrackingContext stub] includeFlagVersion:includeFlagVersion]; +} + ++(NSArray*)stubFlagConfigValuesForFlagKey:(NSString*)flagKey eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext includeFlagVersion:(BOOL)includeFlagVersion { + NSMutableArray *flagConfigValueStubs = [NSMutableArray array]; + + for (NSString *fixtureName in [LDFlagConfigValue fixtureFileNamesForFlagKey:flagKey]) { + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:fixtureName flagKey:flagKey eventTrackingContext:eventTrackingContext]; + flagConfigValue.flagVersion = includeFlagVersion ? flagConfigValue.flagVersion : nil; + [flagConfigValueStubs addObject:flagConfigValue]; + } + + return [NSArray arrayWithArray:flagConfigValueStubs]; +} + ++(NSArray*)fixtureFileNamesForFlagKey:(NSString*)flagKey { + if ([flagKey isEqualToString:kLDFlagKeyIsABool]) { + return @[@"boolConfigIsABool-false", @"boolConfigIsABool-true"]; + } + if ([flagKey isEqualToString:kLDFlagKeyIsANumber]) { + return @[@"numberConfigIsANumber-1", @"numberConfigIsANumber-2"]; + } + if ([flagKey isEqualToString:kLDFlagKeyIsADouble]) { + return @[@"doubleConfigIsADouble-Pi", @"doubleConfigIsADouble-e"]; + } + if ([flagKey isEqualToString:kLDFlagKeyIsAString]) { + return @[@"stringConfigIsAString-someString", @"stringConfigIsAString-someStringA"]; + } + if ([flagKey isEqualToString:kLDFlagKeyIsAnArray]) { + return @[@"arrayConfigIsAnArray-Empty", @"arrayConfigIsAnArray-1", @"arrayConfigIsAnArray-123"]; + } + if ([flagKey isEqualToString:kLDFlagKeyIsADictionary]) { + return @[@"dictionaryConfigIsADictionary-Empty", @"dictionaryConfigIsADictionary-3Key", @"dictionaryConfigIsADictionary-KeyA"]; + } + if ([flagKey isEqualToString:kLDFlagKeyIsANull]) { + return @[@"nullConfigIsANull-null"]; + } + + return @[]; +} + ++(id)defaultValueForFlagKey:(NSString*)flagKey { + if ([flagKey isEqualToString:kLDFlagKeyIsABool]) { + return @(YES); + } + if ([flagKey isEqualToString:kLDFlagKeyIsANumber]) { + return @(7); + } + if ([flagKey isEqualToString:kLDFlagKeyIsADouble]) { + return @(10.27); + } + if ([flagKey isEqualToString:kLDFlagKeyIsAString]) { + return @"Jupiter II"; + } + if ([flagKey isEqualToString:kLDFlagKeyIsAnArray]) { + return @[@(1),@(2),@(3),@(4),@(5),@(6),@(7)]; + } + if ([flagKey isEqualToString:kLDFlagKeyIsADictionary]) { + return @{@"default-dictionary-key": @"default-dictionary-value"}; + } + if ([flagKey isEqualToString:kLDFlagKeyIsANull]) { + return [NSNull null]; + } + return @(YES); +} + ++(id)differentValueForFlagKey:(NSString*)flagKey { + if ([flagKey isEqualToString:kLDFlagKeyIsABool]) { + return @(NO); + } + if ([flagKey isEqualToString:kLDFlagKeyIsANumber]) { + return @(8); + } + if ([flagKey isEqualToString:kLDFlagKeyIsADouble]) { + return @(170.1); + } + if ([flagKey isEqualToString:kLDFlagKeyIsAString]) { + return @"Gallifrey"; + } + if ([flagKey isEqualToString:kLDFlagKeyIsAnArray]) { + return @[@(1),@(2),@(3)]; + } + if ([flagKey isEqualToString:kLDFlagKeyIsADictionary]) { + return @{@"alternate-dictionary-key": @"alternate-dictionary-value"}; + } + if ([flagKey isEqualToString:kLDFlagKeyIsANull]) { + return [NSNull null]; + } + return @(YES); +} + ++(NSArray*)flagKeys { + return @[kLDFlagKeyIsABool, kLDFlagKeyIsANumber, kLDFlagKeyIsADouble, kLDFlagKeyIsAString, kLDFlagKeyIsAnArray, kLDFlagKeyIsADictionary, kLDFlagKeyIsANull]; +} + ++(NSDictionary*>*)flagConfigValues { + NSMutableDictionary *flagConfigValues = [NSMutableDictionary dictionaryWithCapacity:[[LDFlagConfigValue flagKeys] count]]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + flagConfigValues[flagKey] = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey]; + } + return [flagConfigValues copy]; +} + +-(NSDictionary*)dictionaryValueIncludeContext:(BOOL)includeContext { + NSMutableDictionary *dictionaryValue = [NSMutableDictionary dictionaryWithDictionary:[self dictionaryValue]]; + if (includeContext) { + [dictionaryValue addEntriesFromDictionary:[self.eventTrackingContext dictionaryValue]]; + } + + return [dictionaryValue copy]; +} +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagCounter+Testable.h b/DarklyTests/Categories/LDFlagConfig/LDFlagCounter+Testable.h new file mode 100644 index 00000000..41aefff7 --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagCounter+Testable.h @@ -0,0 +1,26 @@ +// +// LDFlagCounter+Testable.h +// DarklyTests +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDFlagCounter.h" + +extern NSString * const kLDFlagCounterKeyDefaultValue; +extern NSString * const kLDFlagCounterKeyCounters; + +@interface LDFlagCounter(Testable) +@property (nonatomic, strong) NSMutableArray *flagValueCounters; + ++(instancetype)stubForFlagKey:(NSString*)flagKey; ++(instancetype)stubForFlagKey:(NSString*)flagKey useKnownValues:(BOOL)useKnownValues; ++(instancetype)stubForFlagKey:(NSString*)flagKey includeFlagVersion:(BOOL)includeFlagVersion; ++(instancetype)stubForFlagKey:(NSString*)flagKey useKnownValues:(BOOL)useKnownValues includeFlagVersion:(BOOL)includeFlagVersion; + +@end + +@interface LDFlagCounter (Private) +-(LDFlagValueCounter*)valueCounterForFlagConfigValue:(LDFlagConfigValue*)flagConfigValue; +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagCounter+Testable.m b/DarklyTests/Categories/LDFlagConfig/LDFlagCounter+Testable.m new file mode 100644 index 00000000..d845098b --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagCounter+Testable.m @@ -0,0 +1,50 @@ +// +// LDFlagCounter+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDFlagCounter+Testable.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDFlagValueCounter+Testable.h" + +extern NSString * const kLDFlagCounterKeyDefaultValue; +extern NSString * const kLDFlagCounterKeyCounters; + +@implementation LDFlagCounter(Testable) +@dynamic flagValueCounters; + ++(instancetype)stubForFlagKey:(NSString*)flagKey { + return [[self class] stubForFlagKey:flagKey useKnownValues:YES]; +} + ++(instancetype)stubForFlagKey:(NSString*)flagKey useKnownValues:(BOOL)useKnownValues { + return [LDFlagCounter stubForFlagKey:flagKey useKnownValues:useKnownValues includeFlagVersion:YES]; +} + ++(instancetype)stubForFlagKey:(NSString*)flagKey includeFlagVersion:(BOOL)includeFlagVersion { + return [LDFlagCounter stubForFlagKey:flagKey useKnownValues:YES includeFlagVersion:includeFlagVersion]; +} + ++(instancetype)stubForFlagKey:(NSString*)flagKey useKnownValues:(BOOL)useKnownValues includeFlagVersion:(BOOL)includeFlagVersion { + id defaultValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagCounter *flagCounter = [LDFlagCounter counterWithFlagKey:flagKey defaultValue:defaultValue]; + + if (useKnownValues) { + NSArray *flagConfigValues = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey includeFlagVersion:includeFlagVersion]; + for (LDFlagConfigValue *flagConfigValue in flagConfigValues) { + for (NSInteger logRequests = 0; logRequests < flagConfigValue.modelVersion; logRequests += 1 ) { + [flagCounter logRequestWithFlagConfigValue:flagConfigValue reportedFlagValue:flagConfigValue.value defaultValue:defaultValue]; + } + } + } else { + for (NSInteger logRequests = 0; logRequests < 3; logRequests += 1 ) { + [flagCounter logRequestWithFlagConfigValue:nil reportedFlagValue:defaultValue defaultValue:defaultValue]; + } + } + + return flagCounter; +} +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagValueCounter+Testable.h b/DarklyTests/Categories/LDFlagConfig/LDFlagValueCounter+Testable.h new file mode 100644 index 00000000..fa94017e --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagValueCounter+Testable.h @@ -0,0 +1,21 @@ +// +// LDFlagValueCounter+Testable.h +// DarklyTests +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDFlagValueCounter.h" + +extern NSString * const kLDFlagValueCounterKeyFlagConfigValue; +extern NSString * const kLDFlagValueCounterKeyValue; +extern NSString * const kLDFlagValueCounterKeyVersion; +extern NSString * const kLDFlagValueCounterKeyVariation; +extern NSString * const kLDFlagValueCounterKeyCount; +extern NSString * const kLDFlagValueCounterKeyUnknown; + + +@interface LDFlagValueCounter(Testable) +-(BOOL)hasPropertiesMatchingDictionary:(NSDictionary*)dictionary; +@end diff --git a/DarklyTests/Categories/LDFlagConfig/LDFlagValueCounter+Testable.m b/DarklyTests/Categories/LDFlagConfig/LDFlagValueCounter+Testable.m new file mode 100644 index 00000000..b7fcfc53 --- /dev/null +++ b/DarklyTests/Categories/LDFlagConfig/LDFlagValueCounter+Testable.m @@ -0,0 +1,47 @@ +// +// LDFlagValueCounter+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "LDFlagValueCounter+Testable.h" +#import "LDFlagConfigValue+Testable.h" + +@implementation LDFlagValueCounter(Testable) +-(BOOL)hasPropertiesMatchingDictionary:(NSDictionary*)dictionary { + NSMutableArray *mismatchedProperties = [NSMutableArray array]; + if (self.known) { + if (self.flagConfigValue) { + if (![self.flagConfigValue hasPropertiesMatchingDictionary:dictionary]) { + [mismatchedProperties addObject:kLDFlagValueCounterKeyFlagConfigValue]; + } + } else { + if (dictionary[kLDFlagValueCounterKeyFlagConfigValue]) { + [mismatchedProperties addObject:kLDFlagValueCounterKeyFlagConfigValue]; + } + } + if (dictionary[kLDFlagValueCounterKeyUnknown]) { + [mismatchedProperties addObject:kLDFlagValueCounterKeyUnknown]; + } + } else { + if (dictionary[kLDFlagValueCounterKeyFlagConfigValue]) { + [mismatchedProperties addObject:kLDFlagValueCounterKeyFlagConfigValue]; + } + if ([dictionary[kLDFlagValueCounterKeyUnknown] boolValue] != YES) { + [mismatchedProperties addObject:kLDFlagValueCounterKeyUnknown]; + } + } + if (self.count != [dictionary[kLDFlagValueCounterKeyCount] integerValue]) { + [mismatchedProperties addObject:kLDFlagValueCounterKeyCount]; + } + + if (mismatchedProperties.count > 0) { + NSLog(@"[%@ %@] unequal properties: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [mismatchedProperties componentsJoinedByString:@", "]); + return NO; + } + + return YES; +} +@end diff --git a/DarklyTests/Categories/LDFlagConfigModel+Testable.m b/DarklyTests/Categories/LDFlagConfigModel+Testable.m deleted file mode 100644 index d38e1982..00000000 --- a/DarklyTests/Categories/LDFlagConfigModel+Testable.m +++ /dev/null @@ -1,39 +0,0 @@ -// -// LDFlagConfigModel+Testable.m -// DarklyTests -// -// Created by Mark Pokorny on 10/19/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import "LDFlagConfigModel.h" -#import "LDFlagConfigModel+Testable.h" -#import "NSJSONSerialization+Testable.h" - -extern NSString * _Nonnull const kLDFlagConfigJsonDictionaryKeyVersion; - -@implementation LDFlagConfigModel(Testable) -+(instancetype)flagConfigFromJsonFileNamed:(NSString *)fileName { - return [[LDFlagConfigModel alloc] initWithDictionary:[NSJSONSerialization jsonObjectFromFileNamed:fileName]]; -} - -+(NSDictionary*)patchFromJsonFileNamed:(NSString *)fileName useVersion:(NSInteger)version { - NSMutableDictionary *patch = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization jsonObjectFromFileNamed:fileName]]; - patch[kLDFlagConfigJsonDictionaryKeyVersion] = @(version); - return patch; -} - -+(NSDictionary*)patchFromJsonFileNamed:(NSString *)fileName omitKey:(NSString*)key { - NSMutableDictionary *patch = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization jsonObjectFromFileNamed:fileName]]; - patch[key] = nil; - return patch; -} - -+(NSDictionary*)deleteFromJsonFileNamed:(NSString *)fileName useVersion:(NSInteger)version { - return [LDFlagConfigModel patchFromJsonFileNamed:fileName useVersion:version]; -} - -+(NSDictionary*)deleteFromJsonFileNamed:(NSString *)fileName omitKey:(NSString*)key { - return [LDFlagConfigModel patchFromJsonFileNamed:fileName omitKey:key]; -} -@end diff --git a/DarklyTests/Categories/LDUserModel+Equatable.h b/DarklyTests/Categories/LDUserModel+Equatable.h deleted file mode 100644 index 51c63c37..00000000 --- a/DarklyTests/Categories/LDUserModel+Equatable.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// LDUserModel+Equatable.h -// Darkly -// -// Created by Mark Pokorny on 7/14/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import - -extern NSString * const kUserAttributeKey; -extern NSString * const kUserAttributeUpdatedAt; -extern NSString * const kUserAttributeConfig; -extern NSString * const kUserAttributeAnonymous; -extern NSString * const kUserAttributePrivateAttributes; -extern NSString * const kUserAttributeDevice; -extern NSString * const kUserAttributeOs; - -@interface LDUserModel (Equatable) --(BOOL)isEqual:(id)object ignoringAttributes:(NSArray*)ignoredAttributes; --(BOOL)matchesDictionary:(NSDictionary *)dictionary includeFlags:(BOOL)includeConfig includePrivateAttributes:(BOOL)includePrivate privateAttributes:(NSArray *)privateAttributes; -@end diff --git a/DarklyTests/Categories/LDUserModel+Equatable.m b/DarklyTests/Categories/LDUserModel+Equatable.m deleted file mode 100644 index a530284a..00000000 --- a/DarklyTests/Categories/LDUserModel+Equatable.m +++ /dev/null @@ -1,201 +0,0 @@ -// -// LDUserModel+Equatable.m -// Darkly -// -// Created by Mark Pokorny on 7/14/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import "LDUserModel+Equatable.h" -#import "LDUserModel+Testable.h" -#import "NSDictionary+StringKey_Matchable.h" -#import "NSDateFormatter+LDUserModel.h" - -@implementation LDUserModel (Equatable) --(BOOL) isEqual:(id)object ignoringAttributes:(NSArray*)ignoredAttributes { - LDUserModel *otherUser = (LDUserModel*)object; - if (otherUser == nil) { - return NO; - } - NSDictionary *dictionary = [self dictionaryValueWithFlags:YES includePrivateAttributes:YES config:nil includePrivateAttributeList:YES]; - NSDictionary *otherDictionary = [otherUser dictionaryValueWithFlags:YES includePrivateAttributes:YES config:nil includePrivateAttributeList:YES]; - NSArray *differingKeys = [dictionary keysWithDifferentValuesIn: otherDictionary ignoringKeys: ignoredAttributes]; - return (differingKeys == nil || [differingKeys count] == 0); -} - --(BOOL)matchesDictionary:(NSDictionary *)dictionary includeFlags:(BOOL)includeConfig includePrivateAttributes:(BOOL)includePrivate privateAttributes:(NSArray *)privateAttributes { - NSString *matchingFailureReason = @"Dictionary value does not match LDUserModel attribute: %@"; - NSString *dictionaryContainsAttributeFailureReason = @"Dictionary contains private attribute: %@"; - NSString *privateAttributeListContainsFailureReason = @"Private Attributes List contains private attribute: %@"; - NSString *privateAttributeListDoesNotContainFailureReason = @"Private Attributes List does not contain private attribute: %@"; - - if (![self.key isEqualToString:dictionary[kUserAttributeKey]]) { - NSLog(matchingFailureReason, kUserAttributeKey); - return NO; - } - - NSArray *stringAttributes = @[kUserAttributeIp, kUserAttributeCountry, kUserAttributeName, kUserAttributeFirstName, kUserAttributeLastName, kUserAttributeEmail, kUserAttributeAvatar]; - for (NSString *attribute in stringAttributes) { - if (!includePrivate && [privateAttributes containsObject:attribute]) { - if (dictionary[attribute] != nil) { - NSLog(@"Dictionary contains attribute %@", attribute); - return NO; - } - continue; - } - id property = [self propertyForAttribute:attribute]; - NSString *dictionaryAttribute = dictionary[attribute]; - if (!property && !dictionaryAttribute) { continue; } - if (![[self propertyForAttribute:attribute] isEqualToString:dictionary[attribute]]) { - NSLog(matchingFailureReason, attribute); - return NO; - } - } - - if (![[[NSDateFormatter userDateFormatter] stringFromDate:self.updatedAt] isEqualToString:dictionary[kUserAttributeUpdatedAt]]) { - NSLog(matchingFailureReason, kUserAttributeUpdatedAt); - return NO; - } - - if (!includePrivate && [privateAttributes containsObject:kUserAttributeCustom]) { - if (dictionary[kUserAttributeCustom] != nil) { - NSMutableDictionary *customDictionary = [dictionary[kUserAttributeCustom] mutableCopy]; - - for (NSString *attribute in @[kUserAttributeDevice, kUserAttributeOs]) { - id property = [self propertyForAttribute:attribute]; - NSString *dictionaryAttribute = customDictionary[attribute]; - if (property && ![property isEqualToString:dictionaryAttribute]) { - NSLog(matchingFailureReason, attribute); - return NO; - } - } - - [customDictionary removeObjectsForKeys:@[kUserAttributeDevice, kUserAttributeOs]]; - - if (customDictionary.count > 0) { - NSLog(dictionaryContainsAttributeFailureReason, kUserAttributeCustom); - return NO; - } - } - } else { - NSDictionary *customDictionary = dictionary[kUserAttributeCustom]; - - for (NSString *customAttribute in self.custom.allKeys) { - if (!includePrivate && [privateAttributes containsObject:customAttribute]) { - if (customDictionary[customAttribute] != nil) { - NSLog(dictionaryContainsAttributeFailureReason, customAttribute); - return NO; - } - continue; - } - - //NOTE: The stubbed custom dictionary only contains string values... - if ([self.custom[customAttribute] isKindOfClass:[NSString class]] && ![self.custom[customAttribute] isEqualToString:customDictionary[customAttribute]]) { - NSLog(dictionaryContainsAttributeFailureReason, customAttribute); - return NO; - } - if (![self.custom[customAttribute] isKindOfClass:[NSString class]]) { NSLog(@"WARNING: Non-string type contained in LDUserModel.custom at the key %@", customAttribute); } - } - - for (NSString *attribute in @[kUserAttributeDevice, kUserAttributeOs]) { - if ([self propertyForAttribute:attribute] == nil) { continue; } - if (!includePrivate && [privateAttributes containsObject:attribute]) { - if (customDictionary[attribute] != nil) { - NSLog(dictionaryContainsAttributeFailureReason, attribute); - return NO; - } - continue; - } - if (![[self propertyForAttribute:attribute] isEqualToString:customDictionary[attribute]]) { - NSLog(matchingFailureReason, attribute); - return NO; - } - } - } - - if (self.anonymous != [dictionary[kUserAttributeAnonymous] boolValue]) { - NSLog(matchingFailureReason, kUserAttributeAnonymous); - return NO; - } - - NSDictionary *dictionaryConfig = dictionary[kUserAttributeConfig]; - if (includeConfig) { - NSDictionary *config = [self.config dictionaryValueIncludeNulls:NO]; - if ( (config && ![config isEqual:dictionaryConfig]) || (!config && dictionaryConfig) ) { - NSLog(matchingFailureReason, kUserAttributeConfig); - return NO; - } - } else { - if (dictionaryConfig) { - NSLog(matchingFailureReason, kUserAttributeConfig); - return NO; - } - } - - NSArray *privateAttributeList = dictionary[kUserAttributePrivateAttributes]; - if (includePrivate) { - if (privateAttributeList) { - NSLog(dictionaryContainsAttributeFailureReason, kUserAttributePrivateAttributes); - return NO; - } - } else { - // !includePrivate - if (privateAttributeList && privateAttributeList.count == 0) { - NSLog(dictionaryContainsAttributeFailureReason, kUserAttributePrivateAttributes); - return NO; - } - for (NSString *attribute in privateAttributes) { - id property = [self propertyForAttribute:attribute]; - if ([attribute isEqualToString:kUserAttributeCustom]) { - //Specialized handling because the dictionary can exist with ONLY the device & os, but no other keys - NSMutableDictionary *customProperty = [property mutableCopy]; - [customProperty removeObjectsForKeys:@[kUserAttributeDevice, kUserAttributeOs]]; - if (customProperty.count == 0 && [privateAttributeList containsObject:kUserAttributeCustom]) { - NSLog(privateAttributeListContainsFailureReason, kUserAttributeCustom); - return NO; - } - if (customProperty.count > 0 && ![privateAttributeList containsObject:kUserAttributeCustom]) { - NSLog(privateAttributeListDoesNotContainFailureReason, kUserAttributeCustom); - return NO; - } - } else { - if (!property && [privateAttributeList containsObject:attribute] ) { - NSLog(privateAttributeListContainsFailureReason, attribute); - return NO; - } - if (property && ![privateAttributeList containsObject:attribute]) { - NSLog(privateAttributeListDoesNotContainFailureReason, attribute); - return NO; - } - } - } - } - - return YES; -} - --(id)propertyForAttribute:(NSString*)attribute { - NSArray *attributeList = @[kUserAttributeKey, kUserAttributeIp, kUserAttributeCountry, kUserAttributeName, kUserAttributeFirstName, kUserAttributeLastName, kUserAttributeEmail, kUserAttributeAvatar, kUserAttributeCustom, kUserAttributeUpdatedAt, kUserAttributeConfig, kUserAttributeAnonymous, kUserAttributeDevice, kUserAttributeOs]; - NSUInteger attributeIndex = [attributeList indexOfObject:attribute]; - if (attributeIndex != NSNotFound) { - switch (attributeIndex) { - case 0: return self.key; - case 1: return self.ip; - case 2: return self.country; - case 3: return self.name; - case 4: return self.firstName; - case 5: return self.lastName; - case 6: return self.email; - case 7: return self.avatar; - case 8: return self.custom; - case 9: return self.updatedAt; - case 10: return self.config; - case 11: return @(self.anonymous); - case 12: return self.device; - case 13: return self.os; - } - } - - return self.custom[attribute]; -} -@end diff --git a/DarklyTests/Categories/LDUserModel+JsonDecodeable.h b/DarklyTests/Categories/LDUserModel+JsonDecodeable.h deleted file mode 100644 index ca78b558..00000000 --- a/DarklyTests/Categories/LDUserModel+JsonDecodeable.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// LDUserModel+JsonDecodeable.h -// Darkly -// -// Created by Mark Pokorny on 7/27/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import - -@interface LDUserModel (JsonDecodeable) -+(LDUserModel*)userFrom:(NSString*)jsonUser; -@end diff --git a/DarklyTests/Categories/LDUserModel+JsonDecodeable.m b/DarklyTests/Categories/LDUserModel+JsonDecodeable.m deleted file mode 100644 index 9022c35c..00000000 --- a/DarklyTests/Categories/LDUserModel+JsonDecodeable.m +++ /dev/null @@ -1,18 +0,0 @@ -// -// LDUserModel+JsonDecodeable.m -// Darkly -// -// Created by Mark Pokorny on 7/27/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import "LDUserModel+JsonDecodeable.h" - -@implementation LDUserModel (JsonDecodeable) -+(LDUserModel*)userFrom:(NSString*)jsonUser { - NSError *jsonError; - NSData *userData = [jsonUser dataUsingEncoding:NSUTF8StringEncoding]; - NSDictionary *userDictionary = [NSJSONSerialization JSONObjectWithData:userData options:0 error:&jsonError]; - return [[LDUserModel alloc] initWithDictionary:userDictionary]; -} -@end diff --git a/DarklyTests/Categories/LDUserModel+Stub.h b/DarklyTests/Categories/LDUserModel+Stub.h deleted file mode 100644 index 89e3d1f9..00000000 --- a/DarklyTests/Categories/LDUserModel+Stub.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// LDUserModel+Stub.h -// DarklyTests -// -// Created by Mark Pokorny on 12/20/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import - -extern NSString * const userModelStubIp; -extern NSString * const userModelStubCountry; -extern NSString * const userModelStubName; -extern NSString * const userModelStubFirstName; -extern NSString * const userModelStubLastName; -extern NSString * const userModelStubEmail; -extern NSString * const userModelStubAvatar; -extern NSString * const userModelStubDevice; -extern NSString * const userModelStubOs; -extern NSString * const userModelStubCustomKey; -extern NSString * const userModelStubCustomValue; - -@interface LDUserModel (Stub) -+(instancetype)stubWithKey:(NSString*)key; -+(NSDictionary*)customStub; -@end diff --git a/DarklyTests/Categories/LDUserModel+Stub.m b/DarklyTests/Categories/LDUserModel+Stub.m deleted file mode 100644 index c7c98016..00000000 --- a/DarklyTests/Categories/LDUserModel+Stub.m +++ /dev/null @@ -1,48 +0,0 @@ -// -// LDUserModel+Stub.m -// DarklyTests -// -// Created by Mark Pokorny on 12/20/17. +JMJ -// Copyright © 2017 LaunchDarkly. All rights reserved. -// - -#import "Darkly/LDUserModel.h" -#import "LDUserModel+Stub.h" -#import "LDUserModel+Equatable.h" -#import "LDFlagConfigModel+Testable.h" - -NSString * const userModelStubIp = @"123.456.789.000"; -NSString * const userModelStubCountry = @"stubCountry"; -NSString * const userModelStubName = @"stubName"; -NSString * const userModelStubFirstName = @"stubFirstName"; -NSString * const userModelStubLastName = @"stubLastName"; -NSString * const userModelStubEmail = @"stub@email.com"; -NSString * const userModelStubAvatar = @"stubAvatar"; -NSString * const userModelStubDevice = @"iPhone"; -NSString * const userModelStubOs = @"IOS 11.2.1"; -NSString * const userModelStubCustomKey = @"userModelStubCustomKey"; -NSString * const userModelStubCustomValue = @"userModelStubCustomValue"; - -@implementation LDUserModel (Stub) -+(instancetype)stubWithKey:(NSString*)key { - LDUserModel *stub = [[LDUserModel alloc] init]; - stub.key = key.length ? key : [[NSUUID UUID] UUIDString]; - stub.ip = userModelStubIp; - stub.country = userModelStubCountry; - stub.name = userModelStubName; - stub.firstName = userModelStubFirstName; - stub.lastName = userModelStubLastName; - stub.email = userModelStubEmail; - stub.avatar = userModelStubAvatar; - stub.custom = [LDUserModel customStub]; - stub.config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-excludeNulls-withVersions"]; - - return stub; -} - -+(NSDictionary*)customStub { - //If you add new values that are non-string type, you might need to add the type to - //-[LDUserModel+Equatable matchesDictionary: includeFlags: includePrivateAttributes: privateAttributes:] to handle the new type. - return @{userModelStubCustomKey: userModelStubCustomValue}; -} -@end diff --git a/DarklyTests/Categories/LDUserModel+Testable.h b/DarklyTests/Categories/LDUserModel+Testable.h index 0ee6cbd0..44b2d918 100644 --- a/DarklyTests/Categories/LDUserModel+Testable.h +++ b/DarklyTests/Categories/LDUserModel+Testable.h @@ -8,9 +8,41 @@ #import +@class LDEventTrackingContext; + +extern NSString * const userModelStubIp; +extern NSString * const userModelStubCountry; +extern NSString * const userModelStubName; +extern NSString * const userModelStubFirstName; +extern NSString * const userModelStubLastName; +extern NSString * const userModelStubEmail; +extern NSString * const userModelStubAvatar; +extern NSString * const userModelStubDevice; +extern NSString * const userModelStubOs; +extern NSString * const userModelStubCustomKey; +extern NSString * const userModelStubCustomValue; + +extern NSString * const kUserAttributeKey; +extern NSString * const kUserAttributeUpdatedAt; +extern NSString * const kUserAttributeConfig; +extern NSString * const kUserAttributeAnonymous; +extern NSString * const kUserAttributePrivateAttributes; +extern NSString * const kUserAttributeDevice; +extern NSString * const kUserAttributeOs; + +extern NSString * const kFlagKeyIsABawler; + @interface LDUserModel (Testable) +@property (nonatomic, strong) LDFlagConfigTracker *flagConfigTracker; + ++(instancetype)stubWithKey:(NSString*)key; ++(instancetype)stubWithKey:(NSString*)key usingTracker:(LDFlagConfigTracker*)tracker eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext; ++(NSDictionary*)customStub; ++(LDUserModel*)userFrom:(NSString*)jsonUser; /** -[LDUserModel dictionaryValueWithFlags: includePrivateAttributes: config:] intentionally omits the private attributes LIST from the dictionary when includePrivateAttributes == YES to satisfy an LD server requirement. This method allows control over including that list for testing. */ -(NSDictionary *)dictionaryValueWithFlags:(BOOL)includeFlags includePrivateAttributes:(BOOL)includePrivate config:(LDConfig*)config includePrivateAttributeList:(BOOL)includePrivateList; +-(BOOL)isEqual:(id)object ignoringAttributes:(NSArray*)ignoredAttributes; +-(BOOL)matchesDictionary:(NSDictionary *)dictionary includeFlags:(BOOL)includeConfig includePrivateAttributes:(BOOL)includePrivate privateAttributes:(NSArray *)privateAttributes; @end diff --git a/DarklyTests/Categories/LDUserModel+Testable.m b/DarklyTests/Categories/LDUserModel+Testable.m index 55a1d761..5aa5af03 100644 --- a/DarklyTests/Categories/LDUserModel+Testable.m +++ b/DarklyTests/Categories/LDUserModel+Testable.m @@ -7,12 +7,257 @@ // #import "LDUserModel+Testable.h" -#import "LDUserModel+Equatable.h" +#import "LDUserModel.h" +#import "LDFlagConfigModel.h" +#import "LDFlagConfigModel+Testable.h" +#import "LDEventTrackingContext.h" +#import "LDEventTrackingContext+Testable.h" +#import "LDFlagConfigTracker+Testable.h" +#import "NSDictionary+StringKey_Matchable.h" +#import "NSDateFormatter+LDUserModel.h" + +NSString * const userModelStubIp = @"123.456.789.000"; +NSString * const userModelStubCountry = @"stubCountry"; +NSString * const userModelStubName = @"stubName"; +NSString * const userModelStubFirstName = @"stubFirstName"; +NSString * const userModelStubLastName = @"stubLastName"; +NSString * const userModelStubEmail = @"stub@email.com"; +NSString * const userModelStubAvatar = @"stubAvatar"; +NSString * const userModelStubDevice = @"iPhone"; +NSString * const userModelStubOs = @"IOS 11.2.1"; +NSString * const userModelStubCustomKey = @"userModelStubCustomKey"; +NSString * const userModelStubCustomValue = @"userModelStubCustomValue"; + +NSString * const kFlagKeyIsABawler = @"isABawler"; @implementation LDUserModel (Testable) +@dynamic flagConfigTracker; + ++(instancetype)stubWithKey:(NSString*)key { + return [LDUserModel stubWithKey:key usingTracker:nil eventTrackingContext:nil]; +} + ++(instancetype)stubWithKey:(NSString*)key usingTracker:(LDFlagConfigTracker*)tracker eventTrackingContext:(LDEventTrackingContext*)eventTrackingContext { + LDUserModel *stub = [[LDUserModel alloc] init]; + stub.key = key.length ? key : [[NSUUID UUID] UUIDString]; + stub.ip = userModelStubIp; + stub.country = userModelStubCountry; + stub.name = userModelStubName; + stub.firstName = userModelStubFirstName; + stub.lastName = userModelStubLastName; + stub.email = userModelStubEmail; + stub.avatar = userModelStubAvatar; + stub.custom = [LDUserModel customStub]; + stub.flagConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags" eventTrackingContext:eventTrackingContext]; + stub.flagConfigTracker = tracker ?: [LDFlagConfigTracker stubTracker]; + + return stub; +} + ++(NSDictionary*)customStub { + //If you add new values that are non-string type, you might need to add the type to + //-[LDUserModel+Equatable matchesDictionary: includeFlags: includePrivateAttributes: privateAttributes:] to handle the new type. + return @{userModelStubCustomKey: userModelStubCustomValue}; +} + ++(LDUserModel*)userFrom:(NSString*)jsonUser { + NSError *jsonError; + NSData *userData = [jsonUser dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary *userDictionary = [NSJSONSerialization JSONObjectWithData:userData options:0 error:&jsonError]; + return [[LDUserModel alloc] initWithDictionary:userDictionary]; +} + -(NSDictionary *)dictionaryValueWithFlags:(BOOL)includeFlags includePrivateAttributes:(BOOL)includePrivate config:(LDConfig*)config includePrivateAttributeList:(BOOL)includePrivateList { NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithDictionary:[self dictionaryValueWithFlagConfig:includeFlags includePrivateAttributes:includePrivate config:config]]; dictionary[kUserAttributePrivateAttributes] = includePrivateList ? self.privateAttributes : nil; return dictionary; } + +-(BOOL) isEqual:(id)object ignoringAttributes:(NSArray*)ignoredAttributes { + LDUserModel *otherUser = (LDUserModel*)object; + if (otherUser == nil) { + return NO; + } + NSDictionary *dictionary = [self dictionaryValueWithFlags:YES includePrivateAttributes:YES config:nil includePrivateAttributeList:YES]; + NSDictionary *otherDictionary = [otherUser dictionaryValueWithFlags:YES includePrivateAttributes:YES config:nil includePrivateAttributeList:YES]; + NSArray *differingKeys = [dictionary keysWithDifferentValuesIn: otherDictionary ignoringKeys: ignoredAttributes]; + return (differingKeys == nil || [differingKeys count] == 0); +} + +-(BOOL)matchesDictionary:(NSDictionary *)dictionary includeFlags:(BOOL)includeConfig includePrivateAttributes:(BOOL)includePrivate privateAttributes:(NSArray *)privateAttributes { + NSString *matchingFailureReason = @"Dictionary value does not match LDUserModel attribute: %@"; + NSString *dictionaryContainsAttributeFailureReason = @"Dictionary contains private attribute: %@"; + NSString *privateAttributeListContainsFailureReason = @"Private Attributes List contains private attribute: %@"; + NSString *privateAttributeListDoesNotContainFailureReason = @"Private Attributes List does not contain private attribute: %@"; + + if (![self.key isEqualToString:dictionary[kUserAttributeKey]]) { + NSLog(matchingFailureReason, kUserAttributeKey); + return NO; + } + + NSArray *stringAttributes = @[kUserAttributeIp, kUserAttributeCountry, kUserAttributeName, kUserAttributeFirstName, kUserAttributeLastName, kUserAttributeEmail, kUserAttributeAvatar]; + for (NSString *attribute in stringAttributes) { + if (!includePrivate && [privateAttributes containsObject:attribute]) { + if (dictionary[attribute] != nil) { + NSLog(@"Dictionary contains attribute %@", attribute); + return NO; + } + continue; + } + id property = [self propertyForAttribute:attribute]; + NSString *dictionaryAttribute = dictionary[attribute]; + if (!property && !dictionaryAttribute) { continue; } + if (![[self propertyForAttribute:attribute] isEqualToString:dictionary[attribute]]) { + NSLog(matchingFailureReason, attribute); + return NO; + } + } + + if (![[[NSDateFormatter userDateFormatter] stringFromDate:self.updatedAt] isEqualToString:dictionary[kUserAttributeUpdatedAt]]) { + NSLog(matchingFailureReason, kUserAttributeUpdatedAt); + return NO; + } + + if (!includePrivate && [privateAttributes containsObject:kUserAttributeCustom]) { + if (dictionary[kUserAttributeCustom] != nil) { + NSMutableDictionary *customDictionary = [dictionary[kUserAttributeCustom] mutableCopy]; + + for (NSString *attribute in @[kUserAttributeDevice, kUserAttributeOs]) { + id property = [self propertyForAttribute:attribute]; + NSString *dictionaryAttribute = customDictionary[attribute]; + if (property && ![property isEqualToString:dictionaryAttribute]) { + NSLog(matchingFailureReason, attribute); + return NO; + } + } + + [customDictionary removeObjectsForKeys:@[kUserAttributeDevice, kUserAttributeOs]]; + + if (customDictionary.count > 0) { + NSLog(dictionaryContainsAttributeFailureReason, kUserAttributeCustom); + return NO; + } + } + } else { + NSDictionary *customDictionary = dictionary[kUserAttributeCustom]; + + for (NSString *customAttribute in self.custom.allKeys) { + if (!includePrivate && [privateAttributes containsObject:customAttribute]) { + if (customDictionary[customAttribute] != nil) { + NSLog(dictionaryContainsAttributeFailureReason, customAttribute); + return NO; + } + continue; + } + + //NOTE: The stubbed custom dictionary only contains string values... + if ([self.custom[customAttribute] isKindOfClass:[NSString class]] && ![self.custom[customAttribute] isEqualToString:customDictionary[customAttribute]]) { + NSLog(dictionaryContainsAttributeFailureReason, customAttribute); + return NO; + } + if (![self.custom[customAttribute] isKindOfClass:[NSString class]]) { NSLog(@"WARNING: Non-string type contained in LDUserModel.custom at the key %@", customAttribute); } + } + + for (NSString *attribute in @[kUserAttributeDevice, kUserAttributeOs]) { + if ([self propertyForAttribute:attribute] == nil) { continue; } + if (!includePrivate && [privateAttributes containsObject:attribute]) { + if (customDictionary[attribute] != nil) { + NSLog(dictionaryContainsAttributeFailureReason, attribute); + return NO; + } + continue; + } + if (![[self propertyForAttribute:attribute] isEqualToString:customDictionary[attribute]]) { + NSLog(matchingFailureReason, attribute); + return NO; + } + } + } + + if (self.anonymous != [dictionary[kUserAttributeAnonymous] boolValue]) { + NSLog(matchingFailureReason, kUserAttributeAnonymous); + return NO; + } + + NSDictionary *dictionaryConfig = dictionary[kUserAttributeConfig]; + if (includeConfig) { + NSDictionary *config = [self.flagConfig dictionaryValueIncludeNulls:NO]; + if ( (config && ![config isEqual:dictionaryConfig]) || (!config && dictionaryConfig) ) { + NSLog(matchingFailureReason, kUserAttributeConfig); + return NO; + } + } else { + if (dictionaryConfig) { + NSLog(matchingFailureReason, kUserAttributeConfig); + return NO; + } + } + + NSArray *privateAttributeList = dictionary[kUserAttributePrivateAttributes]; + if (includePrivate) { + if (privateAttributeList) { + NSLog(dictionaryContainsAttributeFailureReason, kUserAttributePrivateAttributes); + return NO; + } + } else { + // !includePrivate + if (privateAttributeList && privateAttributeList.count == 0) { + NSLog(dictionaryContainsAttributeFailureReason, kUserAttributePrivateAttributes); + return NO; + } + for (NSString *attribute in privateAttributes) { + id property = [self propertyForAttribute:attribute]; + if ([attribute isEqualToString:kUserAttributeCustom]) { + //Specialized handling because the dictionary can exist with ONLY the device & os, but no other keys + NSMutableDictionary *customProperty = [property mutableCopy]; + [customProperty removeObjectsForKeys:@[kUserAttributeDevice, kUserAttributeOs]]; + if (customProperty.count == 0 && [privateAttributeList containsObject:kUserAttributeCustom]) { + NSLog(privateAttributeListContainsFailureReason, kUserAttributeCustom); + return NO; + } + if (customProperty.count > 0 && ![privateAttributeList containsObject:kUserAttributeCustom]) { + NSLog(privateAttributeListDoesNotContainFailureReason, kUserAttributeCustom); + return NO; + } + } else { + if (!property && [privateAttributeList containsObject:attribute] ) { + NSLog(privateAttributeListContainsFailureReason, attribute); + return NO; + } + if (property && ![privateAttributeList containsObject:attribute]) { + NSLog(privateAttributeListDoesNotContainFailureReason, attribute); + return NO; + } + } + } + } + + return YES; +} + +-(id)propertyForAttribute:(NSString*)attribute { + NSArray *attributeList = @[kUserAttributeKey, kUserAttributeIp, kUserAttributeCountry, kUserAttributeName, kUserAttributeFirstName, kUserAttributeLastName, kUserAttributeEmail, kUserAttributeAvatar, kUserAttributeCustom, kUserAttributeUpdatedAt, kUserAttributeConfig, kUserAttributeAnonymous, kUserAttributeDevice, kUserAttributeOs]; + NSUInteger attributeIndex = [attributeList indexOfObject:attribute]; + if (attributeIndex != NSNotFound) { + switch (attributeIndex) { + case 0: return self.key; + case 1: return self.ip; + case 2: return self.country; + case 3: return self.name; + case 4: return self.firstName; + case 5: return self.lastName; + case 6: return self.email; + case 7: return self.avatar; + case 8: return self.custom; + case 9: return self.updatedAt; + case 10: return self.flagConfig; + case 11: return @(self.anonymous); + case 12: return self.device; + case 13: return self.os; + } + } + + return self.custom[attribute]; +} + @end diff --git a/DarklyTests/Categories/NSArray+Testable.h b/DarklyTests/Categories/NSArray+Testable.h new file mode 100644 index 00000000..d2e3c64f --- /dev/null +++ b/DarklyTests/Categories/NSArray+Testable.h @@ -0,0 +1,13 @@ +// +// Copyright © 2015 Catamorphic Co. All rights reserved. +// + +#import + +@class LDFlagValueCounter; +@class LDEventModel; + +@interface NSArray (Testable) +-(NSDictionary*)dictionaryForFlagValueCounter:(LDFlagValueCounter*)flagValueCounter; +-(NSDictionary*)dictionaryForEvent:(LDEventModel*)event; +@end diff --git a/DarklyTests/Categories/NSArray+Testable.m b/DarklyTests/Categories/NSArray+Testable.m new file mode 100644 index 00000000..f530c465 --- /dev/null +++ b/DarklyTests/Categories/NSArray+Testable.m @@ -0,0 +1,36 @@ +// +// Copyright © 2015 Catamorphic Co. All rights reserved. +// + +#import "NSArray+Testable.h" +#import "LDFlagConfigValue.h" +#import "LDFlagValueCounter+Testable.h" +#import "LDEventModel+Testable.h" + +@implementation NSArray (Testable) +-(NSDictionary*)dictionaryForFlagValueCounter:(LDFlagValueCounter*)flagValueCounter { + if (self.count == 0) { return nil; } + if (!flagValueCounter) { return nil; } + NSPredicate *variationPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + if (![evaluatedObject isKindOfClass:[NSDictionary class]]) { return NO; } + NSDictionary *evaluatedDictionary = evaluatedObject; + if (!evaluatedDictionary[kLDFlagValueCounterKeyVariation]) { return NO; } + if (![evaluatedDictionary[kLDFlagValueCounterKeyVariation] isKindOfClass:[NSNumber class]]) { return NO; } + return [evaluatedDictionary[kLDFlagValueCounterKeyVariation] integerValue] == flagValueCounter.flagConfigValue.variation; + }]; + NSArray *selectedCounterDictionaries = [self filteredArrayUsingPredicate:variationPredicate]; + if (selectedCounterDictionaries.count != 1) { return nil; } + return [selectedCounterDictionaries firstObject]; +} + +-(NSDictionary*)dictionaryForEvent:(LDEventModel*)event { + NSPredicate *eventPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + if (![evaluatedObject isKindOfClass:[NSDictionary class]]) { return NO; } + NSDictionary *evaluatedEventDictionary = evaluatedObject; + return [evaluatedEventDictionary[kEventModelKeyKind] isEqualToString:event.kind]; + }]; + NSArray *selectedEventDictionaries = [self filteredArrayUsingPredicate:eventPredicate]; + if (selectedEventDictionaries.count != 1) { return nil; } + return [selectedEventDictionaries firstObject]; +} +@end diff --git a/DarklyTests/Categories/NSArray+UnitTests.h b/DarklyTests/Categories/NSArray+UnitTests.h deleted file mode 100644 index 1df136fb..00000000 --- a/DarklyTests/Categories/NSArray+UnitTests.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright © 2015 Catamorphic Co. All rights reserved. -// - -#import - -@interface NSArray (UnitTests) -- (NSArray *) flatten; -@end diff --git a/DarklyTests/Categories/NSArray+UnitTests.m b/DarklyTests/Categories/NSArray+UnitTests.m deleted file mode 100644 index 658829d9..00000000 --- a/DarklyTests/Categories/NSArray+UnitTests.m +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright © 2015 Catamorphic Co. All rights reserved. -// - -#import "NSArray+UnitTests.h" - -@implementation NSArray (UnitTests) -- (NSArray *) flatten { - NSMutableArray *flattedArray = [NSMutableArray new]; - - for (id item in self) { - if ([[item class] isSubclassOfClass:[NSArray class]]) { - [flattedArray addObjectsFromArray:[item flatten]]; - } else { - [flattedArray addObject:item]; - } - } - - return flattedArray; -} -@end diff --git a/DarklyTests/Categories/NSDate+Testable.h b/DarklyTests/Categories/NSDate+Testable.h new file mode 100644 index 00000000..f6cc7ced --- /dev/null +++ b/DarklyTests/Categories/NSDate+Testable.h @@ -0,0 +1,12 @@ +// +// NSDate+Testable.h +// DarklyTests +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "NSDate+ReferencedDate.h" + +bool Approximately(LDMillisecond num1, LDMillisecond num2, LDMillisecond range); diff --git a/DarklyTests/Categories/NSDate+Testable.m b/DarklyTests/Categories/NSDate+Testable.m new file mode 100644 index 00000000..0bf57d10 --- /dev/null +++ b/DarklyTests/Categories/NSDate+Testable.m @@ -0,0 +1,13 @@ +// +// NSInteger+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "NSDate+Testable.h" + +bool Approximately(LDMillisecond num1, LDMillisecond num2, LDMillisecond range) { + return labs(num1 - num2) <= range; +} diff --git a/DarklyTests/Categories/NSDateFormatter+JsonHeader+Testable.h b/DarklyTests/Categories/NSDateFormatter+JsonHeader+Testable.h new file mode 100644 index 00000000..8e570e05 --- /dev/null +++ b/DarklyTests/Categories/NSDateFormatter+JsonHeader+Testable.h @@ -0,0 +1,15 @@ +// +// NSDateFormatter+JsonHeader+Testable.h +// DarklyTests +// +// Created by Mark Pokorny on 5/8/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import + +extern NSString * const kDateHeaderValueDate; + +@interface NSDateFormatter(JsonHeader_Testable) ++(NSDate*)eventDateHeaderStub; +@end diff --git a/DarklyTests/Categories/NSDateFormatter+JsonHeader+Testable.m b/DarklyTests/Categories/NSDateFormatter+JsonHeader+Testable.m new file mode 100644 index 00000000..cf7de4a1 --- /dev/null +++ b/DarklyTests/Categories/NSDateFormatter+JsonHeader+Testable.m @@ -0,0 +1,18 @@ +// +// NSDateFormatter+JsonHeader+Testable.m +// DarklyTests +// +// Created by Mark Pokorny on 5/8/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import "NSDateFormatter+JsonHeader.h" +#import "NSDateFormatter+JsonHeader+Testable.h" + +NSString * const kDateHeaderValueDate = @"Mon, 07 May 2018 19:46:29 GMT"; + +@implementation NSDateFormatter(JsonHeader_Testable) ++(NSDate*)eventDateHeaderStub { + return [[NSDateFormatter jsonHeaderDateFormatter] dateFromString:kDateHeaderValueDate]; +} +@end diff --git a/DarklyTests/Categories/NSDictionary+StringKey_Matchable.m b/DarklyTests/Categories/NSDictionary+StringKey_Matchable.m index 6c1feb82..e5ce08bc 100644 --- a/DarklyTests/Categories/NSDictionary+StringKey_Matchable.m +++ b/DarklyTests/Categories/NSDictionary+StringKey_Matchable.m @@ -51,6 +51,7 @@ -(NSArray*)keysWithDifferentValuesIn:(id)object ignoringKeys:(NSArray*)ignoreKey } } else { + if (dictionaryValue.count == 0 && otherDictionaryValue.count == 0) { continue; } //isEqualToDictionary fails for empty dictionaries!! if ([dictionaryValue isEqualToDictionary:otherDictionary]) { continue; } [differingKeys addObject:key]; } @@ -69,7 +70,7 @@ -(NSArray*)keysWithDifferentValuesIn:(id)object ignoringKeys:(NSArray*)ignoreKey -(NSArray*)ignoreKeysForKey:(NSString*)key fromKeys:(NSArray*)ignoreKeys { if ([key length] == 0 || [ignoreKeys count] == 0) { return nil; } NSString *prefix = [NSString stringWithFormat:@"%@.", key]; - NSPredicate *matchingPrefixPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + NSPredicate *matchingPrefixPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { if (![evaluatedObject isKindOfClass:[NSString class]]) { return NO; } NSString *evaluatedKey = (NSString*)evaluatedObject; return [evaluatedKey hasPrefix:prefix]; @@ -77,7 +78,7 @@ -(NSArray*)ignoreKeysForKey:(NSString*)key fromKeys:(NSArray*)ignoreKeys { NSArray *matchingIgnoreKeys = [ignoreKeys filteredArrayUsingPredicate:matchingPrefixPredicate]; if ([matchingIgnoreKeys count] == 0) { return matchingIgnoreKeys; } NSMutableArray *unwrappedMatchingIgnoreKeys = [NSMutableArray arrayWithCapacity:[matchingIgnoreKeys count]]; - [matchingIgnoreKeys enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [matchingIgnoreKeys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSString *wrappedKey = (NSString*)obj; NSString *unwrappedKey = [wrappedKey stringByReplacingOccurrencesOfString:prefix withString:@""]; [unwrappedMatchingIgnoreKeys addObject:unwrappedKey]; diff --git a/DarklyTests/Categories/NSHTTPURLResponse+LaunchDarkly+Testable.h b/DarklyTests/Categories/NSHTTPURLResponse+LaunchDarkly+Testable.h new file mode 100644 index 00000000..a69c71bf --- /dev/null +++ b/DarklyTests/Categories/NSHTTPURLResponse+LaunchDarkly+Testable.h @@ -0,0 +1,9 @@ +// +// NSHTTPURLResponse+LaunchDarkly+Testable.h +// Darkly +// +// Created by Mark Pokorny on 5/8/18. +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +extern NSString * const kHeaderKeyDate; diff --git a/DarklyTests/DarklyXCTestCase.h b/DarklyTests/DarklyXCTestCase.h index 4817bbb4..d654ed75 100644 --- a/DarklyTests/DarklyXCTestCase.h +++ b/DarklyTests/DarklyXCTestCase.h @@ -6,6 +6,5 @@ #import "DarklyConstants.h" @interface DarklyXCTestCase : XCTestCase -@property (nonatomic) id dataManagerMock; @end diff --git a/DarklyTests/Fixtures/arrayConfigIsAnArray-1.json b/DarklyTests/Fixtures/arrayConfigIsAnArray-1.json index 77a91ea8..a03d20c6 100644 --- a/DarklyTests/Fixtures/arrayConfigIsAnArray-1.json +++ b/DarklyTests/Fixtures/arrayConfigIsAnArray-1.json @@ -1,6 +1,8 @@ { "isAnArray": { "value": [1], - "version": 4 + "variation": 4, + "version": 4, + "flagVersion": 3 } } diff --git a/DarklyTests/Fixtures/arrayConfigIsAnArray-123-withVersion.json b/DarklyTests/Fixtures/arrayConfigIsAnArray-123-withVersion.json deleted file mode 100644 index 37c8a19d..00000000 --- a/DarklyTests/Fixtures/arrayConfigIsAnArray-123-withVersion.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "isAnArray": { - "value": [1,2,3], - "version": 5 - } -} diff --git a/DarklyTests/Fixtures/arrayConfigIsAnArray-123-withoutVersion.json b/DarklyTests/Fixtures/arrayConfigIsAnArray-123-withoutVersion.json deleted file mode 100644 index 348ca26e..00000000 --- a/DarklyTests/Fixtures/arrayConfigIsAnArray-123-withoutVersion.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "isAnArray": [1,2,3] -} diff --git a/DarklyTests/Fixtures/arrayConfigIsAnArray-123.json b/DarklyTests/Fixtures/arrayConfigIsAnArray-123.json new file mode 100644 index 00000000..b9aca222 --- /dev/null +++ b/DarklyTests/Fixtures/arrayConfigIsAnArray-123.json @@ -0,0 +1,8 @@ +{ + "isAnArray": { + "value": [1,2,3], + "variation": 5, + "version": 5, + "flagVersion": 4 + } +} diff --git a/DarklyTests/Fixtures/arrayConfigIsAnArray-Empty.json b/DarklyTests/Fixtures/arrayConfigIsAnArray-Empty.json index 6d38a093..24be6940 100644 --- a/DarklyTests/Fixtures/arrayConfigIsAnArray-Empty.json +++ b/DarklyTests/Fixtures/arrayConfigIsAnArray-Empty.json @@ -1,6 +1,8 @@ { "isAnArray": { "value": [], - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/arrayConfigIsAnArrayA-123.json b/DarklyTests/Fixtures/arrayConfigIsAnArrayA-123.json index fc4545b3..5b4a7a14 100644 --- a/DarklyTests/Fixtures/arrayConfigIsAnArrayA-123.json +++ b/DarklyTests/Fixtures/arrayConfigIsAnArrayA-123.json @@ -1,6 +1,8 @@ { "isAnArrayA": { "value": [1,2,3], - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/boolConfigIsABool-false.json b/DarklyTests/Fixtures/boolConfigIsABool-false.json index 8b8e371d..167c2d4f 100644 --- a/DarklyTests/Fixtures/boolConfigIsABool-false.json +++ b/DarklyTests/Fixtures/boolConfigIsABool-false.json @@ -1,6 +1,8 @@ { "isABool": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/boolConfigIsABool-true-withVersion.json b/DarklyTests/Fixtures/boolConfigIsABool-true-withVersion.json deleted file mode 100644 index 1c416808..00000000 --- a/DarklyTests/Fixtures/boolConfigIsABool-true-withVersion.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "isABool": { - "value": true, - "version": 4 - } -} diff --git a/DarklyTests/Fixtures/boolConfigIsABool-true-withoutVersion.json b/DarklyTests/Fixtures/boolConfigIsABool-true-withoutVersion.json deleted file mode 100644 index 2d851959..00000000 --- a/DarklyTests/Fixtures/boolConfigIsABool-true-withoutVersion.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "isABool": true -} diff --git a/DarklyTests/Fixtures/boolConfigIsABool-true.json b/DarklyTests/Fixtures/boolConfigIsABool-true.json new file mode 100644 index 00000000..19439f97 --- /dev/null +++ b/DarklyTests/Fixtures/boolConfigIsABool-true.json @@ -0,0 +1,8 @@ +{ + "isABool": { + "value": true, + "variation": 4, + "version": 4, + "flagVersion": 3 + } +} diff --git a/DarklyTests/Fixtures/boolConfigIsABool2-true.json b/DarklyTests/Fixtures/boolConfigIsABool2-true.json index 374c5769..0c6a336e 100644 --- a/DarklyTests/Fixtures/boolConfigIsABool2-true.json +++ b/DarklyTests/Fixtures/boolConfigIsABool2-true.json @@ -1,6 +1,8 @@ { "isABool2": { "value": true, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key-withoutVersion.json b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key-withoutVersion.json deleted file mode 100644 index ea01a201..00000000 --- a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key-withoutVersion.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "isADictionary": { - "keyA": true, - "keyB": [1, 2, 3], - "keyC": { - "keyD": "someStringValue" - } - } -} diff --git a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key-withVersion.json b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key.json similarity index 73% rename from DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key-withVersion.json rename to DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key.json index ee53482d..13c8559c 100644 --- a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key-withVersion.json +++ b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-3Key.json @@ -7,6 +7,8 @@ "keyD": "someStringValue" } }, - "version": 4 + "variation": 4, + "version": 4, + "flagVersion": 3 } } diff --git a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-Empty.json b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-Empty.json index a0104819..50fe3cff 100644 --- a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-Empty.json +++ b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-Empty.json @@ -1,10 +1,8 @@ -{ - "isADictionary":{} -} { "isADictionary": { - "value": {} - }, - "version": 3 + "value": {}, + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyA.json b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyA.json index 49987b7f..8b7de531 100644 --- a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyA.json +++ b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyA.json @@ -3,6 +3,8 @@ "value": { "keyA": true }, - "version": 5 + "variation": 5, + "version": 5, + "flagVersion": 4 } } diff --git a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyB-124.json b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyB-124.json index d13c2033..edfa5082 100644 --- a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyB-124.json +++ b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyB-124.json @@ -3,6 +3,8 @@ "value": { "keyB": [1, 2, 4] }, - "version": 5 + "variation": 5, + "version": 5, + "flagVersion": 4 } } diff --git a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyB.json b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyB.json index 5cc60a10..8cf645d2 100644 --- a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyB.json +++ b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyB.json @@ -3,6 +3,8 @@ "value": { "keyB": [1, 2, 3] }, - "version": 5 + "variation": 5, + "version": 5, + "flagVersion": 4 } } diff --git a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyC-keyDValueDiffers.json b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyC-keyDValueDiffers.json index c97ede1f..bf3af158 100644 --- a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyC-keyDValueDiffers.json +++ b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyC-keyDValueDiffers.json @@ -5,6 +5,8 @@ "keyD": "someStringValueA" } }, - "version": 5 + "variation": 5, + "version": 5, + "flagVersion": 4 } } diff --git a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyC.json b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyC.json index a17be9c7..2b401018 100644 --- a/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyC.json +++ b/DarklyTests/Fixtures/dictionaryConfigIsADictionary-KeyC.json @@ -5,6 +5,8 @@ "keyD": "someStringValue" } }, - "version": 5 + "variation": 5, + "version": 5, + "flagVersion": 4 } } diff --git a/DarklyTests/Fixtures/doubleConfigIsADouble-Pi-withoutVersion.json b/DarklyTests/Fixtures/doubleConfigIsADouble-Pi-withoutVersion.json deleted file mode 100644 index eabc5428..00000000 --- a/DarklyTests/Fixtures/doubleConfigIsADouble-Pi-withoutVersion.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "isADouble": 3.141592653589793238462643383279502884197169399375105820974944592307816406286 -} diff --git a/DarklyTests/Fixtures/doubleConfigIsADouble-Pi-withVersion.json b/DarklyTests/Fixtures/doubleConfigIsADouble-Pi.json similarity index 63% rename from DarklyTests/Fixtures/doubleConfigIsADouble-Pi-withVersion.json rename to DarklyTests/Fixtures/doubleConfigIsADouble-Pi.json index 72b10aa0..88708856 100644 --- a/DarklyTests/Fixtures/doubleConfigIsADouble-Pi-withVersion.json +++ b/DarklyTests/Fixtures/doubleConfigIsADouble-Pi.json @@ -1,6 +1,8 @@ { "isADouble": { "value": 3.141592653589793238462643383279502884197169399375105820974944592307816406286, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/doubleConfigIsADouble-e.json b/DarklyTests/Fixtures/doubleConfigIsADouble-e.json new file mode 100644 index 00000000..27cbe426 --- /dev/null +++ b/DarklyTests/Fixtures/doubleConfigIsADouble-e.json @@ -0,0 +1,8 @@ +{ + "isADouble": { + "value": 2.718281828459045235360287471352662497757247093699959574966967627724076630353, + "variation": 4, + "version": 4, + "flagVersion": 3 + } +} diff --git a/DarklyTests/Fixtures/featureFlags-excludeNulls-withoutVersions.json b/DarklyTests/Fixtures/featureFlags-excludeNulls-withoutVersions.json deleted file mode 100644 index acaafef7..00000000 --- a/DarklyTests/Fixtures/featureFlags-excludeNulls-withoutVersions.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "_links": { - "href": "/api/eval/users/eyJrZXkiOiAiamVmZkB0ZXN0LmNvbSJ9", - "type": "application/json" - }, - "devices.hasipad": false, - "isAString": "test", - "isAArray": [0, 1, 2], - "isAObject": { - "key": 0 - }, - "isANumber": 0, - "isABawler": true -} diff --git a/DarklyTests/Fixtures/featureFlags-excludeNulls-withVersions.json b/DarklyTests/Fixtures/featureFlags-excludeNulls.json similarity index 50% rename from DarklyTests/Fixtures/featureFlags-excludeNulls-withVersions.json rename to DarklyTests/Fixtures/featureFlags-excludeNulls.json index a3be7742..5c5ff8af 100644 --- a/DarklyTests/Fixtures/featureFlags-excludeNulls-withVersions.json +++ b/DarklyTests/Fixtures/featureFlags-excludeNulls.json @@ -4,32 +4,46 @@ "href": "/api/eval/users/eyJrZXkiOiAiamVmZkB0ZXN0LmNvbSJ9", "type": "application/json" }, - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 }, "devices.hasipad": { "value": false, - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 }, "isAString": { "value": "test", - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 }, "isAArray": { "value": [0, 1, 2], - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 }, "isAObject": { "value": { "key": 0 }, - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 }, "isANumber": { "value": 0, - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 }, "isABawler": { "value": true, - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 } } diff --git a/DarklyTests/Fixtures/featureFlags-withVersions.json b/DarklyTests/Fixtures/featureFlags-withVersions.json deleted file mode 100644 index 7f540606..00000000 --- a/DarklyTests/Fixtures/featureFlags-withVersions.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "_links": { - "value": { - "href": "/api/eval/users/eyJrZXkiOiAiamVmZkB0ZXN0LmNvbSJ9", - "type": "application/json" - }, - "version": 2 - }, - "devices.hasipad": { - "value": false, - "version": 2 - }, - "isConnected": { - "value": null, - "version": 2 - }, - "isAString": { - "value": "test", - "version": 2 - }, - "isAArray": { - "value": [0, 1, 2], - "version": 2 - }, - "isAObject": { - "value": { - "key": 0 - }, - "version": 2 - }, - "isANumber": { - "value": 0, - "version": 2 - }, - "isABawler": { - "value": true, - "version": 2 - } -} diff --git a/DarklyTests/Fixtures/featureFlags-withoutVersions.json b/DarklyTests/Fixtures/featureFlags-withoutVersions.json deleted file mode 100644 index 3c15489e..00000000 --- a/DarklyTests/Fixtures/featureFlags-withoutVersions.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "_links": { - "href": "/api/eval/users/eyJrZXkiOiAiamVmZkB0ZXN0LmNvbSJ9", - "type": "application/json" - }, - "devices.hasipad": false, - "isConnected": null, - "isAString": "test", - "isAArray": [0, 1, 2], - "isAObject": { - "key": 0 - }, - "isANumber": 0, - "isABawler": true -} diff --git a/DarklyTests/Fixtures/featureFlags.json b/DarklyTests/Fixtures/featureFlags.json new file mode 100644 index 00000000..bc0d5c25 --- /dev/null +++ b/DarklyTests/Fixtures/featureFlags.json @@ -0,0 +1,55 @@ +{ + "_links": { + "value": { + "href": "/api/eval/users/eyJrZXkiOiAiamVmZkB0ZXN0LmNvbSJ9", + "type": "application/json" + }, + "variation": 2, + "version": 2, + "flagVersion": 1 + }, + "devices.hasipad": { + "value": false, + "variation": 2, + "version": 2, + "flagVersion": 1 + }, + "isConnected": { + "value": null, + "variation": 2, + "version": 2, + "flagVersion": 1 + }, + "isAString": { + "value": "test", + "variation": 2, + "version": 2, + "flagVersion": 1 + }, + "isAArray": { + "value": [0, 1, 2], + "variation": 2, + "version": 2, + "flagVersion": 1 + }, + "isAObject": { + "value": { + "key": 0 + }, + "variation": 2, + "version": 2, + "flagVersion": 1 + }, + "isANumber": { + "value": 0, + "variation": 2, + "version": 2, + "flagVersion": 1 + }, + "isABawler": { + "value": true, + "variation": 2, + "version": 2, + "flagVersion": 1 + } +} diff --git a/DarklyTests/Fixtures/ldClientManagerTestConfigA.json b/DarklyTests/Fixtures/ldClientManagerTestConfigA.json index 0b501661..350f7362 100644 --- a/DarklyTests/Fixtures/ldClientManagerTestConfigA.json +++ b/DarklyTests/Fixtures/ldClientManagerTestConfigA.json @@ -1,32 +1,46 @@ { "ddeefe": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "main-slider": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "new-feature": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "new-gallery": { "value": { "foo": 2 }, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "one-click-checkout": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "shopping-cart": { "value": 20, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "social-icon-toggle": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/ldClientManagerTestConfigB.json b/DarklyTests/Fixtures/ldClientManagerTestConfigB.json index 98281a99..7ac87da0 100644 --- a/DarklyTests/Fixtures/ldClientManagerTestConfigB.json +++ b/DarklyTests/Fixtures/ldClientManagerTestConfigB.json @@ -1,32 +1,46 @@ { "ddeefe": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "main-slider": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "new-feature": { "value": true, - "version": 4 + "variation": 4, + "version": 4, + "flagVersion": 3 }, "new-gallery": { "value": { "foo": 2 }, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "one-click-checkout": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "shopping-cart": { "value": 20, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 }, "social-icon-toggle": { "value": false, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/ldClientManagerTestDeleteIsANumber.json b/DarklyTests/Fixtures/ldClientManagerTestDeleteIsANumber.json index 8998caef..66c4e5cd 100644 --- a/DarklyTests/Fixtures/ldClientManagerTestDeleteIsANumber.json +++ b/DarklyTests/Fixtures/ldClientManagerTestDeleteIsANumber.json @@ -1,5 +1,6 @@ { "key": "isANumber", "value": 8675309, + "variation": 3, "version": 3 } diff --git a/DarklyTests/Fixtures/ldClientManagerTestPatchIsANumber.json b/DarklyTests/Fixtures/ldClientManagerTestPatchIsANumber.json index 31cae209..3b2c6ac7 100644 --- a/DarklyTests/Fixtures/ldClientManagerTestPatchIsANumber.json +++ b/DarklyTests/Fixtures/ldClientManagerTestPatchIsANumber.json @@ -1,4 +1,7 @@ { "key": "isANumber", - "version": 3 + "value": 8675309, + "variation": 3, + "version": 3, + "flagVersion": 2 } diff --git a/DarklyTests/Fixtures/ldDataManagerTestConfig.json b/DarklyTests/Fixtures/ldDataManagerTestConfig.json deleted file mode 100644 index dbe81959..00000000 --- a/DarklyTests/Fixtures/ldDataManagerTestConfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "iosuser": { - "value": false, - "version": 2 - }, - "ipaduser": { - "value": true, - "version": 2 - } -} diff --git a/DarklyTests/Fixtures/ldFlagConfigModelDeleteVersion2Flag.json b/DarklyTests/Fixtures/ldFlagConfigModelDeleteVersion2Flag.json index 7f268ee4..b35a7132 100644 --- a/DarklyTests/Fixtures/ldFlagConfigModelDeleteVersion2Flag.json +++ b/DarklyTests/Fixtures/ldFlagConfigModelDeleteVersion2Flag.json @@ -1,4 +1,5 @@ { "key": "version2Flag", + "variation": 3, "version": 3 } diff --git a/DarklyTests/Fixtures/ldFlagConfigModelPatchNewFlag.json b/DarklyTests/Fixtures/ldFlagConfigModelPatchNewFlag.json index 0b0bcfca..d7a346a8 100644 --- a/DarklyTests/Fixtures/ldFlagConfigModelPatchNewFlag.json +++ b/DarklyTests/Fixtures/ldFlagConfigModelPatchNewFlag.json @@ -1,5 +1,7 @@ { "key": "someNewFlag", "value": true, - "version": 1 + "variation": 1, + "version": 1, + "flagVersion": 0 } diff --git a/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion1Flag.json b/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion1Flag.json index 32f05e3e..b5b3d8f0 100644 --- a/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion1Flag.json +++ b/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion1Flag.json @@ -1,5 +1,7 @@ { "key": "version1Flag", "value": true, - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 } diff --git a/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion2Flag.json b/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion2Flag.json index 83a1f2a2..f44c27b0 100644 --- a/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion2Flag.json +++ b/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion2Flag.json @@ -1,5 +1,7 @@ { "key": "version2Flag", "value": "version2patched", - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } diff --git a/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion2FlagWithNull.json b/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion2FlagWithNull.json index 7d27f409..8e3e778f 100644 --- a/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion2FlagWithNull.json +++ b/DarklyTests/Fixtures/ldFlagConfigModelPatchVersion2FlagWithNull.json @@ -1,5 +1,7 @@ { "key": "version2Flag", "value": null, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } diff --git a/DarklyTests/Fixtures/ldFlagConfigModelTest.json b/DarklyTests/Fixtures/ldFlagConfigModelTest.json index 0419c53f..82f8f83e 100644 --- a/DarklyTests/Fixtures/ldFlagConfigModelTest.json +++ b/DarklyTests/Fixtures/ldFlagConfigModelTest.json @@ -1,14 +1,20 @@ { "version1Flag": { "value": false, - "version": 1 + "variation": 1, + "version": 1, + "flagVersion": 0 }, "version2Flag": { "value": "version2", - "version": 2 + "variation": 2, + "version": 2, + "flagVersion": 1 }, "version3Flag": { "value": 3, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/nullConfigIsANull-null-withVersion.json b/DarklyTests/Fixtures/nullConfigIsANull-null-withVersion.json deleted file mode 100644 index 55730335..00000000 --- a/DarklyTests/Fixtures/nullConfigIsANull-null-withVersion.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "isANull": { - "value": null, - "version": 2 - } -} diff --git a/DarklyTests/Fixtures/nullConfigIsANull-null-withoutVersion.json b/DarklyTests/Fixtures/nullConfigIsANull-null-withoutVersion.json deleted file mode 100644 index ade66043..00000000 --- a/DarklyTests/Fixtures/nullConfigIsANull-null-withoutVersion.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "isANull": null -} diff --git a/DarklyTests/Fixtures/nullConfigIsANull-null.json b/DarklyTests/Fixtures/nullConfigIsANull-null.json new file mode 100644 index 00000000..2b66db88 --- /dev/null +++ b/DarklyTests/Fixtures/nullConfigIsANull-null.json @@ -0,0 +1,8 @@ +{ + "isANull": { + "value": null, + "variation": 2, + "version": 2, + "flagVersion": 1 + } +} diff --git a/DarklyTests/Fixtures/numberConfigIsANumber-1.json b/DarklyTests/Fixtures/numberConfigIsANumber-1.json index 1056c3bd..b58e8f1b 100644 --- a/DarklyTests/Fixtures/numberConfigIsANumber-1.json +++ b/DarklyTests/Fixtures/numberConfigIsANumber-1.json @@ -1,6 +1,8 @@ { "isANumber": { "value": 1, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/numberConfigIsANumber-2-withVersion.json b/DarklyTests/Fixtures/numberConfigIsANumber-2-withVersion.json deleted file mode 100644 index 43064bfb..00000000 --- a/DarklyTests/Fixtures/numberConfigIsANumber-2-withVersion.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "isANumber": { - "value": 2, - "version": 4 - } -} diff --git a/DarklyTests/Fixtures/numberConfigIsANumber-2-withoutVersion.json b/DarklyTests/Fixtures/numberConfigIsANumber-2-withoutVersion.json deleted file mode 100644 index a16d79b4..00000000 --- a/DarklyTests/Fixtures/numberConfigIsANumber-2-withoutVersion.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "isANumber": 2 -} diff --git a/DarklyTests/Fixtures/numberConfigIsANumber-2.json b/DarklyTests/Fixtures/numberConfigIsANumber-2.json new file mode 100644 index 00000000..4c4a1ed0 --- /dev/null +++ b/DarklyTests/Fixtures/numberConfigIsANumber-2.json @@ -0,0 +1,8 @@ +{ + "isANumber": { + "value": 2, + "variation": 4, + "version": 4, + "flagVersion": 3 + } +} diff --git a/DarklyTests/Fixtures/numberConfigIsANumber2-1.json b/DarklyTests/Fixtures/numberConfigIsANumber2-1.json index 21964519..8b944776 100644 --- a/DarklyTests/Fixtures/numberConfigIsANumber2-1.json +++ b/DarklyTests/Fixtures/numberConfigIsANumber2-1.json @@ -1,6 +1,8 @@ { "isANumber2": { "value": 1, - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/Fixtures/stringConfigIsAString-someString-withVersion.json b/DarklyTests/Fixtures/stringConfigIsAString-someString-withVersion.json deleted file mode 100644 index 20aff5bf..00000000 --- a/DarklyTests/Fixtures/stringConfigIsAString-someString-withVersion.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "isAString": { - "value": "someString", - "version": 3 - } -} diff --git a/DarklyTests/Fixtures/stringConfigIsAString-someString-withoutVersion.json b/DarklyTests/Fixtures/stringConfigIsAString-someString-withoutVersion.json deleted file mode 100644 index 36d0761d..00000000 --- a/DarklyTests/Fixtures/stringConfigIsAString-someString-withoutVersion.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "isAString": "someString" -} diff --git a/DarklyTests/Fixtures/stringConfigIsAString-someString.json b/DarklyTests/Fixtures/stringConfigIsAString-someString.json new file mode 100644 index 00000000..7e0e5157 --- /dev/null +++ b/DarklyTests/Fixtures/stringConfigIsAString-someString.json @@ -0,0 +1,8 @@ +{ + "isAString": { + "value": "someString", + "variation": 3, + "version": 3, + "flagVersion": 2 + } +} diff --git a/DarklyTests/Fixtures/stringConfigIsAString-someStringA.json b/DarklyTests/Fixtures/stringConfigIsAString-someStringA.json index 2b7fb193..bea78d9b 100644 --- a/DarklyTests/Fixtures/stringConfigIsAString-someStringA.json +++ b/DarklyTests/Fixtures/stringConfigIsAString-someStringA.json @@ -1,6 +1,8 @@ { "isAString": { "value": "someStringA", - "version": 4 + "variation": 4, + "version": 4, + "flagVersion": 3 } } diff --git a/DarklyTests/Fixtures/stringConfigIsAStringA-someString.json b/DarklyTests/Fixtures/stringConfigIsAStringA-someString.json index 68dd2359..98f5467e 100644 --- a/DarklyTests/Fixtures/stringConfigIsAStringA-someString.json +++ b/DarklyTests/Fixtures/stringConfigIsAStringA-someString.json @@ -1,6 +1,8 @@ { "isAStringA": { "value": "someString", - "version": 3 + "variation": 3, + "version": 3, + "flagVersion": 2 } } diff --git a/DarklyTests/LDClientManagerTest.m b/DarklyTests/LDClientManagerTest.m index 3b93a3e3..ea897165 100644 --- a/DarklyTests/LDClientManagerTest.m +++ b/DarklyTests/LDClientManagerTest.m @@ -11,19 +11,27 @@ #import "LDDataManager.h" #import "LDPollingManager.h" #import "LDEventModel.h" +#import "LDEventModel+Testable.h" #import "LDClientManager+EventSource.h" #import "LDEvent+Testable.h" #import "LDEvent+EventTypes.h" #import "LDFlagConfigModel+Testable.h" -#import "LDUserModel+Stub.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDEventTrackingContext.h" +#import "LDEventTrackingContext+Testable.h" +#import "LDUserModel+Testable.h" #import "NSJSONSerialization+Testable.h" -#import "LDUserModel+Equatable.h" - -extern NSString * _Nonnull const kLDFlagConfigJsonDictionaryKeyValue; -extern NSString * _Nonnull const kLDFlagConfigJsonDictionaryKeyVersion; +#import "LDFlagConfigTracker+Testable.h" +#import "NSDate+ReferencedDate.h" +#import "NSDate+Testable.h" +#import "NSDateFormatter+JsonHeader+Testable.h" +#import "NSDate+ReferencedDate.h" + +extern NSString * _Nonnull const kLDFlagConfigValueKeyValue; +extern NSString * _Nonnull const kLDFlagConfigValueKeyVersion; extern NSString * _Nonnull const kLDClientManagerStreamMethod; - NSString *const mockMobileKey = @"mockMobileKey"; NSString *const kFeaturesJsonDictionary = @"featuresJsonDictionary"; NSString *const kBoolFlagKey = @"isABawler"; @@ -38,11 +46,6 @@ @interface LDClientManagerTest : DarklyXCTestCase @end @implementation LDClientManagerTest -@synthesize requestManagerMock; -@synthesize ldClientMock; -@synthesize dataManagerMock; -@synthesize pollingManagerMock; -@synthesize eventSourceMock; - (void)setUp { [super setUp]; @@ -51,38 +54,38 @@ - (void)setUp { LDConfig *config = [[LDConfig alloc] initWithMobileKey:mockMobileKey]; - ldClientMock = [self mockClientWithUser:user]; - OCMStub(ClassMethod([ldClientMock sharedInstance])).andReturn(ldClientMock); - [[[ldClientMock expect] andReturn: config] ldConfig]; - [[[ldClientMock expect] andReturn: user] ldUser]; + self.ldClientMock = [self mockClientWithUser:user]; + OCMStub(ClassMethod([self.ldClientMock sharedInstance])).andReturn(self.ldClientMock); + [[[self.ldClientMock expect] andReturn: config] ldConfig]; + [[[self.ldClientMock expect] andReturn: user] ldUser]; - requestManagerMock = OCMClassMock([LDRequestManager class]); - OCMStub(ClassMethod([requestManagerMock sharedInstance])).andReturn(requestManagerMock); + self.requestManagerMock = OCMClassMock([LDRequestManager class]); + OCMStub(ClassMethod([self.requestManagerMock sharedInstance])).andReturn(self.requestManagerMock); - dataManagerMock = OCMClassMock([LDDataManager class]); - OCMStub(ClassMethod([dataManagerMock sharedManager])).andReturn(dataManagerMock); + self.dataManagerMock = OCMClassMock([LDDataManager class]); + OCMStub(ClassMethod([self.dataManagerMock sharedManager])).andReturn(self.dataManagerMock); - pollingManagerMock = OCMClassMock([LDPollingManager class]); - OCMStub(ClassMethod([pollingManagerMock sharedInstance])).andReturn(pollingManagerMock); + self.pollingManagerMock = OCMClassMock([LDPollingManager class]); + OCMStub(ClassMethod([self.pollingManagerMock sharedInstance])).andReturn(self.pollingManagerMock); - eventSourceMock = OCMClassMock([LDEventSource class]); - OCMStub(ClassMethod([eventSourceMock eventSourceWithURL:[OCMArg any] httpHeaders:[OCMArg any] connectMethod:[OCMArg any] connectBody:[OCMArg any]])).andReturn(eventSourceMock); + self.eventSourceMock = OCMClassMock([LDEventSource class]); + OCMStub(ClassMethod([self.eventSourceMock eventSourceWithURL:[OCMArg any] httpHeaders:[OCMArg any] connectMethod:[OCMArg any] connectBody:[OCMArg any]])).andReturn(self.eventSourceMock); } - (void)tearDown { if (self.cleanup) { self.cleanup(); } [LDClientManager sharedInstance].online = NO; - [ldClientMock stopMocking]; - [requestManagerMock stopMocking]; - [dataManagerMock stopMocking]; - [pollingManagerMock stopMocking]; - [eventSourceMock stopMocking]; + [self.ldClientMock stopMocking]; + [self.requestManagerMock stopMocking]; + [self.dataManagerMock stopMocking]; + [self.pollingManagerMock stopMocking]; + [self.eventSourceMock stopMocking]; self.cleanup = nil; - ldClientMock = nil; - requestManagerMock = nil; - dataManagerMock = nil; - pollingManagerMock = nil; - eventSourceMock = nil; + self.ldClientMock = nil; + self.requestManagerMock = nil; + self.dataManagerMock = nil; + self.pollingManagerMock = nil; + self.eventSourceMock = nil; [super tearDown]; } @@ -96,7 +99,7 @@ - (void)testEventSourceConfiguredToConnectUsingGetMethod { __block NSURL *streamUrl; __block NSString *streamConnectMethod; __block NSData *streamConnectData; - OCMVerify([eventSourceMock eventSourceWithURL:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMVerify([self.eventSourceMock eventSourceWithURL:[OCMArg checkWithBlock:^BOOL(id obj) { if (![obj isKindOfClass:[NSURL class]]) { return NO; } streamUrl = obj; return YES; @@ -121,11 +124,11 @@ - (void)testEventSourceConfiguredToConnectUsingReportMethod { config.useReport = YES; LDUserModel *user = [LDClient sharedInstance].ldUser; - [ldClientMock stopMocking]; - ldClientMock = OCMClassMock([LDClient class]); - OCMStub(ClassMethod([ldClientMock sharedInstance])).andReturn(ldClientMock); - OCMStub([ldClientMock ldUser]).andReturn(user); - OCMStub([ldClientMock ldConfig]).andReturn(config); + [self.ldClientMock stopMocking]; + self.ldClientMock = OCMClassMock([LDClient class]); + OCMStub(ClassMethod([self.ldClientMock sharedInstance])).andReturn(self.ldClientMock); + OCMStub([self.ldClientMock ldUser]).andReturn(user); + OCMStub([self.ldClientMock ldConfig]).andReturn(config); LDClientManager *clientManager = [LDClientManager sharedInstance]; clientManager.online = YES; @@ -135,7 +138,7 @@ - (void)testEventSourceConfiguredToConnectUsingReportMethod { __block NSURL *streamUrl; __block NSString *streamConnectMethod; __block NSData *streamConnectData; - OCMVerify([eventSourceMock eventSourceWithURL:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMVerify([self.eventSourceMock eventSourceWithURL:[OCMArg checkWithBlock:^BOOL(id obj) { if (![obj isKindOfClass:[NSURL class]]) { return NO; } streamUrl = obj; return YES; @@ -219,20 +222,20 @@ - (void)testEventSourceRemainsConstantAcrossWillEnterForegroundCalls { } - (void)testSyncWithServerForConfigWhenUserExistsAndOnline { - [[requestManagerMock expect] performFeatureFlagRequest:[ldClientMock ldUser]]; + [[self.requestManagerMock expect] performFeatureFlagRequest:[self.ldClientMock ldUser]]; LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager setOnline:YES]; [clientManager syncWithServerForConfig]; - [requestManagerMock verify]; + [self.requestManagerMock verify]; } - (void)testSyncWithServerForConfigWhenUserDoesNotExist { - ldClientMock = [self mockClientWithUser:nil]; + self.ldClientMock = [self mockClientWithUser:nil]; - [[requestManagerMock reject] performFeatureFlagRequest:[OCMArg any]]; + [[self.requestManagerMock reject] performFeatureFlagRequest:[OCMArg any]]; LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager setOnline:YES]; @@ -241,7 +244,7 @@ - (void)testSyncWithServerForConfigWhenUserDoesNotExist { } - (void)testSyncWithServerForConfigWhenOffline { - [[requestManagerMock reject] performFeatureFlagRequest:[OCMArg any]]; + [[self.requestManagerMock reject] performFeatureFlagRequest:[OCMArg any]]; LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager setOnline:NO]; @@ -250,17 +253,23 @@ - (void)testSyncWithServerForConfigWhenOffline { } - (void)testSyncWithServerForEventsWhenEventsExist { - NSData *testData = [[NSData alloc] init]; + LDConfig *config = [[LDConfig alloc] initWithMobileKey:mockMobileKey]; + NSArray *eventDictionaries = [LDEventModel stubEventDictionariesForUser:[LDClient sharedInstance].ldUser config:config]; + OCMStub([self.dataManagerMock allEventDictionaries:[OCMArg checkWithBlock:^BOOL(id obj) { + void (^completion)(NSArray *) = obj; + completion(eventDictionaries); + return YES; + }]]); LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager setOnline:YES]; + LDMillisecond startDateMillis = [[NSDate date] millisSince1970]; + + [clientManager syncWithServerForEvents]; - [dataManagerMock allEventDictionaries:^(NSArray *array) { - OCMStub(array).andReturn(testData); - - [clientManager syncWithServerForEvents]; - - OCMVerify([self.requestManagerMock performEventRequest:[OCMArg isEqual:testData]]); - }]; + OCMVerify([self.requestManagerMock performEventRequest:[OCMArg isEqual:eventDictionaries]]); + XCTAssertNotNil([LDClient sharedInstance].ldUser.flagConfigTracker); + XCTAssertTrue([LDClient sharedInstance].ldUser.flagConfigTracker.flagCounters.count == 0); + XCTAssertTrue(Approximately([LDClient sharedInstance].ldUser.flagConfigTracker.startDateMillis, startDateMillis, 10)); } - (void)testDoNotSyncWithServerForEventsWhenEventsDoNotExist { @@ -268,27 +277,28 @@ - (void)testDoNotSyncWithServerForEventsWhenEventsDoNotExist { LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager setOnline:YES]; - [[requestManagerMock reject] performEventRequest:[OCMArg isEqual:testData]]; + [[self.requestManagerMock reject] performEventRequest:[OCMArg isEqual:testData]]; [clientManager syncWithServerForEvents]; - [requestManagerMock verify]; + [self.requestManagerMock verify]; } - (void)testSyncWithServerForEventsNotProcessedWhenOffline { - NSData *testData = [[NSData alloc] init]; - - [dataManagerMock allEventDictionaries:^(NSArray *array) { - OCMStub(array).andReturn(testData); - - [[self.requestManagerMock reject] performEventRequest:[OCMArg isEqual:testData]]; - - LDClientManager *clientManager = [LDClientManager sharedInstance]; - [clientManager setOnline:NO]; - [clientManager syncWithServerForEvents]; - - [self.requestManagerMock verify]; - }]; + LDConfig *config = [[LDConfig alloc] initWithMobileKey:mockMobileKey]; + NSArray *eventDictionaries = [LDEventModel stubEventDictionariesForUser:[LDClient sharedInstance].ldUser config:config]; + OCMStub([self.dataManagerMock allEventDictionaries:[OCMArg checkWithBlock:^BOOL(id obj) { + void (^completion)(NSArray *) = obj; + completion(eventDictionaries); + return YES; + }]]); + [[self.requestManagerMock reject] performEventRequest:[OCMArg any]]; + [[self.dataManagerMock reject] allEventDictionaries:[OCMArg any]]; + + [[LDClientManager sharedInstance] syncWithServerForEvents]; + + [self.requestManagerMock verify]; + [self.dataManagerMock verify]; } - (void)testStartPollingOnline { @@ -296,8 +306,8 @@ - (void)testStartPollingOnline { clientManager.online = YES; [clientManager startPolling]; - OCMVerify([pollingManagerMock startEventPolling]); - OCMVerify([eventSourceMock onMessage:[OCMArg isNotNil]]); + OCMVerify([self.pollingManagerMock startEventPolling]); + OCMVerify([self.eventSourceMock onMessage:[OCMArg isNotNil]]); } - (void)testStartPollingOffline { @@ -305,7 +315,7 @@ - (void)testStartPollingOffline { clientManager.online = NO; [clientManager startPolling]; - OCMReject([pollingManagerMock startEventPolling]); + OCMReject([self.pollingManagerMock startEventPolling]); XCTAssertNil(clientManager.eventSource); } @@ -313,7 +323,7 @@ - (void)testStopPolling { LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager stopPolling]; - OCMVerify([pollingManagerMock stopEventPolling]); + OCMVerify([self.pollingManagerMock stopEventPolling]); XCTAssertNil([clientManager eventSource]); } @@ -378,44 +388,53 @@ - (void)testWillEnterBackground { LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager willEnterBackground]; - OCMVerify([pollingManagerMock suspendEventPolling]); + OCMVerify([self.pollingManagerMock suspendEventPolling]); } - (void)testWillEnterForeground { LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager willEnterForeground]; - OCMVerify([pollingManagerMock resumeEventPolling]); + OCMVerify([self.pollingManagerMock resumeEventPolling]); } - (void)testProcessedEventsSuccessWithProcessedEvents { - LDEventModel *event = [[LDEventModel alloc] initFeatureEventWithKey:@"blah" keyValue:[NSNumber numberWithBool:NO] defaultKeyValue:[NSNumber numberWithBool:NO] userValue:[[LDClient sharedInstance] ldUser]]; LDConfig *config = [[LDConfig alloc] initWithMobileKey:@"testMobileKey"]; - - LDClientManager *clientManager = [LDClientManager sharedInstance]; - [clientManager processedEvents:YES jsonEventArray:@[[event dictionaryValueUsingConfig:config]]]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"boolConfigIsABool-true" + flagKey:kLDFlagKeyIsABool + eventTrackingContext:[LDEventTrackingContext stub]]; + LDEventModel *event = [LDEventModel featureEventWithFlagKey:kFeatureEventKeyStub + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:[LDClient sharedInstance].ldUser + inlineUser:config.inlineUserInEvents]; + NSArray *events = @[[event dictionaryValueUsingConfig:config]]; + NSDate *headerDate = [NSDateFormatter eventDateHeaderStub]; + [[self.dataManagerMock expect] deleteProcessedEvents:events]; + [[self.dataManagerMock expect] setLastEventResponseDate:headerDate]; + + [[LDClientManager sharedInstance] processedEvents:YES jsonEventArray:events responseDate:headerDate]; - OCMVerify([dataManagerMock deleteProcessedEvents:[OCMArg any]]); + [self.dataManagerMock verify]; } - (void)testProcessedEventsSuccessWithoutProcessedEvents { - - LDClientManager *clientManager = [LDClientManager sharedInstance]; - [clientManager processedEvents:YES jsonEventArray:@[]]; - - [[dataManagerMock reject] deleteProcessedEvents:[OCMArg any]]; - - [dataManagerMock verify]; + NSDate *headerDate = [NSDateFormatter eventDateHeaderStub]; + [[self.dataManagerMock expect] deleteProcessedEvents:@[]]; + [[self.dataManagerMock expect] setLastEventResponseDate:headerDate]; + + [[LDClientManager sharedInstance] processedEvents:YES jsonEventArray:@[] responseDate:headerDate]; + + [self.dataManagerMock verify]; } - (void)testProcessedEventsFailure { + [[self.dataManagerMock reject] deleteProcessedEvents:[OCMArg any]]; + + [[LDClientManager sharedInstance] processedEvents:NO jsonEventArray:nil responseDate:nil]; - LDClientManager *clientManager = [LDClientManager sharedInstance]; - [clientManager processedEvents:NO jsonEventArray:nil]; - - [[dataManagerMock reject] deleteProcessedEvents:[OCMArg any]]; - - [dataManagerMock verify]; + [self.dataManagerMock verify]; } - (void)testProcessedConfigSuccessWithUserConfigChanged { @@ -426,21 +445,21 @@ - (void)testProcessedConfigSuccessWithUserConfigChanged { id mockUserNoChangeObserver = OCMObserverMock(); //expect this NOT to be posted [[NSNotificationCenter defaultCenter] addMockObserver:mockUserNoChangeObserver name:kLDUserNoChangeNotification object:nil]; - LDFlagConfigModel *flagConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; + LDFlagConfigModel *flagConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"]; - LDUserModel *user = [ldClientMock ldUser]; - user.config = flagConfig; - [[[ldClientMock expect] andReturn:user] ldUser]; + LDUserModel *user = [self.ldClientMock ldUser]; + user.flagConfig = flagConfig; + [[[self.ldClientMock expect] andReturn:user] ldUser]; NSMutableDictionary *updatedFlags = [NSMutableDictionary dictionaryWithDictionary:[flagConfig dictionaryValue]]; NSMutableDictionary *updatedBoolFlag = [NSMutableDictionary dictionaryWithDictionary:updatedFlags[kBoolFlagKey]]; - updatedBoolFlag[kLDFlagConfigJsonDictionaryKeyValue] = @(![updatedBoolFlag[kLDFlagConfigJsonDictionaryKeyValue] boolValue]); - updatedBoolFlag[kLDFlagConfigJsonDictionaryKeyVersion] = @([updatedBoolFlag[kLDFlagConfigJsonDictionaryKeyVersion] integerValue] + 1); + updatedBoolFlag[kLDFlagConfigValueKeyValue] = @(![updatedBoolFlag[kLDFlagConfigValueKeyValue] boolValue]); + updatedBoolFlag[kLDFlagConfigValueKeyVersion] = @([updatedBoolFlag[kLDFlagConfigValueKeyVersion] integerValue] + 1); updatedFlags[kBoolFlagKey] = updatedBoolFlag; [[LDClientManager sharedInstance] processedConfig:YES jsonConfigDictionary:[updatedFlags copy]]; - OCMVerify([dataManagerMock saveUser:[OCMArg any]]); + OCMVerify([self.dataManagerMock saveUser:[OCMArg any]]); OCMVerifyAll(mockUserUpdatedObserver); OCMVerifyAll(mockUserNoChangeObserver); @@ -456,17 +475,26 @@ - (void)testProcessedConfigSuccessWithUserConfigUnchanged { [[NSNotificationCenter defaultCenter] addMockObserver:mockUserNoChangeObserver name:kLDUserNoChangeNotification object:nil]; [[mockUserNoChangeObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; - LDFlagConfigModel *flagConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext contextWithTrackEvents:NO debugEventsUntilDate:nil]; + LDFlagConfigModel *flagConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags" eventTrackingContext:eventTrackingContext]; - LDUserModel *user = [ldClientMock ldUser]; - user.config = flagConfig; - [[[ldClientMock expect] andReturn:user] ldUser]; + LDUserModel *user = [self.ldClientMock ldUser]; + user.flagConfig = flagConfig; + [[[self.ldClientMock expect] andReturn:user] ldUser]; - [[LDClientManager sharedInstance] processedConfig:YES jsonConfigDictionary:[flagConfig dictionaryValue]]; + LDEventTrackingContext *updatedEventTrackingContext = [LDEventTrackingContext contextWithTrackEvents:YES debugEventsUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]]; + LDFlagConfigModel *updatedFlagConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags" eventTrackingContext:updatedEventTrackingContext]; - OCMVerifyAll(dataManagerMock); + [[LDClientManager sharedInstance] processedConfig:YES jsonConfigDictionary:[updatedFlagConfig dictionaryValue]]; + + for (NSString *flagKey in flagConfig.featuresJsonDictionary.allKeys) { + LDFlagConfigValue *flagConfigValue = flagConfig.featuresJsonDictionary[flagKey]; + XCTAssertEqualObjects(flagConfigValue.eventTrackingContext, updatedEventTrackingContext); + } + + OCMVerifyAll(self.dataManagerMock); OCMVerifyAll(mockUserUpdatedObserver); OCMVerifyAll(mockUserNoChangeObserver); @@ -478,18 +506,18 @@ - (void)testProcessedConfigSuccessWithoutUserConfig { LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager processedConfig:YES jsonConfigDictionary:nil]; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; - [dataManagerMock verify]; + [self.dataManagerMock verify]; } - (void)testProcessedConfigFailure { LDClientManager *clientManager = [LDClientManager sharedInstance]; [clientManager processedConfig:NO jsonConfigDictionary:nil]; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; - [dataManagerMock verify]; + [self.dataManagerMock verify]; } - (void)testProcessedConfigSuccessWithUserSameUserConfig { @@ -497,10 +525,10 @@ - (void)testProcessedConfigSuccessWithUserSameUserConfig { XCTAssertNotNil(startingConfig); LDUserModel *clientUser = [[LDClient sharedInstance] ldUser]; - clientUser.config = startingConfig; + clientUser.flagConfig = startingConfig; [[LDClientManager sharedInstance] processedConfig:YES jsonConfigDictionary:[startingConfig dictionaryValue]]; - XCTAssertTrue(clientUser.config == startingConfig); //Should be the same object, unchanged + XCTAssertTrue(clientUser.flagConfig == startingConfig); //Should be the same object, unchanged } - (void)testProcessedConfigSuccessWithUserDifferentUserConfig { @@ -508,22 +536,22 @@ - (void)testProcessedConfigSuccessWithUserDifferentUserConfig { XCTAssertNotNil(startingConfig); LDUserModel *clientUser = [[LDClient sharedInstance] ldUser]; - clientUser.config = startingConfig; + clientUser.flagConfig = startingConfig; LDFlagConfigModel *endingConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldClientManagerTestConfigB"]; XCTAssertNotNil(endingConfig); [[LDClientManager sharedInstance] processedConfig:YES jsonConfigDictionary:[endingConfig dictionaryValue]]; - XCTAssertFalse(clientUser.config == startingConfig); //Should not be the same object - XCTAssertTrue([clientUser.config isEqualToConfig:endingConfig]); + XCTAssertFalse(clientUser.flagConfig == startingConfig); //Should not be the same object + XCTAssertTrue([clientUser.flagConfig isEqualToConfig:endingConfig]); } - (void)testSetOnlineYes { LDClientManager *clientManager = [LDClientManager sharedInstance]; clientManager.online = YES; - OCMVerify([pollingManagerMock startEventPolling]); - OCMVerify([eventSourceMock onMessage:[OCMArg any]]); + OCMVerify([self.pollingManagerMock startEventPolling]); + OCMVerify([self.eventSourceMock onMessage:[OCMArg any]]); } - (void)testSetOnlineNo { @@ -531,36 +559,40 @@ - (void)testSetOnlineNo { clientManager.online = YES; clientManager.online = NO; - OCMVerify([pollingManagerMock stopEventPolling]); + OCMVerify([self.pollingManagerMock stopEventPolling]); XCTAssertNil([clientManager eventSource]); } - (void)testFlushEventsWhenOnline { - NSData *testData = [[NSData alloc] init]; - LDClientManager *clientManager = [LDClientManager sharedInstance]; - clientManager.online = YES; - - [dataManagerMock allEventDictionaries:^(NSArray *array) { - OCMStub(array).andReturn(testData); + LDConfig *config = [[LDConfig alloc] initWithMobileKey:mockMobileKey]; + NSArray *eventDictionaries = [LDEventModel stubEventDictionariesForUser:[LDClient sharedInstance].ldUser config:config]; + OCMStub([self.dataManagerMock allEventDictionaries:[OCMArg checkWithBlock:^BOOL(id obj) { + void (^completion)(NSArray *) = obj; + completion(eventDictionaries); + return YES; + }]]); - [clientManager flushEvents]; + [LDClientManager sharedInstance].online = YES; + [[LDClientManager sharedInstance] flushEvents]; - OCMVerify([self.requestManagerMock performEventRequest:[OCMArg isEqual:testData]]); - }]; + OCMVerify([self.requestManagerMock performEventRequest:[OCMArg isEqual:eventDictionaries]]); } - (void)testFlushEventsWhenOffline { - NSData *testData = [[NSData alloc] init]; - LDClientManager *clientManager = [LDClientManager sharedInstance]; - clientManager.online = NO; - - [dataManagerMock allEventDictionaries:^(NSArray *array) { - OCMStub(array).andReturn(testData); + LDConfig *config = [[LDConfig alloc] initWithMobileKey:mockMobileKey]; + NSArray *eventDictionaries = [LDEventModel stubEventDictionariesForUser:[LDClient sharedInstance].ldUser config:config]; + OCMStub([self.dataManagerMock allEventDictionaries:[OCMArg checkWithBlock:^BOOL(id obj) { + void (^completion)(NSArray *) = obj; + completion(eventDictionaries); + return YES; + }]]); + [[self.requestManagerMock reject] performEventRequest:[OCMArg any]]; + [[self.dataManagerMock reject] allEventDictionaries:[OCMArg any]]; - [clientManager flushEvents]; + [[LDClientManager sharedInstance] flushEvents]; - OCMReject([self.requestManagerMock performEventRequest:[OCMArg isEqual:testData]]); - }]; + [self.requestManagerMock verify]; + [self.dataManagerMock verify]; } - (void)testClientUnauthorizedPosted { @@ -573,7 +605,7 @@ - (void)testClientUnauthorizedPosted { [[serverUnavailableObserver expect] notificationWithName: kLDServerConnectionUnavailableNotification object:[OCMArg any]]; __block LDEventSourceEventHandler errorHandler; - OCMStub([eventSourceMock onError:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onError:[OCMArg checkWithBlock:^BOOL(id obj) { errorHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -604,7 +636,7 @@ - (void)testClientUnauthorizedNotPosted { [[serverUnavailableObserver expect] notificationWithName: kLDServerConnectionUnavailableNotification object:[OCMArg any]]; __block LDEventSourceEventHandler errorHandler; - OCMStub([eventSourceMock onError:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onError:[OCMArg checkWithBlock:^BOOL(id obj) { errorHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -627,7 +659,7 @@ - (void)testClientUnauthorizedNotPosted { - (void)testSSEPingEvent { __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -641,7 +673,7 @@ - (void)testSSEPingEvent { if (!messageHandler) { return; } messageHandler([LDEvent stubPingEvent]); - OCMVerify([requestManagerMock performFeatureFlagRequest:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMVerify([self.requestManagerMock performFeatureFlagRequest:[OCMArg checkWithBlock:^BOOL(id obj) { if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } LDUserModel *userFromRequest = (LDUserModel*)obj; return [user isEqual:userFromRequest]; @@ -649,70 +681,74 @@ - (void)testSSEPingEvent { } - (void)testSSEPutEventSuccess { - id notificationObserver = OCMObserverMock(); - [[NSNotificationCenter defaultCenter] addMockObserver:notificationObserver name:kLDUserUpdatedNotification object:nil]; - [[notificationObserver expect] notificationWithName:kLDUserUpdatedNotification object:[OCMArg any]]; + id userUpdatedNotificationObserver = OCMObserverMock(); + [[NSNotificationCenter defaultCenter] addMockObserver:userUpdatedNotificationObserver name:kLDUserUpdatedNotification object:nil]; + [[userUpdatedNotificationObserver expect] notificationWithName:kLDUserUpdatedNotification object:[OCMArg any]]; + + id userNoChangeNotificationObserver = OCMObserverMock(); + [[NSNotificationCenter defaultCenter] addMockObserver:userNoChangeNotificationObserver name:kLDUserNoChangeNotification object:nil]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); - LDUserModel *user = [[LDClient sharedInstance] ldUser]; - LDFlagConfigModel *targetFlagConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; + LDFlagConfigModel *targetFlagConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-excludeNulls"]; self.cleanup = ^{ - [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; + [[NSNotificationCenter defaultCenter] removeObserver:userUpdatedNotificationObserver]; + [[NSNotificationCenter defaultCenter] removeObserver:userNoChangeNotificationObserver]; }; - LDClientManager *clientManager = [LDClientManager sharedInstance]; - clientManager.online = YES; + [LDClientManager sharedInstance].online = YES; XCTAssertNotNil(messageHandler); if (!messageHandler) { return; } + + [[self.dataManagerMock expect] saveUser:[OCMArg checkWithBlock:^BOOL(id obj) { + if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } + LDUserModel *savedUser = obj; + XCTAssertTrue([savedUser.flagConfig isEqualToConfig:targetFlagConfig]); + return YES; + }]]; - LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags-withVersions"]; + LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags-excludeNulls"]; messageHandler(put); - XCTAssertTrue([user.config isEqualToConfig: targetFlagConfig]); - __block LDUserModel *savedUser; - OCMVerify([dataManagerMock saveUser:[OCMArg checkWithBlock:^BOOL(id obj) { - if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } - savedUser = obj; - return YES; - }]]); - XCTAssertTrue([savedUser.config isEqualToConfig:targetFlagConfig]); - OCMVerifyAll(notificationObserver); + [self.dataManagerMock verify]; } - (void)testSSEPutResultedInNoChange { - id notificationObserver = OCMObserverMock(); - [[NSNotificationCenter defaultCenter] addMockObserver:notificationObserver name:kLDUserNoChangeNotification object:nil]; - [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; + id userUpdatedNotificationObserver = OCMObserverMock(); + [[NSNotificationCenter defaultCenter] addMockObserver:userUpdatedNotificationObserver name:kLDUserUpdatedNotification object:nil]; + + id userNoChangeNotificationObserver = OCMObserverMock(); + [[NSNotificationCenter defaultCenter] addMockObserver:userNoChangeNotificationObserver name:kLDUserNoChangeNotification object:nil]; + [[userNoChangeNotificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); LDUserModel *user = [[LDClient sharedInstance] ldUser]; id flagConfigMock = OCMClassMock([LDFlagConfigModel class]); - __block NSDictionary *putDictionary; - [[flagConfigMock expect] addOrReplaceFromDictionary:[OCMArg checkWithBlock:^BOOL(id obj) { - if (![obj isKindOfClass:[NSDictionary class]]) { return NO; } - putDictionary = obj; - return YES; - }]]; OCMStub([flagConfigMock isEqualToConfig:[OCMArg any]]).andReturn(YES); - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock expect] saveUser:[OCMArg checkWithBlock:^BOOL(id obj) { + if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } + LDUserModel *savedUser = obj; + XCTAssertEqualObjects(savedUser, user); + return YES; + }]]; self.cleanup = ^{ - [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; + [[NSNotificationCenter defaultCenter] removeObserver:userUpdatedNotificationObserver]; + [[NSNotificationCenter defaultCenter] removeObserver:userNoChangeNotificationObserver]; [flagConfigMock stopMocking]; }; @@ -723,12 +759,11 @@ - (void)testSSEPutResultedInNoChange { if (!messageHandler) { return; } //NOTE: Because the flag config mock will return YES on a config comparison, the put here doesn't matter - LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags-withVersions"]; + LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags"]; messageHandler(put); - OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + [self.dataManagerMock verify]; } - (void)testSSEPutEventFailedNilData { @@ -737,15 +772,15 @@ - (void)testSSEPutEventFailedNilData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - LDFlagConfigModel *targetFlagConfig = user.config; + LDFlagConfigModel *targetFlagConfig = user.flagConfig; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -757,14 +792,14 @@ - (void)testSSEPutEventFailedNilData { XCTAssertNotNil(messageHandler); if (!messageHandler) { return; } - LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags-withVersions"]; + LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags"]; put.data = nil; messageHandler(put); - XCTAssertTrue([user.config isEqualToConfig: targetFlagConfig]); + XCTAssertTrue([user.flagConfig isEqualToConfig: targetFlagConfig]); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEPutEventFailedEmptyData { @@ -773,15 +808,15 @@ - (void)testSSEPutEventFailedEmptyData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - LDFlagConfigModel *targetFlagConfig = user.config; + LDFlagConfigModel *targetFlagConfig = user.flagConfig; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -793,14 +828,14 @@ - (void)testSSEPutEventFailedEmptyData { XCTAssertNotNil(messageHandler); if (!messageHandler) { return; } - LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags-withVersions"]; + LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags"]; put.data = @""; messageHandler(put); - XCTAssertTrue([user.config isEqualToConfig: targetFlagConfig]); + XCTAssertTrue([user.flagConfig isEqualToConfig: targetFlagConfig]); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEPutEventFailedInvalidData { @@ -809,15 +844,15 @@ - (void)testSSEPutEventFailedInvalidData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - LDFlagConfigModel *targetFlagConfig = user.config; + LDFlagConfigModel *targetFlagConfig = user.flagConfig; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -829,14 +864,14 @@ - (void)testSSEPutEventFailedInvalidData { XCTAssertNotNil(messageHandler); if (!messageHandler) { return; } - LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags-withVersions"]; + LDEvent *put = [LDEvent stubEvent:kLDEventTypePut fromJsonFileNamed:@"featureFlags"]; put.data = @"{\"someInvalidData\":}"; messageHandler(put); - XCTAssertTrue([user.config isEqualToConfig: targetFlagConfig]); + XCTAssertTrue([user.flagConfig isEqualToConfig: targetFlagConfig]); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEUnrecognizedEvent { @@ -846,15 +881,15 @@ - (void)testSSEUnrecognizedEvent { //it's not obvious, but by not setting expect on the mock observer, the observer will fail when verify is called IF it has received the notification __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - LDFlagConfigModel *targetFlagConfig = user.config; + LDFlagConfigModel *targetFlagConfig = user.flagConfig; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -866,13 +901,13 @@ - (void)testSSEUnrecognizedEvent { XCTAssertNotNil(messageHandler); if (!messageHandler) { return; } - LDEvent *put = [LDEvent stubEvent:@"someUnrecognizedEvent" fromJsonFileNamed:@"featureFlags-withVersions"]; + LDEvent *put = [LDEvent stubEvent:@"someUnrecognizedEvent" fromJsonFileNamed:@"featureFlags"]; messageHandler(put); - XCTAssertTrue([user.config isEqualToConfig: targetFlagConfig]); + XCTAssertTrue([user.flagConfig isEqualToConfig: targetFlagConfig]); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEPatchEventSuccess { @@ -881,7 +916,7 @@ - (void)testSSEPatchEventSuccess { [[notificationObserver expect] notificationWithName:kLDUserUpdatedNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -890,7 +925,7 @@ - (void)testSSEPatchEventSuccess { OCMStub([flagConfigMock hasFeaturesEqualToDictionary:[OCMArg any]]).andReturn(NO); LDUserModel *user = [[LDClient sharedInstance] ldUser]; - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -916,7 +951,7 @@ - (void)testSSEPatchEventSuccess { }]]); XCTAssertTrue([patchDictionary isEqualToDictionary:targetPatchDictionary]); __block LDUserModel *savedUser; - OCMVerify([dataManagerMock saveUser:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMVerify([self.dataManagerMock saveUser:[OCMArg checkWithBlock:^BOOL(id obj) { if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } savedUser = obj; return YES; @@ -931,7 +966,7 @@ - (void)testSSEPatchResultedInNoChange { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -945,9 +980,9 @@ - (void)testSSEPatchResultedInNoChange { return YES; }]]; OCMStub([flagConfigMock hasFeaturesEqualToDictionary:[OCMArg any]]).andReturn(YES); - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -966,7 +1001,7 @@ - (void)testSSEPatchResultedInNoChange { messageHandler(patch); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEPatchFailedNilData { @@ -975,7 +1010,7 @@ - (void)testSSEPatchFailedNilData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -983,9 +1018,9 @@ - (void)testSSEPatchFailedNilData { id flagConfigMock = OCMClassMock([LDFlagConfigModel class]); [[flagConfigMock reject] addOrReplaceFromDictionary:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -1004,7 +1039,7 @@ - (void)testSSEPatchFailedNilData { messageHandler(patch); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEPatchFailedEmptyData { @@ -1013,7 +1048,7 @@ - (void)testSSEPatchFailedEmptyData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -1021,9 +1056,9 @@ - (void)testSSEPatchFailedEmptyData { id flagConfigMock = OCMClassMock([LDFlagConfigModel class]); [[flagConfigMock reject] addOrReplaceFromDictionary:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -1042,7 +1077,7 @@ - (void)testSSEPatchFailedEmptyData { messageHandler(patch); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEPatchFailedInvalidData { @@ -1051,7 +1086,7 @@ - (void)testSSEPatchFailedInvalidData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -1059,9 +1094,9 @@ - (void)testSSEPatchFailedInvalidData { id flagConfigMock = OCMClassMock([LDFlagConfigModel class]); [[flagConfigMock reject] addOrReplaceFromDictionary:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -1080,7 +1115,7 @@ - (void)testSSEPatchFailedInvalidData { messageHandler(patch); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEDeleteEventSuccess { @@ -1089,7 +1124,7 @@ - (void)testSSEDeleteEventSuccess { [[notificationObserver expect] notificationWithName:kLDUserUpdatedNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -1098,7 +1133,7 @@ - (void)testSSEDeleteEventSuccess { OCMStub([flagConfigMock hasFeaturesEqualToDictionary:[OCMArg any]]).andReturn(NO); LDUserModel *user = [[LDClient sharedInstance] ldUser]; - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; self.cleanup = ^{ [flagConfigMock stopMocking]; @@ -1124,7 +1159,7 @@ - (void)testSSEDeleteEventSuccess { }]]); XCTAssertTrue([deleteDictionary isEqualToDictionary:targetDeleteDictionary]); __block LDUserModel *savedUser; - OCMVerify([dataManagerMock saveUser:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMVerify([self.dataManagerMock saveUser:[OCMArg checkWithBlock:^BOOL(id obj) { if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } savedUser = obj; return YES; @@ -1140,7 +1175,7 @@ - (void)testSSEDeleteResultedInNoChange { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -1154,9 +1189,9 @@ - (void)testSSEDeleteResultedInNoChange { return YES; }]]; OCMStub([flagConfigMock hasFeaturesEqualToDictionary:[OCMArg any]]).andReturn(YES); - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -1175,7 +1210,7 @@ - (void)testSSEDeleteResultedInNoChange { messageHandler(delete); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEDeleteFailedNilData { @@ -1184,7 +1219,7 @@ - (void)testSSEDeleteFailedNilData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -1192,9 +1227,9 @@ - (void)testSSEDeleteFailedNilData { id flagConfigMock = OCMClassMock([LDFlagConfigModel class]); [[flagConfigMock reject] deleteFromDictionary:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -1213,7 +1248,7 @@ - (void)testSSEDeleteFailedNilData { messageHandler(delete); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEDeleteFailedEmptyData { @@ -1222,7 +1257,7 @@ - (void)testSSEDeleteFailedEmptyData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -1230,9 +1265,9 @@ - (void)testSSEDeleteFailedEmptyData { id flagConfigMock = OCMClassMock([LDFlagConfigModel class]); [[flagConfigMock reject] deleteFromDictionary:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -1251,7 +1286,7 @@ - (void)testSSEDeleteFailedEmptyData { messageHandler(delete); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } - (void)testSSEDeleteFailedInvalidData { @@ -1260,7 +1295,7 @@ - (void)testSSEDeleteFailedInvalidData { [[notificationObserver expect] notificationWithName:kLDUserNoChangeNotification object:[OCMArg any]]; __block LDEventSourceEventHandler messageHandler; - OCMStub([eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { + OCMStub([self.eventSourceMock onMessage:[OCMArg checkWithBlock:^BOOL(id obj) { messageHandler = (LDEventSourceEventHandler)obj; return YES; }]]); @@ -1268,9 +1303,9 @@ - (void)testSSEDeleteFailedInvalidData { id flagConfigMock = OCMClassMock([LDFlagConfigModel class]); [[flagConfigMock reject] deleteFromDictionary:[OCMArg any]]; LDUserModel *user = [[LDClient sharedInstance] ldUser]; - user.config = flagConfigMock; + user.flagConfig = flagConfigMock; - [[dataManagerMock reject] saveUser:[OCMArg any]]; + [[self.dataManagerMock reject] saveUser:[OCMArg any]]; self.cleanup = ^{ [[NSNotificationCenter defaultCenter] removeObserver:notificationObserver]; @@ -1289,7 +1324,7 @@ - (void)testSSEDeleteFailedInvalidData { messageHandler(delete); OCMVerifyAll(notificationObserver); - OCMVerify(dataManagerMock); + OCMVerify(self.dataManagerMock); } #pragma mark - Helpers diff --git a/DarklyTests/LDClientTest.m b/DarklyTests/LDClientTest.m index 2fbae1f2..8f987faa 100644 --- a/DarklyTests/LDClientTest.m +++ b/DarklyTests/LDClientTest.m @@ -6,22 +6,24 @@ #import "LDClient.h" #import "LDDataManager.h" #import "LDUserModel.h" +#import "LDUserModel+Testable.h" #import "LDFlagConfigModel.h" #import "LDFlagConfigModel+Testable.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigTracker.h" #import "LDUserBuilder.h" #import "LDPollingManager.h" #import "LDUserBuilder+Testable.h" #import "LDClient+Testable.h" #import "NSJSONSerialization+Testable.h" #import "LDThrottler.h" +#import "LDFlagConfigValue+Testable.h" #import "OCMock.h" #import typedef void(^MockLDClientDelegateCallbackBlock)(void); -extern NSString * _Nonnull const kLDFlagConfigJsonDictionaryKeyValue; - @interface MockLDClientDelegate : NSObject @property (nonatomic, assign) NSInteger userDidUpdateCallCount; @property (nonatomic, assign) NSInteger userUnchangedCallCount; @@ -60,11 +62,13 @@ -(NSInteger)processCallbackWithCount:(NSInteger)callbackCount block:(MockLDClien @interface LDClientTest : DarklyXCTestCase @property (nonatomic, strong) XCTestExpectation *userConfigUpdatedNotificationExpectation; -@property (nonatomic, assign) BOOL configureUser; -@property (nonatomic, strong) id mockLDClientManager; -@property (nonatomic, strong) id mockLDDataManager; -@property (nonatomic, strong) id mockLDRequestManager; +@property (nonatomic, strong) id clientManagerMock; +@property (nonatomic, strong) id dataManagerMock; +@property (nonatomic, strong) id requestManagerMock; @property (nonatomic, strong) id throttlerMock; +@property (nonatomic, strong) id userBuilderMock; +@property (nonatomic, strong) LDUserModel *user; +@property (nonatomic, strong) LDConfig *config; @end NSString *const kFallbackString = @"fallbackString"; @@ -75,24 +79,30 @@ @implementation LDClientTest - (void)setUp { [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. - self.configureUser = NO; id mockClientManager = OCMClassMock([LDClientManager class]); OCMStub(ClassMethod([mockClientManager sharedInstance])).andReturn(mockClientManager); - self.mockLDClientManager = mockClientManager; + self.clientManagerMock = mockClientManager; id mockDataManager = OCMClassMock([LDDataManager class]); OCMStub(ClassMethod([mockDataManager sharedManager])).andReturn(mockDataManager); - self.mockLDDataManager = mockDataManager; + self.dataManagerMock = mockDataManager; id mockRequestManager = OCMClassMock([LDRequestManager class]); OCMStub(ClassMethod([mockRequestManager sharedInstance])).andReturn(mockRequestManager); - self.mockLDRequestManager = mockRequestManager; + self.requestManagerMock = mockRequestManager; self.throttlerMock = OCMClassMock([LDThrottler class]); OCMStub([self.throttlerMock runThrottled:[OCMArg invokeBlock]]); [LDClient sharedInstance].throttler = self.throttlerMock; + + + self.user = [LDUserModel stubWithKey:nil]; + + self.userBuilderMock = OCMClassMock([LDUserBuilder class]); + OCMStub([self.userBuilderMock build]).andReturn(self.user); + + self.config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; } - (void)tearDown { @@ -100,14 +110,19 @@ - (void)tearDown { [LDClient sharedInstance].delegate = nil; [OHHTTPStubs removeAllStubs]; [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self.mockLDClientManager stopMocking]; - self.mockLDClientManager = nil; - [self.mockLDDataManager stopMocking]; - self.mockLDDataManager = nil; - [self.mockLDRequestManager stopMocking]; - self.mockLDRequestManager = nil; - - self.configureUser = NO; + self.user = nil; + self.config = nil; + [self.clientManagerMock stopMocking]; + self.clientManagerMock = nil; + [self.dataManagerMock stopMocking]; + self.dataManagerMock = nil; + [self.requestManagerMock stopMocking]; + self.requestManagerMock = nil; + [self.throttlerMock stopMocking]; + self.throttlerMock = nil; + [self.userBuilderMock stopMocking]; + self.userBuilderMock = nil; + self.userConfigUpdatedNotificationExpectation = nil; [super tearDown]; } @@ -119,308 +134,509 @@ - (void)testSharedInstance { } - (void)testStartWithoutConfig { + [[self.dataManagerMock reject] createIdentifyEventWithUser:[OCMArg any] config:[OCMArg any]]; XCTAssertFalse([[LDClient sharedInstance] start:nil withUserBuilder:nil]); + [self.dataManagerMock verify]; } - (void)testStartWithValidConfig { LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; LDUserBuilder *userBuilder = [[LDUserBuilder alloc] init]; userBuilder.key = [[NSUUID UUID] UUIDString]; + [[self.dataManagerMock expect] createIdentifyEventWithUser:[OCMArg checkWithBlock:^BOOL(id obj) { + if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } + return [((LDUserModel*)obj).key isEqualToString:userBuilder.key]; + }] config:[OCMArg checkWithBlock:^BOOL(id obj) { + if (![obj isKindOfClass:[LDConfig class]]) { return NO; } + return [((LDConfig*)obj).mobileKey isEqualToString:config.mobileKey]; + }]]; BOOL didStart = [[LDClient sharedInstance] start:config withUserBuilder:userBuilder]; XCTAssertTrue(didStart); + [self.dataManagerMock verify]; } - (void)testStartWithValidConfigMultipleTimes { LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; LDUserBuilder *userBuilder = [[LDUserBuilder alloc] init]; userBuilder.key = [[NSUUID UUID] UUIDString]; - + __block NSInteger createIdentifyEventCallCount = 0; + [[self.dataManagerMock expect] createIdentifyEventWithUser:[OCMArg checkWithBlock:^BOOL(id obj) { + if (createIdentifyEventCallCount > 0) { return NO; } //Make sure the client only records one identify event + if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } + if (![((LDUserModel*)obj).key isEqualToString:userBuilder.key]) { return NO; } + createIdentifyEventCallCount += 1; + return [((LDUserModel*)obj).key isEqualToString:userBuilder.key]; + }] config:[OCMArg checkWithBlock:^BOOL(id obj) { + if (![obj isKindOfClass:[LDConfig class]]) { return NO; } + return [((LDConfig*)obj).mobileKey isEqualToString:config.mobileKey]; + }]]; XCTAssertTrue([[LDClient sharedInstance] start:config withUserBuilder:userBuilder]); XCTAssertFalse([[LDClient sharedInstance] start:config withUserBuilder:userBuilder]); - [self.mockLDDataManager verify]; -} - -- (void)testBoolVariationWithStart { - LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - [[LDClient sharedInstance] start:config withUserBuilder:nil]; - BOOL boolValue = [[LDClient sharedInstance] boolVariation:@"test" fallback:YES]; - XCTAssertTrue(boolValue); -} - -- (void)testBoolVariationWithoutConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - XCTAssertNil([LDClient sharedInstance].ldUser.config); - XCTAssertTrue([[LDClient sharedInstance] boolVariation:@"isABool" fallback:YES]); -} - -- (void)testBoolVariationWithConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"boolConfigIsABool-true-withVersion"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertNotNil([LDClient sharedInstance].ldUser.config); - XCTAssertTrue([[LDClient sharedInstance] boolVariation:@"isABool" fallback:NO]); -} - -- (void)testBoolVariationFallback { - NSString *targetKey = @"isNotABool"; - - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"boolConfigIsABool-false"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertNotNil([LDClient sharedInstance].ldUser.config); - XCTAssertFalse([[[[LDClient sharedInstance] ldUser].config.featuresJsonDictionary allKeys] containsObject:targetKey]); - XCTAssertTrue([[LDClient sharedInstance] boolVariation:targetKey fallback:YES]); -} - -- (void)testStringVariationWithoutConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - XCTAssertNil([LDClient sharedInstance].ldUser.config); - XCTAssertTrue([[[LDClient sharedInstance] stringVariation:@"isAString" fallback:kFallbackString] isEqualToString:kFallbackString]); -} - -- (void)testStringVariationWithConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAString-someString-withVersion"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertTrue([[[LDClient sharedInstance] stringVariation:@"isAString" fallback:kFallbackString] isEqualToString:kTargetValueString]); -} - -- (void)testStringVariationFallback { - NSString *targetKey = @"isNotAString"; - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAString-someString-withVersion"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertFalse([[[[LDClient sharedInstance] ldUser].config.featuresJsonDictionary allKeys] containsObject:targetKey]); - XCTAssertTrue([[[LDClient sharedInstance] stringVariation:targetKey fallback:kFallbackString] isEqualToString:kFallbackString]); -} - -- (void)testNumberVariationWithoutConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - XCTAssertNil([LDClient sharedInstance].ldUser.config); - XCTAssertTrue([[[LDClient sharedInstance] numberVariation:@"isANumber" fallback:@5] intValue] == 5); -} - -- (void)testNumberVariationWithConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"numberConfigIsANumber-2-withVersion"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertTrue([[[LDClient sharedInstance] numberVariation:@"isANumber" fallback:@5] intValue] == 2); -} - -- (void)testNumberVariationFallback { - NSString *targetKey = @"isNotANumber"; - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"numberConfigIsANumber-2-withVersion"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertFalse([[[[LDClient sharedInstance] ldUser].config.featuresJsonDictionary allKeys] containsObject:targetKey]); - XCTAssertTrue([[[LDClient sharedInstance] numberVariation:targetKey fallback:@5] intValue] == 5); -} - -- (void)testDoubleVariationWithoutConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - XCTAssertNil([LDClient sharedInstance].ldUser.config); - XCTAssertTrue([[LDClient sharedInstance] doubleVariation:@"isADouble" fallback:2.71828] == 2.71828); -} - -- (void)testDoubleVariationWithConfig { - NSString *targetKey = @"isADouble"; - NSString *jsonFileName = @"doubleConfigIsADouble-Pi-withVersion"; - double target = [[self valueFromJsonFileNamed:jsonFileName key:targetKey] doubleValue]; - - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:jsonFileName]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertTrue([[LDClient sharedInstance] doubleVariation:targetKey fallback:2.71828] == target); -} - -- (void)testDoubleVariationFallback { - NSString *targetKey = @"isNotADouble"; - - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"doubleConfigIsADouble-Pi-withVersion"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertFalse([[[[LDClient sharedInstance] ldUser].config.featuresJsonDictionary allKeys] containsObject:targetKey]); - XCTAssertTrue([[LDClient sharedInstance] doubleVariation:targetKey fallback:2.71828] == 2.71828); -} - -- (void)testArrayVariationWithoutConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - NSArray *fallbackArray = @[@1, @2]; - - XCTAssertTrue(clientStarted); - XCTAssertNil([LDClient sharedInstance].ldUser.config); - XCTAssertTrue([[LDClient sharedInstance] arrayVariation:@"isAnArray" fallback:fallbackArray] == fallbackArray); //object equality!! -} - -- (void)testArrayVariationWithConfig { - NSString *targetKey = @"isAnArray"; - NSString *jsonFileName = @"arrayConfigIsAnArray-123-withVersion"; - - NSArray *fallbackArray = @[@1, @2]; - NSArray *targetArray = [self valueFromJsonFileNamed:jsonFileName key:targetKey]; - XCTAssertFalse([targetArray isEqualToArray:fallbackArray]); - - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:jsonFileName]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - NSArray *arrayValue = [[LDClient sharedInstance] arrayVariation:targetKey fallback:fallbackArray]; - XCTAssertTrue([arrayValue isEqualToArray:targetArray]); -} - -- (void)testArrayVariationFallback { - NSString *targetKey = @"isNotAnArray"; - NSArray *fallbackArray = @[@1, @2]; - - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"arrayConfigIsAnArray-123-withVersion"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertFalse([[[[LDClient sharedInstance] ldUser].config.featuresJsonDictionary allKeys] containsObject:targetKey]); - NSArray *arrayValue = [[LDClient sharedInstance] arrayVariation:targetKey fallback:fallbackArray]; - XCTAssertTrue(arrayValue == fallbackArray); -} - -- (void)testDictionaryVariationWithoutConfig { - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - NSDictionary *fallback = @{@"key1": @"value1", @"key2": @[@1, @2]}; - - XCTAssertTrue(clientStarted); - XCTAssertNil([LDClient sharedInstance].ldUser.config); - XCTAssertTrue([[LDClient sharedInstance] dictionaryVariation:@"isADictionary" fallback:fallback] == fallback); -} - -- (void)testDictionaryVariationWithConfig { - NSString *targetKey = @"isADictionary"; - NSString *jsonFileName = @"dictionaryConfigIsADictionary-3Key-withVersion"; - - NSDictionary *fallback = @{@"key1": @"value1", @"key2": @[@1, @2]}; - NSDictionary *target = [self valueFromJsonFileNamed:jsonFileName key:targetKey]; - XCTAssertFalse([target isEqualToDictionary:fallback]); - - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:jsonFileName]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertTrue([[[LDClient sharedInstance] dictionaryVariation:targetKey fallback:fallback] isEqualToDictionary:target]); -} - -- (void)testDictionaryVariationFallback { - NSString *targetKey = @"isNotADictionary"; - NSDictionary *fallback = @{@"key1": @"value1", @"key2": @[@1, @2]}; - - LDConfig *clientConfig = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - LDUserBuilder *userBuilder = [LDUserBuilder userBuilderWithKey:[[NSUUID UUID] UUIDString]]; - - LDFlagConfigModel *flags = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key-withVersion"]; - - BOOL clientStarted = [[LDClient sharedInstance] start:clientConfig withUserBuilder:userBuilder]; - XCTAssertTrue(clientStarted); - - [LDClient sharedInstance].ldUser.config = flags; - - XCTAssertFalse([[[[LDClient sharedInstance] ldUser].config.featuresJsonDictionary allKeys] containsObject:targetKey]); - XCTAssertTrue([[LDClient sharedInstance] dictionaryVariation:targetKey fallback:fallback] == fallback); -} - + [self.dataManagerMock verify]; +} + +#pragma mark - Variations +#pragma mark Bool Variation +- (void)testBoolVariation_knownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"isABool"; + id defaultFlagValue = @(NO); + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"boolConfigIsABool-true"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + id targetFlagValue = flagConfigValue.value; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:targetFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + BOOL flagValue = [[LDClient sharedInstance] boolVariation:flagKey fallback:[defaultFlagValue boolValue]]; + + XCTAssertEqual(flagValue, [targetFlagValue boolValue]); + [self.dataManagerMock verify]; +} + +- (void)testBoolVariation_knownFlag_nullValue { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = kLDFlagKeyIsABool; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"boolConfigIsABool-false"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + flagConfigValue.value = [NSNull null]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + BOOL flagValue = [[LDClient sharedInstance] boolVariation:flagKey fallback:[defaultFlagValue boolValue]]; + + XCTAssertEqualObjects(@(flagValue), defaultFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testBoolVariation_unknownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"dummy-flag-key"; + id defaultFlagValue = @(YES); + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"boolConfigIsABool-false"]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey reportedFlagValue:defaultFlagValue flagConfigValue:nil defaultFlagValue:defaultFlagValue user:self.user config:self.config]; + + BOOL flagValue = [[LDClient sharedInstance] boolVariation:flagKey fallback:[defaultFlagValue boolValue]]; + + XCTAssertEqual(flagValue, [targetFlagValue boolValue]); + [self.dataManagerMock verify]; +} + +- (void)testBoolVariation_knownFlag_withoutStart { + NSString *flagKey = @"isABool"; + id defaultFlagValue = @(YES); + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"boolConfigIsABool-false"]; + [[self.dataManagerMock reject] createFlagEvaluationEventsWithFlagKey:[OCMArg any] reportedFlagValue:[OCMArg any] flagConfigValue:[OCMArg any] defaultFlagValue:[OCMArg any] user:self.user config:self.config]; + + BOOL flagValue = [[LDClient sharedInstance] boolVariation:flagKey fallback:[defaultFlagValue boolValue]]; + + XCTAssertEqual(flagValue, [targetFlagValue boolValue]); + [self.dataManagerMock verify]; +} + +#pragma mark Number Variation +- (void)testNumberVariation_knownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"isANumber"; + id defaultFlagValue = @5; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"numberConfigIsANumber-2"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + id targetFlagValue = flagConfigValue.value; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:targetFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSNumber *flagValue = [[LDClient sharedInstance] numberVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testNumberVariation_knownFlag_nullValue { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = kLDFlagKeyIsANumber; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"numberConfigIsANumber-2"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + flagConfigValue.value = [NSNull null]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSNumber *flagValue = [[LDClient sharedInstance] numberVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, defaultFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testNumberVariation_unknownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"dummy-flag-key"; + id defaultFlagValue = @5; + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"numberConfigIsANumber-2"]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:nil + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSNumber *flagValue = [[LDClient sharedInstance] numberVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testNumberVariation_knownFlag_withoutStart { + NSString *flagKey = @"isANumber"; + id defaultFlagValue = @5; + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"numberConfigIsANumber-2"]; + [[self.dataManagerMock reject] createFlagEvaluationEventsWithFlagKey:[OCMArg any] + reportedFlagValue:[OCMArg any] + flagConfigValue:[OCMArg any] + defaultFlagValue:[OCMArg any] + user:self.user + config:self.config]; + + NSNumber *flagValue = [[LDClient sharedInstance] numberVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +#pragma mark Double Variation +- (void)testDoubleVariation_knownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"isADouble"; + id defaultFlagValue = @(2.71828); + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"doubleConfigIsADouble-Pi"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + id targetFlagValue = flagConfigValue.value; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:targetFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + double flagValue = [[LDClient sharedInstance] doubleVariation:flagKey fallback:[defaultFlagValue doubleValue]]; + + XCTAssertEqual(flagValue, [targetFlagValue doubleValue]); + [self.dataManagerMock verify]; +} + +- (void)testDoubleVariation_knownFlag_nullValue { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = kLDFlagKeyIsADouble; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"doubleConfigIsADouble-Pi"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + flagConfigValue.value = [NSNull null]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + double flagValue = [[LDClient sharedInstance] doubleVariation:flagKey fallback:[defaultFlagValue doubleValue]]; + + XCTAssertEqualObjects(@(flagValue), defaultFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testDoubleVariation_unknownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"dummy-flag-key"; + id defaultFlagValue = @(2.71828); + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"doubleConfigIsADouble-Pi"]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:nil + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + double flagValue = [[LDClient sharedInstance] doubleVariation:flagKey fallback:[defaultFlagValue doubleValue]]; + + XCTAssertEqual(flagValue, [targetFlagValue doubleValue]); + [self.dataManagerMock verify]; +} + +- (void)testDoubleVariation_knownFlag_withoutStart { + NSString *flagKey = @"isADouble"; + id defaultFlagValue = @(2.71828); + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"doubleConfigIsADouble-Pi"]; + [[self.dataManagerMock reject] createFlagEvaluationEventsWithFlagKey:[OCMArg any] + reportedFlagValue:[OCMArg any] + flagConfigValue:[OCMArg any] + defaultFlagValue:[OCMArg any] + user:self.user + config:self.config]; + + double flagValue = [[LDClient sharedInstance] doubleVariation:flagKey fallback:[defaultFlagValue doubleValue]]; + + XCTAssertEqual(flagValue, [targetFlagValue doubleValue]); + [self.dataManagerMock verify]; +} + +#pragma mark String Variation +- (void)testStringVariation_knownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"isAString"; + id defaultFlagValue = kFallbackString; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"stringConfigIsAString-someString"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + id targetFlagValue = flagConfigValue.value; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:targetFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSString *flagValue = [[LDClient sharedInstance] stringVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testStringVariation_knownFlag_nullValue { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = kLDFlagKeyIsAString; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"stringConfigIsAString-someString"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + flagConfigValue.value = [NSNull null]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSString *flagValue = [[LDClient sharedInstance] stringVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, defaultFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testStringVariation_unknownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"dummy-flag-key"; + id defaultFlagValue = kFallbackString; + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"stringConfigIsAString-someString"]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:nil + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSString *flagValue = [[LDClient sharedInstance] stringVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testStringVariation_knownFlag_withoutStart { + NSString *flagKey = @"isAString"; + id defaultFlagValue = kFallbackString; + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"stringConfigIsAString-someString"]; + [[self.dataManagerMock reject] createFlagEvaluationEventsWithFlagKey:[OCMArg any] + reportedFlagValue:[OCMArg any] + flagConfigValue:[OCMArg any] + defaultFlagValue:[OCMArg any] + user:self.user + config:self.config]; + + NSString *flagValue = [[LDClient sharedInstance] stringVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +#pragma mark Array Variation +- (void)testArrayVariation_knownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"isAnArray"; + id defaultFlagValue = @[@1, @2]; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"arrayConfigIsAnArray-123"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + id targetFlagValue = flagConfigValue.value; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:targetFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSArray *flagValue = [[LDClient sharedInstance] arrayVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testArrayVariation_knownFlag_nullValue { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = kLDFlagKeyIsAnArray; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"arrayConfigIsAnArray-123"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + flagConfigValue.value = [NSNull null]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSArray *flagValue = [[LDClient sharedInstance] arrayVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, defaultFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testArrayVariation_unknownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"dummy-flag-key"; + id defaultFlagValue = @[@1, @2]; + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"arrayConfigIsAnArray-123"]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:nil + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSArray *flagValue = [[LDClient sharedInstance] arrayVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testArrayVariation_knownFlag_withoutStart { + NSString *flagKey = @"isAnArray"; + id defaultFlagValue = @[@1, @2]; + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"arrayConfigIsAnArray-123"]; + [[self.dataManagerMock reject] createFlagEvaluationEventsWithFlagKey:[OCMArg any] + reportedFlagValue:[OCMArg any] + flagConfigValue:[OCMArg any] + defaultFlagValue:[OCMArg any] + user:self.user + config:self.config]; + + NSArray *flagValue = [[LDClient sharedInstance] arrayVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +#pragma mark Dictionary Variation +- (void)testDictionaryVariation_knownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"isADictionary"; + id defaultFlagValue = @{@"key1": @"value1", @"key2": @[@1, @2]}; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + id targetFlagValue = flagConfigValue.value; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:targetFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSDictionary *flagValue = [[LDClient sharedInstance] dictionaryVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testDictionaryVariation_knownFlag_nullValue { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = kLDFlagKeyIsADictionary; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagConfigModel *flagConfigModel = [self configureUserWithFlagConfigModelFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key"]; + LDFlagConfigValue *flagConfigValue = [flagConfigModel flagConfigValueForFlagKey:flagKey]; + flagConfigValue.value = [NSNull null]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSDictionary *flagValue = [[LDClient sharedInstance] dictionaryVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, defaultFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testDictionaryVariation_unknownFlag { + [[LDClient sharedInstance] start:self.config withUserBuilder:self.userBuilderMock]; + NSString *flagKey = @"dummy-flag-key"; + id defaultFlagValue = @{@"key1": @"value1", @"key2": @[@1, @2]}; + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key"]; + [[self.dataManagerMock expect] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:defaultFlagValue + flagConfigValue:nil + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + NSDictionary *flagValue = [[LDClient sharedInstance] dictionaryVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +- (void)testDictionaryVariation_knownFlag_withoutStart { + NSString *flagKey = @"isADictionary"; + id defaultFlagValue = @{@"key1": @"value1", @"key2": @[@1, @2]}; + id targetFlagValue = defaultFlagValue; + [self configureUserWithFlagConfigModelFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key"]; + [[self.dataManagerMock reject] createFlagEvaluationEventsWithFlagKey:[OCMArg any] + reportedFlagValue:[OCMArg any] + flagConfigValue:[OCMArg any] + defaultFlagValue:[OCMArg any] + user:self.user + config:self.config]; + + NSDictionary *flagValue = [[LDClient sharedInstance] dictionaryVariation:flagKey fallback:defaultFlagValue]; + + XCTAssertEqualObjects(flagValue, targetFlagValue); + [self.dataManagerMock verify]; +} + +#pragma mark - #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - (void)testDeprecatedStartWithValidConfig { @@ -470,18 +686,6 @@ - (void)testUserPersisted { XCTAssertEqual(user.email, @"my@email.com"); } --(void)testToggleCreatesEventWithCorrectArguments { - NSString *toggleName = @"test"; - BOOL fallbackValue = YES; - LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - OCMStub([self.dataManagerMock createFeatureEvent:[OCMArg any] keyValue:[OCMArg any] defaultKeyValue:[OCMArg any] user:[OCMArg any] config:[OCMArg any]]); - [[LDClient sharedInstance] start:config withUserBuilder:nil]; - [[LDClient sharedInstance] boolVariation:toggleName fallback:fallbackValue]; - - OCMVerify([self.dataManagerMock createFeatureEvent:toggleName keyValue:[NSNumber numberWithBool:fallbackValue] defaultKeyValue:[NSNumber numberWithBool:fallbackValue] user:[OCMArg isKindOfClass:[LDUserModel class]] config:config]); - [self.dataManagerMock stopMocking]; -} - - (void)testTrackWithoutStart { XCTAssertFalse([[LDClient sharedInstance] track:@"test" data:nil]); } @@ -491,15 +695,15 @@ - (void)testTrackWithStart { LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; [[LDClient sharedInstance] start:config withUserBuilder:nil]; - OCMStub([self.dataManagerMock createCustomEvent:[OCMArg isKindOfClass:[NSString class]] withCustomValuesDictionary:[OCMArg isKindOfClass:[NSDictionary class]] user:[OCMArg any] config:[OCMArg any]]); + OCMStub([self.dataManagerMock createCustomEventWithKey:[OCMArg isKindOfClass:[NSString class]] customData:[OCMArg isKindOfClass:[NSDictionary class]] user:[OCMArg any] config:[OCMArg any]]); XCTAssertTrue([[LDClient sharedInstance] track:@"test" data:customData]); - OCMVerify([self.dataManagerMock createCustomEvent:@"test" withCustomValuesDictionary:customData user:[OCMArg isKindOfClass:[LDUserModel class]] config:config]); + OCMVerify([self.dataManagerMock createCustomEventWithKey: @"test" customData: customData user:[OCMArg isKindOfClass:[LDUserModel class]] config:config]); } - (void)testSetOnline_NO_beforeStart { - [[self.mockLDClientManager reject] setOnline:[OCMArg any]]; + [[self.clientManagerMock reject] setOnline:[OCMArg any]]; __block NSInteger completionCallCount = 0; [[LDClient sharedInstance] setOnline:NO completion: ^{ @@ -507,7 +711,7 @@ - (void)testSetOnline_NO_beforeStart { }]; XCTAssertFalse([LDClient sharedInstance].isOnline); - [self.mockLDClientManager verify]; + [self.clientManagerMock verify]; XCTAssertEqual(completionCallCount, 1); } @@ -515,7 +719,7 @@ - (void)testSetOnline_NO_afterStart { LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; [[LDClient sharedInstance] start:config withUserBuilder:nil]; [[self.throttlerMock reject] runThrottled:[OCMArg any]]; - [[self.mockLDClientManager expect] setOnline:NO]; + [[self.clientManagerMock expect] setOnline:NO]; __block NSInteger completionCallCount = 0; [[LDClient sharedInstance] setOnline:NO completion: ^{ @@ -523,13 +727,13 @@ - (void)testSetOnline_NO_afterStart { }]; XCTAssertFalse([LDClient sharedInstance].isOnline); - [self.mockLDClientManager verify]; + [self.clientManagerMock verify]; [self.throttlerMock verify]; XCTAssertEqual(completionCallCount, 1); } - (void)testSetOnline_YES_beforeStart { - [[self.mockLDClientManager reject] setOnline:[OCMArg any]]; + [[self.clientManagerMock reject] setOnline:[OCMArg any]]; __block NSInteger completionCallCount = 0; [[LDClient sharedInstance] setOnline:YES completion: ^{ @@ -537,7 +741,7 @@ - (void)testSetOnline_YES_beforeStart { }]; XCTAssertFalse([LDClient sharedInstance].isOnline); - [self.mockLDClientManager verify]; + [self.clientManagerMock verify]; XCTAssertEqual(completionCallCount, 1); } @@ -545,7 +749,7 @@ - (void)testSetOnline_YES_afterStart { LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; [[LDClient sharedInstance] start:config withUserBuilder:nil]; [[LDClient sharedInstance] setOnline:NO]; - [[self.mockLDClientManager expect] setOnline:YES]; + [[self.clientManagerMock expect] setOnline:YES]; __block NSInteger completionCallCount = 0; //The throttler mock expectation is not getting fulfilled even though the LDClient does invoke it. //Since the throttler mock is set to execute blocks, setting the expectation on the client manager mock verifies that the client is calling the throttler @@ -554,7 +758,7 @@ - (void)testSetOnline_YES_afterStart { completionCallCount += 1; }]; - [self.mockLDClientManager verify]; + [self.clientManagerMock verify]; XCTAssertEqual(completionCallCount, 1); } @@ -571,29 +775,40 @@ - (void)testFlushWithStart { - (void)testStopClient { LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; [[LDClient sharedInstance] start:config withUserBuilder:nil]; - [[self.mockLDClientManager expect] setOnline:NO]; + [[self.clientManagerMock expect] setOnline:NO]; XCTAssertTrue([[LDClient sharedInstance] stopClient]); XCTAssertFalse([[LDClient sharedInstance] clientStarted]); - OCMVerifyAll(self.mockLDClientManager); + OCMVerifyAll(self.clientManagerMock); } - (void)testUpdateUserWithoutStart { - [[self.mockLDClientManager reject] updateUser]; + [[self.clientManagerMock reject] updateUser]; + [[self.dataManagerMock reject] createIdentifyEventWithUser:[OCMArg any] config:[OCMArg any]]; XCTAssertFalse([[LDClient sharedInstance] updateUser:[[LDUserBuilder alloc] init]]); - [self.mockLDClientManager verify]; + [self.clientManagerMock verify]; + [self.dataManagerMock verify]; + [self.clientManagerMock verify]; } -(void)testUpdateUserWithStart { LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; - [[self.mockLDClientManager expect] updateUser]; + [[self.clientManagerMock expect] updateUser]; LDUserBuilder *userBuilder = [[LDUserBuilder alloc] init]; userBuilder.key = [[NSUUID UUID] UUIDString]; + [[self.dataManagerMock expect] createIdentifyEventWithUser:[OCMArg checkWithBlock:^BOOL(id obj) { + if (![obj isKindOfClass:[LDUserModel class]]) { return NO; } + return [((LDUserModel*)obj).key isEqualToString:userBuilder.key]; + }] config:[OCMArg checkWithBlock:^BOOL(id obj) { + if (![obj isKindOfClass:[LDConfig class]]) { return NO; } + return [((LDConfig*)obj).mobileKey isEqualToString:config.mobileKey]; + }]]; [[LDClient sharedInstance] start:config withUserBuilder:nil]; XCTAssertTrue([[LDClient sharedInstance] updateUser:userBuilder]); - [self.mockLDClientManager verify]; + [self.clientManagerMock verify]; + [self.dataManagerMock verify]; } - (void)testCurrentUserBuilderWithoutStart { @@ -656,11 +871,11 @@ - (void)testOfflineOnClientUnauthorizedNotification { LDConfig *config = [[LDConfig alloc] initWithMobileKey:kTestMobileKey]; [[LDClient sharedInstance] start:config withUserBuilder:nil]; - [[self.mockLDClientManager expect] setOnline:NO]; + [[self.clientManagerMock expect] setOnline:NO]; [[NSNotificationCenter defaultCenter] postNotificationName:kLDClientUnauthorizedNotification object:nil]; - [self.mockLDClientManager verify]; + [self.clientManagerMock verify]; } - (void)testUserUpdatedCalled { @@ -688,6 +903,14 @@ - (id)objectFromJsonFileNamed:(NSString*)jsonFileName key:(NSString*)key { } - (id)valueFromJsonFileNamed:(NSString*)jsonFileName key:(NSString*)key { - return [self objectFromJsonFileNamed:jsonFileName key:key][kLDFlagConfigJsonDictionaryKeyValue]; + return [self objectFromJsonFileNamed:jsonFileName key:key][kLDFlagConfigValueKeyValue]; } + +-(LDFlagConfigModel*)configureUserWithFlagConfigModelFromJsonFileNamed:(NSString*)fileName { + LDFlagConfigModel *flagConfigModel = [LDFlagConfigModel flagConfigFromJsonFileNamed:fileName]; + [LDClient sharedInstance].ldUser.flagConfig = flagConfigModel; + + return flagConfigModel; +} + @end diff --git a/DarklyTests/LDConfigTest.m b/DarklyTests/LDConfigTest.m index f77b3ccb..187f5437 100644 --- a/DarklyTests/LDConfigTest.m +++ b/DarklyTests/LDConfigTest.m @@ -31,6 +31,7 @@ - (void)testConfigDefaultValues { XCTAssertEqualObjects([config capacity], [NSNumber numberWithInt:kCapacity]); XCTAssertEqualObjects([config connectionTimeout], [NSNumber numberWithInt:kConnectionTimeout]); XCTAssertEqualObjects([config flushInterval], [NSNumber numberWithInt:kDefaultFlushInterval]); + XCTAssertFalse([config inlineUserInEvents]); XCTAssertFalse([config debugEnabled]); } @@ -110,7 +111,7 @@ - (void)testConfigOverridePollingInterval { config.pollingInterval = pollingInterval; XCTAssertEqualObjects(config.pollingInterval, pollingInterval); - pollingInterval = [NSNumber numberWithInt:50]; + pollingInterval = @(kMinimumPollingInterval - 5.0); config.pollingInterval = pollingInterval; XCTAssertEqualObjects([config pollingInterval], [NSNumber numberWithInt:kMinimumPollingInterval]); } @@ -132,6 +133,13 @@ - (void)testConfigSetPrivateAttributes { XCTAssertEqualObjects(config.privateUserAttributes, LDUserModel.allUserAttributes); } +- (void)testConfigSetInlineUserInEvents { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:LDConfigTestMobileKey]; + config.inlineUserInEvents = YES; + + XCTAssertTrue(config.inlineUserInEvents); +} + - (void)testConfigOverrideDebug { LDConfig *config = [[LDConfig alloc] initWithMobileKey:LDConfigTestMobileKey]; config.debugEnabled = YES; @@ -148,7 +156,7 @@ - (void)testIsFlagRetryStatusCode { NSMutableSet *selectedStatusCodes = [NSMutableSet setWithArray:@[@405, @400, @501, @200, @304, @307, @401, @404, @412, @500]]; [selectedStatusCodes unionSet:[NSSet setWithArray:config.flagRetryStatusCodes]]; //allow flagRetryStatusCodes to change without changing the test NSMutableDictionary *statusCodeResults = [NSMutableDictionary dictionaryWithCapacity:selectedStatusCodes.count]; - [selectedStatusCodes enumerateObjectsUsingBlock:^(NSNumber * _Nonnull statusCode, BOOL * _Nonnull stop) { + [selectedStatusCodes enumerateObjectsUsingBlock:^(NSNumber *statusCode, BOOL *stop) { statusCodeResults[statusCode] = @([config.flagRetryStatusCodes containsObject:statusCode]); }]; diff --git a/DarklyTests/LDRequestManagerTest.m b/DarklyTests/LDRequestManagerTest.m index 05a3c063..4d692a37 100644 --- a/DarklyTests/LDRequestManagerTest.m +++ b/DarklyTests/LDRequestManagerTest.m @@ -13,6 +13,12 @@ #import "LDConfig.h" #import "LDConfig+Testable.h" #import "LDClient.h" +#import "NSDateFormatter+JsonHeader.h" +#import "NSDateFormatter+JsonHeader+Testable.h" +#import "NSHTTPURLResponse+LaunchDarkly+Testable.h" + +extern NSString * const kEventHeaderLaunchDarklyEventSchema; +extern NSString * const kEventSchema; static NSString *const httpMethodGet = @"GET"; static NSString *const testMobileKey = @"testMobileKey"; @@ -24,8 +30,10 @@ static const int httpStatusCodeInternalServerError = 500; @interface LDRequestManagerTest : DarklyXCTestCase -@property (nonatomic) id clientManagerMock; -@property (nonatomic) id ldClientMock; +@property (nonatomic, strong) id clientManagerMock; +@property (nonatomic, strong) id ldClientMock; +@property (nonatomic, strong) id requestManagerDelegateMock; + @end @implementation LDRequestManagerTest @@ -44,14 +52,16 @@ - (void)setUp { } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; + [LDRequestManager sharedInstance].delegate = nil; + [self.requestManagerDelegateMock stopMocking]; + self.requestManagerDelegateMock = nil; [self.ldClientMock stopMocking]; self.ldClientMock = nil; [self.clientManagerMock stopMocking]; self.clientManagerMock = nil; - [LDRequestManager sharedInstance].delegate = nil; + [OHHTTPStubs onStubActivation:nil]; [OHHTTPStubs removeAllStubs]; + [super tearDown]; } - (void)testPerformFeatureFlagRequest_GetRequest_Success { @@ -378,31 +388,31 @@ - (void)testFlagRequestDoesNotPostClientUnauthorizedNotificationOnErrorResponse [[NSNotificationCenter defaultCenter] removeObserver:clientUnauthorizedObserver]; } -- (void)testEventRequestMakesHttpRequestWithMobileKey { - - XCTestExpectation* responseArrived = [self expectationWithDescription:@"response of async request has arrived"]; - __block BOOL httpRequestAttempted = NO; +- (void)testPerformEventRequest_Online { + XCTestExpectation* responseArrivedExpectation = [self expectationWithDescription:@"response of async request has arrived"]; NSData *data = [[NSData alloc] initWithBase64EncodedString:@"" options: 0] ; - [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return [request.URL.host isEqualToString:@"mobile.launchdarkly.com"]; + return [request.URL.host isEqualToString:@"mobile.launchdarkly.com"] && [[request valueForHTTPHeaderField:kEventHeaderLaunchDarklyEventSchema] isEqualToString:kEventSchema]; } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { - httpRequestAttempted = YES; - [responseArrived fulfill]; - return [OHHTTPStubsResponse responseWithData: data statusCode:httpStatusCodeOk headers:[self headerForStatusCode:httpStatusCodeOk]]; + return [OHHTTPStubsResponse responseWithData:data statusCode:httpStatusCodeOk headers:[self headerForStatusCode:httpStatusCodeOk]]; }]; - + self.requestManagerDelegateMock = OCMProtocolMock(@protocol(RequestManagerDelegate)); + [LDRequestManager sharedInstance].delegate = self.requestManagerDelegateMock; + NSDate *targetHeaderDate = [NSDateFormatter eventDateHeaderStub]; + [[self.requestManagerDelegateMock expect] processedEvents:YES jsonEventArray:[OCMArg isKindOfClass:[NSArray class]] responseDate:[OCMArg checkWithBlock:^BOOL(id obj) { + XCTAssertEqualObjects(obj, targetHeaderDate); + [responseArrivedExpectation fulfill]; + return YES; + }]]; + [[LDRequestManager sharedInstance] performEventRequest:[self stubEvents]]; - [self waitForExpectationsWithTimeout:10 handler:^(NSError *error){ - // By the time we reach this code, the while loop has exited - // so the response has arrived or the test has timed out - XCTAssertTrue(httpRequestAttempted); - [OHHTTPStubs removeAllStubs]; - }]; + [self waitForExpectations:@[responseArrivedExpectation] timeout:1.0]; + [self.requestManagerDelegateMock verify]; } -- (void)testPerformEventRequestOffline { +- (void)testPerformEventRequest_Offline { + [self.clientManagerMock stopMocking]; id clientManagerMock = OCMClassMock([LDClientManager class]); OCMStub(ClassMethod([clientManagerMock sharedInstance])).andReturn(clientManagerMock); OCMStub([clientManagerMock isOnline]).andReturn(NO); @@ -500,9 +510,9 @@ - (LDConfig*)testConfig { - (NSDictionary*)headerForStatusCode:(int)statusCode { if (statusCode == httpStatusCodeOk) { - return @{@"Content-Type":@"application/json"}; + return @{@"Content-Type":@"application/json", kHeaderKeyDate:kDateHeaderValueDate}; } - return @{@"Content-Type":@"text"}; + return @{@"Content-Type":@"text", kHeaderKeyDate:kDateHeaderValueDate}; } - (NSData*)emptyJsonData { diff --git a/DarklyTests/LDUtilTest.m b/DarklyTests/LDUtilTest.m index 3f9c5f7b..435d9306 100644 --- a/DarklyTests/LDUtilTest.m +++ b/DarklyTests/LDUtilTest.m @@ -36,7 +36,7 @@ - (void)testBase64DecodeString { } - (void)testBase64UrlEncodeString { - [[self unencodedStrings] enumerateObjectsUsingBlock:^(NSString * _Nonnull input, NSUInteger index, BOOL * _Nonnull stop) { + [[self unencodedStrings] enumerateObjectsUsingBlock:^(NSString *input, NSUInteger index, BOOL *stop) { XCTAssertTrue([[self base64UrlEncodedStrings][index] isEqualToString:[LDUtil base64UrlEncodeString: input]]); }]; } diff --git a/DarklyTests/Models/LDDataManagerTest.m b/DarklyTests/Models/LDDataManagerTest.m index 9818f074..44d19cd8 100644 --- a/DarklyTests/Models/LDDataManagerTest.m +++ b/DarklyTests/Models/LDDataManagerTest.m @@ -6,17 +6,32 @@ #import "LDFlagConfigModel.h" #import "LDDataManager.h" #import "LDUserModel.h" +#import "LDUserModel+Testable.h" #import "LDFlagConfigModel.h" #import "LDFlagConfigModel+Testable.h" #import "LDEventModel.h" +#import "LDEventModel+Testable.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDEventTrackingContext.h" +#import "LDEventTrackingContext+Testable.h" #import "LDClient.h" #import "OCMock.h" -#import "NSArray+UnitTests.h" #import "LDDataManager+Testable.h" +#import "LDFlagConfigTracker.h" +#import "LDFlagConfigTracker+Testable.h" +#import "LDConfig.h" +#import "NSDate+Testable.h" +#import "NSArray+Testable.h" +#import "NSNumber+LaunchDarkly.h" + +NSString * const kMobileKeyMock = @"LDDataManagerTest.mobileKeyMock"; @interface LDDataManagerTest : DarklyXCTestCase -@property (nonatomic) id clientMock; -@property (nonnull) LDUserModel *user; +@property (nonatomic, strong) id clientMock; +@property (nonatomic, strong) id eventModelMock; +@property (nonatomic, strong) LDUserModel *user; +@property (nonatomic, strong) LDConfig *config; @end @@ -26,92 +41,371 @@ @implementation LDDataManagerTest - (void)setUp { [super setUp]; - user = [[LDUserModel alloc] init]; - user.firstName = @"Bob"; - user.lastName = @"Giffy"; - user.email = @"bob@gmail.com"; - user.updatedAt = [NSDate date]; - - LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldDataManagerTestConfig"]; - user.config = config; + self.config = [[LDConfig alloc] initWithMobileKey:kMobileKeyMock]; + self.user = [LDUserModel stubWithKey:nil]; - clientMock = OCMClassMock([LDClient class]); - OCMStub(ClassMethod([clientMock sharedInstance])).andReturn(clientMock); - OCMStub([clientMock ldUser]).andReturn(user); + self.clientMock = OCMClassMock([LDClient class]); + OCMStub(ClassMethod([self.clientMock sharedInstance])).andReturn(clientMock); + OCMStub([self.clientMock ldUser]).andReturn(user); + OCMStub([self.clientMock ldConfig]).andReturn(self.config); } - (void)tearDown { - [clientMock stopMocking]; - clientMock = nil; + [self.clientMock stopMocking]; + self.clientMock = nil; + [self.eventModelMock stopMocking]; + self.eventModelMock = nil; + [[LDDataManager sharedManager] flushEventsDictionary]; [super tearDown]; } -- (void)testisFlagOnForKey { - LDClient *client = [LDClient sharedInstance]; - LDUserModel * theUser = client.ldUser; - - BOOL ipaduserFlag = [(NSNumber *)[theUser flagValue: @"ipaduser"] boolValue]; - BOOL iosuserFlag = [(NSNumber *)[theUser flagValue: @"iosuser"] boolValue]; - - XCTAssertFalse(iosuserFlag); - XCTAssertTrue(ipaduserFlag); +-(LDFlagConfigValue*)setupCreateFeatureEventTestWithTrackEvents:(BOOL)trackEvents { + return [self setupCreateFeatureEventTestWithTrackEvents:trackEvents includeTrackingContext:YES]; } --(void)testAllEventsDictionaryArray { - NSString *eventKey1 = @"foo"; - NSString *eventKey2 = @"fi"; - LDConfig *config = [[LDConfig alloc] initWithMobileKey:@"stubMobileKey"]; - [[LDDataManager sharedManager] createFeatureEvent:eventKey1 keyValue:[NSNumber numberWithBool:NO] defaultKeyValue:[NSNumber numberWithBool:NO] user:self.user config:config]; - [[LDDataManager sharedManager] createCustomEvent:eventKey2 withCustomValuesDictionary:@{@"carrot": @"cake"} user:self.user config:config]; - +-(LDFlagConfigValue*)setupCreateFeatureEventTestWithTrackEvents:(BOOL)trackEvents includeTrackingContext:(BOOL)includeTrackingContext { + LDEventTrackingContext *eventTrackingContext = includeTrackingContext ? [LDEventTrackingContext contextWithTrackEvents:trackEvents debugEventsUntilDate:nil] : nil; + self.user = [LDUserModel stubWithKey:nil usingTracker:nil eventTrackingContext:eventTrackingContext]; + LDFlagConfigValue *flagConfigValue = [self.user.flagConfig flagConfigValueForFlagKey:kFlagKeyIsABawler]; + LDEventModel *featureEvent = [LDEventModel featureEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + inlineUser:NO]; + self.eventModelMock = OCMClassMock([LDEventModel class]); + OCMStub(ClassMethod([self.eventModelMock featureEventWithFlagKey:[OCMArg any] + reportedFlagValue:[OCMArg any] + flagConfigValue:[OCMArg any] + defaultFlagValue:[OCMArg any] + user:[OCMArg any] + inlineUser:[OCMArg any]])) + .andReturn(featureEvent); + + return flagConfigValue; +} + +-(LDFlagConfigValue*)setupCreateDebugEventTestWithLastEventResponseDate:(NSDate*)lastResponse debugUntil:(NSDate*)debugUntil { + return [self setupCreateDebugEventTestWithLastEventResponseDate:lastResponse debugUntil:debugUntil includeTrackingContext:YES]; +} + +-(LDFlagConfigValue*)setupCreateDebugEventTestWithLastEventResponseDate:(NSDate*)lastResponse debugUntil:(NSDate*)debugUntil includeTrackingContext:(BOOL)includeTrackingContext { + [LDDataManager sharedManager].lastEventResponseDate = lastResponse; + LDEventTrackingContext *eventTrackingContext = includeTrackingContext ? [LDEventTrackingContext contextWithTrackEvents:NO debugEventsUntilDate:debugUntil] : nil; + self.user = [LDUserModel stubWithKey:nil usingTracker:nil eventTrackingContext:eventTrackingContext]; + LDFlagConfigValue *flagConfigValue = [self.user.flagConfig flagConfigValueForFlagKey:kFlagKeyIsABawler]; + LDEventModel *debugEvent = [LDEventModel debugEventWithFlagKey:kFlagKeyIsABawler reportedFlagValue:flagConfigValue.value flagConfigValue:flagConfigValue defaultFlagValue:@(NO) user:self.user]; + self.eventModelMock = OCMClassMock([LDEventModel class]); + OCMStub(ClassMethod([self.eventModelMock featureEventWithFlagKey:[OCMArg any] + reportedFlagValue:[OCMArg any] + flagConfigValue:[OCMArg any] + defaultFlagValue:[OCMArg any] + user:[OCMArg any] + inlineUser:[OCMArg any]])) + .andReturn(debugEvent); + + return flagConfigValue; +} + +-(void)testCreateFlagEvaluationEvents { + id trackerMock = OCMClassMock([LDFlagConfigTracker class]); + self.user = [LDUserModel stubWithKey:nil usingTracker:trackerMock eventTrackingContext:nil]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + NSArray *flagConfigValues = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey]; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + for (LDFlagConfigValue *flagConfigValue in flagConfigValues) { + [[LDDataManager sharedManager] flushEventsDictionary]; + + XCTestExpectation *eventsExpectation = [self expectationWithDescription:@"LDDataManagerTest.testCreateFlagEvaluationEvents.allEvents"]; + [[trackerMock expect] logRequestForFlagKey:flagKey reportedFlagValue:flagConfigValue.value flagConfigValue:flagConfigValue defaultValue:defaultFlagValue]; + + [[LDDataManager sharedManager] createFlagEvaluationEventsWithFlagKey:flagKey + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + config:self.config]; + + [[LDDataManager sharedManager] allEventDictionaries:^(NSArray *eventDictionaries) { + XCTAssertEqual(eventDictionaries.count, 2); + for (NSString *eventKind in @[kEventModelKindFeature, kEventModelKindDebug]) { + NSPredicate *eventPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + if (![evaluatedObject isKindOfClass:[NSDictionary class]]) { return NO; } + NSDictionary *evaluatedDictionary = evaluatedObject; + return [evaluatedDictionary[kEventModelKeyKind] isEqualToString:eventKind] && [evaluatedDictionary[kEventModelKeyKey] isEqualToString:flagKey]; + }]; + XCTAssertEqual([eventDictionaries filteredArrayUsingPredicate:eventPredicate].count, 1); + } + [eventsExpectation fulfill]; + }]; + [trackerMock verify]; + [self waitForExpectations:@[eventsExpectation] timeout:1.0]; + } + } +} + +-(void)testCreateSummaryEvent_noCounters { + LDFlagConfigTracker *trackerStub = [LDFlagConfigTracker tracker]; XCTestExpectation *expectation = [self expectationWithDescription:@"All events dictionary expectation"]; - + + [[LDDataManager sharedManager] createSummaryEventWithTracker:trackerStub config:self.config]; + [[LDDataManager sharedManager] allEventDictionaries:^(NSArray *eventDictionaries) { - NSMutableArray *eventKeys = [[NSMutableArray alloc] init]; - for (NSDictionary *eventDictionary in eventDictionaries) { - [eventKeys addObject:[eventDictionary objectForKey:@"key"]]; - } - - XCTAssertTrue([eventKeys containsObject:eventKey1]); - XCTAssertTrue([eventKeys containsObject:eventKey2]); + XCTAssertEqual(eventDictionaries.count, 0); [expectation fulfill]; }]; - - [self waitForExpectations:@[expectation] timeout:10]; - + [self waitForExpectations:@[expectation] timeout:1]; } --(void)testAllEventDictionaries { - LDConfig *config = [[LDConfig alloc] initWithMobileKey:@"stubMobileKey"]; - [[LDDataManager sharedManager] createCustomEvent:@"foo" withCustomValuesDictionary:nil user:self.user config:config]; - [[LDDataManager sharedManager] createCustomEvent:@"fi" withCustomValuesDictionary:nil user:self.user config:config]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"All events json data expectation"]; +-(void)testCreateFeatureEvent_trackEvents_YES { + LDFlagConfigValue *flagConfigValue = [self setupCreateFeatureEventTestWithTrackEvents:YES]; + [[self.eventModelMock expect] featureEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + inlineUser:NO]; + + [[LDDataManager sharedManager] createFeatureEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateFeatureEvent_trackEvents_NO { + LDFlagConfigValue *flagConfigValue = [self setupCreateFeatureEventTestWithTrackEvents:NO]; + [[self.eventModelMock reject] featureEventWithFlagKey:[OCMArg any] reportedFlagValue:[OCMArg any] flagConfigValue:[OCMArg any] defaultFlagValue:[OCMArg any] user:[OCMArg any] inlineUser:[OCMArg any]]; + + [[LDDataManager sharedManager] createFeatureEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateFeatureEvent_eventTrackingContext_nil { + LDFlagConfigValue *flagConfigValue = [self setupCreateFeatureEventTestWithTrackEvents:YES includeTrackingContext:NO]; + [[self.eventModelMock reject] featureEventWithFlagKey:[OCMArg any] + reportedFlagValue:[OCMArg any] + flagConfigValue:[OCMArg any] + defaultFlagValue:[OCMArg any] + user:[OCMArg any] + inlineUser:[OCMArg any]]; + + [[LDDataManager sharedManager] createFeatureEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateDebugEvent_lastEventResponseDate_systemDate_debugEventsUntilDate_createEvent { + //lastEventResponseDate < systemDate < debugEventsUntilDate create event + LDFlagConfigValue *flagConfigValue = [self setupCreateDebugEventTestWithLastEventResponseDate:[NSDate dateWithTimeIntervalSinceNow:-1.0] debugUntil:[NSDate dateWithTimeIntervalSinceNow:1.0]]; + [[self.eventModelMock expect] debugEventWithFlagKey:kFlagKeyIsABawler reportedFlagValue:flagConfigValue.value flagConfigValue:flagConfigValue defaultFlagValue:@(NO) user:self.user]; + + [[LDDataManager sharedManager] createDebugEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateDebugEvent_systemDate_lastEventResponseDate_debugEventsUntilDate_createEvent { + //systemDate < lastEventResponseDate < debugEventsUntilDate create event //system time not right, set too far in the past, but lastEventResponse hasn't reached debug + LDFlagConfigValue *flagConfigValue = [self setupCreateDebugEventTestWithLastEventResponseDate:[NSDate dateWithTimeIntervalSinceNow:1.0] debugUntil:[NSDate dateWithTimeIntervalSinceNow:2.0]]; + [[self.eventModelMock expect] debugEventWithFlagKey:kFlagKeyIsABawler reportedFlagValue:flagConfigValue.value flagConfigValue:flagConfigValue defaultFlagValue:@(NO) user:self.user]; + + [[LDDataManager sharedManager] createDebugEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateDebugEvent_lastEventResponseDate_debugEventsUntilDate_systemDate_dontCreateEvent { + //lastEventResponseDate < debugEventsUntilDate < systemDate no event + LDFlagConfigValue *flagConfigValue = [self setupCreateDebugEventTestWithLastEventResponseDate:[NSDate dateWithTimeIntervalSinceNow:-1.0] debugUntil:[NSDate date]]; + [[self.eventModelMock reject] debugEventWithFlagKey:[OCMArg any] reportedFlagValue:[OCMArg any] flagConfigValue:[OCMArg any] defaultFlagValue:[OCMArg any] user:[OCMArg any]]; + + [[LDDataManager sharedManager] createDebugEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateDebugEvent_debugEventsUntilDate_lastEventResponseDate_systemDate_dontCreateEvent { + //debugEventsUntilDate < lastEventResponseDate < systemDate no event + LDFlagConfigValue *flagConfigValue = [self setupCreateDebugEventTestWithLastEventResponseDate:[NSDate dateWithTimeIntervalSinceNow:-1.0] debugUntil:[NSDate dateWithTimeIntervalSinceNow:-2.0]]; + [[self.eventModelMock reject] debugEventWithFlagKey:[OCMArg any] reportedFlagValue:[OCMArg any] flagConfigValue:[OCMArg any] defaultFlagValue:[OCMArg any] user:[OCMArg any]]; + + [[LDDataManager sharedManager] createDebugEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateDebugEvent_debugEventsUntilDate_systemDate_lastEventResponseDate_dontCreateEvent { + //debugEventsUntilDate < systemDate < lastEventResponseDate no event + LDFlagConfigValue *flagConfigValue = [self setupCreateDebugEventTestWithLastEventResponseDate:[NSDate dateWithTimeIntervalSinceNow:1.0] debugUntil:[NSDate dateWithTimeIntervalSinceNow:-1.0]]; + [[self.eventModelMock reject] debugEventWithFlagKey:[OCMArg any] reportedFlagValue:[OCMArg any] flagConfigValue:[OCMArg any] defaultFlagValue:[OCMArg any] user:[OCMArg any]]; + + [[LDDataManager sharedManager] createDebugEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateDebugEvent_systemDate_debugEventsUntilDate_lastEventResponseDate_dontCreateEvent { + //systemDate < debugEventsUntilDate < lastEventResponseDate no event //system time not right, set too far in the past, lastEventResponse past debug + LDFlagConfigValue *flagConfigValue = [self setupCreateDebugEventTestWithLastEventResponseDate:[NSDate dateWithTimeIntervalSinceNow:2.0] debugUntil:[NSDate dateWithTimeIntervalSinceNow:1.0]]; + [[self.eventModelMock reject] debugEventWithFlagKey:[OCMArg any] reportedFlagValue:[OCMArg any] flagConfigValue:[OCMArg any] defaultFlagValue:[OCMArg any] user:[OCMArg any]]; + + [[LDDataManager sharedManager] createDebugEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateDebugEvent_missingDebugEventsUntilDate_dontCreateEvent { + LDFlagConfigValue *flagConfigValue = [self setupCreateDebugEventTestWithLastEventResponseDate:[NSDate dateWithTimeIntervalSinceNow:-1.0] debugUntil:nil]; + [[self.eventModelMock reject] debugEventWithFlagKey:[OCMArg any] reportedFlagValue:[OCMArg any] flagConfigValue:[OCMArg any] defaultFlagValue:[OCMArg any] user:[OCMArg any]]; + + [[LDDataManager sharedManager] createDebugEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testCreateDebugEvent_missingEventTrackingContext_dontCreateEvent { + LDFlagConfigValue *flagConfigValue = [self setupCreateDebugEventTestWithLastEventResponseDate:[NSDate dateWithTimeIntervalSinceNow:-1.0] debugUntil:nil includeTrackingContext:NO]; + [[self.eventModelMock reject] debugEventWithFlagKey:[OCMArg any] reportedFlagValue:[OCMArg any] flagConfigValue:[OCMArg any] defaultFlagValue:[OCMArg any] user:[OCMArg any]]; + + [[LDDataManager sharedManager] createDebugEventWithFlagKey:kFlagKeyIsABawler + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + + [self.eventModelMock verify]; +} + +-(void)testAllEventsDictionaryArray { + LDEventModel *featureEvent = [LDEventModel stubEventWithKind:kEventModelKindFeature user:self.user config:self.config]; + [[LDDataManager sharedManager] createFeatureEventWithFlagKey:featureEvent.key + reportedFlagValue:featureEvent.reportedValue + flagConfigValue:featureEvent.flagConfigValue + defaultFlagValue:featureEvent.defaultValue + user:self.user + config:self.config]; + LDEventModel *customEvent = [LDEventModel stubEventWithKind:kEventModelKindCustom user:self.user config:self.config]; + [[LDDataManager sharedManager] createCustomEventWithKey:customEvent.key customData:customEvent.data user:self.user config:self.config]; + LDEventModel *identifyEvent = [LDEventModel stubEventWithKind:kEventModelKindIdentify user:self.user config:self.config]; + [[LDDataManager sharedManager] createIdentifyEventWithUser:self.user config:self.config]; + LDFlagConfigTracker *trackerStub = [LDFlagConfigTracker stubTracker]; + LDEventModel *summaryEvent = [LDEventModel summaryEventWithTracker:trackerStub]; + [[LDDataManager sharedManager] createSummaryEventWithTracker:trackerStub config:self.config]; + LDEventModel *debugEvent = [LDEventModel stubEventWithKind:kEventModelKindDebug user:self.user config:self.config]; + [[LDDataManager sharedManager] createDebugEventWithFlagKey:debugEvent.key + reportedFlagValue:debugEvent.reportedValue + flagConfigValue:debugEvent.flagConfigValue + defaultFlagValue:debugEvent.defaultValue + user:self.user + config:self.config]; + NSArray *eventStubs = @[featureEvent, customEvent, identifyEvent, summaryEvent, debugEvent]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"All events dictionary expectation"]; [[LDDataManager sharedManager] allEventDictionaries:^(NSArray *eventDictionaries) { - - NSMutableDictionary *events = [[NSMutableDictionary alloc] init]; - for (NSDictionary *eventDictionary in eventDictionaries) { - XCTAssertTrue([eventDictionary[@"user"] isKindOfClass:[NSDictionary class]]); - [events setObject:[[LDEventModel alloc] initWithDictionary:eventDictionary] forKey:[eventDictionary objectForKey:@"key"]]; + for (LDEventModel *event in eventStubs) { + NSDictionary *eventDictionary = [eventDictionaries dictionaryForEvent:event]; + + XCTAssertNotNil(eventDictionary); + if (!eventDictionary) { + NSLog(@"Did not find matching event dictionary for event: %@", event.kind); + continue; + } + + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKind], event.kind); + if (event.hasCommonFields) { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKey], event.key); + if (event.alwaysInlinesUser) { + XCTAssertNotNil(eventDictionary[kEventModelKeyUser]); + } else { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyUserKey], event.user.key); + } + XCTAssertNil(eventDictionary[kEventModelKeyInlineUser]); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyCreationDate] ldMillisecondValue], event.creationDate, 1)); + } + if (event.isFlagRequestEventKind) { + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyValue], event.flagConfigValue.value); + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyVariation], @(event.flagConfigValue.variation)); + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyVersion], event.flagConfigValue.flagVersion); + XCTAssertNil(eventDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyDefault], event.defaultValue); + } + if ([event.kind isEqualToString:kEventModelKindCustom]) { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyData], event.data); + } + if ([event.kind isEqualToString:kEventModelKindFeatureSummary]) { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyStartDate], @(event.startDateMillis)); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyEndDate] ldMillisecondValue], event.endDateMillis, 10)); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyFeatures], event.flagRequestSummary); + } } - XCTAssertEqual([events count], 2); [expectation fulfill]; }]; - [self waitForExpectations:@[expectation] timeout:10]; - + [self waitForExpectations:@[expectation] timeout:1]; } - -(void)testFindOrCreateUser { NSString *userKey = @"thisisgus"; LDUserModel *aUser = [[LDUserModel alloc] init]; aUser.key = userKey; aUser.email = @"gus@anemail.com"; aUser.updatedAt = [NSDate date]; - aUser.config = user.config; + aUser.flagConfig = user.flagConfig; [[LDDataManager sharedManager] saveUser: aUser]; LDUserModel *foundAgainUser = [[LDDataManager sharedManager] findUserWithkey: userKey]; @@ -152,26 +446,32 @@ -(void) testPurgeUsers { } -(void)testCreateEventAfterCapacityReached { - LDConfig *config = [[LDConfig alloc] initWithMobileKey:@"AMobileKey"]; - config.capacity = [NSNumber numberWithInt:2]; + self.config.capacity = @(2); XCTestExpectation *expectation = [self expectationWithDescription:@"All events dictionary expectation"]; - OCMStub([clientMock ldConfig]).andReturn(config); - + LDDataManager *manager = [LDDataManager sharedManager]; [manager.eventsArray removeAllObjects]; - [manager createCustomEvent:@"aKey" withCustomValuesDictionary: @{@"carrot": @"cake"} user:self.user config:config]; - [manager createCustomEvent:@"aKey" withCustomValuesDictionary: @{@"carrot": @"cake"} user:self.user config:config]; - [manager createCustomEvent:@"aKey" withCustomValuesDictionary: @{@"carrot": @"cake"} user:self.user config:config]; - [manager createFeatureEvent: @"anotherKet" keyValue: [NSNumber numberWithBool:YES] defaultKeyValue: [NSNumber numberWithBool:NO] user:self.user config:config]; - + [manager createCustomEventWithKey:@"aKey" customData: @{@"carrot": @"cake"} user:self.user config:self.config]; + [manager createCustomEventWithKey:@"aKey" customData: @{@"carrot": @"cake"} user:self.user config:self.config]; + [manager createCustomEventWithKey:@"aKey" customData: @{@"carrot": @"cake"} user:self.user config:self.config]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"boolConfigIsABool-true" + flagKey:kLDFlagKeyIsABool + eventTrackingContext:[LDEventTrackingContext stub]]; + [manager createFeatureEventWithFlagKey: @"anotherKey" + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:@(NO) + user:self.user + config:self.config]; + [manager allEventDictionaries:^(NSArray *array) { XCTAssertEqual([array count],2); [expectation fulfill]; }]; - [self waitForExpectations:@[expectation] timeout:10]; + [self waitForExpectations:@[expectation] timeout:1.0]; } @end diff --git a/DarklyTests/Models/LDEventModelTest.m b/DarklyTests/Models/LDEventModelTest.m index 5d0ed35f..6108f51c 100644 --- a/DarklyTests/Models/LDEventModelTest.m +++ b/DarklyTests/Models/LDEventModelTest.m @@ -4,11 +4,29 @@ #import #import "LDEventModel.h" +#import "LDEventModel+Testable.h" #import "LDUserModel.h" -#import "LDUserModel+Stub.h" +#import "LDUserModel+Testable.h" +#import "NSDate+ReferencedDate.h" +#import "LDFlagConfigTracker+Testable.h" +#import "NSDate+Testable.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDFlagCounter+Testable.h" +#import "NSArray+Testable.h" +#import "LDFlagValueCounter+Testable.h" +#import "LDEventTrackingContext+Testable.h" +#import "NSArray+Testable.h" +#import "NSNumber+LaunchDarkly.h" -NSString * const kUserKeyUser = @"user"; -NSString * const kUserKeyConfig = @"config"; +extern NSString * const kEventModelKeyUser; +extern NSString * const kEventModelKeyUserKey; +extern NSString * const kEventModelKeyInlineUser; + +extern NSString * const kUserAttributeConfig; + +extern const NSTimeInterval kLDFlagConfigTrackerTrackingInterval; + +NSString * const testMobileKey = @"EventModelTest.testMobileKey"; @interface LDEventModelTest : XCTestCase @property LDUserModel *user; @@ -18,6 +36,7 @@ @implementation LDEventModelTest - (void)setUp { [super setUp]; self.user = [[LDUserModel alloc] init]; + self.user.key = [[NSUUID UUID] UUIDString]; } - (void)tearDown { @@ -25,35 +44,371 @@ - (void)tearDown { [super tearDown]; } -- (void)testFeatureEventWithKeyCreatesEventWithDefaults { - LDEventModel *event = [[LDEventModel alloc] initFeatureEventWithKey:@"red" keyValue:[NSNumber numberWithBool:NO] defaultKeyValue:[NSNumber numberWithBool:NO] userValue:self.user]; - - XCTAssertEqualObjects(event.key, @"red"); - XCTAssertEqualObjects(event.kind, @"feature"); - XCTAssertFalse([(NSNumber *)event.value boolValue]); - XCTAssertFalse([(NSNumber *)event.isDefault boolValue]); +- (void)testInitAndFeatureEvent { + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + NSArray *flagConfigValues = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey]; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + for (LDFlagConfigValue *flagConfigValue in flagConfigValues) { + LDMillisecond creationDateMillis = [[NSDate date] millisSince1970]; + LDEventModel *featureEvent = [LDEventModel featureEventWithFlagKey:flagKey + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + inlineUser:NO]; + + XCTAssertTrue([featureEvent hasPropertiesMatchingFlagKey:flagKey + eventKind:kEventModelKindFeature + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + inlineUser:NO + creationDateMillis:creationDateMillis]); + } + } } -- (void)testCustomEventWithKeyCreatesEventWithDefaults { +- (void)testCustomEvent { NSDictionary *dictionary = @{@"red": @"is not blue"}; - - LDEventModel *event = [[LDEventModel alloc] initCustomEventWithKey:@"red" andDataDictionary:dictionary userValue:self.user]; - - XCTAssertEqualObjects(event.key, @"red"); - XCTAssertEqualObjects(event.kind, @"custom"); - XCTAssertEqual([event.data allValues].firstObject, - [dictionary allValues].firstObject); + NSArray *boolValues = @[@NO, @YES]; + for (NSNumber *value in boolValues) { + BOOL boolValue = [value boolValue]; + LDMillisecond referenceMillis = [[NSDate date] millisSince1970]; + LDEventModel *event = [LDEventModel customEventWithKey:@"red" customData:dictionary userValue:self.user inlineUser:boolValue]; + + XCTAssertEqualObjects(event.key, @"red"); + XCTAssertEqualObjects(event.kind, kEventModelKindCustom); + XCTAssertEqualObjects(event.data, dictionary); + XCTAssertTrue([event.user isEqual:self.user ignoringAttributes:@[]]); + XCTAssertEqual(event.inlineUser, boolValue); + XCTAssertTrue(event.creationDate >= referenceMillis); + } +} + +- (void)testIdentifyEvent { + LDMillisecond referenceMillis = [[NSDate date] millisSince1970]; + LDEventModel *event = [LDEventModel identifyEventWithUser:self.user]; + + XCTAssertEqualObjects(event.kind, kEventModelKindIdentify); + XCTAssertTrue([event.user isEqual:self.user ignoringAttributes:@[]]); + XCTAssertEqual(event.inlineUser, YES); + XCTAssertTrue(event.creationDate >= referenceMillis); +} + +- (void)testSummaryEvent { + NSDate *creationDate = [NSDate date]; + LDFlagConfigTracker *trackerStub = [LDFlagConfigTracker stubTracker]; + + LDEventModel *event = [LDEventModel summaryEventWithTracker:trackerStub]; + + XCTAssertNotNil(event); + LDMillisecond startDateMillis = [[creationDate dateByAddingTimeInterval:kLDFlagConfigTrackerTrackingInterval] millisSince1970]; + XCTAssertTrue(Approximately(event.startDateMillis, startDateMillis, 10)); + XCTAssertTrue(Approximately(event.endDateMillis, [creationDate millisSince1970], 10)); + //Verify flagCounter dictionaries match the tracker + XCTAssertEqual(event.flagRequestSummary.allKeys.count, trackerStub.flagCounters.allKeys.count); + if (event.flagRequestSummary.allKeys.count != trackerStub.flagCounters.allKeys.count) { return; } + for (NSString *flagKey in trackerStub.flagCounters.allKeys) { + LDFlagCounter *flagCounter = trackerStub.flagCounters[flagKey]; + NSDictionary *flagCounterDictionary = event.flagRequestSummary[flagKey]; + + XCTAssertEqualObjects(flagCounterDictionary[kLDFlagCounterKeyDefaultValue], flagCounter.defaultValue); + NSArray *counterDictionaries = flagCounterDictionary[kLDFlagCounterKeyCounters]; + XCTAssertEqual(counterDictionaries.count, flagCounter.flagValueCounters.count); + if (counterDictionaries.count != flagCounter.flagValueCounters.count) { continue; } + for (LDFlagValueCounter *flagValueCounter in flagCounter.flagValueCounters) { + NSDictionary *selectedCounterDictionary = [counterDictionaries dictionaryForFlagValueCounter:flagValueCounter]; + XCTAssertNotNil(selectedCounterDictionary, @"counter dictionary not found for flagValueCounter: %@", [flagValueCounter description]); + if (!selectedCounterDictionary) { continue; } + + XCTAssertEqualObjects(selectedCounterDictionary[kLDFlagConfigValueKeyValue], flagValueCounter.flagConfigValue.value); + XCTAssertEqual([selectedCounterDictionary[kLDFlagConfigValueKeyVariation] integerValue], flagValueCounter.flagConfigValue.variation); + XCTAssertEqualObjects(selectedCounterDictionary[kLDFlagConfigValueKeyVersion], flagValueCounter.flagConfigValue.flagVersion); + XCTAssertEqual([selectedCounterDictionary[kLDFlagValueCounterKeyCount] integerValue], flagValueCounter.count); + XCTAssertNil(selectedCounterDictionary[kLDFlagValueCounterKeyUnknown]); + XCTAssertNil(selectedCounterDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(selectedCounterDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(selectedCounterDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + } + } +} + +- (void)testInitAndDebugEvent { + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + NSArray *flagConfigValues = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey]; + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + for (LDFlagConfigValue *flagConfigValue in flagConfigValues) { + LDMillisecond creationDateMillis = [[NSDate date] millisSince1970]; + LDEventModel *debugEvent = [LDEventModel debugEventWithFlagKey:flagKey + reportedFlagValue:flagConfigValue.value + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user]; + + XCTAssertTrue([debugEvent hasPropertiesMatchingFlagKey:flagKey + eventKind:kEventModelKindDebug + flagConfigValue:flagConfigValue + defaultFlagValue:defaultFlagValue + user:self.user + inlineUser:YES + creationDateMillis:creationDateMillis]); + } + } +} + +-(void)testEncodeAndDecodeEvent { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + config.inlineUserInEvents = YES; + for (NSString *eventKind in [LDEventModel allEventKinds]) { + LDEventModel *originalEvent = [LDEventModel stubEventWithKind:eventKind user:self.user config:config]; + + NSData *encodedEventData = [NSKeyedArchiver archivedDataWithRootObject:originalEvent]; + XCTAssertNotNil(encodedEventData); + + LDEventModel *decodedEvent = [NSKeyedUnarchiver unarchiveObjectWithData:encodedEventData]; + XCTAssertTrue([originalEvent isEqual:decodedEvent]); + } +} + +-(void)testDictionaryValue { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + for (NSString *eventKind in [LDEventModel allEventKinds]) { + LDEventModel *event = [LDEventModel stubEventWithKind:eventKind user:self.user config:config]; + + NSDictionary *eventDictionary = [event dictionaryValueUsingConfig:config]; + + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKind], event.kind); + if (event.hasCommonFields) { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKey], event.key); + if (event.alwaysInlinesUser) { + XCTAssertNotNil(eventDictionary[kEventModelKeyUser]); + } else { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyUserKey], event.user.key); + } + XCTAssertNil(eventDictionary[kEventModelKeyInlineUser]); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyCreationDate] ldMillisecondValue], event.creationDate, 1)); + } + if (event.isFlagRequestEventKind) { + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyValue], event.flagConfigValue.value); + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyVariation], @(event.flagConfigValue.variation)); + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyVersion], event.flagConfigValue.flagVersion); + XCTAssertNil(eventDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyDefault], event.defaultValue); + } else { + XCTAssertNil(eventDictionary[kLDFlagConfigValueKeyValue]); + } + if ([event.kind isEqualToString:kEventModelKindCustom]) { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyData], event.data); + } + if ([event.kind isEqualToString:kEventModelKindFeatureSummary]) { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyStartDate], @(event.startDateMillis)); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyEndDate] ldMillisecondValue], event.endDateMillis, 10)); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyFeatures], event.flagRequestSummary); + } + } +} + +-(void)testDictionaryValue_inlineUser_YES { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + config.inlineUserInEvents = YES; + for (NSString *eventKind in [LDEventModel eventKindsWithCommonFields]) { + LDEventModel *event = [LDEventModel stubEventWithKind:eventKind user:self.user config:config]; + + NSDictionary *eventDictionary = [event dictionaryValueUsingConfig:config]; + + XCTAssertNotNil(eventDictionary[kEventModelKeyUser]); + XCTAssertNil(eventDictionary[kEventModelKeyUserKey]); + LDUserModel *restoredUser = [[LDUserModel alloc] initWithDictionary:eventDictionary[kEventModelKeyUser]]; + XCTAssertTrue([event.user isEqual:restoredUser ignoringAttributes:@[kUserAttributeConfig]]); + XCTAssertNil(eventDictionary[kEventModelKeyInlineUser]); + } +} + +-(void)testDictionaryValue_flagRequestEvents_missingFlagConfigValue { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + for (NSString *eventKind in [LDEventModel eventKindsForFlagRequests]) { + LDEventModel *event = [LDEventModel stubEventWithKind:eventKind user:self.user config:config]; + event.reportedValue = event.defaultValue; + event.flagConfigValue = nil; + + NSDictionary *eventDictionary = [event dictionaryValueUsingConfig:config]; + + //Checks that differ from testDictionaryValue + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyValue], event.defaultValue); + XCTAssertNil(eventDictionary[kLDFlagConfigValueKeyVariation]); + XCTAssertNil(eventDictionary[kLDFlagConfigValueKeyVersion]); + + //Checks that are the same as in testDictionaryValue + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKind], event.kind); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKey], event.key); + if (event.alwaysInlinesUser) { + XCTAssertNotNil(eventDictionary[kEventModelKeyUser]); + } else { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyUserKey], event.user.key); + } + XCTAssertNil(eventDictionary[kEventModelKeyInlineUser]); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyCreationDate] ldMillisecondValue], event.creationDate, 1)); + XCTAssertNil(eventDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyDefault], event.defaultValue); + } +} + +-(void)testDictionaryValue_flagRequestEvents_flagConfigValueHasNullValue { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + for (NSString *eventKind in [LDEventModel eventKindsForFlagRequests]) { + LDEventModel *event = [LDEventModel stubEventWithKind:eventKind user:self.user config:config]; + event.reportedValue = event.defaultValue; + event.flagConfigValue.value = [NSNull null]; + + NSDictionary *eventDictionary = [event dictionaryValueUsingConfig:config]; + + //Checks that differ from testDictionaryValue + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyValue], event.defaultValue); + + //Checks that are the same as in testDictionaryValue + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKind], event.kind); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKey], event.key); + if (event.alwaysInlinesUser) { + XCTAssertNotNil(eventDictionary[kEventModelKeyUser]); + } else { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyUserKey], event.user.key); + } + XCTAssertNil(eventDictionary[kEventModelKeyInlineUser]); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyCreationDate] ldMillisecondValue], event.creationDate, 1)); + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyVariation], @(event.flagConfigValue.variation)); + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyVersion], event.flagConfigValue.flagVersion); + XCTAssertNil(eventDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyDefault], event.defaultValue); + } +} + +-(void)testDictionaryValue_summaryEvents_missingFlagConfigValue { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + LDFlagConfigTracker *tracker = [LDFlagConfigTracker stubTrackerUseKnownValues:NO]; + LDEventModel *event = [LDEventModel summaryEventWithTracker:tracker]; + + NSDictionary *eventDictionary = [event dictionaryValueUsingConfig:config]; + + //Checks that differ from testDictionaryValue + XCTAssertEqualObjects(eventDictionary[kEventModelKeyStartDate], @(event.startDateMillis)); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyEndDate] ldMillisecondValue], event.endDateMillis, 10)); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyFeatures], event.flagRequestSummary); + + //Checks that are the same as in testDictionaryValue + for (NSString *flagKey in tracker.flagCounters.allKeys) { + LDFlagCounter *flagCounter = tracker.flagCounters[flagKey]; + NSDictionary *flagCounterDictionary = eventDictionary[kEventModelKeyFeatures][flagKey]; + LDFlagValueCounter *flagValueCounter = flagCounter.valueCounters.firstObject; + XCTAssertEqual(((NSArray*)flagCounterDictionary[kLDFlagCounterKeyCounters]).count, 1); + NSDictionary *flagConfigValueSummary = [flagCounterDictionary[kLDFlagCounterKeyCounters] firstObject]; + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagConfigValueKeyValue], [LDFlagConfigValue defaultValueForFlagKey:flagKey]); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagValueCounterKeyUnknown], @(YES)); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagValueCounterKeyCount], @(flagValueCounter.count)); + XCTAssertNil(flagConfigValueSummary[kLDFlagConfigValueKeyVersion]); + XCTAssertNil(flagConfigValueSummary[kLDFlagConfigValueKeyVariation]); + } +} + +-(void)testDictionaryValue_summaryEvents_flagConfigValueHasNullValue { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + LDFlagConfigTracker *tracker = [LDFlagConfigTracker stubTrackerWithNullValuesInFlagConfigValue]; + LDEventModel *event = [LDEventModel summaryEventWithTracker:tracker]; + + NSDictionary *eventDictionary = [event dictionaryValueUsingConfig:config]; + + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKind], event.kind); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyStartDate], @(event.startDateMillis)); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyEndDate] ldMillisecondValue], event.endDateMillis, 10)); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyFeatures], event.flagRequestSummary); + for (NSString *flagKey in tracker.flagCounters.allKeys) { + LDFlagCounter *flagCounter = tracker.flagCounters[flagKey]; + NSDictionary *flagCounterDictionary = eventDictionary[kEventModelKeyFeatures][flagKey]; + LDFlagValueCounter *flagValueCounter = flagCounter.valueCounters.firstObject; + LDFlagConfigValue *flagConfigValue = flagValueCounter.flagConfigValue; + NSDictionary *flagConfigValueSummary = [flagCounterDictionary[kLDFlagCounterKeyCounters] firstObject]; + XCTAssertEqual(((NSArray*)flagCounterDictionary[kLDFlagCounterKeyCounters]).count, 1); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagConfigValueKeyValue], [LDFlagConfigValue defaultValueForFlagKey:flagKey]); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagConfigValueKeyVariation], @(flagConfigValue.variation)); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagConfigValueKeyVersion], flagConfigValue.flagVersion); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagValueCounterKeyCount], @(flagValueCounter.count)); + XCTAssertNil(flagConfigValueSummary[kLDFlagValueCounterKeyUnknown]); + } +} + +-(void)testDictionaryValue_flagRequestEvents_missingFlagVersion { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + for (NSString *eventKind in [LDEventModel eventKindsForFlagRequests]) { + LDEventModel *event = [LDEventModel stubEventWithKind:eventKind user:self.user config:config]; + event.flagConfigValue.flagVersion = nil; + + NSDictionary *eventDictionary = [event dictionaryValueUsingConfig:config]; + + //Checks that differ from testDictionaryValue + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyVersion], @(event.flagConfigValue.modelVersion)); + + //Checks that are the same as in testDictionaryValue + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKind], event.kind); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyKey], event.key); + if (event.alwaysInlinesUser) { + XCTAssertNotNil(eventDictionary[kEventModelKeyUser]); + } else { + XCTAssertEqualObjects(eventDictionary[kEventModelKeyUserKey], event.user.key); + } + XCTAssertNil(eventDictionary[kEventModelKeyInlineUser]); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyCreationDate] ldMillisecondValue], event.creationDate, 1)); + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyValue], event.flagConfigValue.value); + XCTAssertEqualObjects(eventDictionary[kLDFlagConfigValueKeyVariation], @(event.flagConfigValue.variation)); + XCTAssertNil(eventDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(eventDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyDefault], event.defaultValue); + } +} + +-(void)testDictionaryValue_summaryEvents_missingFlagVersion { + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; + LDFlagConfigTracker *tracker = [LDFlagConfigTracker stubTrackerIncludeFlagVersion:NO]; + LDEventModel *event = [LDEventModel summaryEventWithTracker:tracker]; + + NSDictionary *eventDictionary = [event dictionaryValueUsingConfig:config]; + + //Checks that differ from testDictionaryValue + XCTAssertEqualObjects(eventDictionary[kEventModelKeyStartDate], @(event.startDateMillis)); + XCTAssertTrue(Approximately([eventDictionary[kEventModelKeyEndDate] ldMillisecondValue], event.endDateMillis, 10)); + XCTAssertEqualObjects(eventDictionary[kEventModelKeyFeatures], event.flagRequestSummary); + + //Checks that are the same as in testDictionaryValue + for (NSString *flagKey in tracker.flagCounters.allKeys) { + LDFlagCounter *flagCounter = tracker.flagCounters[flagKey]; + NSDictionary *flagCounterDictionary = eventDictionary[kEventModelKeyFeatures][flagKey]; + for (LDFlagValueCounter *flagValueCounter in flagCounter.valueCounters) { + LDFlagConfigValue *flagConfigValue = flagValueCounter.flagConfigValue; + NSDictionary *flagConfigValueSummary = [flagCounterDictionary[kLDFlagCounterKeyCounters] dictionaryForFlagValueCounter:flagValueCounter]; + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagConfigValueKeyVersion], @(flagConfigValue.modelVersion)); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagConfigValueKeyValue], flagConfigValue.value); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagConfigValueKeyVariation], @(flagConfigValue.variation)); + XCTAssertEqualObjects(flagConfigValueSummary[kLDFlagValueCounterKeyCount], @(flagValueCounter.count)); + XCTAssertNil(eventDictionary[kLDFlagValueCounterKeyUnknown]); + } + } } -(void)testEventDictionaryOmitsUserFlagConfig { - LDConfig *config = [[LDConfig alloc] initWithMobileKey:@"eventDictionaryTestMobileKey"]; + LDConfig *config = [[LDConfig alloc] initWithMobileKey:testMobileKey]; self.user = [LDUserModel stubWithKey:[[NSUUID UUID] UUIDString]]; - LDEventModel *eventStub = [[LDEventModel alloc] initCustomEventWithKey:@"eventStubKey" andDataDictionary:@{} userValue:self.user]; - NSDictionary *subject = [eventStub dictionaryValueUsingConfig:config][kUserKeyUser]; + LDEventModel *eventStub = [[LDEventModel alloc] initCustomEventWithKey:@"eventStubKey" customData:@{} userValue:self.user inlineUser:YES]; + + NSDictionary *eventDictionary = [eventStub dictionaryValueUsingConfig:config][kEventModelKeyUser]; - XCTAssertNotNil(subject); - XCTAssertTrue(subject.allKeys.count > 0); - XCTAssertFalse([subject.allKeys containsObject:kUserKeyConfig]); + XCTAssertNotNil(eventDictionary); + XCTAssertTrue(eventDictionary.allKeys.count > 0); + XCTAssertFalse([eventDictionary.allKeys containsObject:kUserAttributeConfig]); } @end diff --git a/DarklyTests/Models/LDFlagConfig/LDEventTrackingContextTest.m b/DarklyTests/Models/LDFlagConfig/LDEventTrackingContextTest.m new file mode 100644 index 00000000..23826c80 --- /dev/null +++ b/DarklyTests/Models/LDFlagConfig/LDEventTrackingContextTest.m @@ -0,0 +1,89 @@ +// +// LDEventTrackingContextTest.m +// DarklyTests +// +// Created by Mark Pokorny on 5/4/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDEventTrackingContext.h" +#import "LDEventTrackingContext+Testable.h" +#import "NSDate+ReferencedDate.h" + +@interface LDEventTrackingContextTest : XCTestCase + +@end + +@implementation LDEventTrackingContextTest + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +-(void)testContextAndInitWithObjectAndDictionaryValue { + NSDate *debugEventsUntilDate = [[NSDate date] dateByAddingTimeInterval:30.0]; + for (NSNumber *boolObject in @[@NO, @YES]) { + BOOL trackEvents = [boolObject boolValue]; + LDEventTrackingContext *originalContext = [LDEventTrackingContext contextWithTrackEvents:trackEvents debugEventsUntilDate:debugEventsUntilDate]; + NSMutableDictionary *contextDictionary = [NSMutableDictionary dictionaryWithDictionary:[originalContext dictionaryValue]]; + + LDEventTrackingContext *restoredContext = [LDEventTrackingContext contextWithObject:[contextDictionary copy]]; + + XCTAssertEqualObjects(restoredContext, originalContext); + + //without the debugEventsUntilDate + originalContext = [LDEventTrackingContext contextWithTrackEvents:trackEvents debugEventsUntilDate:nil]; + contextDictionary = [NSMutableDictionary dictionaryWithDictionary:[originalContext dictionaryValue]]; + + restoredContext = [LDEventTrackingContext contextWithObject:[contextDictionary copy]]; + + XCTAssertEqualObjects(restoredContext, originalContext); + + //null debugEventsUntilDate + originalContext = [LDEventTrackingContext contextWithTrackEvents:trackEvents debugEventsUntilDate:nil]; + contextDictionary = [NSMutableDictionary dictionaryWithDictionary:[originalContext dictionaryValue]]; + contextDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate] = [NSNull null]; + + restoredContext = [LDEventTrackingContext contextWithObject:[contextDictionary copy]]; + + XCTAssertEqualObjects(restoredContext, originalContext); + } + + LDEventTrackingContext *trackingContext = [LDEventTrackingContext contextWithObject:nil]; + XCTAssertNil(trackingContext); + + trackingContext = [LDEventTrackingContext contextWithObject:@{}]; + XCTAssertNil(trackingContext); + + trackingContext = [LDEventTrackingContext contextWithObject:@{kLDEventTrackingContextKeyTrackEvents: [NSNull null]}]; + XCTAssertNil(trackingContext); + + trackingContext = [LDEventTrackingContext contextWithObject:@{kLDEventTrackingContextKeyDebugEventsUntilDate: @([debugEventsUntilDate millisSince1970])}]; + XCTAssertNil(trackingContext); + + trackingContext = [LDEventTrackingContext contextWithObject:@(3)]; + XCTAssertNil(trackingContext); +} + +-(void)testEncodeAndDecodeWithCoder { + NSDate *debugEventsUntilDate = [[NSDate date] dateByAddingTimeInterval:30.0]; + for (NSNumber *boolObject in @[@NO, @YES]) { + BOOL trackEvents = [boolObject boolValue]; + LDEventTrackingContext *originalContext = [LDEventTrackingContext contextWithTrackEvents:trackEvents debugEventsUntilDate:debugEventsUntilDate]; + + NSData *encodedContext = [NSKeyedArchiver archivedDataWithRootObject:originalContext]; + XCTAssertNotNil(encodedContext); + + LDEventTrackingContext *restoredContext = [NSKeyedUnarchiver unarchiveObjectWithData:encodedContext]; + XCTAssertEqualObjects(restoredContext, originalContext); + } +} + +@end diff --git a/DarklyTests/Models/LDFlagConfigModelTest.m b/DarklyTests/Models/LDFlagConfig/LDFlagConfigModelTest.m similarity index 65% rename from DarklyTests/Models/LDFlagConfigModelTest.m rename to DarklyTests/Models/LDFlagConfig/LDFlagConfigModelTest.m index d746fcca..2ac6771b 100644 --- a/DarklyTests/Models/LDFlagConfigModelTest.m +++ b/DarklyTests/Models/LDFlagConfig/LDFlagConfigModelTest.m @@ -5,13 +5,16 @@ #import #import "LDFlagConfigModel.h" #import "LDFlagConfigModel+Testable.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDEventTrackingContext.h" +#import "LDEventTrackingContext+Testable.h" #import "NSJSONSerialization+Testable.h" #import "NSDictionary+Testable.h" +#import "NSDate+ReferencedDate.h" +#import "NSDate+Testable.h" -extern NSString *const kLDFlagConfigJsonDictionaryKeyKey; -extern NSString *const kLDFlagConfigJsonDictionaryKeyValue; -extern NSString *const kLDFlagConfigJsonDictionaryKeyVersion; -extern const NSInteger kLDFlagConfigVersionDoesNotExist; +extern NSString *const kLDFlagConfigModelKeyKey; @interface LDFlagConfigModelTest : XCTestCase @@ -20,26 +23,22 @@ @interface LDFlagConfigModelTest : XCTestCase @implementation LDFlagConfigModelTest -(void)testEncodeAndDecode { - LDFlagConfigModel *originalConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; + LDFlagConfigModel *originalConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"]; NSData *archive = [NSKeyedArchiver archivedDataWithRootObject:originalConfig]; LDFlagConfigModel *restoredConfig = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; XCTAssertTrue([restoredConfig isEqualToConfig:originalConfig]); } --(void)testInitWithDictionary_withVersions { - NSDictionary *flagConfigDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withVersions"]; - LDFlagConfigModel *subject = [[LDFlagConfigModel alloc] initWithDictionary:flagConfigDictionary]; - - for (NSString *key in [flagConfigDictionary.allKeys copy]) { - LDFlagConfigValue *flagConfigValueFromDictionary = [[LDFlagConfigValue alloc] initWithObject:flagConfigDictionary[key]]; +-(void)testInit { + LDFlagConfigModel *subject = [[LDFlagConfigModel alloc] init]; - XCTAssertTrue([subject.featuresJsonDictionary[key] isEqual:flagConfigValueFromDictionary]); - } + XCTAssertNotNil(subject.featuresJsonDictionary); + XCTAssertTrue(subject.featuresJsonDictionary.count == 0); } --(void)testInitWithDictionary_withoutVersions { - NSDictionary *flagConfigDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withoutVersions"]; +-(void)testInitWithDictionary { + NSDictionary *flagConfigDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags"]; LDFlagConfigModel *subject = [[LDFlagConfigModel alloc] initWithDictionary:flagConfigDictionary]; for (NSString *key in [flagConfigDictionary.allKeys copy]) { @@ -49,179 +48,135 @@ -(void)testInitWithDictionary_withoutVersions { } } --(void)testDictionaryValue_includeNullValues_withVersions { - LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; - - NSDictionary *flagConfigDictionary = [subject dictionaryValueIncludeNulls:YES]; - XCTAssertTrue([subject hasFeaturesEqualToDictionary:flagConfigDictionary]); - - NSDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-excludeNulls-withVersions"]; - XCTAssertFalse([subject hasFeaturesEqualToDictionary:differentDictionary]); -} - --(void)testDictionaryValue_excludeNullValues_withVersions { - LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-excludeNulls-withVersions"]; - - NSDictionary *flagConfigDictionary = [subject dictionaryValueIncludeNulls:NO]; - XCTAssertTrue([subject hasFeaturesEqualToDictionary:flagConfigDictionary]); - - NSDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withVersions"]; - XCTAssertFalse([subject hasFeaturesEqualToDictionary:differentDictionary]); -} - --(void)testDictionaryValue_includeNullValues_withoutVersions { - LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withoutVersions"]; +-(void)testDictionaryValue_includeNullValues { + LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"]; NSDictionary *flagConfigDictionary = [subject dictionaryValueIncludeNulls:YES]; XCTAssertTrue([subject hasFeaturesEqualToDictionary:flagConfigDictionary]); - NSDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-excludeNulls-withoutVersions"]; + NSDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-excludeNulls"]; XCTAssertFalse([subject hasFeaturesEqualToDictionary:differentDictionary]); } --(void)testDictionaryValue_excludeNullValues_withoutVersions { - LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-excludeNulls-withoutVersions"]; +-(void)testDictionaryValue_excludeNullValues { + LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-excludeNulls"]; NSDictionary *flagConfigDictionary = [subject dictionaryValueIncludeNulls:NO]; XCTAssertTrue([subject hasFeaturesEqualToDictionary:flagConfigDictionary]); - NSDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withoutVersions"]; + NSDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags"]; XCTAssertFalse([subject hasFeaturesEqualToDictionary:differentDictionary]); } --(void)testFlagConfigValue_withVersions { - LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; - NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withVersions"]; +-(void)testFlagConfigValueForFlagKey { + LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"]; + NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags"]; for (NSString *key in [flagValues.allKeys copy]) { - id targetValue = flagValues[key][kLDFlagConfigJsonDictionaryKeyValue]; - if ([targetValue isKindOfClass:[NSNull class]]) { - XCTAssertNil([config configFlagValue:key]); - continue; - } - XCTAssertTrue([[config configFlagValue:key] isEqual:targetValue]); + id targetFlagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:flagValues[key]]; + XCTAssertEqualObjects([subject flagConfigValueForFlagKey:key], targetFlagConfigValue); } - XCTAssertNil([config configFlagValue:@"someMissingKey"]); + XCTAssertNil([subject flagValueForFlagKey:@"someMissingKey"]); } --(void)testFlagConfigValue_withoutVersions { - LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withoutVersions"]; - NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withoutVersions"]; +-(void)testFlagValueForFlagKey { + LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"]; + NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags"]; for (NSString *key in [flagValues.allKeys copy]) { - id targetValue = flagValues[key]; + id targetValue = flagValues[key][kLDFlagConfigValueKeyValue]; if ([targetValue isKindOfClass:[NSNull class]]) { - XCTAssertNil([config configFlagValue:key]); + XCTAssertNil([config flagValueForFlagKey:key]); continue; } - XCTAssertTrue([[config configFlagValue:key] isEqual:targetValue]); + XCTAssertTrue([[config flagValueForFlagKey:key] isEqual:targetValue]); } - XCTAssertNil([config configFlagValue:@"someMissingKey"]); + XCTAssertNil([config flagValueForFlagKey:@"someMissingKey"]); } --(void)testFlagConfigVersion_withVersions { - LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; - NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withVersions"]; +-(void)testFlagVersionForFlagKey { + LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"]; + NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags"]; for (NSString *key in [flagValues.allKeys copy]) { - NSInteger targetVersion = [(NSNumber*)(flagValues[key])[kLDFlagConfigJsonDictionaryKeyVersion] integerValue]; - XCTAssertTrue([config configFlagVersion:key] == targetVersion); + NSInteger targetVersion = [flagValues[key][kLDFlagConfigValueKeyVersion] integerValue]; + XCTAssertTrue([config flagModelVersionForFlagKey:key] == targetVersion); } - XCTAssertTrue([config configFlagVersion:@"someMissingKey"] == kLDFlagConfigVersionDoesNotExist); + XCTAssertTrue([config flagModelVersionForFlagKey:@"someMissingKey"] == kLDFlagConfigValueItemDoesNotExist); } --(void)testFlagConfigVersion_withoutVersions { - LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withoutVersions"]; - NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withoutVersions"]; +- (void)testDoesFlagConfigValueExistForFlagKey { + LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"]; + NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags"]; for (NSString *key in [flagValues.allKeys copy]) { - XCTAssertTrue([config configFlagVersion:key] == kLDFlagConfigVersionDoesNotExist); + XCTAssertTrue([config doesFlagConfigValueExistForFlagKey:key]); } - XCTAssertTrue([config configFlagVersion:@"someMissingKey"] == kLDFlagConfigVersionDoesNotExist); -} - -- (void)testDoesFlagExist { - LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; - NSDictionary *flagValues = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withVersions"]; - - for (NSString *key in [flagValues.allKeys copy]) { - XCTAssertTrue([config doesConfigFlagExist:key]); - } - - XCTAssertFalse([config doesConfigFlagExist:@"someMissingKey"]); + XCTAssertFalse([config doesFlagConfigValueExistForFlagKey:@"someMissingKey"]); } - (void)testAddOrReplaceFromDictionaryWhenKeyDoesntExist { LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; NSDictionary *patch = [NSJSONSerialization jsonObjectFromFileNamed:@"ldFlagConfigModelPatchNewFlag"]; - NSString *patchedFlagKey = patch[kLDFlagConfigJsonDictionaryKeyKey]; - BOOL patchedFlagValue = [patch boolValueForKey:kLDFlagConfigJsonDictionaryKeyValue]; - NSInteger patchedFlagVersion = [patch integerValueForKey:kLDFlagConfigJsonDictionaryKeyVersion]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"ldFlagConfigModelPatchNewFlag" flagKey:nil eventTrackingContext:nil]; + NSString *patchedFlagKey = patch[kLDFlagConfigModelKeyKey]; [config addOrReplaceFromDictionary:patch]; - XCTAssertTrue([config doesConfigFlagExist:patchedFlagKey]); - XCTAssertEqual([[config configFlagValue:patchedFlagKey] boolValue], patchedFlagValue); - XCTAssertEqual([config configFlagVersion:patchedFlagKey], patchedFlagVersion); + XCTAssertTrue([config doesFlagConfigValueExistForFlagKey:patchedFlagKey]); + XCTAssertEqualObjects([config flagConfigValueForFlagKey:patchedFlagKey], flagConfigValue); } - (void)testAddOrReplaceFromDictionaryWhenKeyExistsWithPreviousVersion { LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; NSDictionary *patch = [NSJSONSerialization jsonObjectFromFileNamed:@"ldFlagConfigModelPatchVersion1Flag"]; - NSString *patchedFlagKey = patch[kLDFlagConfigJsonDictionaryKeyKey]; - BOOL patchedFlagValue = [patch boolValueForKey:kLDFlagConfigJsonDictionaryKeyValue]; - NSInteger patchedFlagVersion = [patch integerValueForKey:kLDFlagConfigJsonDictionaryKeyVersion]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"ldFlagConfigModelPatchVersion1Flag" flagKey:nil eventTrackingContext:nil]; + NSString *patchedFlagKey = patch[kLDFlagConfigModelKeyKey]; [config addOrReplaceFromDictionary:patch]; - XCTAssertTrue([config doesConfigFlagExist:patchedFlagKey]); - XCTAssertEqual([[config configFlagValue:patchedFlagKey] boolValue], patchedFlagValue); - XCTAssertEqual([config configFlagVersion:patchedFlagKey], patchedFlagVersion); + XCTAssertTrue([config doesFlagConfigValueExistForFlagKey:patchedFlagKey]); + XCTAssertEqualObjects([config flagConfigValueForFlagKey:patchedFlagKey], flagConfigValue); } - (void)testAddOrReplaceFromDictionaryWhenPatchValueIsNull { LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; NSDictionary *patch = [NSJSONSerialization jsonObjectFromFileNamed:@"ldFlagConfigModelPatchVersion2FlagWithNull"]; - NSString *patchedFlagKey = patch[kLDFlagConfigJsonDictionaryKeyKey]; - NSInteger patchedFlagVersion = [patch integerValueForKey:kLDFlagConfigJsonDictionaryKeyVersion]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2FlagWithNull" flagKey:nil eventTrackingContext:nil]; + NSString *patchedFlagKey = patch[kLDFlagConfigModelKeyKey]; [config addOrReplaceFromDictionary:patch]; - XCTAssertTrue([config doesConfigFlagExist:patchedFlagKey]); - XCTAssertNil([config configFlagValue:patchedFlagKey]); - XCTAssertEqual([config configFlagVersion:patchedFlagKey], patchedFlagVersion); + XCTAssertTrue([config doesFlagConfigValueExistForFlagKey:patchedFlagKey]); + XCTAssertEqualObjects([config flagConfigValueForFlagKey:patchedFlagKey], flagConfigValue); } - (void)testAddOrReplaceFromDictionaryWhenKeyExistsWithSameVersion { LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" useVersion:2]; - NSString *patchedFlagKey = patch[kLDFlagConfigJsonDictionaryKeyKey]; - NSString *originalFlagValue = (NSString*)[config configFlagValue:patchedFlagKey]; - NSInteger originalFlagVersion = [config configFlagVersion:patchedFlagKey]; + NSString *patchedFlagKey = patch[kLDFlagConfigModelKeyKey]; + LDFlagConfigValue *originalFlagConfigValue = [config flagConfigValueForFlagKey:patchedFlagKey]; [config addOrReplaceFromDictionary:patch]; - XCTAssertTrue([config doesConfigFlagExist:patchedFlagKey]); - XCTAssertEqual([config configFlagValue:patchedFlagKey], originalFlagValue); - XCTAssertEqual([config configFlagVersion:patchedFlagKey], originalFlagVersion); + XCTAssertTrue([config doesFlagConfigValueExistForFlagKey:patchedFlagKey]); + XCTAssertEqualObjects([config flagConfigValueForFlagKey:patchedFlagKey], originalFlagConfigValue); } - (void)testAddOrReplaceFromDictionaryWhenKeyExistsWithLaterVersion { LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" useVersion:1]; - NSString *patchedFlagKey = patch[kLDFlagConfigJsonDictionaryKeyKey]; - NSString *originalFlagValue = (NSString*)[config configFlagValue:patchedFlagKey]; - NSInteger originalFlagVersion = [config configFlagVersion:patchedFlagKey]; + NSString *patchedFlagKey = patch[kLDFlagConfigModelKeyKey]; + LDFlagConfigValue *originalFlagConfigValue = [config flagConfigValueForFlagKey:patchedFlagKey]; [config addOrReplaceFromDictionary:patch]; - XCTAssertTrue([config doesConfigFlagExist:patchedFlagKey]); - XCTAssertEqual([config configFlagValue:patchedFlagKey], originalFlagValue); - XCTAssertEqual([config configFlagVersion:patchedFlagKey], originalFlagVersion); + XCTAssertTrue([config doesFlagConfigValueExistForFlagKey:patchedFlagKey]); + XCTAssertEqualObjects([config flagConfigValueForFlagKey:patchedFlagKey], originalFlagConfigValue); } - (void)testAddOrReplaceFromDictionaryWhenDictionaryIsNil { @@ -245,29 +200,45 @@ - (void)testAddOrReplaceFromDictionaryWhenDictionaryIsEmpty { } - (void)testAddOrReplaceFromDictionaryWhenDictionaryIsMissingValue { - LDFlagConfigModel *targetConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; - NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" omitKey:kLDFlagConfigJsonDictionaryKeyValue]; + NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" omitKey:kLDFlagConfigValueKeyValue]; + NSString *patchFlagKey = patch[kLDFlagConfigModelKeyKey]; [config addOrReplaceFromDictionary:patch]; - XCTAssertTrue([config isEqualToConfig:targetConfig]); + LDFlagConfigValue *flagConfigValue = [config flagConfigValueForFlagKey:patchFlagKey]; + XCTAssertEqualObjects(flagConfigValue.value, [NSNull null]); + XCTAssertEqual(flagConfigValue.variation, [patch[kLDFlagConfigValueKeyVariation] integerValue]); + XCTAssertEqual(flagConfigValue.modelVersion, [patch[kLDFlagConfigValueKeyVersion] integerValue]); } - (void)testAddOrReplaceFromDictionaryWhenDictionaryIsMissingVersion { - LDFlagConfigModel *targetConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; - NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" omitKey:kLDFlagConfigJsonDictionaryKeyVersion]; + NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" omitKey:kLDFlagConfigValueKeyVersion]; + LDFlagConfigValue *patchedFlagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" flagKey:nil eventTrackingContext:nil]; + patchedFlagConfigValue.modelVersion = kLDFlagConfigValueItemDoesNotExist; + NSString *patchedFlagKey = patch[kLDFlagConfigModelKeyKey]; [config addOrReplaceFromDictionary:patch]; - XCTAssertTrue([config isEqualToConfig:targetConfig]); + XCTAssertEqualObjects([config flagConfigValueForFlagKey:patchedFlagKey], patchedFlagConfigValue); +} + +- (void)testAddOrReplaceFromDictionaryWhenFlagConfigModelIsMissingVersion { + LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest" omitKey:kLDFlagConfigValueKeyVersion]; + NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" omitKey:nil]; + LDFlagConfigValue *patchedFlagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" flagKey:nil eventTrackingContext:nil]; + NSString *patchedFlagKey = patch[kLDFlagConfigModelKeyKey]; + + [config addOrReplaceFromDictionary:patch]; + + XCTAssertEqualObjects([config flagConfigValueForFlagKey:patchedFlagKey], patchedFlagConfigValue); } - (void)testAddOrReplaceFromDictionaryWhenDictionaryIsUnexpectedFormat { LDFlagConfigModel *targetConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; - NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" omitKey:kLDFlagConfigJsonDictionaryKeyKey]; + NSDictionary *patch = [LDFlagConfigModel patchFromJsonFileNamed:@"ldFlagConfigModelPatchVersion2Flag" omitKey:kLDFlagConfigModelKeyKey]; [config addOrReplaceFromDictionary:patch]; @@ -277,11 +248,11 @@ - (void)testAddOrReplaceFromDictionaryWhenDictionaryIsUnexpectedFormat { - (void)testDeleteFromDictionaryWhenKeyExistsWithPreviousVersion { LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; NSDictionary *delete = [NSJSONSerialization jsonObjectFromFileNamed:@"ldFlagConfigModelDeleteVersion2Flag"]; - NSString *deletedFlagKey = delete[kLDFlagConfigJsonDictionaryKeyKey]; + NSString *deletedFlagKey = delete[kLDFlagConfigModelKeyKey]; [config deleteFromDictionary:delete]; - XCTAssertFalse([config doesConfigFlagExist:deletedFlagKey]); + XCTAssertFalse([config doesFlagConfigValueExistForFlagKey:deletedFlagKey]); } - (void)testDeleteFromDictionaryWhenKeyDoesntExist { @@ -337,7 +308,7 @@ - (void)testDeleteFromDictionaryWhenDictionaryIsEmpty { - (void)testDeleteFromDictionaryWhenDictionaryIsMissingVersion { LDFlagConfigModel *targetConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; - NSDictionary *delete = [LDFlagConfigModel deleteFromJsonFileNamed:@"ldFlagConfigModelDeleteVersion2Flag" omitKey:kLDFlagConfigJsonDictionaryKeyVersion]; + NSDictionary *delete = [LDFlagConfigModel deleteFromJsonFileNamed:@"ldFlagConfigModelDeleteVersion2Flag" omitKey:kLDFlagConfigValueKeyVersion]; [config deleteFromDictionary:delete]; @@ -347,7 +318,7 @@ - (void)testDeleteFromDictionaryWhenDictionaryIsMissingVersion { - (void)testDeleteFromDictionaryWhenDictionaryIsUnexpectedFormat { LDFlagConfigModel *targetConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; LDFlagConfigModel *config = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"ldFlagConfigModelTest"]; - NSDictionary *delete = [LDFlagConfigModel deleteFromJsonFileNamed:@"ldFlagConfigModelDeleteVersion2Flag" omitKey:kLDFlagConfigJsonDictionaryKeyKey]; + NSDictionary *delete = [LDFlagConfigModel deleteFromJsonFileNamed:@"ldFlagConfigModelDeleteVersion2Flag" omitKey:kLDFlagConfigModelKeyKey]; [config deleteFromDictionary:delete]; @@ -355,8 +326,8 @@ - (void)testDeleteFromDictionaryWhenDictionaryIsUnexpectedFormat { } - (void)testIsEqualToConfigBoolValues { - LDFlagConfigModel *boolConfigIsABool_true = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigModel *boolConfigIsABool_trueCopy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"boolConfigIsABool-true-withVersion"]; + LDFlagConfigModel *boolConfigIsABool_true = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"boolConfigIsABool-true"]; + LDFlagConfigModel *boolConfigIsABool_trueCopy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"boolConfigIsABool-true"]; LDFlagConfigModel *boolConfigIsABool_false = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"boolConfigIsABool-false"]; LDFlagConfigModel *boolConfigIsABool2_true = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"boolConfigIsABool2-true"]; @@ -373,7 +344,7 @@ - (void)testIsEqualToConfigBoolValues { - (void)testIsEqualToConfigNumberValues { LDFlagConfigModel *numberConfigIsANumber_1 = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"numberConfigIsANumber-1"]; LDFlagConfigModel *numberConfigIsANumber_1Copy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"numberConfigIsANumber-1"]; - LDFlagConfigModel *numberConfigIsANumber_2 = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"numberConfigIsANumber-2-withVersion"]; + LDFlagConfigModel *numberConfigIsANumber_2 = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"numberConfigIsANumber-2"]; LDFlagConfigModel *numberConfigIsANumber2_1 = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"numberConfigIsANumber2-1"]; XCTAssertFalse([numberConfigIsANumber_1 isEqualToConfig:nil]); @@ -386,9 +357,9 @@ - (void)testIsEqualToConfigNumberValues { } - (void)testIsEqualToConfigStringValues { - LDFlagConfigModel *stringConfigIsAString_someString = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAString-someString-withVersion"]; - LDFlagConfigModel *stringConfigIsAString_someStringCopy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAString-someString-withVersion"]; - LDFlagConfigModel *stringConfigIsAString_someStringA = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAString-someString-withVersionA"]; + LDFlagConfigModel *stringConfigIsAString_someString = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAString-someString"]; + LDFlagConfigModel *stringConfigIsAString_someStringCopy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAString-someString"]; + LDFlagConfigModel *stringConfigIsAString_someStringA = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAString-someStringA"]; LDFlagConfigModel *stringConfigIsAStringA_someString = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"stringConfigIsAStringA-someString"]; XCTAssertFalse([stringConfigIsAString_someString isEqualToConfig:nil]); @@ -401,8 +372,8 @@ - (void)testIsEqualToConfigStringValues { } - (void)testIsEqualToConfigArrayValues { - LDFlagConfigModel *arrayConfigIsAnArray_123 = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"arrayConfigIsAnArray-123-withVersion"]; - LDFlagConfigModel *arrayConfigIsAnArray_123Copy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"arrayConfigIsAnArray-123-withVersion"]; + LDFlagConfigModel *arrayConfigIsAnArray_123 = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"arrayConfigIsAnArray-123"]; + LDFlagConfigModel *arrayConfigIsAnArray_123Copy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"arrayConfigIsAnArray-123"]; LDFlagConfigModel *arrayConfigIsAnArray_Empty = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"arrayConfigIsAnArray-Empty"]; LDFlagConfigModel *arrayConfigIsAnArray_1 = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"arrayConfigIsAnArray-1"]; LDFlagConfigModel *arrayConfigIsAnArrayA_123 = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"arrayConfigIsAnArrayA-123"]; @@ -419,8 +390,8 @@ - (void)testIsEqualToConfigArrayValues { } - (void)testIsEqualToConfigDictionaryValues { - LDFlagConfigModel *dictionaryConfigIsADictionary_3Key = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key-withVersion"]; - LDFlagConfigModel *dictionaryConfigIsADictionary_3KeyCopy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key-withVersion"]; + LDFlagConfigModel *dictionaryConfigIsADictionary_3Key = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key"]; + LDFlagConfigModel *dictionaryConfigIsADictionary_3KeyCopy = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"dictionaryConfigIsADictionary-3Key"]; LDFlagConfigModel *dictionaryConfigIsADictionary_Empty = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"dictionaryConfigIsADictionary-Empty"]; LDFlagConfigModel *dictionaryConfigIsADictionary_KeyA = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"dictionaryConfigIsADictionary-KeyA"]; LDFlagConfigModel *dictionaryConfigIsADictionary_KeyB = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"dictionaryConfigIsADictionary-KeyB"]; @@ -445,29 +416,27 @@ - (void)testIsEqualToConfigDictionaryValues { XCTAssertFalse([dictionaryConfigIsADictionary_3Key isEqualToConfig:dictionaryConfigIsADictionary_KeyC_keyDValueDiffers]); } -- (void)testHasFeaturesEqualToDictionary_withVersion { - LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withVersions"]; - NSDictionary *sameDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withVersions"]; +- (void)testHasFeaturesEqualToDictionary { + LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"]; + NSDictionary *sameDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags"]; XCTAssertTrue([subject hasFeaturesEqualToDictionary:sameDictionary]); - NSDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-excludeNulls-withVersions"]; + NSDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-excludeNulls"]; XCTAssertFalse([subject hasFeaturesEqualToDictionary:differentDictionary]); } -- (void)testHasFeaturesEqualToDictionary_withoutVersion { - LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags-withoutVersions"]; - NSMutableDictionary *sameDictionary = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withVersions"]]; - for (NSString* key in [sameDictionary.allKeys copy]) { - NSMutableDictionary *flagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:sameDictionary[key]]; - flagConfigValueDictionary[kLDFlagConfigJsonDictionaryKeyVersion] = @(kLDFlagConfigVersionDoesNotExist); - sameDictionary[key] = flagConfigValueDictionary; - } +-(void)testUpdateEventTrackingContextFromConfig { + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext contextWithTrackEvents:NO debugEventsUntilDate:nil]; + LDFlagConfigModel *subject = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags" eventTrackingContext:eventTrackingContext omitKey:nil]; + LDEventTrackingContext *updatedEventTrackingContext = [LDEventTrackingContext contextWithTrackEvents:YES debugEventsUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]]; + LDFlagConfigModel *updatedConfig = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags" eventTrackingContext:updatedEventTrackingContext omitKey:nil]; - XCTAssertTrue([subject hasFeaturesEqualToDictionary:sameDictionary]); + [subject updateEventTrackingContextFromConfig:updatedConfig]; - NSMutableDictionary *differentDictionary = [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-excludeNulls-withoutVersions"]; - XCTAssertFalse([subject hasFeaturesEqualToDictionary:differentDictionary]); + for (NSString *flagKey in subject.featuresJsonDictionary.allKeys) { + LDFlagConfigValue *flagConfigValue = subject.featuresJsonDictionary[flagKey]; + XCTAssertEqualObjects(flagConfigValue.eventTrackingContext, updatedEventTrackingContext); + } } - @end diff --git a/DarklyTests/Models/LDFlagConfig/LDFlagConfigTrackerTest.m b/DarklyTests/Models/LDFlagConfig/LDFlagConfigTrackerTest.m new file mode 100644 index 00000000..fe232595 --- /dev/null +++ b/DarklyTests/Models/LDFlagConfig/LDFlagConfigTrackerTest.m @@ -0,0 +1,110 @@ +// +// LDFlagConfigTrackerTest.m +// DarklyTests +// +// Created by Mark Pokorny on 4/19/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDFlagConfigTracker.h" +#import "NSDate+ReferencedDate.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDFlagCounter.h" +#import "LDFlagCounter+Testable.h" +#import "LDFlagValueCounter.h" + +@interface LDFlagConfigTrackerTest : XCTestCase + +@end + +@implementation LDFlagConfigTrackerTest + +-(void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +-(void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +-(void)testInitAndTrackerConstructors { + LDMillisecond creationMillis = [[NSDate date] millisSince1970]; + LDFlagConfigTracker *tracker = [LDFlagConfigTracker tracker]; + + XCTAssertTrue(labs(tracker.startDateMillis - creationMillis) <= 1); + XCTAssertNotNil(tracker.flagCounters); + XCTAssertEqual(tracker.flagCounters.count, 0); +} + +-(void)testLogRequestForKnownValues { + LDFlagConfigTracker *tracker = [LDFlagConfigTracker tracker]; + for (NSString* flagKey in [LDFlagConfigValue flagKeys]) { + NSArray *flagConfigValues = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey]; + id defaultValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + NSInteger logRequestForUniqueValueCount = 0; + + for (LDFlagConfigValue *flagConfigValue in flagConfigValues) { + logRequestForUniqueValueCount += 1; + [tracker logRequestForFlagKey:flagKey reportedFlagValue:flagConfigValue.value flagConfigValue:flagConfigValue defaultValue:defaultValue]; + + LDFlagCounter *flagCounter = tracker.flagCounters[flagKey]; + XCTAssertNotNil(flagCounter); //Verify the logRequest call added a new LDFlagCounter + if (!flagCounter) { continue; } + + XCTAssertEqual(flagCounter.valueCounters.count, logRequestForUniqueValueCount); //Verify the logRequest call added a new LDFlagValueCounter + LDFlagValueCounter *flagValueCounter = [flagCounter valueCounterForFlagConfigValue:flagConfigValue]; + XCTAssertNotNil(flagValueCounter); + if (!flagValueCounter) { continue; } + + XCTAssertEqualObjects(flagValueCounter.flagConfigValue, flagConfigValue); + XCTAssertEqual(flagValueCounter.known, YES); + XCTAssertEqual(flagValueCounter.count, 1); + + //Make a second call to logRequest with the same flag key & value. Verify no new flagValueCounters, and the existing flagValueCounter was incremented + [tracker logRequestForFlagKey:flagKey reportedFlagValue:flagConfigValue.value flagConfigValue:flagConfigValue defaultValue:defaultValue]; + XCTAssertEqual(flagCounter.valueCounters.count, logRequestForUniqueValueCount); + XCTAssertEqual(flagValueCounter.count, 2); + + //Make a third call to logRequest with the same value and verify no new flagValueCounters, and the count was incremented + [tracker logRequestForFlagKey:flagKey reportedFlagValue:flagConfigValue.value flagConfigValue:flagConfigValue defaultValue:defaultValue]; + XCTAssertEqual(flagCounter.valueCounters.count, logRequestForUniqueValueCount); + XCTAssertEqual(flagValueCounter.count, 3); + } + } +} + +-(void)testLogRequestForUnknownFlagValues { + LDFlagConfigTracker *tracker = [LDFlagConfigTracker tracker]; + for (NSString* flagKey in [LDFlagConfigValue flagKeys]) { + id defaultValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + + [tracker logRequestForFlagKey:flagKey reportedFlagValue:defaultValue flagConfigValue:nil defaultValue:defaultValue]; + + LDFlagCounter *flagCounter = tracker.flagCounters[flagKey]; + XCTAssertNotNil(flagCounter); //Verify the logRequest call added a new LDFlagCounter + if (!flagCounter) { continue; } + + XCTAssertEqual(flagCounter.valueCounters.count, 1); //Verify the logRequest call added a new LDFlagValueCounter + LDFlagValueCounter *flagValueCounter = [flagCounter valueCounterForFlagConfigValue:nil]; + XCTAssertNotNil(flagValueCounter); + if (!flagValueCounter) { continue; } + + XCTAssertNil(flagValueCounter.flagConfigValue); + XCTAssertEqual(flagValueCounter.known, NO); + XCTAssertEqual(flagValueCounter.count, 1); + + //Make a second call to logRequest with an unknown value. Verify no new flagValueCounters, and the existing flagValueCounter was incremented + [tracker logRequestForFlagKey:flagKey reportedFlagValue:defaultValue flagConfigValue:nil defaultValue:defaultValue]; + XCTAssertEqual(flagCounter.valueCounters.count, 1); + XCTAssertEqual(flagValueCounter.count, 2); + + //Make a third call to logRequest with the same value and verify no new flagValueCounters, and the count was incremented + [tracker logRequestForFlagKey:flagKey reportedFlagValue:defaultValue flagConfigValue:nil defaultValue:defaultValue]; + XCTAssertEqual(flagCounter.valueCounters.count, 1); + XCTAssertEqual(flagValueCounter.count, 3); + } +} +@end diff --git a/DarklyTests/Models/LDFlagConfig/LDFlagConfigValueTest.m b/DarklyTests/Models/LDFlagConfig/LDFlagConfigValueTest.m new file mode 100644 index 00000000..80b33b35 --- /dev/null +++ b/DarklyTests/Models/LDFlagConfig/LDFlagConfigValueTest.m @@ -0,0 +1,317 @@ +// +// LDFlagConfigValueTest.m +// DarklyTests +// +// Created by Mark Pokorny on 1/31/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "NSJSONSerialization+Testable.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDEventTrackingContext+Testable.h" +#import "NSDate+ReferencedDate.h" +#import "NSNumber+LaunchDarkly.h" + +@interface LDFlagConfigValueTest : XCTestCase + +@end + +@implementation LDFlagConfigValueTest + +-(void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +-(void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +-(void)testInitAndConstructor { + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext stub]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + for (NSString *fixtureFileName in [LDFlagConfigValue fixtureFileNamesForFlagKey:flagKey]) { + NSDictionary *flagConfigValueDictionary = [LDFlagConfigValue flagConfigJsonObjectFromFileNamed:fixtureFileName + flagKey:flagKey + eventTrackingContext:eventTrackingContext][flagKey]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:flagConfigValueDictionary]; + + //Core items + XCTAssertEqualObjects(flagConfigValue.value, flagConfigValueDictionary[kLDFlagConfigValueKeyValue]); + XCTAssertEqual(flagConfigValue.modelVersion, [flagConfigValueDictionary[kLDFlagConfigValueKeyVersion] integerValue]); + XCTAssertEqual(flagConfigValue.variation, [flagConfigValueDictionary[kLDFlagConfigValueKeyVariation] integerValue]); + + //Optional items + XCTAssertEqualObjects(flagConfigValue.flagVersion, flagConfigValueDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertEqual(flagConfigValue.eventTrackingContext.trackEvents, eventTrackingContext.trackEvents); + XCTAssertTrue([flagConfigValue.eventTrackingContext.debugEventsUntilDate isWithinTimeInterval:1.0 ofDate:eventTrackingContext.debugEventsUntilDate]); + + //omit value + NSMutableDictionary *partialFlagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:flagConfigValueDictionary]; + [partialFlagConfigValueDictionary removeObjectForKey:kLDFlagConfigValueKeyValue]; + + flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:[partialFlagConfigValueDictionary copy]]; + XCTAssertEqualObjects(flagConfigValue.value, [NSNull null]); + XCTAssertEqual(flagConfigValue.modelVersion, [partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVersion] integerValue]); + XCTAssertEqual(flagConfigValue.variation, [partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVariation] integerValue]); + + //omit version + partialFlagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:flagConfigValueDictionary]; + [partialFlagConfigValueDictionary removeObjectForKey:kLDFlagConfigValueKeyVersion]; + + flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:[partialFlagConfigValueDictionary copy]]; + XCTAssertEqualObjects(flagConfigValue.value, partialFlagConfigValueDictionary[kLDFlagConfigValueKeyValue]); + XCTAssertEqual(flagConfigValue.modelVersion, kLDFlagConfigValueItemDoesNotExist); + XCTAssertEqual(flagConfigValue.variation, [partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVariation] integerValue]); + + //null version + partialFlagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:flagConfigValueDictionary]; + partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVersion] = [NSNull null]; + + flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:[partialFlagConfigValueDictionary copy]]; + XCTAssertEqualObjects(flagConfigValue.value, partialFlagConfigValueDictionary[kLDFlagConfigValueKeyValue]); + XCTAssertEqual(flagConfigValue.modelVersion, kLDFlagConfigValueItemDoesNotExist); + XCTAssertEqual(flagConfigValue.variation, [partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVariation] integerValue]); + + //omit variation + partialFlagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:flagConfigValueDictionary]; + [partialFlagConfigValueDictionary removeObjectForKey:kLDFlagConfigValueKeyVariation]; + + flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:[partialFlagConfigValueDictionary copy]]; + XCTAssertEqualObjects(flagConfigValue.value, partialFlagConfigValueDictionary[kLDFlagConfigValueKeyValue]); + XCTAssertEqual(flagConfigValue.modelVersion, [partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVersion] integerValue]); + XCTAssertEqual(flagConfigValue.variation, kLDFlagConfigValueItemDoesNotExist); + + //null variation + partialFlagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:flagConfigValueDictionary]; + partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVariation] = [NSNull null]; + + flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:[partialFlagConfigValueDictionary copy]]; + XCTAssertEqualObjects(flagConfigValue.value, partialFlagConfigValueDictionary[kLDFlagConfigValueKeyValue]); + XCTAssertEqual(flagConfigValue.modelVersion, [partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVersion] integerValue]); + XCTAssertEqual(flagConfigValue.variation, kLDFlagConfigValueItemDoesNotExist); + + //omit optional items + partialFlagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:flagConfigValueDictionary]; + [partialFlagConfigValueDictionary removeObjectForKey:kLDFlagConfigValueKeyFlagVersion]; + [partialFlagConfigValueDictionary removeObjectForKey:kLDEventTrackingContextKeyTrackEvents]; + [partialFlagConfigValueDictionary removeObjectForKey:kLDEventTrackingContextKeyDebugEventsUntilDate]; + + flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:[partialFlagConfigValueDictionary copy]]; + XCTAssertNil(flagConfigValue.eventTrackingContext); + XCTAssertNil(flagConfigValue.flagVersion); + + //null flag version + partialFlagConfigValueDictionary = [NSMutableDictionary dictionaryWithDictionary:flagConfigValueDictionary]; + partialFlagConfigValueDictionary[kLDFlagConfigValueKeyFlagVersion] = [NSNull null]; + + flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:[partialFlagConfigValueDictionary copy]]; + XCTAssertEqualObjects(flagConfigValue.value, partialFlagConfigValueDictionary[kLDFlagConfigValueKeyValue]); + XCTAssertEqual(flagConfigValue.modelVersion, [partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVersion] integerValue]); + XCTAssertEqual(flagConfigValue.variation, [partialFlagConfigValueDictionary[kLDFlagConfigValueKeyVariation] integerValue]); + XCTAssertNil(flagConfigValue.flagVersion); + } + } +} + +-(void)testInitializer_ObjectIsNil { + LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:nil]; + + XCTAssertNil(subject); +} + +-(void)testEncodeAndDecode { + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext stub]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"boolConfigIsABool-true" flagKey:@"isABool" eventTrackingContext:eventTrackingContext]; + + NSData *archive = [NSKeyedArchiver archivedDataWithRootObject:flagConfigValue]; + LDFlagConfigValue *restoredFlagConfigValue = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; + + XCTAssertEqualObjects(flagConfigValue, restoredFlagConfigValue); + //Optional items + XCTAssertEqualObjects(flagConfigValue.flagVersion, restoredFlagConfigValue.flagVersion); + XCTAssertEqual(restoredFlagConfigValue.eventTrackingContext.trackEvents, eventTrackingContext.trackEvents); + XCTAssertTrue([restoredFlagConfigValue.eventTrackingContext.debugEventsUntilDate isWithinTimeInterval:1.0 ofDate:eventTrackingContext.debugEventsUntilDate]); + + flagConfigValue.value = [NSNull null]; + flagConfigValue.modelVersion = kLDFlagConfigValueItemDoesNotExist; + flagConfigValue.variation = kLDFlagConfigValueItemDoesNotExist; + flagConfigValue.flagVersion = nil; + flagConfigValue.eventTrackingContext = nil; + + archive = [NSKeyedArchiver archivedDataWithRootObject:flagConfigValue]; + restoredFlagConfigValue = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; + + XCTAssertTrue([flagConfigValue isEqual:restoredFlagConfigValue]); + XCTAssertNil(flagConfigValue.eventTrackingContext); + XCTAssertNil(flagConfigValue.flagVersion); +} + +-(void)testDictionaryValue { + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext stub]; + LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"boolConfigIsABool-true" + flagKey:@"isABool" + eventTrackingContext:eventTrackingContext]; + + NSDictionary *flagDictionary = [flagConfigValue dictionaryValueUseFlagVersionForVersion:NO includeEventTrackingContext:YES]; + + //Core items + XCTAssertEqualObjects(flagDictionary[kLDFlagConfigValueKeyValue], flagConfigValue.value); + XCTAssertEqual([flagDictionary[kLDFlagConfigValueKeyVersion] integerValue], flagConfigValue.modelVersion); + XCTAssertEqual([flagDictionary[kLDFlagConfigValueKeyVariation] integerValue], flagConfigValue.variation); + //Optional items + XCTAssertEqualObjects(flagDictionary[kLDFlagConfigValueKeyFlagVersion], flagConfigValue.flagVersion); + XCTAssertEqual([flagDictionary[kLDEventTrackingContextKeyTrackEvents] boolValue], eventTrackingContext.trackEvents); + XCTAssertTrue([[NSDate dateFromMillisSince1970:[flagDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate] ldMillisecondValue]] + isWithinTimeInterval:1.0 ofDate:eventTrackingContext.debugEventsUntilDate]); + + flagDictionary = [flagConfigValue dictionaryValueUseFlagVersionForVersion:YES includeEventTrackingContext:NO]; + + //Core items + XCTAssertEqualObjects(flagDictionary[kLDFlagConfigValueKeyValue], flagConfigValue.value); + XCTAssertEqualObjects(flagDictionary[kLDFlagConfigValueKeyVersion], flagConfigValue.flagVersion); + XCTAssertEqual([flagDictionary[kLDFlagConfigValueKeyVariation] integerValue], flagConfigValue.variation); + //Optional items + XCTAssertNil(flagDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(flagDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(flagDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + + flagConfigValue.value = [NSNull null]; + flagConfigValue.variation = kLDFlagConfigValueItemDoesNotExist; + flagConfigValue.modelVersion = kLDFlagConfigValueItemDoesNotExist; + flagConfigValue.flagVersion = nil; + flagConfigValue.eventTrackingContext = nil; + + flagDictionary = [flagConfigValue dictionaryValue]; + + //Core items + XCTAssertEqualObjects(flagConfigValue.value, [NSNull null]); + XCTAssertNil(flagDictionary[kLDFlagConfigValueKeyVariation]); + XCTAssertNil(flagDictionary[kLDFlagConfigValueKeyVersion]); + //Optional items + XCTAssertNil(flagDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(flagDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(flagDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); +} + +-(void)testIsEqual_valuesAreTheSame { + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext stub]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + for (NSString *fixtureFileName in [LDFlagConfigValue fixtureFileNamesForFlagKey:flagKey]) { + NSDictionary *flagConfigValueDictionary = [NSJSONSerialization jsonObjectFromFileNamed:fixtureFileName]; + LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigValueDictionary[flagKey]]; + //Include optional items that differ in other to ensure equality only considers core items + LDFlagConfigValue *other = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:fixtureFileName flagKey:flagKey eventTrackingContext:eventTrackingContext]; + other.flagVersion = @([other.flagVersion integerValue] + 1); + + XCTAssertTrue([subject isEqual:other]); + } + } +} + +-(void)testIsEqual_valuesDiffer_differentVariations { + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext stub]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + for (NSString *fixtureFileName in [LDFlagConfigValue fixtureFileNamesForFlagKey:flagKey]) { + //Include optional items that are the same in other to ensure equality only considers core items + LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:fixtureFileName flagKey:flagKey eventTrackingContext:eventTrackingContext]; + LDFlagConfigValue *other = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:fixtureFileName flagKey:flagKey eventTrackingContext:eventTrackingContext]; + other.variation += 1; + + XCTAssertFalse([subject isEqual:other]); + + other.variation = kLDFlagConfigValueItemDoesNotExist; + + XCTAssertFalse([subject isEqual:other]); + + other.variation = subject.variation; + subject.variation = kLDFlagConfigValueItemDoesNotExist; + + XCTAssertFalse([subject isEqual:other]); + } + } +} + +-(void)testIsEqual_valuesDiffer_differentVersions { + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext stub]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + for (NSString *fixtureFileName in [LDFlagConfigValue fixtureFileNamesForFlagKey:flagKey]) { + //Include optional items that are the same in other to ensure equality only considers core items + LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:fixtureFileName flagKey:flagKey eventTrackingContext:eventTrackingContext]; + LDFlagConfigValue *other = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:fixtureFileName flagKey:flagKey eventTrackingContext:eventTrackingContext]; + other.modelVersion += 1; + + XCTAssertFalse([subject isEqual:other]); + + other.modelVersion = kLDFlagConfigValueItemDoesNotExist; + + XCTAssertFalse([subject isEqual:other]); + + other.modelVersion = subject.modelVersion; + subject.modelVersion = kLDFlagConfigValueItemDoesNotExist; + + XCTAssertFalse([subject isEqual:other]); + } + } +} + +-(void)testIsEqual_valuesDiffer_differentObjects { + LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"boolConfigIsABool-true" flagKey:@"isABool" eventTrackingContext:nil]; + + XCTAssertFalse([subject isEqual:@"someString"]); +} + +-(void)testIsEqual_valuesDiffer_otherIsNil { + LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueFromJsonFileNamed:@"boolConfigIsABool-true" flagKey:@"isABool" eventTrackingContext:nil]; + + XCTAssertFalse([subject isEqual:nil]); +} + +- (void)testHasPropertiesMatchingDictionary { + LDEventTrackingContext *eventTrackingContext = [LDEventTrackingContext stub]; + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + id defaultFlagValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + + NSArray *flagConfigValues = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey eventTrackingContext:eventTrackingContext]; + for (LDFlagConfigValue *flagConfigValue in flagConfigValues) { + //matching dictionary + NSDictionary *flagConfigValueDictionary = [flagConfigValue dictionaryValue]; + + XCTAssertTrue([flagConfigValue hasPropertiesMatchingDictionary:flagConfigValueDictionary]); + XCTAssertNotNil(flagConfigValueDictionary[kLDFlagConfigValueKeyVersion]); + XCTAssertNotNil(flagConfigValueDictionary[kLDFlagConfigValueKeyVariation]); + XCTAssertTrue([flagConfigValue.eventTrackingContext hasPropertiesMatchingDictionary:flagConfigValueDictionary]); + + //mismatched dictionary + LDFlagConfigValue *differingFlagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:flagConfigValueDictionary]; + if (![flagKey isEqualToString:kLDFlagKeyIsANull]) { //There's no alternate value to supply for null values + differingFlagConfigValue.value = defaultFlagValue; + if ([flagKey isEqualToString:kLDFlagKeyIsABool]) { //Since there are only YES/NO, make it different by taking the complement + differingFlagConfigValue.value = @(![flagConfigValue.value boolValue]); + } + XCTAssertFalse([differingFlagConfigValue hasPropertiesMatchingDictionary:flagConfigValueDictionary]); + } + + differingFlagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:flagConfigValueDictionary]; + differingFlagConfigValue.variation = flagConfigValue.variation += 1; + XCTAssertFalse([differingFlagConfigValue hasPropertiesMatchingDictionary:flagConfigValueDictionary]); + + NSMutableDictionary *differingFlagConfigDictionary = [NSMutableDictionary dictionaryWithDictionary:[flagConfigValue dictionaryValue]]; + [differingFlagConfigDictionary removeObjectForKey:kLDFlagConfigValueKeyVariation]; + XCTAssertFalse([flagConfigValue hasPropertiesMatchingDictionary:differingFlagConfigDictionary]); + + differingFlagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:flagConfigValueDictionary]; + differingFlagConfigValue.modelVersion = flagConfigValue.modelVersion += 1; + XCTAssertFalse([differingFlagConfigValue hasPropertiesMatchingDictionary:flagConfigValueDictionary]); + + differingFlagConfigDictionary = [NSMutableDictionary dictionaryWithDictionary:[flagConfigValue dictionaryValue]]; + [differingFlagConfigDictionary removeObjectForKey:kLDFlagConfigValueKeyVersion]; + XCTAssertFalse([flagConfigValue hasPropertiesMatchingDictionary:differingFlagConfigDictionary]); + } + } +} + +@end diff --git a/DarklyTests/Models/LDFlagConfig/LDFlagCounterTest.m b/DarklyTests/Models/LDFlagConfig/LDFlagCounterTest.m new file mode 100644 index 00000000..e6cec6c1 --- /dev/null +++ b/DarklyTests/Models/LDFlagConfig/LDFlagCounterTest.m @@ -0,0 +1,174 @@ +// +// LDFlagCounterTest.m +// DarklyTests +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDFlagCounter.h" +#import "LDFlagCounter+Testable.h" +#import "LDFlagValueCounter.h" +#import "LDFlagValueCounter+Testable.h" +#import "LDFlagConfigModel.h" +#import "LDFlagConfigModel+Testable.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDEventTrackingContext+Testable.h" +#import "NSArray+Testable.h" + +extern NSString * const kLDFlagKeyIsABool; +extern NSString * const kLDFlagKeyIsANumber; +extern NSString * const kLDFlagKeyIsADouble; +extern NSString * const kLDFlagKeyIsAString; +extern NSString * const kLDFlagKeyIsAnArray; +extern NSString * const kLDFlagKeyIsADictionary; +extern NSString * const kLDFlagKeyIsANull; + +@interface LDFlagCounterTest : XCTestCase +@end + +@implementation LDFlagCounterTest + +-(void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +-(void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +-(void)testInitAndCounterWithFlagKey { + NSDictionary *flagConfigDictionary = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags"].featuresJsonDictionary; + for (NSString *flagKey in flagConfigDictionary.allKeys) { + LDFlagConfigValue *flagConfigValue = flagConfigDictionary[flagKey]; + + LDFlagCounter *flagCounter = [LDFlagCounter counterWithFlagKey:flagKey defaultValue:flagConfigValue.value]; + + XCTAssertEqualObjects(flagCounter.flagKey, flagKey); + XCTAssertEqualObjects(flagCounter.defaultValue, flagConfigValue.value); + XCTAssertEqual(flagCounter.valueCounters.count, 0); + } +} + +/* Tests logRequest by logging a simulated known flagConfigValue for each flag value type 3 times. The first should add a flagValueCounter that contains the value, version, & variation. Subsequent logRequests that have the same variation should increment the counter on the existing flagValueCounter. The test also verifies that the logRequest call doesn't create a new flagValueCounter. + The default value can change with every call to the LDClient's variation method. The defaultValue passed into logRequest updates the defaultValue recorded. Since LaunchDarkly doesn't care if the version changes, there is no test to verify the default value is updated by a logRequest call. + NOTE: The variation comes from LD to indicate a unique flag value. + */ +-(void)testLogRequestForKnownFlagValues_usingFlagConfigValue { + for (NSString* flagKey in [LDFlagConfigValue flagKeys]) { + NSArray *flagConfigValues = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey]; + id defaultValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagCounter *flagCounter = [LDFlagCounter counterWithFlagKey:flagKey defaultValue:defaultValue]; + NSInteger logRequestForUniqueValueCount = 0; + + for (LDFlagConfigValue *flagConfigValue in flagConfigValues) { + logRequestForUniqueValueCount += 1; + [flagCounter logRequestWithFlagConfigValue:flagConfigValue reportedFlagValue:flagConfigValue.value defaultValue:defaultValue]; + + XCTAssertEqual(flagCounter.valueCounters.count, logRequestForUniqueValueCount); //Verify the logRequest call added a new LDFlagValueCounter + LDFlagValueCounter *flagValueCounter = [flagCounter valueCounterForFlagConfigValue:flagConfigValue]; + XCTAssertNotNil(flagValueCounter); + if (!flagValueCounter) { continue; } + + XCTAssertEqualObjects(flagValueCounter.flagConfigValue, flagConfigValue); + XCTAssertEqualObjects(flagValueCounter.reportedFlagValue, flagConfigValue.value); + XCTAssertEqual(flagValueCounter.known, YES); + XCTAssertEqual(flagValueCounter.count, 1); + + //Make a second call to logRequest with the same value. Verify no new flagValueCounters, and the existing flagValueCounter was incremented + [flagCounter logRequestWithFlagConfigValue:flagConfigValue reportedFlagValue:flagConfigValue.value defaultValue:defaultValue]; + XCTAssertEqual(flagCounter.valueCounters.count, logRequestForUniqueValueCount); + XCTAssertEqual(flagValueCounter.count, 2); + + //Make a third call to logRequest with the same value and verify no new flagValueCounters, and the count was incremented + [flagCounter logRequestWithFlagConfigValue:flagConfigValue reportedFlagValue:flagConfigValue.value defaultValue:defaultValue]; + XCTAssertEqual(flagCounter.valueCounters.count, logRequestForUniqueValueCount); + XCTAssertEqual(flagValueCounter.count, 3); + } + } +} + +-(void)testLogRequestForUnknownFlagValues_usingFlagConfigValue { + for (NSString* flagKey in [LDFlagConfigValue flagKeys]) { + id defaultValue = [LDFlagConfigValue defaultValueForFlagKey:flagKey]; + LDFlagCounter *flagCounter = [LDFlagCounter counterWithFlagKey:flagKey defaultValue:defaultValue]; + + [flagCounter logRequestWithFlagConfigValue:nil reportedFlagValue:defaultValue defaultValue:defaultValue]; + + XCTAssertEqual(flagCounter.valueCounters.count, 1); //Verify the logRequest call added a new LDFlagValueCounter + LDFlagValueCounter *flagValueCounter = [flagCounter valueCounterForFlagConfigValue:nil]; + XCTAssertNotNil(flagValueCounter); + if (!flagValueCounter) { continue; } + + XCTAssertNil(flagValueCounter.flagConfigValue); + XCTAssertEqualObjects(flagValueCounter.reportedFlagValue, defaultValue); + XCTAssertEqual(flagValueCounter.known, NO); + XCTAssertEqual(flagValueCounter.count, 1); + + //Make a second call to logRequest with an unknown value. Verify no new flagValueCounters, and the existing flagValueCounter was incremented + [flagCounter logRequestWithFlagConfigValue:nil reportedFlagValue:defaultValue defaultValue:defaultValue]; + XCTAssertEqual(flagCounter.valueCounters.count, 1); + XCTAssertEqual(flagValueCounter.count, 2); + + //Make a third call to logRequest with the same value and verify no new flagValueCounters, and the count was incremented + [flagCounter logRequestWithFlagConfigValue:nil reportedFlagValue:defaultValue defaultValue:defaultValue]; + XCTAssertEqual(flagCounter.valueCounters.count, 1); + XCTAssertEqual(flagValueCounter.count, 3); + } +} + +-(void)testDictionaryValueForKnownFlagValues { + for (NSString* flagKey in [LDFlagConfigValue flagKeys]) { + LDFlagCounter *flagCounter = [LDFlagCounter stubForFlagKey:flagKey]; + + NSDictionary *flagCounterDictionary = [flagCounter dictionaryValue]; + + XCTAssertEqualObjects(flagCounterDictionary[kLDFlagCounterKeyDefaultValue], flagCounter.defaultValue); + NSArray *counterDictionaries = flagCounterDictionary[kLDFlagCounterKeyCounters]; + XCTAssertEqual(counterDictionaries.count, flagCounter.flagValueCounters.count); + if (counterDictionaries.count != flagCounter.flagValueCounters.count) { continue; } + for (LDFlagValueCounter *flagValueCounter in flagCounter.flagValueCounters) { + NSDictionary *selectedCounterDictionary = [counterDictionaries dictionaryForFlagValueCounter:flagValueCounter]; + XCTAssertNotNil(selectedCounterDictionary, @"counter dictionary not found for flagValueCounter: %@", [flagValueCounter description]); + if (!selectedCounterDictionary) { continue; } + + XCTAssertEqualObjects(selectedCounterDictionary[kLDFlagConfigValueKeyValue], flagValueCounter.flagConfigValue.value); + XCTAssertEqual([selectedCounterDictionary[kLDFlagConfigValueKeyVariation] integerValue], flagValueCounter.flagConfigValue.variation); + XCTAssertEqualObjects(selectedCounterDictionary[kLDFlagConfigValueKeyVersion], flagValueCounter.flagConfigValue.flagVersion); + XCTAssertEqual([selectedCounterDictionary[kLDFlagValueCounterKeyCount] integerValue], flagValueCounter.count); + XCTAssertNil(selectedCounterDictionary[kLDFlagValueCounterKeyUnknown]); + XCTAssertNil(selectedCounterDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(selectedCounterDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(selectedCounterDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + } + } +} + +-(void)testDictionaryValueForUnknownFlagValues { + for (NSString* flagKey in [LDFlagConfigValue flagKeys]) { + LDFlagCounter *flagCounter = [LDFlagCounter stubForFlagKey:flagKey useKnownValues:NO]; + LDFlagValueCounter *flagValueCounter = [flagCounter.flagValueCounters firstObject]; + + NSDictionary *flagCounterDictionary = [flagCounter dictionaryValue]; + + XCTAssertEqualObjects(flagCounterDictionary[kLDFlagCounterKeyDefaultValue], flagCounter.defaultValue); + NSArray *counterDictionaries = flagCounterDictionary[kLDFlagCounterKeyCounters]; + XCTAssertEqual(counterDictionaries.count, 1); + if (counterDictionaries.count != 1) { continue; } + NSDictionary *selectedCounterDictionary = [counterDictionaries firstObject]; + XCTAssertEqual([selectedCounterDictionary[kLDFlagValueCounterKeyUnknown] boolValue], YES); + XCTAssertEqual([selectedCounterDictionary[kLDFlagValueCounterKeyCount] integerValue], flagValueCounter.count); + XCTAssertEqualObjects(selectedCounterDictionary[kLDFlagConfigValueKeyValue], [LDFlagConfigValue defaultValueForFlagKey:flagKey]); + XCTAssertNil(selectedCounterDictionary[kLDFlagConfigValueKeyVariation]); + XCTAssertNil(selectedCounterDictionary[kLDFlagConfigValueKeyVersion]); + XCTAssertNil(selectedCounterDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(selectedCounterDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(selectedCounterDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + } +} + +@end diff --git a/DarklyTests/Models/LDFlagConfig/LDFlagValueCounterTest.m b/DarklyTests/Models/LDFlagConfig/LDFlagValueCounterTest.m new file mode 100644 index 00000000..f629be50 --- /dev/null +++ b/DarklyTests/Models/LDFlagConfig/LDFlagValueCounterTest.m @@ -0,0 +1,104 @@ +// +// LDFlagValueCounterTest.m +// DarklyTests +// +// Created by Mark Pokorny on 4/18/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "LDFlagValueCounter.h" +#import "LDFlagValueCounter+Testable.h" +#import "LDFlagConfigModel.h" +#import "LDFlagConfigModel+Testable.h" +#import "LDFlagConfigValue.h" +#import "LDFlagConfigValue+Testable.h" +#import "LDEventTrackingContext+Testable.h" + +extern const NSInteger kLDFlagConfigValueItemDoesNotExist; +extern const NSInteger kLDFlagConfigValueItemDoesNotExist; + +@interface LDFlagValueCounterTest : XCTestCase +@property (nonatomic, strong) NSDictionary *flagConfigDictionary; +@end + +@implementation LDFlagValueCounterTest + +- (void)setUp { + [super setUp]; + self.flagConfigDictionary = [LDFlagConfigModel flagConfigFromJsonFileNamed:@"featureFlags" eventTrackingContext:[LDEventTrackingContext stub]].featuresJsonDictionary; +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +-(void)testInitAndCounterWithFlagConfigValueConstructors { + for (NSString *flagKey in [LDFlagConfigValue flagKeys]) { + NSArray *flagConfigValues = [LDFlagConfigValue stubFlagConfigValuesForFlagKey:flagKey]; + for (LDFlagConfigValue *flagConfigValue in flagConfigValues) { + LDFlagValueCounter *flagValueCounter = [LDFlagValueCounter counterWithFlagConfigValue:flagConfigValue reportedFlagValue:flagConfigValue.value]; + + XCTAssertEqualObjects(flagValueCounter.flagConfigValue, flagConfigValue); + XCTAssertEqualObjects(flagValueCounter.reportedFlagValue, flagConfigValue.value); + XCTAssertEqual(flagValueCounter.count, 1); + XCTAssertEqual(flagValueCounter.known, YES); + } + } + + id defaultValue = [LDFlagConfigValue defaultValueForFlagKey:kLDFlagKeyIsABool]; + LDFlagValueCounter *flagValueCounter = [LDFlagValueCounter counterWithFlagConfigValue:nil reportedFlagValue:defaultValue]; + XCTAssertNil(flagValueCounter.flagConfigValue); + XCTAssertEqualObjects(flagValueCounter.reportedFlagValue, defaultValue); + XCTAssertEqual(flagValueCounter.count, 1); + XCTAssertEqual(flagValueCounter.known, NO); +} + +-(void)testDictionaryValue { + for (NSString *flagKey in self.flagConfigDictionary.allKeys) { + LDFlagConfigValue *flagConfigValue = self.flagConfigDictionary[flagKey]; + LDFlagValueCounter *flagValueCounter = [LDFlagValueCounter counterWithFlagConfigValue:flagConfigValue reportedFlagValue:flagConfigValue.value]; + + NSDictionary *flagValueCounterDictionary = [flagValueCounter dictionaryValue]; + + XCTAssertEqualObjects(flagValueCounterDictionary[kLDFlagConfigValueKeyValue], flagConfigValue.value); + XCTAssertEqual([flagValueCounterDictionary[kLDFlagConfigValueKeyVariation] integerValue], flagConfigValue.variation); + XCTAssertEqualObjects(flagValueCounterDictionary[kLDFlagConfigValueKeyVersion], flagConfigValue.flagVersion); + XCTAssertNil(flagValueCounterDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(flagValueCounterDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(flagValueCounterDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + + //test version & variation do not exist + flagConfigValue.modelVersion = kLDFlagConfigValueItemDoesNotExist; + flagConfigValue.variation = kLDFlagConfigValueItemDoesNotExist; + flagConfigValue.flagVersion = nil; + flagValueCounter = [LDFlagValueCounter counterWithFlagConfigValue:flagConfigValue reportedFlagValue:flagConfigValue.value]; + + flagValueCounterDictionary = [flagValueCounter dictionaryValue]; + + XCTAssertEqualObjects(flagValueCounterDictionary[kLDFlagConfigValueKeyValue], flagConfigValue.value); + XCTAssertNil(flagValueCounterDictionary[kLDFlagConfigValueKeyVariation]); + XCTAssertNil(flagValueCounterDictionary[kLDFlagConfigValueKeyVersion]); + XCTAssertNil(flagValueCounterDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(flagValueCounterDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(flagValueCounterDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); + } + + //flagConfigValue nil + id defaultValue = @(YES); + LDFlagValueCounter *flagValueCounter = [LDFlagValueCounter counterWithFlagConfigValue:nil reportedFlagValue:defaultValue]; + + NSDictionary *flagValueCounterDictionary = [flagValueCounter dictionaryValue]; + + XCTAssertEqualObjects(flagValueCounterDictionary[kLDFlagConfigValueKeyValue], defaultValue); + XCTAssertEqualObjects(flagValueCounterDictionary[kLDFlagValueCounterKeyUnknown], @(YES)); + XCTAssertEqualObjects(flagValueCounterDictionary[kLDFlagValueCounterKeyCount], @(1)); + XCTAssertNil(flagValueCounterDictionary[kLDFlagConfigValueKeyVariation]); + XCTAssertNil(flagValueCounterDictionary[kLDFlagConfigValueKeyVersion]); + XCTAssertNil(flagValueCounterDictionary[kLDFlagConfigValueKeyFlagVersion]); + XCTAssertNil(flagValueCounterDictionary[kLDEventTrackingContextKeyTrackEvents]); + XCTAssertNil(flagValueCounterDictionary[kLDEventTrackingContextKeyDebugEventsUntilDate]); +} + +@end diff --git a/DarklyTests/Models/LDFlagConfigValueTest.m b/DarklyTests/Models/LDFlagConfigValueTest.m deleted file mode 100644 index 5c6c1694..00000000 --- a/DarklyTests/Models/LDFlagConfigValueTest.m +++ /dev/null @@ -1,257 +0,0 @@ -// -// LDFlagConfigValueTest.m -// DarklyTests -// -// Created by Mark Pokorny on 1/31/18. +JMJ -// Copyright © 2018 LaunchDarkly. All rights reserved. -// - -#import -#import "NSJSONSerialization+Testable.h" -#import "LDFlagConfigValue.h" - -extern NSString * const kLDFlagConfigJsonDictionaryKeyValue; -extern NSString * const kLDFlagConfigJsonDictionaryKeyVersion; - -@interface LDFlagConfigValueTest : XCTestCase - -@end - -@implementation LDFlagConfigValueTest - --(void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. -} - --(void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - --(void)testInitializer_boolValue_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - - XCTAssertTrue([(NSNumber*)subject.value boolValue]); - XCTAssertEqual(subject.version, 4); -} - --(void)testInitializer_boolValue_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - - XCTAssertTrue([(NSNumber*)subject.value boolValue]); - XCTAssertEqual(subject.version, kLDFlagConfigVersionDoesNotExist); -} - --(void)testInitializer_numberValue_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"numberConfigIsANumber-2-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isANumber"]]; - - XCTAssertEqual([(NSNumber*)subject.value integerValue], 2); - XCTAssertEqual(subject.version, 4); -} - --(void)testInitializer_numberValue_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"numberConfigIsANumber-2-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isANumber"]]; - - XCTAssertEqual([(NSNumber*)subject.value integerValue], 2); - XCTAssertEqual(subject.version, kLDFlagConfigVersionDoesNotExist); -} - --(void)testInitializer_doubleValue_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"doubleConfigIsADouble-Pi-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isADouble"]]; - - XCTAssertEqual([(NSNumber*)subject.value doubleValue], M_PI); - XCTAssertEqual(subject.version, 3); -} - --(void)testInitializer_doubleValue_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"doubleConfigIsADouble-Pi-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isADouble"]]; - - XCTAssertEqual([(NSNumber*)subject.value doubleValue], M_PI); - XCTAssertEqual(subject.version, kLDFlagConfigVersionDoesNotExist); -} - --(void)testInitializer_stringValue_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"stringConfigIsAString-someString-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isAString"]]; - - XCTAssertTrue([subject.value isEqualToString:@"someString"]); - XCTAssertEqual(subject.version, 3); -} - --(void)testInitializer_stringValue_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"stringConfigIsAString-someString-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isAString"]]; - - XCTAssertTrue([subject.value isEqualToString:@"someString"]); - XCTAssertEqual(subject.version, kLDFlagConfigVersionDoesNotExist); -} - --(void)testInitializer_arrayValue_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"arrayConfigIsAnArray-123-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isAnArray"]]; - - NSArray *targetArray = @[@(1), @(2), @(3)]; - XCTAssertTrue([subject.value isEqualToArray:targetArray]); - XCTAssertEqual(subject.version, 5); -} - --(void)testInitializer_arrayValue_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"arrayConfigIsAnArray-123-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isAnArray"]]; - - NSArray *targetArray = @[@(1), @(2), @(3)]; - XCTAssertTrue([subject.value isEqualToArray:targetArray]); - XCTAssertEqual(subject.version, kLDFlagConfigVersionDoesNotExist); -} - --(void)testInitializer_dictionaryValue_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"dictionaryConfigIsADictionary-3Key-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isADictionary"]]; - - NSDictionary *targetDictionary = @{@"keyA": @(true), @"keyB": @[@(1), @(2), @(3)], @"keyC": @{@"keyD": @"someStringValue"}}; - XCTAssertTrue([subject.value isEqualToDictionary:targetDictionary]); - XCTAssertEqual(subject.version, 4); -} - --(void)testInitializer_dictionaryValue_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"dictionaryConfigIsADictionary-3Key-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isADictionary"]]; - - NSDictionary *targetDictionary = @{@"keyA": @(true), @"keyB": @[@(1), @(2), @(3)], @"keyC": @{@"keyD": @"someStringValue"}}; - XCTAssertTrue([subject.value isEqualToDictionary:targetDictionary]); - XCTAssertEqual(subject.version, kLDFlagConfigVersionDoesNotExist); -} - --(void)testInitializer_ObjectIsNil { - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:nil]; - - XCTAssertNil(subject); -} - --(void)testInitializer_nullValue_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"nullConfigIsANull-null-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isANull"]]; - - XCTAssertTrue([subject.value isEqual:[NSNull null]]); - XCTAssertEqual(subject.version, 2); -} - --(void)testInitializer_nullValue_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"nullConfigIsANull-null-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isANull"]]; - - XCTAssertTrue([subject.value isEqual:[NSNull null]]); - XCTAssertEqual(subject.version, kLDFlagConfigVersionDoesNotExist); -} - --(void)testEncodeAndDecode_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - - NSData *archive = [NSKeyedArchiver archivedDataWithRootObject:subject]; - LDFlagConfigValue *restored = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; - - XCTAssertTrue([subject isEqual:restored]); -} - --(void)testEncodeAndDecode_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - - NSData *archive = [NSKeyedArchiver archivedDataWithRootObject:subject]; - LDFlagConfigValue *restored = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; - - XCTAssertTrue([subject isEqual:restored]); -} - --(void)testDictionaryValue_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - - NSDictionary *flagDictionary = [flagConfigValue dictionaryValue]; - - XCTAssertEqual(flagConfigValue.value, flagDictionary[kLDFlagConfigJsonDictionaryKeyValue]); - XCTAssertTrue([flagDictionary[kLDFlagConfigJsonDictionaryKeyVersion] isKindOfClass:[NSNumber class]]); - XCTAssertTrue(flagConfigValue.version == [(NSNumber*)flagDictionary[kLDFlagConfigJsonDictionaryKeyVersion] integerValue]); -} - --(void)testDictionaryValue_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withoutVersion"]; - LDFlagConfigValue *flagConfigValue = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - - NSDictionary *flagDictionary = [flagConfigValue dictionaryValue]; - - XCTAssertEqual(flagConfigValue.value, flagDictionary[kLDFlagConfigJsonDictionaryKeyValue]); - XCTAssertTrue([flagDictionary[kLDFlagConfigJsonDictionaryKeyVersion] isKindOfClass:[NSNumber class]]); - XCTAssertTrue(flagConfigValue.version == [(NSNumber*)flagDictionary[kLDFlagConfigJsonDictionaryKeyVersion] integerValue]); -} - --(void)testEqual_valuesAreTheSame_withVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - LDFlagConfigValue *other = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - - XCTAssertTrue([subject isEqual:other]); -} - --(void)testEqual_valuesAreTheSame_withoutVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - LDFlagConfigValue *other = [LDFlagConfigValue flagConfigValueWithObject:flagConfigStub[@"isABool"]]; - - XCTAssertTrue([subject isEqual:other]); -} - --(void)testEqual_valuesDiffer_differentValues_withVersion { - id subjectFlagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:subjectFlagConfigStub[@"isABool"]]; - - id otherFlagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-false-withVersion"]; - LDFlagConfigValue *other = [LDFlagConfigValue flagConfigValueWithObject:otherFlagConfigStub[@"isABool"]]; - - XCTAssertFalse([subject isEqual:other]); -} - --(void)testEqual_valuesDiffer_differentValues_withoutVersion { - id subjectFlagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withoutVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:subjectFlagConfigStub[@"isABool"]]; - - id otherFlagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-false-withoutVersion"]; - LDFlagConfigValue *other = [LDFlagConfigValue flagConfigValueWithObject:otherFlagConfigStub[@"isABool"]]; - - XCTAssertFalse([subject isEqual:other]); -} - --(void)testEqual_valuesDiffer_differentVersions { - id subjectFlagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:subjectFlagConfigStub[@"isABool"]]; - - id otherFlagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *other = [LDFlagConfigValue flagConfigValueWithObject:otherFlagConfigStub[@"isABool"]]; - other.version += 1; - - XCTAssertFalse([subject isEqual:other]); -} - --(void)testEqual_valuesDiffer_differentObjects { - id subjectFlagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:subjectFlagConfigStub[@"isABool"]]; - - XCTAssertFalse([subject isEqual:@"someString"]); -} - --(void)testEqual_valuesDiffer_otherIsNil { - id subjectFlagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - LDFlagConfigValue *subject = [LDFlagConfigValue flagConfigValueWithObject:subjectFlagConfigStub[@"isABool"]]; - - XCTAssertFalse([subject isEqual:nil]); -} - -@end diff --git a/DarklyTests/Models/LDUserModelTest.m b/DarklyTests/Models/LDUserModelTest.m index b65f94da..806ab55a 100644 --- a/DarklyTests/Models/LDUserModelTest.m +++ b/DarklyTests/Models/LDUserModelTest.m @@ -6,13 +6,15 @@ #import "LDUserModel.h" #import "LDDataManager.h" #import "LDUserModel.h" -#import "LDUserModel+Stub.h" #import "LDUserModel+Testable.h" -#import "LDUserModel+Equatable.h" -#import "LDUserModel+JsonDecodeable.h" #import "NSMutableDictionary+NullRemovable.h" #import "NSString+RemoveWhitespace.h" #import "NSJSONSerialization+Testable.h" +#import "LDUtil.h" +#import "NSDate+ReferencedDate.h" +#import "LDFlagConfigModel.h" +#import "LDFlagConfigTracker.h" +#import "NSDate+Testable.h" @interface LDUserModelTest : XCTestCase @end @@ -36,6 +38,27 @@ -(void)testNewUserSetupProperly { XCTAssertNil(user.privateAttributes); } +-(void)testInit { + NSDate *creationDate = [NSDate date]; + LDUserModel *user = [[LDUserModel alloc] init]; + + XCTAssertNotNil(user); + XCTAssertEqualObjects(user.device, [LDUtil getDeviceAsString]); + XCTAssertEqualObjects(user.os, [LDUtil getSystemVersionAsString]); + XCTAssertTrue([user.updatedAt isWithinTimeInterval:0.01 ofDate:creationDate]); + XCTAssertNotNil(user.custom); + XCTAssertTrue(user.custom.count == 0); + XCTAssertNotNil(user.flagConfig); + if (user.flagConfig) { + XCTAssertTrue(user.flagConfig.featuresJsonDictionary.count == 0); + } + XCTAssertNotNil(user.flagConfigTracker); + if (user.flagConfigTracker) { + XCTAssertTrue(user.flagConfigTracker.flagCounters.count == 0); + XCTAssertTrue(Approximately(user.flagConfigTracker.startDateMillis, [creationDate millisSince1970], 10)); + } +} + -(void)testDictionaryValueWithFlags_Yes_AndPrivateProperties_Yes { LDUserModel *userStub = [LDUserModel stubWithKey:[[NSUUID UUID] UUIDString]]; NSMutableArray *allAttributes = [NSMutableArray arrayWithArray:[LDUserModel allUserAttributes]]; @@ -408,8 +431,14 @@ -(void)testEncodeAndDecode { NSData *encodedUserData = [NSKeyedArchiver archivedDataWithRootObject:userStub]; XCTAssertNotNil(encodedUserData); + LDMillisecond startDateMillis = [[NSDate date] millisSince1970]; LDUserModel *decodedUser = [NSKeyedUnarchiver unarchiveObjectWithData:encodedUserData]; XCTAssertTrue([userStub isEqual:decodedUser ignoringAttributes:@[kUserAttributeUpdatedAt]]); + XCTAssertNotNil(decodedUser.flagConfigTracker); + if (decodedUser.flagConfigTracker) { + XCTAssertTrue(decodedUser.flagConfigTracker.flagCounters.count == 0); + XCTAssertTrue(Approximately(decodedUser.flagConfigTracker.startDateMillis, startDateMillis, 10)); + } } -(void)testInitWithDictionary { @@ -421,8 +450,14 @@ -(void)testInitWithDictionary { NSDictionary *userDictionary = [userStub dictionaryValueWithFlags:YES includePrivateAttributes:YES config:nil includePrivateAttributeList:YES]; XCTAssertTrue(userDictionary && [userDictionary count]); + LDMillisecond startDateMillis = [[NSDate date] millisSince1970]; LDUserModel *reinflatedUser = [[LDUserModel alloc] initWithDictionary:userDictionary]; XCTAssertTrue([userStub isEqual:reinflatedUser ignoringAttributes:nil]); + XCTAssertNotNil(reinflatedUser.flagConfigTracker); + if (reinflatedUser.flagConfigTracker) { + XCTAssertTrue(reinflatedUser.flagConfigTracker.flagCounters.count == 0); + XCTAssertTrue(Approximately(reinflatedUser.flagConfigTracker.startDateMillis, startDateMillis, 10)); + } } -(void)testUserJsonContainsNoWhitespace { @@ -467,6 +502,18 @@ -(void)testUserBackwardsCompatibility { XCTAssertTrue([user isEqual:retrievedUser ignoringAttributes:@[@"updatedAt"]]); } +- (void)testResetTracker { + LDUserModel *subject = [LDUserModel stubWithKey:[[NSUUID UUID] UUIDString]]; + LDMillisecond startDateMillis = [[NSDate date] millisSince1970]; + [subject resetTracker]; + + XCTAssertNotNil(subject.flagConfigTracker); + if (subject.flagConfigTracker) { + XCTAssertTrue(subject.flagConfigTracker.flagCounters.count == 0); + XCTAssertTrue(Approximately(subject.flagConfigTracker.startDateMillis, startDateMillis, 10)); + } +} + #pragma mark - Helpers ///Trims out null values, and config -(NSDictionary*)targetUserDictionaryFrom:(NSDictionary*)userDictionary withConfig:(BOOL)withConfig { @@ -491,7 +538,7 @@ -(void)validateUserModelIsEqualBehaviorUsingUserDictionary:(NSMutableDictionary* } -(NSDictionary*)serverJson { - return [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags-withVersions"]; + return [NSJSONSerialization jsonObjectFromFileNamed:@"featureFlags"]; } -(NSMutableDictionary*)customDictionary { diff --git a/DarklyTests/Models/NSDateFormatter+JsonHeaderTest.m b/DarklyTests/Models/NSDateFormatter+JsonHeaderTest.m new file mode 100644 index 00000000..f5e78510 --- /dev/null +++ b/DarklyTests/Models/NSDateFormatter+JsonHeaderTest.m @@ -0,0 +1,53 @@ +// +// NSDateFormatter+JsonHeaderTest.m +// DarklyTests +// +// Created by Mark Pokorny on 5/8/18. +JMJ +// Copyright © 2018 LaunchDarkly. All rights reserved. +// + +#import +#import "NSDateFormatter+JsonHeader.h" + +@interface NSDateFormatter_JsonHeaderTest : XCTestCase + +@end + +@implementation NSDateFormatter_JsonHeaderTest + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +-(void)testJsonHeaderDateFormatter { + NSString *targetDateString = @"Mon, 07 May 2018 19:46:29 GMT"; + + //Mon, 07 May 2018 19:46:29 GMT + NSDateComponents *dateComponents = [[NSDateComponents alloc] init]; + dateComponents.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; + dateComponents.year = 2018; + dateComponents.month = 5; + dateComponents.day = 7; + dateComponents.hour = 19; + dateComponents.minute = 46; + dateComponents.second = 29; + dateComponents.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"]; + NSDate *targetDate = [dateComponents date]; + + NSDateFormatter *subject = [NSDateFormatter jsonHeaderDateFormatter]; + + NSDate *formatterDate = [subject dateFromString:targetDateString]; + XCTAssertEqualObjects(formatterDate, targetDate); + + NSString *formatterDateString = [subject stringFromDate:targetDate]; + XCTAssertEqualObjects(formatterDateString, targetDateString); + +} + +@end diff --git a/DarklyTests/Models/NSObject+LDFlagConfigValueTest.m b/DarklyTests/Models/NSObject+LDFlagConfigValueTest.m deleted file mode 100644 index c4e778d0..00000000 --- a/DarklyTests/Models/NSObject+LDFlagConfigValueTest.m +++ /dev/null @@ -1,64 +0,0 @@ -// -// NSObject+LDFlagConfigValueTest.m -// DarklyTests -// -// Created by Mark Pokorny on 1/31/18. +JMJ -// Copyright © 2018 LaunchDarkly. All rights reserved. -// - -#import -#import "NSObject+LDFlagConfigValue.h" -#import "NSJSONSerialization+Testable.h" - -extern NSString * const kLDFlagConfigJsonDictionaryKeyValue; -extern NSString * const kLDFlagConfigJsonDictionaryKeyVersion; - -@interface NSObject_LDFlagConfigValueTest : XCTestCase -@end - -@implementation NSObject_LDFlagConfigValueTest - --(void)testIsValueAndVersionDictionary_Yes { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - id subject = (NSDictionary*)flagConfigStub[@"isABool"]; - - XCTAssertTrue([subject isValueAndVersionDictionary]); -} - --(void)testIsValueAndVersionDictionary_No_MissingValue { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - id subject = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)flagConfigStub[@"isABool"]]; - subject[kLDFlagConfigJsonDictionaryKeyValue] = nil; - - XCTAssertFalse([subject isValueAndVersionDictionary]); -} - --(void)testIsValueAndVersionDictionary_No_MissingVersion { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - id subject = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)flagConfigStub[@"isABool"]]; - subject[kLDFlagConfigJsonDictionaryKeyVersion] = nil; - - XCTAssertFalse([subject isValueAndVersionDictionary]); -} - --(void)testIsValueAndVersionDictionary_No_VersionIsNotANumber { - id flagConfigStub = [NSJSONSerialization jsonObjectFromFileNamed:@"boolConfigIsABool-true-withVersion"]; - id subject = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)flagConfigStub[@"isABool"]]; - subject[kLDFlagConfigJsonDictionaryKeyVersion] = @"not a number"; - - XCTAssertFalse([subject isValueAndVersionDictionary]); -} - --(void)testIsValueAndVersionDictionary_No_EmptyDictionary { - id subject = [NSJSONSerialization jsonObjectFromFileNamed:@"emptyConfig"]; - - XCTAssertFalse([subject isValueAndVersionDictionary]); -} - --(void)testIsValueAndVersionDictionary_No_NotADictionary { - id subject = @"not a dictionary"; - - XCTAssertFalse([subject isValueAndVersionDictionary]); -} - -@end diff --git a/Framework/Darkly.h b/Framework/Darkly.h index c28be8c4..278d7e28 100644 --- a/Framework/Darkly.h +++ b/Framework/Darkly.h @@ -6,7 +6,6 @@ // Copyright © 2017 LaunchDarkly. All rights reserved. // -#import #import //! Project version number for Darkly. @@ -17,16 +16,15 @@ FOUNDATION_EXPORT const unsigned char DarklyVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import +#import +#import +#import +#import + #import #import -#import #import -#import -#import -#import #import #import -#import -#import #import #import diff --git a/LaunchDarkly.podspec b/LaunchDarkly.podspec index 1669b75f..2f4c5f6b 100644 --- a/LaunchDarkly.podspec +++ b/LaunchDarkly.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "LaunchDarkly" - s.version = "2.12.1" + s.version = "2.13.0" s.summary = "iOS SDK for LaunchDarkly" s.description = <<-DESC @@ -23,15 +23,15 @@ Pod::Spec.new do |s| s.tvos.deployment_target = "9.0" s.osx.deployment_target = '10.10' - s.source = { :git => "https://github.com/launchdarkly/ios-client.git", :tag => "2.12.1" } + s.source = { :git => "https://github.com/launchdarkly/ios-client.git", :tag => "2.13.0" } - s.source_files = "Darkly/*.{h,m}" + s.source_files = 'Darkly/**/*.{h,m}' s.requires_arc = true s.xcconfig = { 'CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS' => 'NO' } s.subspec 'Core' do |eventSource| - eventSource.dependency 'DarklyEventSource', '~>3.2.3' + eventSource.dependency 'DarklyEventSource', '~>3.2.4' end end diff --git a/Podfile b/Podfile index e56482c1..4578d11d 100644 --- a/Podfile +++ b/Podfile @@ -1,22 +1,22 @@ use_frameworks! target 'Darkly_iOS' do platform :ios, '8.0' - pod 'DarklyEventSource', '~> 3.2.3' + pod 'DarklyEventSource', '~> 3.2.4' end target 'Darkly_tvOS' do platform :tvos, '9.0' - pod 'DarklyEventSource', '~> 3.2.3' + pod 'DarklyEventSource', '~> 3.2.4' end target 'Darkly_watchOS' do platform :watchos, '2.0' - pod 'DarklyEventSource', '~> 3.2.3' + pod 'DarklyEventSource', '~> 3.2.4' end target 'Darkly_osx' do platform :osx, '10.10' - pod 'DarklyEventSource', '~> 3.2.3' + pod 'DarklyEventSource', '~> 3.2.4' end target 'DarklyTests' do diff --git a/Podfile.lock b/Podfile.lock index 26c939e2..9f41cd31 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - DarklyEventSource (3.2.3) + - DarklyEventSource (3.2.4) - OCMock (3.4.1) - OHHTTPStubs (4.8.0): - OHHTTPStubs/Default (= 4.8.0) @@ -16,15 +16,15 @@ PODS: - OHHTTPStubs/OHPathHelpers (4.8.0) DEPENDENCIES: - - DarklyEventSource (~> 3.2.3) + - DarklyEventSource (~> 3.2.4) - OCMock (~> 3.1) - OHHTTPStubs (~> 4.2) SPEC CHECKSUMS: - DarklyEventSource: d5c8e000988ee1feac6c3ffa3dcbdfbaca10d444 + DarklyEventSource: 82340541afe6251d11767eb6f3ce53f4257729ba OCMock: 2cd0716969bab32a2283ff3a46fd26a8c8b4c5e3 OHHTTPStubs: b393565822317305b87a1440d4c7aff131679f66 -PODFILE CHECKSUM: 09df0b8e563d60585362751cc2450fe49d915cc9 +PODFILE CHECKSUM: 464d91876b1e18746c3732341dc0518520c165f3 COCOAPODS: 1.4.0 diff --git a/Pods/DarklyEventSource/LDEventSource/LDEventParser.h b/Pods/DarklyEventSource/LDEventSource/LDEventParser.h new file mode 100644 index 00000000..18d769de --- /dev/null +++ b/Pods/DarklyEventSource/LDEventSource/LDEventParser.h @@ -0,0 +1,23 @@ +// +// LDEventParser.h +// DarklyEventSource +// +// Created by Mark Pokorny on 5/30/18. +JMJ +// Copyright © 2018 Catamorphic Co. All rights reserved. +// + +#import + +@class LDEvent; + +extern NSString * const kLDEventSourceEventTerminator; + +@interface LDEventParser : NSObject +@property (nonatomic, copy, readonly) NSString *eventString; +@property (nonatomic, strong, readonly) LDEvent *event; +@property (nonatomic, strong, readonly) NSNumber *retryInterval; +@property (nonatomic, copy, readonly) NSString *remainingEventString; + ++(instancetype)eventParserWithEventString:(NSString*)eventString; +-(instancetype)initWithEventString:(NSString*)eventString; +@end diff --git a/Pods/DarklyEventSource/LDEventSource/LDEventParser.m b/Pods/DarklyEventSource/LDEventSource/LDEventParser.m new file mode 100644 index 00000000..e9351d6d --- /dev/null +++ b/Pods/DarklyEventSource/LDEventSource/LDEventParser.m @@ -0,0 +1,129 @@ +// +// LDEventParser.m +// DarklyEventSource +// +// Created by Mark Pokorny on 5/30/18. +JMJ +// Copyright © 2018 Catamorphic Co. All rights reserved. +// + +#import +#import "LDEventParser.h" +#import "LDEventSource.h" +#import "NSString+LDEventSource.h" +#import "NSArray+LDEventSource.h" + +static NSString *const ESKeyValueDelimiter = @":"; + +static NSString *const LDEventDataKey = @"data"; +static NSString *const LDEventIDKey = @"id"; +static NSString *const LDEventEventKey = @"event"; +static NSString *const LDEventRetryKey = @"retry"; + +NSString * const kLDEventSourceEventTerminator = @"\n\n"; + +@interface LDEventParser() +@property (nonatomic, copy) NSString *eventString; +@property (nonatomic, strong) LDEvent *event; +@property (nonatomic, strong) NSNumber *retryInterval; +@property (nonatomic, copy) NSString *remainingEventString; +@end + +@implementation LDEventParser ++(instancetype)eventParserWithEventString:(NSString*)eventString { + return [[LDEventParser alloc] initWithEventString:eventString]; +} + +-(instancetype)initWithEventString:(NSString*)eventString { + if (!(self = [super init])) { return nil; } + + self.eventString = eventString; + [self parseEventString]; + + return self; +} + +-(void)parseEventString { + if (self.eventString.length == 0) { return; } + + NSArray *linesToParse = [self linesToParseFromEventString]; + self.remainingEventString = [self remainingEventStringAfterParsingEventString]; + if (linesToParse.count == 0) { return; } + + LDEvent *event = [LDEvent new]; + event.readyState = kEventStateOpen; + + for (NSString *line in linesToParse) { + if ([line hasPrefix:ESKeyValueDelimiter]) { + continue; + } + + if (line.length == 0) { + self.event = event; + return; + } + + @autoreleasepool { + NSScanner *scanner = [NSScanner scannerWithString:line]; + scanner.charactersToBeSkipped = [NSCharacterSet whitespaceCharacterSet]; + + NSString *key, *value; + [scanner scanUpToString:ESKeyValueDelimiter intoString:&key]; + [scanner scanString:ESKeyValueDelimiter intoString:nil]; + [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&value]; + + if (key && value) { + if ([key isEqualToString:LDEventEventKey]) { + event.event = value; + } else if ([key isEqualToString:LDEventDataKey]) { + if (event.data != nil) { + event.data = [event.data stringByAppendingFormat:@"\n%@", value]; + } else { + event.data = value; + } + } else if ([key isEqualToString:LDEventIDKey]) { + event.id = value; + } else if ([key isEqualToString:LDEventRetryKey]) { + if ([value isKindOfClass:[NSNumber class]]) { + self.retryInterval = @([value doubleValue]); + } + } + } + } + } +} + +//extracts lines from the first thru the event terminator +-(nullable NSArray*)linesToParseFromEventString { + if (self.eventString.length == 0) { return nil; } + if (![self.eventString containsString:kLDEventSourceEventTerminator]) { return nil; } + + NSArray *eventStringParts = [self.eventString componentsSeparatedByString:kLDEventSourceEventTerminator]; + if (eventStringParts.count == 0) { return nil; } //This should never happen because the guard for the terminator's presence passed...defensive + NSString *eventStringToParse = [eventStringParts.firstObject stringByAppendingString:kLDEventSourceEventTerminator]; + + return [eventStringToParse lines]; +} + +-(nullable NSString*)remainingEventStringAfterParsingEventString { + if (self.eventString.length == 0) { return nil; } + if (![self.eventString containsString:kLDEventSourceEventTerminator]) { return self.eventString; } + + NSArray *eventStringParts = [self.eventString componentsSeparatedByString:kLDEventSourceEventTerminator]; + if (eventStringParts.count < 2) { return nil; } //This should never happen because the guard for the terminator's presence passed...defensive + if (eventStringParts.count == 2 && eventStringParts[1].length == 0) { return nil; } //There is no remaining string after the terminator...this should be the normal exit + + NSArray *remainingEventStringParts = [eventStringParts subArrayFromIndex:1]; + NSPredicate *nonemptyStringPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + if (![evaluatedObject isKindOfClass:[NSString class]]) { return NO; } + NSString *evaluatedString = evaluatedObject; + return evaluatedString.length > 0; + }]; + NSArray *nonEmptyRemainingEventStringParts = [remainingEventStringParts filteredArrayUsingPredicate:nonemptyStringPredicate]; + NSString *remainingEventString = [nonEmptyRemainingEventStringParts componentsJoinedByString:kLDEventSourceEventTerminator]; + if (remainingEventString.length == 0) { + return nil; + } + + return remainingEventString; +} +@end diff --git a/Pods/DarklyEventSource/LDEventSource/LDEventSource.h b/Pods/DarklyEventSource/LDEventSource/LDEventSource.h index c24645b6..e3923f08 100644 --- a/Pods/DarklyEventSource/LDEventSource/LDEventSource.h +++ b/Pods/DarklyEventSource/LDEventSource/LDEventSource.h @@ -19,7 +19,7 @@ extern NSString *const LDEventSourceErrorDomain; // --------------------------------------------------------------------------------------------------------------------- /// Describes an Event received from an EventSource -@interface LDEvent : NSObject +@interface LDEvent : NSObject /// The Event ID @property (nonatomic, strong) id id; @@ -33,6 +33,7 @@ extern NSString *const LDEventSourceErrorDomain; /// Provides details of any errors with the connection to the EventSource @property (nonatomic, strong) NSError *error; +-(id)copyWithZone:(NSZone*)zone; @end // --------------------------------------------------------------------------------------------------------------------- diff --git a/Pods/DarklyEventSource/LDEventSource/LDEventSource.m b/Pods/DarklyEventSource/LDEventSource/LDEventSource.m index b87a3727..61cbb850 100644 --- a/Pods/DarklyEventSource/LDEventSource/LDEventSource.m +++ b/Pods/DarklyEventSource/LDEventSource/LDEventSource.m @@ -7,22 +7,13 @@ // #import "LDEventSource.h" -#import - -static CGFloat const ES_RETRY_INTERVAL = 1.0; -static CGFloat const ES_DEFAULT_TIMEOUT = 300.0; -static CGFloat const ES_MAX_RECONNECT_TIME = 3600.0; - -static NSString *const ESKeyValueDelimiter = @":"; -static NSString *const LDEventSeparatorLFLF = @"\n\n"; -static NSString *const LDEventSeparatorCRCR = @"\r\r"; -static NSString *const LDEventSeparatorCRLFCRLF = @"\r\n\r\n"; -static NSString *const LDEventKeyValuePairSeparator = @"\n"; - -static NSString *const LDEventDataKey = @"data"; -static NSString *const LDEventIDKey = @"id"; -static NSString *const LDEventEventKey = @"event"; -static NSString *const LDEventRetryKey = @"retry"; +#import "LDEventParser.h" +#import "LDEventStringAccumulator.h" + +static NSTimeInterval const ES_RETRY_INTERVAL = 1.0; +static NSTimeInterval const ES_DEFAULT_TIMEOUT = 300.0; +static NSTimeInterval const ES_MAX_RECONNECT_TIME = 3600.0; + NSString *const LDEventSourceErrorDomain = @"LDEventSourceErrorDomain"; static NSInteger const HTTPStatusCodeUnauthorized = 401; @@ -40,10 +31,11 @@ @interface LDEventSource () { @property (nonatomic, assign) NSTimeInterval timeoutInterval; @property (nonatomic, assign) NSTimeInterval retryInterval; @property (nonatomic, assign) NSInteger retryAttempt; -@property (readonly, nonatomic, strong) NSDictionary *httpRequestHeaders; +@property (nonatomic, strong) NSDictionary *httpRequestHeaders; @property (nonatomic, strong) NSString *connectMethod; @property (nonatomic, strong) NSData *connectBody; @property (nonatomic, strong) id lastEventID; +@property (nonatomic, strong) LDEventStringAccumulator *eventStringAccumulator; - (void)_open; - (void)_dispatchEvent:(LDEvent *)e; @@ -80,24 +72,26 @@ - (instancetype)initWithURL:(NSURL *)URL httpHeaders:(NSDictionary*) headers timeoutInterval:(NSTimeInterval)timeoutInterval connectMethod:(NSString*)connectMethod connectBody:(NSData*)connectBody { self = [super init]; - if (self) { - _listeners = [NSMutableDictionary dictionary]; - _eventURL = URL; - _timeoutInterval = timeoutInterval; - _retryInterval = ES_RETRY_INTERVAL; - _retryAttempt = 0; - _httpRequestHeaders = headers; - _connectMethod = connectMethod; - _connectBody = connectBody; - messageQueue = dispatch_queue_create("com.launchdarkly.eventsource-queue", DISPATCH_QUEUE_SERIAL); - connectionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); - - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_retryInterval * NSEC_PER_SEC)); - __weak typeof(self) weakSelf = self; - dispatch_after(popTime, connectionQueue, ^(void){ - [weakSelf _open]; - }); - } + if (!self) { return nil; } + + self.listeners = [NSMutableDictionary dictionary]; + self.eventURL = URL; + self.timeoutInterval = timeoutInterval; + self.retryInterval = ES_RETRY_INTERVAL; + self.retryAttempt = 0; + self.httpRequestHeaders = headers; + self.connectMethod = connectMethod; + self.connectBody = connectBody; + messageQueue = dispatch_queue_create("com.launchdarkly.eventsource-queue", DISPATCH_QUEUE_SERIAL); + connectionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + self.eventStringAccumulator = [[LDEventStringAccumulator alloc] init]; + + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_retryInterval * NSEC_PER_SEC)); + __weak typeof(self) weakSelf = self; + dispatch_after(popTime, connectionQueue, ^(void){ + [weakSelf _open]; + }); + return self; } @@ -160,54 +154,31 @@ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)data - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { NSString *eventString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSArray *lines = [eventString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; - - LDEvent *event = [LDEvent new]; - event.readyState = kEventStateOpen; - - for (NSString *line in lines) { - if ([line hasPrefix:ESKeyValueDelimiter]) { - continue; - } - - if (!line || line.length == 0) { - __weak typeof(self) weakSelf = self; - dispatch_async(messageQueue, ^{ - [weakSelf _dispatchEvent:event]; - }); - - event = [LDEvent new]; - event.readyState = kEventStateOpen; - continue; - } - - @autoreleasepool { - NSScanner *scanner = [NSScanner scannerWithString:line]; - scanner.charactersToBeSkipped = [NSCharacterSet whitespaceCharacterSet]; - - NSString *key, *value; - [scanner scanUpToString:ESKeyValueDelimiter intoString:&key]; - [scanner scanString:ESKeyValueDelimiter intoString:nil]; - [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&value]; - - if (key && value) { - if ([key isEqualToString:LDEventEventKey]) { - event.event = value; - } else if ([key isEqualToString:LDEventDataKey]) { - if (event.data != nil) { - event.data = [event.data stringByAppendingFormat:@"\n%@", value]; - } else { - event.data = value; - } - } else if ([key isEqualToString:LDEventIDKey]) { - event.id = value; - self.lastEventID = event.id; - } else if ([key isEqualToString:LDEventRetryKey]) { - self.retryInterval = [value doubleValue]; - } - } + [self.eventStringAccumulator accumulateEventStringWithString:eventString]; + if ([self.eventStringAccumulator isReadyToParseEvent]) { + [self parseEventString:self.eventStringAccumulator.eventString]; + [self.eventStringAccumulator reset]; + } +} + +- (void)parseEventString:(NSString*)eventString { + if (eventString.length == 0) { return; } + LDEventParser *parser = [LDEventParser eventParserWithEventString:eventString]; + if (parser.event) { + __weak typeof(self) weakSelf = self; + dispatch_async(messageQueue, ^{ + [weakSelf _dispatchEvent:parser.event]; + }); + if (parser.event.id) { + self.lastEventID = parser.event.id; } } + if (parser.retryInterval) { + self.retryInterval = [parser.retryInterval doubleValue]; + } + if (parser.remainingEventString.length > 0) { + [self parseEventString:parser.remainingEventString]; + } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error @@ -318,7 +289,7 @@ - (void)_dispatchEvent:(LDEvent *)event } } -- (CGFloat)increaseIntervalWithBackoff { +- (NSTimeInterval)increaseIntervalWithBackoff { _retryAttempt++; return arc4random_uniform(MIN(ES_MAX_RECONNECT_TIME, _retryInterval * pow(2, _retryAttempt))); } @@ -351,6 +322,16 @@ - (NSString *)description self.data]; } +-(id)copyWithZone:(NSZone*)zone { + LDEvent *copiedEvent = [[LDEvent alloc] init]; + copiedEvent.id = self.id; + copiedEvent.event = self.event; + copiedEvent.data = self.data; + copiedEvent.readyState = self.readyState; + copiedEvent.error = self.error; + return copiedEvent; +} + @end NSString *const MessageEvent = @"message"; diff --git a/Pods/DarklyEventSource/LDEventSource/LDEventStringAccumulator.h b/Pods/DarklyEventSource/LDEventSource/LDEventStringAccumulator.h new file mode 100644 index 00000000..0b37607d --- /dev/null +++ b/Pods/DarklyEventSource/LDEventSource/LDEventStringAccumulator.h @@ -0,0 +1,17 @@ +// +// LDDataAccumulator.h +// DarklyEventSource +// +// Created by Mark Pokorny on 5/30/18. +JMJ +// Copyright © 2018 Catamorphic Co. All rights reserved. +// + +#import + +@interface LDEventStringAccumulator : NSObject +@property (nonatomic, copy) NSString *eventString; + +-(void)accumulateEventStringWithString:(NSString*)eventString; +-(BOOL)isReadyToParseEvent; +-(void)reset; +@end diff --git a/Pods/DarklyEventSource/LDEventSource/LDEventStringAccumulator.m b/Pods/DarklyEventSource/LDEventSource/LDEventStringAccumulator.m new file mode 100644 index 00000000..4e68a940 --- /dev/null +++ b/Pods/DarklyEventSource/LDEventSource/LDEventStringAccumulator.m @@ -0,0 +1,31 @@ +// +// LDEventStringAccumulator.m +// DarklyEventSource +// +// Created by Mark Pokorny on 5/30/18. +JMJ +// Copyright © 2018 Catamorphic Co. All rights reserved. +// + +#import +#import "LDEventStringAccumulator.h" +#import "LDEventParser.h" + +@implementation LDEventStringAccumulator +-(void)accumulateEventStringWithString:(NSString*)eventString { + if (eventString.length == 0) { return; } + if (self.eventString == nil) { + self.eventString = eventString; + return; + } + self.eventString = [self.eventString stringByAppendingString:eventString]; +} + +-(BOOL)isReadyToParseEvent { + if (self.eventString.length == 0) { return NO; } + return [self.eventString containsString:kLDEventSourceEventTerminator]; +} + +-(void)reset { + self.eventString = nil; +} +@end diff --git a/Pods/DarklyEventSource/LDEventSource/NSArray+LDEventSource.h b/Pods/DarklyEventSource/LDEventSource/NSArray+LDEventSource.h new file mode 100644 index 00000000..f856f293 --- /dev/null +++ b/Pods/DarklyEventSource/LDEventSource/NSArray+LDEventSource.h @@ -0,0 +1,15 @@ +// +// NSArray+LDEventSource.h +// DarklyEventSource +// +// Created by Mark Pokorny on 5/31/18. +JMJ +// Copyright © 2018 Catamorphic Co. All rights reserved. +// + +#import + +@interface NSArray(LDEventSource) +-(NSUInteger)indexOfFirstEmptyLine; +//Returns the array beyond the index, or nil if the index is at the end of the array or beyond +-(NSArray*)subArrayFromIndex:(NSUInteger)index; +@end diff --git a/Pods/DarklyEventSource/LDEventSource/NSArray+LDEventSource.m b/Pods/DarklyEventSource/LDEventSource/NSArray+LDEventSource.m new file mode 100644 index 00000000..02190a0e --- /dev/null +++ b/Pods/DarklyEventSource/LDEventSource/NSArray+LDEventSource.m @@ -0,0 +1,21 @@ +// +// NSArray+LDEventSource.m +// DarklyEventSource +// +// Created by Mark Pokorny on 5/31/18. +JMJ +// Copyright © 2018 Catamorphic Co. All rights reserved. +// + +#import +#import "NSArray+LDEventSource.h" + +@implementation NSArray(LDEventSource) +-(NSUInteger)indexOfFirstEmptyLine { + if (![self.firstObject isKindOfClass:[NSString class]]) { return NSNotFound; } + return [self indexOfObject:@""]; +} +-(NSArray*)subArrayFromIndex:(NSUInteger)index { + if (index >= (self.count - 1)) { return nil; } //index is at or beyond the last element + return [self subarrayWithRange:NSMakeRange(index + 1, self.count - index - 1)]; +} +@end diff --git a/Pods/DarklyEventSource/LDEventSource/NSString+LDEventSource.h b/Pods/DarklyEventSource/LDEventSource/NSString+LDEventSource.h new file mode 100644 index 00000000..76dc8cd5 --- /dev/null +++ b/Pods/DarklyEventSource/LDEventSource/NSString+LDEventSource.h @@ -0,0 +1,13 @@ +// +// NSString+LDEventSource.h +// DarklyEventSource +// +// Created by Mark Pokorny on 5/31/18. +JMJ +// Copyright © 2018 Catamorphic Co. All rights reserved. +// + +#import + +@interface NSString(LDEventSource) +-(NSArray*)lines; +@end diff --git a/Pods/DarklyEventSource/LDEventSource/NSString+LDEventSource.m b/Pods/DarklyEventSource/LDEventSource/NSString+LDEventSource.m new file mode 100644 index 00000000..17b218f4 --- /dev/null +++ b/Pods/DarklyEventSource/LDEventSource/NSString+LDEventSource.m @@ -0,0 +1,16 @@ +// +// NSString+LDEventSource.m +// DarklyEventSource +// +// Created by Mark Pokorny on 5/31/18. +JMJ +// Copyright © 2018 Catamorphic Co. All rights reserved. +// + +#import "NSString+LDEventSource.h" + +@implementation NSString(LDEventSource) +-(NSArray*)lines { + if (self.length == 0) { return nil; } + return [self componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; +} +@end diff --git a/Pods/DarklyEventSource/README.md b/Pods/DarklyEventSource/README.md index a34f56f9..2f8cb939 100644 --- a/Pods/DarklyEventSource/README.md +++ b/Pods/DarklyEventSource/README.md @@ -93,7 +93,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' target 'TargetName' do -pod 'DarklyEventSource', '~> 3.2.3' +pod 'DarklyEventSource', '~> 3.2.4' end ``` @@ -117,7 +117,7 @@ $ brew install carthage To integrate EventSource into your Xcode project using Carthage, specify it in your `Cartfile`: ```ogdl -github "launchdarkly/ios-eventsource" >= 3.2.3 +github "launchdarkly/ios-eventsource" >= 3.2.4 ``` Run `carthage` to build the framework and drag the built `EventSource.framework` into your Xcode project. diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock index 26c939e2..9f41cd31 100644 --- a/Pods/Manifest.lock +++ b/Pods/Manifest.lock @@ -1,5 +1,5 @@ PODS: - - DarklyEventSource (3.2.3) + - DarklyEventSource (3.2.4) - OCMock (3.4.1) - OHHTTPStubs (4.8.0): - OHHTTPStubs/Default (= 4.8.0) @@ -16,15 +16,15 @@ PODS: - OHHTTPStubs/OHPathHelpers (4.8.0) DEPENDENCIES: - - DarklyEventSource (~> 3.2.3) + - DarklyEventSource (~> 3.2.4) - OCMock (~> 3.1) - OHHTTPStubs (~> 4.2) SPEC CHECKSUMS: - DarklyEventSource: d5c8e000988ee1feac6c3ffa3dcbdfbaca10d444 + DarklyEventSource: 82340541afe6251d11767eb6f3ce53f4257729ba OCMock: 2cd0716969bab32a2283ff3a46fd26a8c8b4c5e3 OHHTTPStubs: b393565822317305b87a1440d4c7aff131679f66 -PODFILE CHECKSUM: 09df0b8e563d60585362751cc2450fe49d915cc9 +PODFILE CHECKSUM: 464d91876b1e18746c3732341dc0518520c165f3 COCOAPODS: 1.4.0 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj index c79c6729..9110d155 100644 --- a/Pods/Pods.xcodeproj/project.pbxproj +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -7,130 +7,162 @@ objects = { /* Begin PBXBuildFile section */ - 01F7D55919C59833456188558FE8D7FA /* OCMExpectationRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 1541B7DBA1AD0A7B65F8FFC607CBD5B5 /* OCMExpectationRecorder.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 0310B1523123778B0E3FB78BE3A14095 /* OCMBlockCaller.h in Headers */ = {isa = PBXBuildFile; fileRef = D530BD02AFFFAC8355740377AFEE3DA7 /* OCMBlockCaller.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 076CC6AEFAE06B5E31AE2CA3860FD54A /* DarklyEventSource-macOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A61A859375079B183E1F4F6E8F4466A7 /* DarklyEventSource-macOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 08B95EC0E9E8E488C1F210D31873A678 /* OHHTTPStubs.h in Headers */ = {isa = PBXBuildFile; fileRef = EF760A95F22C50A6B60AECA5D320903C /* OHHTTPStubs.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0AEAD2BEB61CCA26BB0798F2E527BF84 /* OCMRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 65C0919CA083CA15AE160F0D4A37025B /* OCMRecorder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 0D183650A2D88CB6DAD886030A3A2B62 /* OCMBoxedReturnValueProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A99F324709D15E97D9B7E7EFD390B810 /* OCMBoxedReturnValueProvider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 0E32A8C1B69250199C8EC0794CF3A3B6 /* OCMStubRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 64E4BC5A9889E0DB1F19CDE2935049BB /* OCMStubRecorder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 116FA1BBD719AFAA5593D71332D2A926 /* OCClassMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 904227180DDE68F8E40C85777E8B4A62 /* OCClassMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 1329EFE30461FD51B3F88475820C06AC /* OHHTTPStubsResponse+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 22DBC6DB853E340179B4940B05979383 /* OHHTTPStubsResponse+JSON.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 132CF79716773FE77D67268D06A35978 /* LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 27467853512D2003F1639D1425ABA364 /* LDEventSource.m */; }; - 13A1051CA96D84674E588FECAF52DD43 /* OCMExpectationRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 79234D9DB87E5C7FB6B8F7FAC6C79FCA /* OCMExpectationRecorder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 13AA024ACA7FE3FAD7E0FFD5AC77370D /* OCMVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = C9EDE1211E7858C38AF1D41AB7FEC624 /* OCMVerifier.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 01F7D55919C59833456188558FE8D7FA /* OCMExpectationRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = B2E760A6F1FCCA6AE1FEFC1C3C88BF43 /* OCMExpectationRecorder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0310B1523123778B0E3FB78BE3A14095 /* OCMBlockCaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 434B5DF5224FEDADE75A5E689D146E90 /* OCMBlockCaller.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 0402FEBC05924668535DB83CFBC838FA /* LDEventParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C225A59880C3017D042E0BEFF60A9520 /* LDEventParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 04F38EEB14F5F1762A4C39256951BA3A /* LDEventStringAccumulator.h in Headers */ = {isa = PBXBuildFile; fileRef = A313E7D0C6F134B251DD75F2871B57D1 /* LDEventStringAccumulator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 08B95EC0E9E8E488C1F210D31873A678 /* OHHTTPStubs.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F7263EB5FDBA9374A691926217D7C8E /* OHHTTPStubs.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0AE40A1E851BE5D309075E387BDBA9EB /* LDEventStringAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BD336159F272CBBE1150A6F6C01B8A8 /* LDEventStringAccumulator.m */; }; + 0AEAD2BEB61CCA26BB0798F2E527BF84 /* OCMRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 55B5741CCC432EC8DCFD5BE5F72FBB9F /* OCMRecorder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 0D183650A2D88CB6DAD886030A3A2B62 /* OCMBoxedReturnValueProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 256289EC71769D11C26C75322CA9E14E /* OCMBoxedReturnValueProvider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 0E32A8C1B69250199C8EC0794CF3A3B6 /* OCMStubRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 70098D1C0228050C3D6F1E89DAD2685B /* OCMStubRecorder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 116FA1BBD719AFAA5593D71332D2A926 /* OCClassMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = E649A16E0C0CC9794FADEB73D226FD04 /* OCClassMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 11DE0D9009566837614C175C449D7A30 /* DarklyEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E6AA2AC1F3A30B8D20CE8B588A25F8 /* DarklyEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 12DC5FE2A5D12F52C6F46B3A01B653E9 /* DarklyEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E6AA2AC1F3A30B8D20CE8B588A25F8 /* DarklyEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1329EFE30461FD51B3F88475820C06AC /* OHHTTPStubsResponse+JSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F5930E82A640BA89F9A60B548EE79C /* OHHTTPStubsResponse+JSON.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 13A1051CA96D84674E588FECAF52DD43 /* OCMExpectationRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 19753520BE3CA6C03AA17595F381533A /* OCMExpectationRecorder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 13AA024ACA7FE3FAD7E0FFD5AC77370D /* OCMVerifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 4927BE6EEC04B10C75C1FA438914CB8E /* OCMVerifier.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 1444216614868BF70A7078E46CB7680A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACDF0E08285415092448B937524BBFBC /* Foundation.framework */; }; - 147E00CDCEDB145F0012FA174BA4E545 /* OCObserverMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 1AFC0F519CA2ABEEF80E7347EF34382C /* OCObserverMockObject.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 15081C3A4D626D9631A9594847D2E2A6 /* OCMFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 45C88E7B8B282A7217B488B0529A4485 /* OCMFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1583087CE691D66E050A0AE7C07296BD /* OCMNotificationPoster.h in Headers */ = {isa = PBXBuildFile; fileRef = 52DECA100A517BF5699E9107D127C358 /* OCMNotificationPoster.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 15FEDB8A315321D48BC7E535429ADB48 /* OCMInvocationStub.h in Headers */ = {isa = PBXBuildFile; fileRef = 258EB08B29B8B6225E55EF8CFD05B229 /* OCMInvocationStub.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 147E00CDCEDB145F0012FA174BA4E545 /* OCObserverMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A4541F05BE15B48B91C0C05BB3A2E351 /* OCObserverMockObject.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 15081C3A4D626D9631A9594847D2E2A6 /* OCMFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 64E7CE8152263383C56E51268DC58D63 /* OCMFunctions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1583087CE691D66E050A0AE7C07296BD /* OCMNotificationPoster.h in Headers */ = {isa = PBXBuildFile; fileRef = A9DF034B73D13BDA81F88F70F50913C8 /* OCMNotificationPoster.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 15FEDB8A315321D48BC7E535429ADB48 /* OCMInvocationStub.h in Headers */ = {isa = PBXBuildFile; fileRef = EB220CE5C49A8E1654877D5DEC917754 /* OCMInvocationStub.h */; settings = {ATTRIBUTES = (Project, ); }; }; 16866E92B794BD09F8DAB24905133470 /* Pods-Darkly_iOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = F3FDD2EE95173021BEF192DE3D6CF6D4 /* Pods-Darkly_iOS-dummy.m */; }; - 1D53147F49F02FC693C33D8526049C90 /* OCMIndirectReturnValueProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 463DD1D3C40C8339739DF808CF0C84D2 /* OCMIndirectReturnValueProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 1DD2C9A900B7D7B5F3862D15C1A8AD7E /* NSValue+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 44E13D1AB2457923E89EDABB9FE7D5C5 /* NSValue+OCMAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1688C178D208025491DB68DC91C7D3E5 /* NSString+LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F03D99AD09DAEA70CA34975D9B60C1 /* NSString+LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1D53147F49F02FC693C33D8526049C90 /* OCMIndirectReturnValueProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 003AB641624DD367808954F05509B17A /* OCMIndirectReturnValueProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1D96772AA2887C52505E87AF89AB5959 /* LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = BABC81EC3CA732F91FFAA9E8C91200E8 /* LDEventSource.m */; }; + 1DBBF95199F3F19C49EACB655ACA342C /* LDEventParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C225A59880C3017D042E0BEFF60A9520 /* LDEventParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1DD2C9A900B7D7B5F3862D15C1A8AD7E /* NSValue+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9269E4DB085E9703969EFCB1CD2DE757 /* NSValue+OCMAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 236353C203FBF8AA4F6BBE247917E0DF /* NSString+LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F03D99AD09DAEA70CA34975D9B60C1 /* NSString+LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 236CD6C00A1644753BAF198CAB42E6CD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACDF0E08285415092448B937524BBFBC /* Foundation.framework */; }; - 24394811C369F117D75337F436C81222 /* OCMock-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 525390A8E371A083C74C84FA87DA1B8C /* OCMock-dummy.m */; }; - 28B5E9BDDC2F53432C7D7D244837F1B5 /* OCClassMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 9E649BE19BCA5516709BC755EC9FEEC7 /* OCClassMockObject.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 2CE09E2B1AC5D86B66F237C58C1C612B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBFEAC80AB2E00CFC60743201EA79E8E /* Foundation.framework */; }; - 2CEF054AD61CF46CAFB3ACD5329CAB8C /* OHPathHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B6647FF28FFA097E8F2269CFD4713C8 /* OHPathHelpers.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 2F55F80110FF9D204BCFB846A8270508 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D2BC21DEA3ACE1E0185614B2CCC24DC /* Cocoa.framework */; }; + 24394811C369F117D75337F436C81222 /* OCMock-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 47B73DDE334F6D4A01A21D8194861C85 /* OCMock-dummy.m */; }; + 28B5E9BDDC2F53432C7D7D244837F1B5 /* OCClassMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 23A9B1BAFAFED46EF4C338455C3593D2 /* OCClassMockObject.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2B2D0FCD965DA9CA9F738633D9121EA1 /* NSArray+LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D1F1BDA14548B452E79BA387D85DFFF9 /* NSArray+LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2CEF054AD61CF46CAFB3ACD5329CAB8C /* OHPathHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = FAB8ABEA5FDD50C6707CDCDBD3AFAEA0 /* OHPathHelpers.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; 2FE8CDA9C5D0067526BF4F857B06C0AC /* Pods-Darkly_tvOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 95B4F44EF48D7469F021EF0AFAB7DA9C /* Pods-Darkly_tvOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 31DE30A78E0C0F9440A2B571B9E62F23 /* LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 27467853512D2003F1639D1425ABA364 /* LDEventSource.m */; }; - 32C5DB81D5BB4D7B80A36763D595B318 /* OHHTTPStubs-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = CABB23078EEF17F61B31494DB75B7A83 /* OHHTTPStubs-dummy.m */; }; - 353759005B92EEC29E4B15765C4D3EEB /* OCPartialMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 9FA926717A94389A01F7FE7C87C4FDC8 /* OCPartialMockObject.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 35675FF5ABBB5D07872C3F48DA96ED6D /* LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = AA49AFC7E73CAB7B63A97FC6963BD84B /* LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32C5DB81D5BB4D7B80A36763D595B318 /* OHHTTPStubs-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E239BC788138F289F7F1023EA0B9F908 /* OHHTTPStubs-dummy.m */; }; + 347D74A81D0C344A8E45EFCA3AC2059C /* NSArray+LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 663B2E9A93F8EE36BF7D622EBE9F5D64 /* NSArray+LDEventSource.m */; }; + 353759005B92EEC29E4B15765C4D3EEB /* OCPartialMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CC7AD574057A73DA9297797A75CEFBB /* OCPartialMockObject.h */; settings = {ATTRIBUTES = (Project, ); }; }; 35D4B99EC89BD8895C82FA64695C0125 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACDF0E08285415092448B937524BBFBC /* Foundation.framework */; }; - 368CB50FFBFF7DB7F817A4F36572BBCB /* DarklyEventSource-tvOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 2CA2FFC0331CABB7FE9E51E64057D4F1 /* DarklyEventSource-tvOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 38D5085895B3757AF0DCED078EF8B145 /* OCMRealObjectForwarder.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FBF42418B3DBA6C8DFFF85F037DBDFF /* OCMRealObjectForwarder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 37E93D19B89637966E0112DD9318BC78 /* DarklyEventSource-macOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = ABFB220046434E2FE02BBBA6E93B1F6B /* DarklyEventSource-macOS-dummy.m */; }; + 38D5085895B3757AF0DCED078EF8B145 /* OCMRealObjectForwarder.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A97493D9FBA59B4575383AB9A15B287 /* OCMRealObjectForwarder.h */; settings = {ATTRIBUTES = (Project, ); }; }; 397B254D629C8F6DCDEB6982B52EE1B6 /* Pods-Darkly_watchOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 4781232EDEA682C534BE3C7F024AACFA /* Pods-Darkly_watchOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3AC7DF75455976534659CC5ED2436C27 /* OHHTTPStubs-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A0CD449DFE4111D9DA40063F59D0F1C /* OHHTTPStubs-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3D1CB7A4AB9B00BC95F06CF858F18BCE /* OCMFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EB28608957FFF3FE5D9F73B3FBFA7F3 /* OCMFunctions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 3DC778E2C31E48A95C709B00CA42B6E2 /* LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 27467853512D2003F1639D1425ABA364 /* LDEventSource.m */; }; + 3AC7DF75455976534659CC5ED2436C27 /* OHHTTPStubs-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3F9F37AD5F918763317FD22F67056E6D /* OHHTTPStubs-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3D1CB7A4AB9B00BC95F06CF858F18BCE /* OCMFunctions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9028A0B4BC6EC42D3737F48BD89B8BC6 /* OCMFunctions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 4096732DF27B7812AD9EB46F491BEEFA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D2BC21DEA3ACE1E0185614B2CCC24DC /* Cocoa.framework */; }; - 42624ACF24F9C1BB0BB53C47F081AD55 /* LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 27467853512D2003F1639D1425ABA364 /* LDEventSource.m */; }; - 441D5F52730CBB1B1FE8D84FAAC23D1B /* OHHTTPStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = D183ABD8F5D0BF9ED235E3E57FF77187 /* OHHTTPStubs.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; - 4552366371FE1ACA9600897C31D70E4C /* OCMObserverRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = A8A9B0ADD858339D3D15CABD60856848 /* OCMObserverRecorder.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 45E91E6E79D1EEACF0F7A668E3D9A89D /* OCProtocolMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 96715FCBD819314A9A12ADE3BEFE83EC /* OCProtocolMockObject.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 4D1A5B357BFC3194E892FB6933989940 /* OHHTTPStubsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = B309EA5ED7C7F03F08835B7C48F3B1E8 /* OHHTTPStubsResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 4E8250BDB662F542F9294F6941333373 /* OCMPassByRefSetter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D02AD8B359F811CAEC7E5C2A272E5DE /* OCMPassByRefSetter.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 4F02F5DD4EC7C3AFD1EC38E2F55B0C4A /* OCMBlockArgCaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 518214EDCE7B01015C2BB6271BB772FD /* OCMBlockArgCaller.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 4F4A7459D96FDABBAB640D1D1B1BB42E /* OCMNotificationPoster.m in Sources */ = {isa = PBXBuildFile; fileRef = 49DBE0583AD97FC7AFD40E44B6ADD0C3 /* OCMNotificationPoster.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 5037A042D2012439DEC932F10FC51B95 /* NSMethodSignature+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FA9BAC5F5AA8C7555A63F5E968C150C /* NSMethodSignature+OCMAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 51CAB174257235556CE8902084F0E662 /* NSNotificationCenter+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BEB5371A9B9E754AC32F232018AE56C /* NSNotificationCenter+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 543D9D71DD92175A9CC5F0831CF2A028 /* OCPartialMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = DE7B054EF1D513FB1CDAF343DF48E695 /* OCPartialMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 54471D429F63760B736E52506A0B861B /* OCMInvocationMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BBF7B3840A36892D14C230FC55B3B64 /* OCMInvocationMatcher.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 58FB50DB554239F854ECBEC3C86F3A92 /* OCMRealObjectForwarder.m in Sources */ = {isa = PBXBuildFile; fileRef = A528657BFE7C565C8EE90EC0CDCDE385 /* OCMRealObjectForwarder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 59578DBAB2520015A65B5B160722A370 /* NSValue+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 819D8ECA744A54362D07EA95D9379062 /* NSValue+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 42B089CA2DA1147F0EDDAD112688A8DB /* NSArray+LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 663B2E9A93F8EE36BF7D622EBE9F5D64 /* NSArray+LDEventSource.m */; }; + 44064E11259641498118404EC4CD3FB3 /* DarklyEventSource-watchOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = BD6D790929BCF50491E14B828FDCF732 /* DarklyEventSource-watchOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 441D5F52730CBB1B1FE8D84FAAC23D1B /* OHHTTPStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = 68B5CFA8361696B34926AC638CF51858 /* OHHTTPStubs.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 4552366371FE1ACA9600897C31D70E4C /* OCMObserverRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = B3193F9B76AC15F23E75418697A0ADBD /* OCMObserverRecorder.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 45E91E6E79D1EEACF0F7A668E3D9A89D /* OCProtocolMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 310F89F7AFD4AD6A0ACF5D36DE44757B /* OCProtocolMockObject.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 4D1A5B357BFC3194E892FB6933989940 /* OHHTTPStubsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 00C177350037FC007B07D98EFBFC21D3 /* OHHTTPStubsResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4E8250BDB662F542F9294F6941333373 /* OCMPassByRefSetter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6352107EC0A782D74D3BF44A85D3A476 /* OCMPassByRefSetter.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 4F02F5DD4EC7C3AFD1EC38E2F55B0C4A /* OCMBlockArgCaller.m in Sources */ = {isa = PBXBuildFile; fileRef = B53B2A149FB90B83E32D568BF1BBF7F3 /* OCMBlockArgCaller.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 4F4A7459D96FDABBAB640D1D1B1BB42E /* OCMNotificationPoster.m in Sources */ = {isa = PBXBuildFile; fileRef = B61614B63DFF88EDF160FEBF7EB05484 /* OCMNotificationPoster.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 4F6D69615A0C04979C46224CBDA40248 /* DarklyEventSource-iOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A684FF78260E2892869AB05307F8A12 /* DarklyEventSource-iOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5037A042D2012439DEC932F10FC51B95 /* NSMethodSignature+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = BC8860E68463F92438DD36F071B29A55 /* NSMethodSignature+OCMAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 513E960012E60C5C27E3E81C27530BFE /* LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = BABC81EC3CA732F91FFAA9E8C91200E8 /* LDEventSource.m */; }; + 516BF1D058670F046085DB4A05AA7A16 /* DarklyEventSource-tvOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E9A4009B6F747D00F9B42B9D986E98D6 /* DarklyEventSource-tvOS-dummy.m */; }; + 51CAB174257235556CE8902084F0E662 /* NSNotificationCenter+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B6F2C53A8B680B9712FCADA894F7A99 /* NSNotificationCenter+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 52AD26246A4D997309909632620EE74A /* DarklyEventSource-iOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = E99EDCB4BF6E43D1BB8BF9809189FADC /* DarklyEventSource-iOS-dummy.m */; }; + 54385A4080A43BD8EF31C38CD229E445 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 528B66A2BC53927BB64C91DB055F0D5D /* Foundation.framework */; }; + 543D9D71DD92175A9CC5F0831CF2A028 /* OCPartialMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 507069B5285158B9E9B93D9F5B2456FB /* OCPartialMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 54471D429F63760B736E52506A0B861B /* OCMInvocationMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = FCB99FA900D325BA0BB2195CED057DF8 /* OCMInvocationMatcher.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 54F85174C22EDF0D7D6085FC56ADEC8A /* NSArray+LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 663B2E9A93F8EE36BF7D622EBE9F5D64 /* NSArray+LDEventSource.m */; }; + 56510998D6857BE91A0B3EFD76591175 /* LDEventParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C50DAE28B8386CDA6B51492071510AE /* LDEventParser.m */; }; + 58BCAEFDBA55841E4F906B01CF49C52F /* LDEventParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C50DAE28B8386CDA6B51492071510AE /* LDEventParser.m */; }; + 58FB50DB554239F854ECBEC3C86F3A92 /* OCMRealObjectForwarder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5EF931F06BF1C1F76D5CE2C1BBC704 /* OCMRealObjectForwarder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 59578DBAB2520015A65B5B160722A370 /* NSValue+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 635E8F6D068B917C59F86E7C9038EC04 /* NSValue+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 5C84D78343D338348A6B022472839715 /* DarklyEventSource-watchOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 901EB4B981DFBB59BBB5CD4EA34AD42A /* DarklyEventSource-watchOS-dummy.m */; }; + 5F708D4CBB9F343B7A61F048C15396EE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBFEAC80AB2E00CFC60743201EA79E8E /* Foundation.framework */; }; 6192414559E9F4A40385F133B167A247 /* Pods-Darkly_watchOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A72D12FAF4CA6C4705065580B766EEF /* Pods-Darkly_watchOS-dummy.m */; }; - 64EAAFD6C90AB106CC975DB79853F924 /* LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = AA49AFC7E73CAB7B63A97FC6963BD84B /* LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 66274D41CA7F2FA90B14E33C80ACA3FC /* Pods-Darkly_osx-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7B3FB5C3808BF903624018B25BA375 /* Pods-Darkly_osx-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 670B7B73AAFAA768FA905B5AEF0F7F77 /* Compatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 36EF2A58593920A528EB346E42F18C19 /* Compatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 670B7B73AAFAA768FA905B5AEF0F7F77 /* Compatibility.h in Headers */ = {isa = PBXBuildFile; fileRef = F39D8D66CA8C5F94886F8314A00290CF /* Compatibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6AC8C86899E8F2BC09A3CA0D986D4E51 /* Pods-Darkly_iOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B126A552AA300B3CBFE87ED55490A98 /* Pods-Darkly_iOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6C600444BFBF4AA0E980A91C91624323 /* Pods-Darkly_tvOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 2FA394D19683E365635D4F0610E24199 /* Pods-Darkly_tvOS-dummy.m */; }; - 6DEAD4CD0FF9467DA3FD3F22816418C6 /* OCMArgAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CDDBBF20B9E8B60CCF52A35F23C2768 /* OCMArgAction.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 71213523998EC4038EEF2881118A57A1 /* NSInvocation+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = F90968080A13E2B5A51548EB12B78716 /* NSInvocation+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 72C8CC2855787B9DC1A37DDEB5E24155 /* OCMFunctionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = CF4197454D6EDFC7E82D1585BA642113 /* OCMFunctionsPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 74BC62D6FEB722F4976016FD4DCA870B /* OCMObserverRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F51EF986FA63F62D8ABE46B5A0EF12E /* OCMObserverRecorder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 7AD8CDC8F994F9A30A70576930F1EF95 /* DarklyEventSource-iOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = BCF6BBBE88BA653337EDA896BDA90286 /* DarklyEventSource-iOS-dummy.m */; }; - 7B006E74668E41702B8881E724728DE9 /* OCMBlockCaller.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E025880705C042E53B065C6BF916CB5 /* OCMBlockCaller.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 7B3D75F6DB24A43FB02D2818D4864958 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 528B66A2BC53927BB64C91DB055F0D5D /* Foundation.framework */; }; - 7D94DE66B8B5E3B4A0B5E53A88C3FAEE /* OCMIndirectReturnValueProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = F0ECB43B373A36914B537E7B9B076175 /* OCMIndirectReturnValueProvider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 82558AE4441757075270A8A260B34EC9 /* OCObserverMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 588831B3C11013BFB9C663AE9D5DE56F /* OCObserverMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 86FF90618135B7C9032BC7075343F236 /* OCMBlockArgCaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 3054A0C1BA1392D2CFA6E4540AF0C3CF /* OCMBlockArgCaller.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 8EE2E0824E8AD382EC6553104A60430E /* OCMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 2AC64AA2A942523B078445719823594D /* OCMockObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6DEAD4CD0FF9467DA3FD3F22816418C6 /* OCMArgAction.h in Headers */ = {isa = PBXBuildFile; fileRef = E15BA3BA7678D2BB853AF7106C10E12C /* OCMArgAction.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6FA55259B9EA9D1852CD39C13B8F7DDB /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D2BC21DEA3ACE1E0185614B2CCC24DC /* Cocoa.framework */; }; + 71213523998EC4038EEF2881118A57A1 /* NSInvocation+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DB3E908E5778B6FED03EF1CAAA1298C5 /* NSInvocation+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 72C8CC2855787B9DC1A37DDEB5E24155 /* OCMFunctionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = BE2F248E5EE9A96D4804C2AFC12FD878 /* OCMFunctionsPrivate.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7302D41CB0CC8A04F2A872F9A9215060 /* NSArray+LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D1F1BDA14548B452E79BA387D85DFFF9 /* NSArray+LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 74BC62D6FEB722F4976016FD4DCA870B /* OCMObserverRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1723599B03030AB02D5CFFD698FA836A /* OCMObserverRecorder.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 764D2C2F0BD1E022E03FA591E7BE8448 /* LDEventStringAccumulator.h in Headers */ = {isa = PBXBuildFile; fileRef = A313E7D0C6F134B251DD75F2871B57D1 /* LDEventStringAccumulator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7948015575C406A1165344B9B8040970 /* LDEventStringAccumulator.h in Headers */ = {isa = PBXBuildFile; fileRef = A313E7D0C6F134B251DD75F2871B57D1 /* LDEventStringAccumulator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7B006E74668E41702B8881E724728DE9 /* OCMBlockCaller.m in Sources */ = {isa = PBXBuildFile; fileRef = AC1AA489B5E1706BC7BDA2BF73EE987D /* OCMBlockCaller.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 7B8249528D562F3778CAA304EBAF2E48 /* LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = BABC81EC3CA732F91FFAA9E8C91200E8 /* LDEventSource.m */; }; + 7D94DE66B8B5E3B4A0B5E53A88C3FAEE /* OCMIndirectReturnValueProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = A585C385272D6EB0580E2DD3C756C161 /* OCMIndirectReturnValueProvider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 7DF516CC95FFABF24F5B3F3AACAFA16C /* LDEventStringAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BD336159F272CBBE1150A6F6C01B8A8 /* LDEventStringAccumulator.m */; }; + 82558AE4441757075270A8A260B34EC9 /* OCObserverMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = EADBD9D42E2BA673C654C69A62A9A456 /* OCObserverMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 83AF9E0B3681FAC1077E0147AFEA4DC6 /* LDEventParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C225A59880C3017D042E0BEFF60A9520 /* LDEventParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 86FF90618135B7C9032BC7075343F236 /* OCMBlockArgCaller.h in Headers */ = {isa = PBXBuildFile; fileRef = 58A94BF01E22C379CEB90FDCD1FD0726 /* OCMBlockArgCaller.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 8B03DF7981B4BF251F279EAA48E7A442 /* LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = FBDDFF74F07A19E7D9FD71218146378B /* LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8EE2E0824E8AD382EC6553104A60430E /* OCMockObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 03186D06B7B71AE137317BC60A6CE121 /* OCMockObject.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8F30CAC67C96350CFF1E9722246D0E16 /* Pods-Darkly_osx-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FAD2DD8D645AEC80BFB0CCA170813C7 /* Pods-Darkly_osx-dummy.m */; }; - 905A8D955AE02C6B8D2DBBFD1FF33427 /* LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = AA49AFC7E73CAB7B63A97FC6963BD84B /* LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 915751C97068AF644D3FE4498633DAB1 /* OCMExceptionReturnValueProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DDCE1AC39485BC4C5E1642371CBC5BD /* OCMExceptionReturnValueProvider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 92F61FA0ACB1B709FFEBE1D9DC2C68E5 /* OCMPassByRefSetter.h in Headers */ = {isa = PBXBuildFile; fileRef = A28DA38325D7E02D324E31F1806EF191 /* OCMPassByRefSetter.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 95687EEBD9E7FCE1BFDBAF31A3397B1F /* DarklyEventSource-watchOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 05F3FA1F97CBD4005BEF2CF650BEBB66 /* DarklyEventSource-watchOS-dummy.m */; }; - 974362D040AC870A5D72E365B21A8CE4 /* OCMExceptionReturnValueProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 60700428BE87830C6BA702CCD3998F89 /* OCMExceptionReturnValueProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 9A92686CF21CC8ECAB69A3CFB621FA4B /* OCMock-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = EA932F95D294477A15F2195FAA819A89 /* OCMock-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9AD757E4C5E491AD307CF40DAAA81645 /* OHHTTPStubs+NSURLSessionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = B73316F0AB2ADA478403780E72C212FD /* OHHTTPStubs+NSURLSessionConfiguration.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 915751C97068AF644D3FE4498633DAB1 /* OCMExceptionReturnValueProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F24EA101E361D393A8F3A7BF72E9D05 /* OCMExceptionReturnValueProvider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + 92F61FA0ACB1B709FFEBE1D9DC2C68E5 /* OCMPassByRefSetter.h in Headers */ = {isa = PBXBuildFile; fileRef = F1B09AAA2C17F712826EF600D86DC883 /* OCMPassByRefSetter.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9362C21CB9FD8EED2DFE9CBC526D3468 /* NSArray+LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D1F1BDA14548B452E79BA387D85DFFF9 /* NSArray+LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 974362D040AC870A5D72E365B21A8CE4 /* OCMExceptionReturnValueProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E3A9B1DB35CBA2E6E3446BE49773A9 /* OCMExceptionReturnValueProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 975D664B5CE539645C2398648083A0BF /* NSString+LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F03D99AD09DAEA70CA34975D9B60C1 /* NSString+LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9A911861498FDE14AEC7BFC78272C2D1 /* LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = FBDDFF74F07A19E7D9FD71218146378B /* LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9A92686CF21CC8ECAB69A3CFB621FA4B /* OCMock-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 784CBECF8028CC4AC5B0B14D7E2CB0B9 /* OCMock-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9AD757E4C5E491AD307CF40DAAA81645 /* OHHTTPStubs+NSURLSessionConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = CA23B66702EE0C700A3FA460633677D1 /* OHHTTPStubs+NSURLSessionConfiguration.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + 9D4E7A33AAC08041F4C84C6CBFE481EE /* LDEventStringAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BD336159F272CBBE1150A6F6C01B8A8 /* LDEventStringAccumulator.m */; }; 9D540B1B53E9249DC4751A1403E0D1A5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACDF0E08285415092448B937524BBFBC /* Foundation.framework */; }; - A1268C3E1B318712F159EF222F19BD8B /* NSNotificationCenter+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = A4ECE4E53237844F4AE3D13B41379C45 /* NSNotificationCenter+OCMAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A4AF7E8EF9BB26F4CE7FD9EA32D4AD4C /* OCMMacroState.h in Headers */ = {isa = PBXBuildFile; fileRef = FF53F1EAB9D81A6DED2CC5DDC68998B5 /* OCMMacroState.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A4CD01540B9FCF8F26D52856704571D7 /* OCMReturnValueProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C3D6B768DE17A3E7C6D7B12C635477B /* OCMReturnValueProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; - A537068C9D2B7475B22CB50D9C44C76B /* LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = AA49AFC7E73CAB7B63A97FC6963BD84B /* LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A58AB2AAA4C1B2BEFEE21DC0EF2194C4 /* OHHTTPStubsResponse+JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = DC67523FE012358DE75EE4A668A5A664 /* OHHTTPStubsResponse+JSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A59AE6011C58D3D2F009DEF7F5AA259B /* OCMInvocationExpectation.m in Sources */ = {isa = PBXBuildFile; fileRef = 20086391A8D8523A58D226AAEA76DA06 /* OCMInvocationExpectation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - A9571E56544CD912F0DA62E545DB2419 /* OCMReturnValueProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = E9F043D9D4D153EF80F85834A3D728DE /* OCMReturnValueProvider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - A992DC3E5A593C9641D59707DBF9CF0E /* DarklyEventSource-iOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 08FF4A78E367C4F9B25AE240A893A6F4 /* DarklyEventSource-iOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9EEF57CBD78D08F318622E44C56AF195 /* LDEventStringAccumulator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BD336159F272CBBE1150A6F6C01B8A8 /* LDEventStringAccumulator.m */; }; + 9FD2E9450E00FC312D4848508887D0F0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACDF0E08285415092448B937524BBFBC /* Foundation.framework */; }; + A04AC375476D11A1389172A9299D7823 /* LDEventParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C50DAE28B8386CDA6B51492071510AE /* LDEventParser.m */; }; + A1268C3E1B318712F159EF222F19BD8B /* NSNotificationCenter+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = E99375BDAA9C578B2D765A92929C150D /* NSNotificationCenter+OCMAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A4AF7E8EF9BB26F4CE7FD9EA32D4AD4C /* OCMMacroState.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EBE790C1C098221D320D3E63EB2BC60 /* OCMMacroState.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A4CD01540B9FCF8F26D52856704571D7 /* OCMReturnValueProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = BDCB316A69C3F852581FF6CC53E64652 /* OCMReturnValueProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + A58AB2AAA4C1B2BEFEE21DC0EF2194C4 /* OHHTTPStubsResponse+JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = CC4AA6DBB4820C6C213B32F52ACC75F3 /* OHHTTPStubsResponse+JSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A59AE6011C58D3D2F009DEF7F5AA259B /* OCMInvocationExpectation.m in Sources */ = {isa = PBXBuildFile; fileRef = A89DB22242D0EA1FE21D35BEE5FED120 /* OCMInvocationExpectation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + A634F7839279355FC428FBC9F8EAE69F /* LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = BABC81EC3CA732F91FFAA9E8C91200E8 /* LDEventSource.m */; }; + A92799D357ECEF8522109331DDB1E776 /* NSArray+LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = D1F1BDA14548B452E79BA387D85DFFF9 /* NSArray+LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A9571E56544CD912F0DA62E545DB2419 /* OCMReturnValueProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B372630F618271420D4D0488EB06082 /* OCMReturnValueProvider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + AB04782D2FF294BAA37A65D874BE2D62 /* LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = FBDDFF74F07A19E7D9FD71218146378B /* LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; ABC47FD397187DA12E4CB4C46F125957 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 528B66A2BC53927BB64C91DB055F0D5D /* Foundation.framework */; }; - AF8EFDAD5DAF96A49EE0BC9A948280F3 /* DarklyEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D5DD619EF039D6A394E253938F85A12 /* DarklyEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B1960493BE31653F99CCAE64D1AAC7E0 /* OHPathHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = A9CF35EF466BC4B915F48EF27C1846AB /* OHPathHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B200AEB2373802A1B871987A18D08F75 /* OCMConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BD1F6749A3FA26D87844207325DE6A1 /* OCMConstraint.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - B33B6DFAA43992C898AF6D4F1C539A8A /* OCMInvocationStub.m in Sources */ = {isa = PBXBuildFile; fileRef = A252F33D314FA7E7ECBDF0DF9A105B26 /* OCMInvocationStub.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - B3E6AA126A971418B57B78A6B4E753E3 /* OCMInvocationExpectation.h in Headers */ = {isa = PBXBuildFile; fileRef = EEF775C9BCEB8619F891533C3F4D5442 /* OCMInvocationExpectation.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B40D8DAB504B721D2BC6A6DDFDD82067 /* OCMConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 46D954933A68BCA132BA80E0347290D5 /* OCMConstraint.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B42B8EFE65F8B07957190A15F590093A /* OCMInvocationMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 159BA483E378F5A628582FF68CE1783E /* OCMInvocationMatcher.h */; settings = {ATTRIBUTES = (Project, ); }; }; - B66FB06824BBED66619F17DB82CC2A64 /* OCMLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = EAD198096F351A1A1FBAFD2D6B8FF082 /* OCMLocation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ACF5BA5D185CFEDD532E64EC32492949 /* DarklyEventSource-macOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = CB4C1F3AA83AE758A49096AAA2C34AF1 /* DarklyEventSource-macOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AD216A93F5B1E3984DB3E04AE877A797 /* NSString+LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 18969F2E878C55DACB8FC8858D2C5100 /* NSString+LDEventSource.m */; }; + B1960493BE31653F99CCAE64D1AAC7E0 /* OHPathHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 01D31133F0D3AEF0B3A66C21F9DFAE2B /* OHPathHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B200AEB2373802A1B871987A18D08F75 /* OCMConstraint.m in Sources */ = {isa = PBXBuildFile; fileRef = E9E985CC46AF6BFB841D0106DF8831D9 /* OCMConstraint.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + B33B6DFAA43992C898AF6D4F1C539A8A /* OCMInvocationStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 17E0CF03259C28A0039DB6847945CD9D /* OCMInvocationStub.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + B3E6AA126A971418B57B78A6B4E753E3 /* OCMInvocationExpectation.h in Headers */ = {isa = PBXBuildFile; fileRef = C272E53CB64FE49AF2832CB69221D896 /* OCMInvocationExpectation.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B40D8DAB504B721D2BC6A6DDFDD82067 /* OCMConstraint.h in Headers */ = {isa = PBXBuildFile; fileRef = C1753C4E5A7538431EFC5243A309A458 /* OCMConstraint.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B42B8EFE65F8B07957190A15F590093A /* OCMInvocationMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = EC2E8A1FEB1BD595CEC105DF1FC6D551 /* OCMInvocationMatcher.h */; settings = {ATTRIBUTES = (Project, ); }; }; + B66FB06824BBED66619F17DB82CC2A64 /* OCMLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = EA636CDA22A32949D3F7E1D3CD5DDBC4 /* OCMLocation.h */; settings = {ATTRIBUTES = (Public, ); }; }; B677137A7B135394B0C7185E9D3D91A9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FBFEAC80AB2E00CFC60743201EA79E8E /* Foundation.framework */; }; - B852DBE3593443215A94EEFD59786402 /* NSObject+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 02335183C4ABC789319BEC9F361F888E /* NSObject+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - BD181F7E8C0BB325B051DA9F2FBD71BC /* NSMethodSignature+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DFF803BFA99780D0C3AFFBD95CBA6BF /* NSMethodSignature+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - BE82F80E7D6D501CF46925D6E25B15E1 /* DarklyEventSource-watchOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 551E4D0696B6AAC86FC5B613618BF934 /* DarklyEventSource-watchOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C12846B090288AFA42CEA099276F8437 /* DarklyEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D5DD619EF039D6A394E253938F85A12 /* DarklyEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B852DBE3593443215A94EEFD59786402 /* NSObject+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D28BAC6EEBBFD2F187B880C2293F3A9 /* NSObject+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + BD181F7E8C0BB325B051DA9F2FBD71BC /* NSMethodSignature+OCMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 66406B161168717D6C0C3F5C10922505 /* NSMethodSignature+OCMAdditions.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; C390FE838362EBE94E5A5052CBF6196F /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D922F2D72779A1967524BBBAECEF3120 /* CFNetwork.framework */; }; - C65A28088F0668711386816290C3880E /* OCMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F1B9C604582097FDE56BFE0E5DBD7D8 /* OCMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - C744661BA7AA467154A805FDC2C6B63C /* DarklyEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D5DD619EF039D6A394E253938F85A12 /* DarklyEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CF451EAA94004B370612757B61B46D2D /* OCMArgAction.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DD72F9E4A9CF0C4E710F30B8C337243 /* OCMArgAction.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - CFFD80CB651FDC49953FB5D8672038AC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACDF0E08285415092448B937524BBFBC /* Foundation.framework */; }; - D9C7A57A574090442C8631BA856B1599 /* OCMMacroState.m in Sources */ = {isa = PBXBuildFile; fileRef = 69AC1833240D28BD3D8461ED1935EDB1 /* OCMMacroState.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - E14A5FC189CD75054825E45DA6D9F293 /* OCMBoxedReturnValueProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 83C7FB0444707E5FEEB30DACDFD164A0 /* OCMBoxedReturnValueProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; - E17BB96FF8264DADDCAE9B5932C94BF3 /* DarklyEventSource-macOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = AF29C88DAD0388B63CC569E3BF29E5E5 /* DarklyEventSource-macOS-dummy.m */; }; + C65A28088F0668711386816290C3880E /* OCMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = ECD1DF4E7C8DB57E5E4BE48FDDAF3091 /* OCMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + C8040A454AC476D244C119D9209BE885 /* NSString+LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = E1F03D99AD09DAEA70CA34975D9B60C1 /* NSString+LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CD8E222FA718C250BF148F4AEDB2D830 /* NSString+LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 18969F2E878C55DACB8FC8858D2C5100 /* NSString+LDEventSource.m */; }; + CE514D77DA72D78D9C132E139DBFDF65 /* NSString+LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 18969F2E878C55DACB8FC8858D2C5100 /* NSString+LDEventSource.m */; }; + CEE265209CB926C5500EA652FC50D71C /* NSArray+LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 663B2E9A93F8EE36BF7D622EBE9F5D64 /* NSArray+LDEventSource.m */; }; + CF451EAA94004B370612757B61B46D2D /* OCMArgAction.m in Sources */ = {isa = PBXBuildFile; fileRef = B0E8EB42A65A20CFE56B5F7703728D4D /* OCMArgAction.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + D25C35F5B0196C1AFFAE6E77FC03BF7C /* DarklyEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E6AA2AC1F3A30B8D20CE8B588A25F8 /* DarklyEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D492EBF6E985107A62B877B98B22DCA9 /* LDEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = FBDDFF74F07A19E7D9FD71218146378B /* LDEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D8E669F4646F84CAB8F6A945E11085A2 /* DarklyEventSource-tvOS-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B8936874B33B59D837A5068B80AE063A /* DarklyEventSource-tvOS-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D9C7A57A574090442C8631BA856B1599 /* OCMMacroState.m in Sources */ = {isa = PBXBuildFile; fileRef = 231E38E10643D323C59757FCA0F1996C /* OCMMacroState.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + E14A5FC189CD75054825E45DA6D9F293 /* OCMBoxedReturnValueProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = E8A2F29D2CBBFD05E8C0957B415BD244 /* OCMBoxedReturnValueProvider.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E259E475E1415923B893619ACB07EBE3 /* NSString+LDEventSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 18969F2E878C55DACB8FC8858D2C5100 /* NSString+LDEventSource.m */; }; E3D64C1E36A44947D6B7B2A63224C5AC /* Pods-DarklyTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BED7B9A687B9027AC6065453990C128 /* Pods-DarklyTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E4070427AD7F08E573CAA22DCEB79FB1 /* OCProtocolMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312591EA5F95D2A26205C563B9FC872 /* OCProtocolMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - E4A87D8BA9BA24EE81DEC41C8BE98F2B /* OCMVerifier.h in Headers */ = {isa = PBXBuildFile; fileRef = E736DD7FFDD3BE1C73A56D0CCAE7AD54 /* OCMVerifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; - E61FEAB563814E928FFF2C1EF239058A /* NSObject+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = D6B17CFA609D4F1BCEB6CD952F6BDF8D /* NSObject+OCMAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; - E6F1B20197D630B80285B5F6E5FAE80F /* OCMRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 66AB82BF52B8DE62BC2A72C2592E23F1 /* OCMRecorder.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EC33739AB54F71355C4FD5AAEF92F4B1 /* OCMStubRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B0192087B27BECEC0F7E1799558045 /* OCMStubRecorder.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EFC1318446EB79DC091867000F45D61C /* OCMLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 786D1550089C847D816C575010098672 /* OCMLocation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - F02C2CD0A94965FFA8928B316D5825CB /* OCMArg.h in Headers */ = {isa = PBXBuildFile; fileRef = ECCFC10038A1B1EDC39E7F558EBF050B /* OCMArg.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E4070427AD7F08E573CAA22DCEB79FB1 /* OCProtocolMockObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 6CAA365947ED1983438715063E164BCE /* OCProtocolMockObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + E4A87D8BA9BA24EE81DEC41C8BE98F2B /* OCMVerifier.h in Headers */ = {isa = PBXBuildFile; fileRef = D298344BF37A3B7C5700A48D47C8C21E /* OCMVerifier.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E61FEAB563814E928FFF2C1EF239058A /* NSObject+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9FD8121492A6334FDA00330D0DA407E3 /* NSObject+OCMAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + E6F1B20197D630B80285B5F6E5FAE80F /* OCMRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 0599C4609A3264BE0A6DA170D4520C40 /* OCMRecorder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E95D294A9543055B453867A057AD02A8 /* LDEventParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C50DAE28B8386CDA6B51492071510AE /* LDEventParser.m */; }; + EC33739AB54F71355C4FD5AAEF92F4B1 /* OCMStubRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 41B77BDCF056039A9C354161666FCED7 /* OCMStubRecorder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EFC1318446EB79DC091867000F45D61C /* OCMLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = CE92382320DDEE9158D74990E3956AE2 /* OCMLocation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + F02C2CD0A94965FFA8928B316D5825CB /* OCMArg.h in Headers */ = {isa = PBXBuildFile; fileRef = D7872C42E57DED130958BF19CB6F53E7 /* OCMArg.h */; settings = {ATTRIBUTES = (Public, ); }; }; F03D3AA4304C5670F89855FFBEC8B652 /* Pods-DarklyTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 03F404D6B4A1A641E945F9142955FF2C /* Pods-DarklyTests-dummy.m */; }; - F22E6173EA55588F0007F80E936A13F5 /* DarklyEventSource-tvOS-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D94DD2D5D1140DF93748DB6D561CB1F /* DarklyEventSource-tvOS-dummy.m */; }; - F90421F35E91B0A952234BEF474030B6 /* DarklyEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D5DD619EF039D6A394E253938F85A12 /* DarklyEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F98852F58D5FDF0191647087678ABE70 /* OCMArg.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C457218CF6FB6FB940AF05DA169610 /* OCMArg.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - FAB7A1E7E1414DC657BA79D07135E15B /* OCMock.h in Headers */ = {isa = PBXBuildFile; fileRef = E7EEA70FD196A9350783D5F26267EA36 /* OCMock.h */; settings = {ATTRIBUTES = (Public, ); }; }; - FBD596B095C58C7A3AA647CAB7802226 /* NSInvocation+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CA0B8765C7CF08D814754C0A0F8C1203 /* NSInvocation+OCMAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; - FF8020F2FB983454BCF191099619A719 /* OHHTTPStubsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DDB0218B64211CC03C9DC1FF617998F /* OHHTTPStubsResponse.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; + F14B9BCDC8C388965C9F2EA4CBEFC332 /* DarklyEventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 35E6AA2AC1F3A30B8D20CE8B588A25F8 /* DarklyEventSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F58B2C0727FA65E03CF6E16F35732B36 /* LDEventStringAccumulator.h in Headers */ = {isa = PBXBuildFile; fileRef = A313E7D0C6F134B251DD75F2871B57D1 /* LDEventStringAccumulator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F98852F58D5FDF0191647087678ABE70 /* OCMArg.m in Sources */ = {isa = PBXBuildFile; fileRef = 67467940FD2C95EC4D20DCABECABCD81 /* OCMArg.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; + F9DACB363D43317F77E29E6EA9EB0C0F /* LDEventParser.h in Headers */ = {isa = PBXBuildFile; fileRef = C225A59880C3017D042E0BEFF60A9520 /* LDEventParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FAB7A1E7E1414DC657BA79D07135E15B /* OCMock.h in Headers */ = {isa = PBXBuildFile; fileRef = 090B89A18D929EA6CEFD35331DFFBA55 /* OCMock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FBD596B095C58C7A3AA647CAB7802226 /* NSInvocation+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = E7C2A9E6F1B39A2F18618A62B47E88A3 /* NSInvocation+OCMAdditions.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FF8020F2FB983454BCF191099619A719 /* OHHTTPStubsResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B74E3BE6886528BD7236293763EC298 /* OHHTTPStubsResponse.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -138,7 +170,7 @@ isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; - remoteGlobalIDString = 8E061107B742D8EF4D21D5B1ACD3767D; + remoteGlobalIDString = AC7A2FCACC2CADFC962A30DC28D71677; remoteInfo = "DarklyEventSource-watchOS"; }; 931A8F613389AC37161850C9B7EDC8EA /* PBXContainerItemProxy */ = { @@ -152,7 +184,7 @@ isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; - remoteGlobalIDString = 23353DF7BDC41C3D6012FB12E34C8BC2; + remoteGlobalIDString = 68D1B6C8D28C8A9C0D8496A5D39F2BB2; remoteInfo = "DarklyEventSource-macOS"; }; 970CC720C0AA52821CF4721542E89119 /* PBXContainerItemProxy */ = { @@ -166,209 +198,225 @@ isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; - remoteGlobalIDString = DEDCD49950C45F16C07E6D98D2C96B27; + remoteGlobalIDString = 4CB4D69CF5144447B5EDA7EEB0CF8FB2; remoteInfo = "DarklyEventSource-iOS"; }; EAB2DE532572D85409AA6D1ED76A00CB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; - remoteGlobalIDString = DEDCD49950C45F16C07E6D98D2C96B27; + remoteGlobalIDString = 4CB4D69CF5144447B5EDA7EEB0CF8FB2; remoteInfo = "DarklyEventSource-iOS"; }; FFA32DB3EB1843333366A8DC0D3F02BD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; proxyType = 1; - remoteGlobalIDString = 71346A8B51F5CEE42752F393F365D76C; + remoteGlobalIDString = 5BEC0EEA943679FEADECF05233863088; remoteInfo = "DarklyEventSource-tvOS"; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 01C36B32B951D33CF0C2806C2DA582DA /* OCMock.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = OCMock.xcconfig; sourceTree = ""; }; + 003AB641624DD367808954F05509B17A /* OCMIndirectReturnValueProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMIndirectReturnValueProvider.h; path = Source/OCMock/OCMIndirectReturnValueProvider.h; sourceTree = ""; }; + 00C177350037FC007B07D98EFBFC21D3 /* OHHTTPStubsResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OHHTTPStubsResponse.h; path = OHHTTPStubs/Sources/OHHTTPStubsResponse.h; sourceTree = ""; }; + 01D31133F0D3AEF0B3A66C21F9DFAE2B /* OHPathHelpers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OHPathHelpers.h; path = OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h; sourceTree = ""; }; 01FCE6E495553E209A455B8F5412E9D1 /* DarklyEventSource.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = DarklyEventSource.framework; path = "DarklyEventSource-watchOS.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - 02335183C4ABC789319BEC9F361F888E /* NSObject+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+OCMAdditions.m"; path = "Source/OCMock/NSObject+OCMAdditions.m"; sourceTree = ""; }; 02A12773C854E2AC002B99024C80969C /* Pods-Darkly_iOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Darkly_iOS.modulemap"; sourceTree = ""; }; 0310327CC32E5CFEBF1C0877E44C9538 /* Pods-Darkly_tvOS-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Darkly_tvOS-resources.sh"; sourceTree = ""; }; + 03186D06B7B71AE137317BC60A6CE121 /* OCMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMockObject.h; path = Source/OCMock/OCMockObject.h; sourceTree = ""; }; 03F404D6B4A1A641E945F9142955FF2C /* Pods-DarklyTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-DarklyTests-dummy.m"; sourceTree = ""; }; - 05F3FA1F97CBD4005BEF2CF650BEBB66 /* DarklyEventSource-watchOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DarklyEventSource-watchOS-dummy.m"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS-dummy.m"; sourceTree = ""; }; - 08FF4A78E367C4F9B25AE240A893A6F4 /* DarklyEventSource-iOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DarklyEventSource-iOS-umbrella.h"; sourceTree = ""; }; - 0A0CD449DFE4111D9DA40063F59D0F1C /* OHHTTPStubs-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OHHTTPStubs-umbrella.h"; sourceTree = ""; }; + 05839ABAB9A0D60510F4B5F84D0E89CD /* DarklyEventSource-macOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; name = "DarklyEventSource-macOS.modulemap"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS.modulemap"; sourceTree = ""; }; + 0599C4609A3264BE0A6DA170D4520C40 /* OCMRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMRecorder.h; path = Source/OCMock/OCMRecorder.h; sourceTree = ""; }; + 090B89A18D929EA6CEFD35331DFFBA55 /* OCMock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMock.h; path = Source/OCMock/OCMock.h; sourceTree = ""; }; 0B126A552AA300B3CBFE87ED55490A98 /* Pods-Darkly_iOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Darkly_iOS-umbrella.h"; sourceTree = ""; }; - 0C6A85CE7D53722B586097905D107537 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../DarklyEventSource-macOS/Info.plist"; sourceTree = ""; }; 0CC4F03DA9E9B1D48355E1E15BC481D8 /* Pods-Darkly_tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Darkly_tvOS.release.xcconfig"; sourceTree = ""; }; - 0CDDBBF20B9E8B60CCF52A35F23C2768 /* OCMArgAction.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMArgAction.h; path = Source/OCMock/OCMArgAction.h; sourceTree = ""; }; 0D21F47030DCB4AEC9D1276839F85FCC /* Pods-Darkly_tvOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Darkly_tvOS.modulemap"; sourceTree = ""; }; - 0DDCE1AC39485BC4C5E1642371CBC5BD /* OCMExceptionReturnValueProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMExceptionReturnValueProvider.m; path = Source/OCMock/OCMExceptionReturnValueProvider.m; sourceTree = ""; }; - 0E025880705C042E53B065C6BF916CB5 /* OCMBlockCaller.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMBlockCaller.m; path = Source/OCMock/OCMBlockCaller.m; sourceTree = ""; }; 0EA749016C80373C93C7C8E1F737F85C /* Pods-Darkly_tvOS-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Darkly_tvOS-acknowledgements.markdown"; sourceTree = ""; }; - 0FA9BAC5F5AA8C7555A63F5E968C150C /* NSMethodSignature+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSMethodSignature+OCMAdditions.h"; path = "Source/OCMock/NSMethodSignature+OCMAdditions.h"; sourceTree = ""; }; + 0EBE790C1C098221D320D3E63EB2BC60 /* OCMMacroState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMMacroState.h; path = Source/OCMock/OCMMacroState.h; sourceTree = ""; }; + 0F24EA101E361D393A8F3A7BF72E9D05 /* OCMExceptionReturnValueProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMExceptionReturnValueProvider.m; path = Source/OCMock/OCMExceptionReturnValueProvider.m; sourceTree = ""; }; 10E6CA7BE641DB9C1C9C64943117D68D /* Pods-Darkly_watchOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Darkly_watchOS.modulemap"; sourceTree = ""; }; 11643A7BBC7D3EA58E5686D588FC3AB0 /* Pods-Darkly_osx.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Darkly_osx.modulemap"; sourceTree = ""; }; - 1541B7DBA1AD0A7B65F8FFC607CBD5B5 /* OCMExpectationRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMExpectationRecorder.h; path = Source/OCMock/OCMExpectationRecorder.h; sourceTree = ""; }; - 159BA483E378F5A628582FF68CE1783E /* OCMInvocationMatcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMInvocationMatcher.h; path = Source/OCMock/OCMInvocationMatcher.h; sourceTree = ""; }; - 18C457218CF6FB6FB940AF05DA169610 /* OCMArg.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMArg.m; path = Source/OCMock/OCMArg.m; sourceTree = ""; }; + 13EF633664B9DF304A8EF579DC80553C /* OHHTTPStubs.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = OHHTTPStubs.xcconfig; sourceTree = ""; }; + 1705667676E63E509AFEFFECAD13126D /* OCMock-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OCMock-prefix.pch"; sourceTree = ""; }; + 1723599B03030AB02D5CFFD698FA836A /* OCMObserverRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMObserverRecorder.m; path = Source/OCMock/OCMObserverRecorder.m; sourceTree = ""; }; + 17E0CF03259C28A0039DB6847945CD9D /* OCMInvocationStub.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMInvocationStub.m; path = Source/OCMock/OCMInvocationStub.m; sourceTree = ""; }; + 17F5930E82A640BA89F9A60B548EE79C /* OHHTTPStubsResponse+JSON.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubsResponse+JSON.m"; path = "OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m"; sourceTree = ""; }; + 18969F2E878C55DACB8FC8858D2C5100 /* NSString+LDEventSource.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSString+LDEventSource.m"; path = "LDEventSource/NSString+LDEventSource.m"; sourceTree = ""; }; + 19753520BE3CA6C03AA17595F381533A /* OCMExpectationRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMExpectationRecorder.m; path = Source/OCMock/OCMExpectationRecorder.m; sourceTree = ""; }; + 1A684FF78260E2892869AB05307F8A12 /* DarklyEventSource-iOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DarklyEventSource-iOS-umbrella.h"; sourceTree = ""; }; 1A6F8B27825D9B1C2F2366113443D259 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 1AFC0F519CA2ABEEF80E7347EF34382C /* OCObserverMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCObserverMockObject.h; path = Source/OCMock/OCObserverMockObject.h; sourceTree = ""; }; - 1DD72F9E4A9CF0C4E710F30B8C337243 /* OCMArgAction.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMArgAction.m; path = Source/OCMock/OCMArgAction.m; sourceTree = ""; }; + 1A97493D9FBA59B4575383AB9A15B287 /* OCMRealObjectForwarder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMRealObjectForwarder.h; path = Source/OCMock/OCMRealObjectForwarder.h; sourceTree = ""; }; + 1B372630F618271420D4D0488EB06082 /* OCMReturnValueProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMReturnValueProvider.m; path = Source/OCMock/OCMReturnValueProvider.m; sourceTree = ""; }; + 1CC7AD574057A73DA9297797A75CEFBB /* OCPartialMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCPartialMockObject.h; path = Source/OCMock/OCPartialMockObject.h; sourceTree = ""; }; + 1CF3530E70708E5FE428B724474D2232 /* OHHTTPStubs.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = OHHTTPStubs.modulemap; sourceTree = ""; }; 1E62CEE1415BE0C2ADEFC8211D871406 /* Pods-Darkly_osx-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Darkly_osx-acknowledgements.markdown"; sourceTree = ""; }; 1E82F65521609344B4ACE5A0E0626C98 /* Pods_Darkly_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Darkly_iOS.framework; path = "Pods-Darkly_iOS.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - 20086391A8D8523A58D226AAEA76DA06 /* OCMInvocationExpectation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMInvocationExpectation.m; path = Source/OCMock/OCMInvocationExpectation.m; sourceTree = ""; }; - 22DBC6DB853E340179B4940B05979383 /* OHHTTPStubsResponse+JSON.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubsResponse+JSON.m"; path = "OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m"; sourceTree = ""; }; + 1F99E36BBF1EB4E987E3359B92F7DD07 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../DarklyEventSource-tvOS/Info.plist"; sourceTree = ""; }; + 231E38E10643D323C59757FCA0F1996C /* OCMMacroState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMMacroState.m; path = Source/OCMock/OCMMacroState.m; sourceTree = ""; }; + 23A9B1BAFAFED46EF4C338455C3593D2 /* OCClassMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCClassMockObject.h; path = Source/OCMock/OCClassMockObject.h; sourceTree = ""; }; 243F720FA9C04B43254EEA98400F7B1A /* Pods-Darkly_iOS-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Darkly_iOS-acknowledgements.plist"; sourceTree = ""; }; 24B720F4F66D5399540949C2350A7494 /* OCMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = OCMock.framework; path = OCMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 258EB08B29B8B6225E55EF8CFD05B229 /* OCMInvocationStub.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMInvocationStub.h; path = Source/OCMock/OCMInvocationStub.h; sourceTree = ""; }; - 27467853512D2003F1639D1425ABA364 /* LDEventSource.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = LDEventSource.m; path = LDEventSource/LDEventSource.m; sourceTree = ""; }; + 256289EC71769D11C26C75322CA9E14E /* OCMBoxedReturnValueProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMBoxedReturnValueProvider.m; path = Source/OCMock/OCMBoxedReturnValueProvider.m; sourceTree = ""; }; 2A72D12FAF4CA6C4705065580B766EEF /* Pods-Darkly_watchOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Darkly_watchOS-dummy.m"; sourceTree = ""; }; - 2AC64AA2A942523B078445719823594D /* OCMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMockObject.h; path = Source/OCMock/OCMockObject.h; sourceTree = ""; }; - 2CA2FFC0331CABB7FE9E51E64057D4F1 /* DarklyEventSource-tvOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-tvOS-umbrella.h"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS-umbrella.h"; sourceTree = ""; }; - 2E208BFC2495D241F777F565DF350CC8 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../DarklyEventSource-tvOS/Info.plist"; sourceTree = ""; }; 2F1C676241DB03C6F2DF34AE5F08222E /* Pods-Darkly_watchOS-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Darkly_watchOS-acknowledgements.plist"; sourceTree = ""; }; + 2F7263EB5FDBA9374A691926217D7C8E /* OHHTTPStubs.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OHHTTPStubs.h; path = OHHTTPStubs/Sources/OHHTTPStubs.h; sourceTree = ""; }; 2FA394D19683E365635D4F0610E24199 /* Pods-Darkly_tvOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Darkly_tvOS-dummy.m"; sourceTree = ""; }; - 3054A0C1BA1392D2CFA6E4540AF0C3CF /* OCMBlockArgCaller.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMBlockArgCaller.h; path = Source/OCMock/OCMBlockArgCaller.h; sourceTree = ""; }; + 310F89F7AFD4AD6A0ACF5D36DE44757B /* OCProtocolMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCProtocolMockObject.h; path = Source/OCMock/OCProtocolMockObject.h; sourceTree = ""; }; 3236760AC617A093E12C2210F6458195 /* Pods-Darkly_osx.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Darkly_osx.debug.xcconfig"; sourceTree = ""; }; - 36EF2A58593920A528EB346E42F18C19 /* Compatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Compatibility.h; path = OHHTTPStubs/Sources/Compatibility.h; sourceTree = ""; }; + 35E6AA2AC1F3A30B8D20CE8B588A25F8 /* DarklyEventSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DarklyEventSource.h; path = LDEventSource/DarklyEventSource.h; sourceTree = ""; }; + 37E3A9B1DB35CBA2E6E3446BE49773A9 /* OCMExceptionReturnValueProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMExceptionReturnValueProvider.h; path = Source/OCMock/OCMExceptionReturnValueProvider.h; sourceTree = ""; }; 3A0D5E3E736C8A12D2EDE8B6AD45E4A5 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3B35EB7B4AF09B17AF04C4C3910387E0 /* Pods-DarklyTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-DarklyTests-acknowledgements.plist"; sourceTree = ""; }; - 3B6647FF28FFA097E8F2269CFD4713C8 /* OHPathHelpers.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OHPathHelpers.m; path = OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m; sourceTree = ""; }; + 3BD336159F272CBBE1150A6F6C01B8A8 /* LDEventStringAccumulator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = LDEventStringAccumulator.m; path = LDEventSource/LDEventStringAccumulator.m; sourceTree = ""; }; + 3C50DAE28B8386CDA6B51492071510AE /* LDEventParser.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = LDEventParser.m; path = LDEventSource/LDEventParser.m; sourceTree = ""; }; + 3D28BAC6EEBBFD2F187B880C2293F3A9 /* NSObject+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSObject+OCMAdditions.m"; path = "Source/OCMock/NSObject+OCMAdditions.m"; sourceTree = ""; }; + 3F9F37AD5F918763317FD22F67056E6D /* OHHTTPStubs-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OHHTTPStubs-umbrella.h"; sourceTree = ""; }; 417330A959441E6EE371892AD0DD3B4F /* Pods-DarklyTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-DarklyTests-frameworks.sh"; sourceTree = ""; }; - 44E13D1AB2457923E89EDABB9FE7D5C5 /* NSValue+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSValue+OCMAdditions.h"; path = "Source/OCMock/NSValue+OCMAdditions.h"; sourceTree = ""; }; - 45C88E7B8B282A7217B488B0529A4485 /* OCMFunctions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMFunctions.h; path = Source/OCMock/OCMFunctions.h; sourceTree = ""; }; - 463DD1D3C40C8339739DF808CF0C84D2 /* OCMIndirectReturnValueProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMIndirectReturnValueProvider.h; path = Source/OCMock/OCMIndirectReturnValueProvider.h; sourceTree = ""; }; - 46D954933A68BCA132BA80E0347290D5 /* OCMConstraint.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMConstraint.h; path = Source/OCMock/OCMConstraint.h; sourceTree = ""; }; + 41B77BDCF056039A9C354161666FCED7 /* OCMStubRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMStubRecorder.h; path = Source/OCMock/OCMStubRecorder.h; sourceTree = ""; }; + 434B5DF5224FEDADE75A5E689D146E90 /* OCMBlockCaller.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMBlockCaller.h; path = Source/OCMock/OCMBlockCaller.h; sourceTree = ""; }; 4781232EDEA682C534BE3C7F024AACFA /* Pods-Darkly_watchOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Darkly_watchOS-umbrella.h"; sourceTree = ""; }; + 47B73DDE334F6D4A01A21D8194861C85 /* OCMock-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "OCMock-dummy.m"; sourceTree = ""; }; 4806A26F6F06A60EDC216E00AD3F6069 /* DarklyEventSource.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = DarklyEventSource.framework; path = "DarklyEventSource-macOS.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - 49DBE0583AD97FC7AFD40E44B6ADD0C3 /* OCMNotificationPoster.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMNotificationPoster.m; path = Source/OCMock/OCMNotificationPoster.m; sourceTree = ""; }; - 4DFF803BFA99780D0C3AFFBD95CBA6BF /* NSMethodSignature+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSMethodSignature+OCMAdditions.m"; path = "Source/OCMock/NSMethodSignature+OCMAdditions.m"; sourceTree = ""; }; - 4FBF42418B3DBA6C8DFFF85F037DBDFF /* OCMRealObjectForwarder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMRealObjectForwarder.h; path = Source/OCMock/OCMRealObjectForwarder.h; sourceTree = ""; }; - 518214EDCE7B01015C2BB6271BB772FD /* OCMBlockArgCaller.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMBlockArgCaller.m; path = Source/OCMock/OCMBlockArgCaller.m; sourceTree = ""; }; - 525390A8E371A083C74C84FA87DA1B8C /* OCMock-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "OCMock-dummy.m"; sourceTree = ""; }; + 4927BE6EEC04B10C75C1FA438914CB8E /* OCMVerifier.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMVerifier.m; path = Source/OCMock/OCMVerifier.m; sourceTree = ""; }; + 4A5EF931F06BF1C1F76D5CE2C1BBC704 /* OCMRealObjectForwarder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMRealObjectForwarder.m; path = Source/OCMock/OCMRealObjectForwarder.m; sourceTree = ""; }; + 507069B5285158B9E9B93D9F5B2456FB /* OCPartialMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCPartialMockObject.m; path = Source/OCMock/OCPartialMockObject.m; sourceTree = ""; }; 528B66A2BC53927BB64C91DB055F0D5D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.2.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - 52DECA100A517BF5699E9107D127C358 /* OCMNotificationPoster.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMNotificationPoster.h; path = Source/OCMock/OCMNotificationPoster.h; sourceTree = ""; }; - 53C639006DD65A1AB87E8294614015A8 /* DarklyEventSource-watchOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; name = "DarklyEventSource-watchOS.modulemap"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS.modulemap"; sourceTree = ""; }; - 551E4D0696B6AAC86FC5B613618BF934 /* DarklyEventSource-watchOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-watchOS-umbrella.h"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS-umbrella.h"; sourceTree = ""; }; 555FD603D5365F6C3C41E35AC29D8B8F /* Pods_DarklyTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_DarklyTests.framework; path = "Pods-DarklyTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 55B5741CCC432EC8DCFD5BE5F72FBB9F /* OCMRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMRecorder.m; path = Source/OCMock/OCMRecorder.m; sourceTree = ""; }; 5837689E0B0D2E7B94854C9C6D88E63F /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 588831B3C11013BFB9C663AE9D5DE56F /* OCObserverMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCObserverMockObject.m; path = Source/OCMock/OCObserverMockObject.m; sourceTree = ""; }; - 5AE7493F94D2FBC5B5F66688C6D9F366 /* DarklyEventSource-watchOS.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "DarklyEventSource-watchOS.xcconfig"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS.xcconfig"; sourceTree = ""; }; + 58A94BF01E22C379CEB90FDCD1FD0726 /* OCMBlockArgCaller.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMBlockArgCaller.h; path = Source/OCMock/OCMBlockArgCaller.h; sourceTree = ""; }; 5BFE84161C4B878A418F595FF3B30019 /* Pods-Darkly_osx-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Darkly_osx-acknowledgements.plist"; sourceTree = ""; }; - 5C3D6B768DE17A3E7C6D7B12C635477B /* OCMReturnValueProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMReturnValueProvider.h; path = Source/OCMock/OCMReturnValueProvider.h; sourceTree = ""; }; - 5D02AD8B359F811CAEC7E5C2A272E5DE /* OCMPassByRefSetter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMPassByRefSetter.m; path = Source/OCMock/OCMPassByRefSetter.m; sourceTree = ""; }; - 5DDB0218B64211CC03C9DC1FF617998F /* OHHTTPStubsResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OHHTTPStubsResponse.m; path = OHHTTPStubs/Sources/OHHTTPStubsResponse.m; sourceTree = ""; }; - 5E0A94E4BDADF3C941DD3C42FE7CBEEA /* DarklyEventSource-macOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; name = "DarklyEventSource-macOS.modulemap"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS.modulemap"; sourceTree = ""; }; - 60700428BE87830C6BA702CCD3998F89 /* OCMExceptionReturnValueProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMExceptionReturnValueProvider.h; path = Source/OCMock/OCMExceptionReturnValueProvider.h; sourceTree = ""; }; - 6312591EA5F95D2A26205C563B9FC872 /* OCProtocolMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCProtocolMockObject.m; path = Source/OCMock/OCProtocolMockObject.m; sourceTree = ""; }; + 5CD52D204894A6629ECD216D949E2AE2 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6352107EC0A782D74D3BF44A85D3A476 /* OCMPassByRefSetter.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMPassByRefSetter.m; path = Source/OCMock/OCMPassByRefSetter.m; sourceTree = ""; }; + 635E8F6D068B917C59F86E7C9038EC04 /* NSValue+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSValue+OCMAdditions.m"; path = "Source/OCMock/NSValue+OCMAdditions.m"; sourceTree = ""; }; 636037A70C735E8AB1796925A1F3B75C /* Pods-DarklyTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DarklyTests.debug.xcconfig"; sourceTree = ""; }; - 64E4BC5A9889E0DB1F19CDE2935049BB /* OCMStubRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMStubRecorder.m; path = Source/OCMock/OCMStubRecorder.m; sourceTree = ""; }; - 65C0919CA083CA15AE160F0D4A37025B /* OCMRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMRecorder.m; path = Source/OCMock/OCMRecorder.m; sourceTree = ""; }; + 64E7CE8152263383C56E51268DC58D63 /* OCMFunctions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMFunctions.h; path = Source/OCMock/OCMFunctions.h; sourceTree = ""; }; + 663B2E9A93F8EE36BF7D622EBE9F5D64 /* NSArray+LDEventSource.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSArray+LDEventSource.m"; path = "LDEventSource/NSArray+LDEventSource.m"; sourceTree = ""; }; 663BB2A66BA5DA19B1A633F164FAEED4 /* Pods-Darkly_osx-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Darkly_osx-resources.sh"; sourceTree = ""; }; + 66406B161168717D6C0C3F5C10922505 /* NSMethodSignature+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSMethodSignature+OCMAdditions.m"; path = "Source/OCMock/NSMethodSignature+OCMAdditions.m"; sourceTree = ""; }; 6651AFDE204AB83298689F7C31A9E5C2 /* Pods-DarklyTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-DarklyTests.modulemap"; sourceTree = ""; }; - 66AB82BF52B8DE62BC2A72C2592E23F1 /* OCMRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMRecorder.h; path = Source/OCMock/OCMRecorder.h; sourceTree = ""; }; + 67467940FD2C95EC4D20DCABECABCD81 /* OCMArg.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMArg.m; path = Source/OCMock/OCMArg.m; sourceTree = ""; }; 6884B1633D8C48722BD4437669CF07BA /* Pods-Darkly_watchOS-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Darkly_watchOS-resources.sh"; sourceTree = ""; }; - 69AC1833240D28BD3D8461ED1935EDB1 /* OCMMacroState.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMMacroState.m; path = Source/OCMock/OCMMacroState.m; sourceTree = ""; }; - 69E471AA3D416E30075E8211F8F18658 /* DarklyEventSource-tvOS.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "DarklyEventSource-tvOS.xcconfig"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS.xcconfig"; sourceTree = ""; }; - 6BEB5371A9B9E754AC32F232018AE56C /* NSNotificationCenter+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSNotificationCenter+OCMAdditions.m"; path = "Source/OCMock/NSNotificationCenter+OCMAdditions.m"; sourceTree = ""; }; + 68B5CFA8361696B34926AC638CF51858 /* OHHTTPStubs.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OHHTTPStubs.m; path = OHHTTPStubs/Sources/OHHTTPStubs.m; sourceTree = ""; }; 6BED7B9A687B9027AC6065453990C128 /* Pods-DarklyTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-DarklyTests-umbrella.h"; sourceTree = ""; }; + 6CAA365947ED1983438715063E164BCE /* OCProtocolMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCProtocolMockObject.m; path = Source/OCMock/OCProtocolMockObject.m; sourceTree = ""; }; + 6D1989826B44821C35BA9D1771779DB0 /* DarklyEventSource-iOS.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "DarklyEventSource-iOS.xcconfig"; sourceTree = ""; }; + 70098D1C0228050C3D6F1E89DAD2685B /* OCMStubRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMStubRecorder.m; path = Source/OCMock/OCMStubRecorder.m; sourceTree = ""; }; + 70D0377CCE42B5A35DB52DF03FC3C5C8 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../DarklyEventSource-macOS/Info.plist"; sourceTree = ""; }; + 71A7FFF7124ECB11D8DA8949F5BF69FF /* DarklyEventSource-watchOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; name = "DarklyEventSource-watchOS.modulemap"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS.modulemap"; sourceTree = ""; }; 723984019F1F7AFC4465135DD72467D9 /* Pods-Darkly_iOS-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Darkly_iOS-acknowledgements.markdown"; sourceTree = ""; }; - 727D176E2322A0F07FE730B07254293F /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 72CB2F44FF1F40E925087A112FD20F53 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 734443C5512B7DE665014EB4917C96DF /* Pods-Darkly_tvOS-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Darkly_tvOS-acknowledgements.plist"; sourceTree = ""; }; 735AAE7464D8A1B9B4E20546D3DB8C37 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 786D1550089C847D816C575010098672 /* OCMLocation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMLocation.m; path = Source/OCMock/OCMLocation.m; sourceTree = ""; }; - 79234D9DB87E5C7FB6B8F7FAC6C79FCA /* OCMExpectationRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMExpectationRecorder.m; path = Source/OCMock/OCMExpectationRecorder.m; sourceTree = ""; }; - 7BB9C703084B753AC26EBB8FCEA4D80A /* OCMock.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = OCMock.modulemap; sourceTree = ""; }; - 7BD1F6749A3FA26D87844207325DE6A1 /* OCMConstraint.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMConstraint.m; path = Source/OCMock/OCMConstraint.m; sourceTree = ""; }; - 7D94DD2D5D1140DF93748DB6D561CB1F /* DarklyEventSource-tvOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DarklyEventSource-tvOS-dummy.m"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS-dummy.m"; sourceTree = ""; }; + 784CBECF8028CC4AC5B0B14D7E2CB0B9 /* OCMock-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OCMock-umbrella.h"; sourceTree = ""; }; + 7B7B1514B90CBC53B52DD4DCECE8299E /* DarklyEventSource-macOS-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-macOS-prefix.pch"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS-prefix.pch"; sourceTree = ""; }; 7FAD2DD8D645AEC80BFB0CCA170813C7 /* Pods-Darkly_osx-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Darkly_osx-dummy.m"; sourceTree = ""; }; - 819D8ECA744A54362D07EA95D9379062 /* NSValue+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSValue+OCMAdditions.m"; path = "Source/OCMock/NSValue+OCMAdditions.m"; sourceTree = ""; }; - 83C7FB0444707E5FEEB30DACDFD164A0 /* OCMBoxedReturnValueProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMBoxedReturnValueProvider.h; path = Source/OCMock/OCMBoxedReturnValueProvider.h; sourceTree = ""; }; + 826FD6C5044ECF8A5CB7355F77BB3E3C /* DarklyEventSource-watchOS.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "DarklyEventSource-watchOS.xcconfig"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS.xcconfig"; sourceTree = ""; }; 8451E511B5A2EB7FEDD129E4940CC68A /* Pods-DarklyTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-DarklyTests.release.xcconfig"; sourceTree = ""; }; - 8BBF7B3840A36892D14C230FC55B3B64 /* OCMInvocationMatcher.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMInvocationMatcher.m; path = Source/OCMock/OCMInvocationMatcher.m; sourceTree = ""; }; - 8D5DD619EF039D6A394E253938F85A12 /* DarklyEventSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = DarklyEventSource.h; path = LDEventSource/DarklyEventSource.h; sourceTree = ""; }; - 8F1B9C604582097FDE56BFE0E5DBD7D8 /* OCMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMockObject.m; path = Source/OCMock/OCMockObject.m; sourceTree = ""; }; - 8F51EF986FA63F62D8ABE46B5A0EF12E /* OCMObserverRecorder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMObserverRecorder.m; path = Source/OCMock/OCMObserverRecorder.m; sourceTree = ""; }; - 904227180DDE68F8E40C85777E8B4A62 /* OCClassMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCClassMockObject.m; path = Source/OCMock/OCClassMockObject.m; sourceTree = ""; }; + 8B6F2C53A8B680B9712FCADA894F7A99 /* NSNotificationCenter+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSNotificationCenter+OCMAdditions.m"; path = "Source/OCMock/NSNotificationCenter+OCMAdditions.m"; sourceTree = ""; }; + 8B74E3BE6886528BD7236293763EC298 /* OHHTTPStubsResponse.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OHHTTPStubsResponse.m; path = OHHTTPStubs/Sources/OHHTTPStubsResponse.m; sourceTree = ""; }; + 901EB4B981DFBB59BBB5CD4EA34AD42A /* DarklyEventSource-watchOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DarklyEventSource-watchOS-dummy.m"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS-dummy.m"; sourceTree = ""; }; + 9028A0B4BC6EC42D3737F48BD89B8BC6 /* OCMFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMFunctions.m; path = Source/OCMock/OCMFunctions.m; sourceTree = ""; }; + 9269E4DB085E9703969EFCB1CD2DE757 /* NSValue+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSValue+OCMAdditions.h"; path = "Source/OCMock/NSValue+OCMAdditions.h"; sourceTree = ""; }; 92A0D6A6BF74D42B98D2D372CD1DBB72 /* Pods-Darkly_tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Darkly_tvOS.debug.xcconfig"; sourceTree = ""; }; 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 95B4F44EF48D7469F021EF0AFAB7DA9C /* Pods-Darkly_tvOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Darkly_tvOS-umbrella.h"; sourceTree = ""; }; 95CD663A83BD4063D2D8E621423DE9D9 /* Pods-Darkly_watchOS-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Darkly_watchOS-acknowledgements.markdown"; sourceTree = ""; }; - 9614AA8C47626D166394777219CA6313 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 96715FCBD819314A9A12ADE3BEFE83EC /* OCProtocolMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCProtocolMockObject.h; path = Source/OCMock/OCProtocolMockObject.h; sourceTree = ""; }; 9D2BC21DEA3ACE1E0185614B2CCC24DC /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; - 9E649BE19BCA5516709BC755EC9FEEC7 /* OCClassMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCClassMockObject.h; path = Source/OCMock/OCClassMockObject.h; sourceTree = ""; }; - 9EB28608957FFF3FE5D9F73B3FBFA7F3 /* OCMFunctions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMFunctions.m; path = Source/OCMock/OCMFunctions.m; sourceTree = ""; }; - 9FA926717A94389A01F7FE7C87C4FDC8 /* OCPartialMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCPartialMockObject.h; path = Source/OCMock/OCPartialMockObject.h; sourceTree = ""; }; + 9FD8121492A6334FDA00330D0DA407E3 /* NSObject+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+OCMAdditions.h"; path = "Source/OCMock/NSObject+OCMAdditions.h"; sourceTree = ""; }; A041844F3037943A8C55D5CE3D1526BF /* Pods-Darkly_watchOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Darkly_watchOS.release.xcconfig"; sourceTree = ""; }; - A252F33D314FA7E7ECBDF0DF9A105B26 /* OCMInvocationStub.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMInvocationStub.m; path = Source/OCMock/OCMInvocationStub.m; sourceTree = ""; }; - A28DA38325D7E02D324E31F1806EF191 /* OCMPassByRefSetter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMPassByRefSetter.h; path = Source/OCMock/OCMPassByRefSetter.h; sourceTree = ""; }; - A2F25E21642A11CD0BC591D37DF2F00D /* OHHTTPStubs.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = OHHTTPStubs.modulemap; sourceTree = ""; }; + A313E7D0C6F134B251DD75F2871B57D1 /* LDEventStringAccumulator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = LDEventStringAccumulator.h; path = LDEventSource/LDEventStringAccumulator.h; sourceTree = ""; }; A4424D4B5E382A3ECC8EEFECD5134053 /* Pods_Darkly_watchOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Darkly_watchOS.framework; path = "Pods-Darkly_watchOS.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - A4ECE4E53237844F4AE3D13B41379C45 /* NSNotificationCenter+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSNotificationCenter+OCMAdditions.h"; path = "Source/OCMock/NSNotificationCenter+OCMAdditions.h"; sourceTree = ""; }; - A528657BFE7C565C8EE90EC0CDCDE385 /* OCMRealObjectForwarder.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMRealObjectForwarder.m; path = Source/OCMock/OCMRealObjectForwarder.m; sourceTree = ""; }; + A4541F05BE15B48B91C0C05BB3A2E351 /* OCObserverMockObject.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCObserverMockObject.h; path = Source/OCMock/OCObserverMockObject.h; sourceTree = ""; }; A55F61C2B80C4D9FCF16108F6CDDCAFF /* Pods_Darkly_osx.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Darkly_osx.framework; path = "Pods-Darkly_osx.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - A61A859375079B183E1F4F6E8F4466A7 /* DarklyEventSource-macOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-macOS-umbrella.h"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS-umbrella.h"; sourceTree = ""; }; + A585C385272D6EB0580E2DD3C756C161 /* OCMIndirectReturnValueProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMIndirectReturnValueProvider.m; path = Source/OCMock/OCMIndirectReturnValueProvider.m; sourceTree = ""; }; + A5F28D54A402CF22C945A53B609AA298 /* DarklyEventSource-iOS-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DarklyEventSource-iOS-prefix.pch"; sourceTree = ""; }; A64AB32C6D35EC7C6A163E5FAE5EAE8A /* Pods-DarklyTests-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-DarklyTests-resources.sh"; sourceTree = ""; }; - A8A9B0ADD858339D3D15CABD60856848 /* OCMObserverRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMObserverRecorder.h; path = Source/OCMock/OCMObserverRecorder.h; sourceTree = ""; }; - A99F324709D15E97D9B7E7EFD390B810 /* OCMBoxedReturnValueProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMBoxedReturnValueProvider.m; path = Source/OCMock/OCMBoxedReturnValueProvider.m; sourceTree = ""; }; - A9BE3017575270B4AA55F3CCED88D45C /* DarklyEventSource-tvOS-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-tvOS-prefix.pch"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS-prefix.pch"; sourceTree = ""; }; - A9CF35EF466BC4B915F48EF27C1846AB /* OHPathHelpers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OHPathHelpers.h; path = OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h; sourceTree = ""; }; - AA49AFC7E73CAB7B63A97FC6963BD84B /* LDEventSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = LDEventSource.h; path = LDEventSource/LDEventSource.h; sourceTree = ""; }; + A89DB22242D0EA1FE21D35BEE5FED120 /* OCMInvocationExpectation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMInvocationExpectation.m; path = Source/OCMock/OCMInvocationExpectation.m; sourceTree = ""; }; + A9DF034B73D13BDA81F88F70F50913C8 /* OCMNotificationPoster.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMNotificationPoster.h; path = Source/OCMock/OCMNotificationPoster.h; sourceTree = ""; }; + ABFB220046434E2FE02BBBA6E93B1F6B /* DarklyEventSource-macOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DarklyEventSource-macOS-dummy.m"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS-dummy.m"; sourceTree = ""; }; + AC1AA489B5E1706BC7BDA2BF73EE987D /* OCMBlockCaller.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMBlockCaller.m; path = Source/OCMock/OCMBlockCaller.m; sourceTree = ""; }; ACDF0E08285415092448B937524BBFBC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; - AF29C88DAD0388B63CC569E3BF29E5E5 /* DarklyEventSource-macOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DarklyEventSource-macOS-dummy.m"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS-dummy.m"; sourceTree = ""; }; - B309EA5ED7C7F03F08835B7C48F3B1E8 /* OHHTTPStubsResponse.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OHHTTPStubsResponse.h; path = OHHTTPStubs/Sources/OHHTTPStubsResponse.h; sourceTree = ""; }; - B345DF905AABDA409B1830831D150BF0 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B4C08EC41761484BD661B45AE7FCBE69 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../DarklyEventSource-watchOS/Info.plist"; sourceTree = ""; }; + AFE61AE662DB83FA37127D3A73FA9C0E /* DarklyEventSource-tvOS-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-tvOS-prefix.pch"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS-prefix.pch"; sourceTree = ""; }; + B0E8EB42A65A20CFE56B5F7703728D4D /* OCMArgAction.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMArgAction.m; path = Source/OCMock/OCMArgAction.m; sourceTree = ""; }; + B1135E2A827CEB63AADE591470E7CB6E /* OHHTTPStubs-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OHHTTPStubs-prefix.pch"; sourceTree = ""; }; + B2E760A6F1FCCA6AE1FEFC1C3C88BF43 /* OCMExpectationRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMExpectationRecorder.h; path = Source/OCMock/OCMExpectationRecorder.h; sourceTree = ""; }; + B3193F9B76AC15F23E75418697A0ADBD /* OCMObserverRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMObserverRecorder.h; path = Source/OCMock/OCMObserverRecorder.h; sourceTree = ""; }; + B3D22F2E5065CC534CFDCFFB70C49330 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../DarklyEventSource-watchOS/Info.plist"; sourceTree = ""; }; + B50FEB3145A28BA92F3208017D485EA8 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B53B2A149FB90B83E32D568BF1BBF7F3 /* OCMBlockArgCaller.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMBlockArgCaller.m; path = Source/OCMock/OCMBlockArgCaller.m; sourceTree = ""; }; + B61614B63DFF88EDF160FEBF7EB05484 /* OCMNotificationPoster.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMNotificationPoster.m; path = Source/OCMock/OCMNotificationPoster.m; sourceTree = ""; }; B67F188C9F7107615562C35BE204FB48 /* OHHTTPStubs.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = OHHTTPStubs.framework; path = OHHTTPStubs.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - B73316F0AB2ADA478403780E72C212FD /* OHHTTPStubs+NSURLSessionConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubs+NSURLSessionConfiguration.m"; path = "OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m"; sourceTree = ""; }; B884B5022A93838F2A2DC641043A4E43 /* Pods_Darkly_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Darkly_tvOS.framework; path = "Pods-Darkly_tvOS.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - B8BF5256A9C8C89D14B7E5583327B291 /* OHHTTPStubs.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = OHHTTPStubs.xcconfig; sourceTree = ""; }; + B8936874B33B59D837A5068B80AE063A /* DarklyEventSource-tvOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-tvOS-umbrella.h"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS-umbrella.h"; sourceTree = ""; }; + BABC81EC3CA732F91FFAA9E8C91200E8 /* LDEventSource.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = LDEventSource.m; path = LDEventSource/LDEventSource.m; sourceTree = ""; }; BBC5EE03E32233D0CB90090019393036 /* DarklyEventSource.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = DarklyEventSource.framework; path = "DarklyEventSource-iOS.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - BCCC2502B43C367FD6F0198B4CD2537F /* OCMock-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OCMock-prefix.pch"; sourceTree = ""; }; - BCF6BBBE88BA653337EDA896BDA90286 /* DarklyEventSource-iOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "DarklyEventSource-iOS-dummy.m"; sourceTree = ""; }; - C9EDE1211E7858C38AF1D41AB7FEC624 /* OCMVerifier.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMVerifier.m; path = Source/OCMock/OCMVerifier.m; sourceTree = ""; }; - CA0B8765C7CF08D814754C0A0F8C1203 /* NSInvocation+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSInvocation+OCMAdditions.h"; path = "Source/OCMock/NSInvocation+OCMAdditions.h"; sourceTree = ""; }; - CABB23078EEF17F61B31494DB75B7A83 /* OHHTTPStubs-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "OHHTTPStubs-dummy.m"; sourceTree = ""; }; + BC8860E68463F92438DD36F071B29A55 /* NSMethodSignature+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSMethodSignature+OCMAdditions.h"; path = "Source/OCMock/NSMethodSignature+OCMAdditions.h"; sourceTree = ""; }; + BD6D790929BCF50491E14B828FDCF732 /* DarklyEventSource-watchOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-watchOS-umbrella.h"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS-umbrella.h"; sourceTree = ""; }; + BDCB316A69C3F852581FF6CC53E64652 /* OCMReturnValueProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMReturnValueProvider.h; path = Source/OCMock/OCMReturnValueProvider.h; sourceTree = ""; }; + BE2F248E5EE9A96D4804C2AFC12FD878 /* OCMFunctionsPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMFunctionsPrivate.h; path = Source/OCMock/OCMFunctionsPrivate.h; sourceTree = ""; }; + C1753C4E5A7538431EFC5243A309A458 /* OCMConstraint.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMConstraint.h; path = Source/OCMock/OCMConstraint.h; sourceTree = ""; }; + C225A59880C3017D042E0BEFF60A9520 /* LDEventParser.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = LDEventParser.h; path = LDEventSource/LDEventParser.h; sourceTree = ""; }; + C272E53CB64FE49AF2832CB69221D896 /* OCMInvocationExpectation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMInvocationExpectation.h; path = Source/OCMock/OCMInvocationExpectation.h; sourceTree = ""; }; + C8AE966F631635520F76CD2027348FBE /* DarklyEventSource-iOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "DarklyEventSource-iOS.modulemap"; sourceTree = ""; }; + CA23B66702EE0C700A3FA460633677D1 /* OHHTTPStubs+NSURLSessionConfiguration.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "OHHTTPStubs+NSURLSessionConfiguration.m"; path = "OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m"; sourceTree = ""; }; + CB4C1F3AA83AE758A49096AAA2C34AF1 /* DarklyEventSource-macOS-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-macOS-umbrella.h"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS-umbrella.h"; sourceTree = ""; }; + CC4AA6DBB4820C6C213B32F52ACC75F3 /* OHHTTPStubsResponse+JSON.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "OHHTTPStubsResponse+JSON.h"; path = "OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h"; sourceTree = ""; }; CC797E628B9C5EE04D38F89AF74B4870 /* Pods-Darkly_watchOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Darkly_watchOS.debug.xcconfig"; sourceTree = ""; }; - CD1EE456601A5EB7D58613A43F503E4D /* DarklyEventSource-macOS-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-macOS-prefix.pch"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS-prefix.pch"; sourceTree = ""; }; - CF4197454D6EDFC7E82D1585BA642113 /* OCMFunctionsPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMFunctionsPrivate.h; path = Source/OCMock/OCMFunctionsPrivate.h; sourceTree = ""; }; - D183ABD8F5D0BF9ED235E3E57FF77187 /* OHHTTPStubs.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OHHTTPStubs.m; path = OHHTTPStubs/Sources/OHHTTPStubs.m; sourceTree = ""; }; - D530BD02AFFFAC8355740377AFEE3DA7 /* OCMBlockCaller.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMBlockCaller.h; path = Source/OCMock/OCMBlockCaller.h; sourceTree = ""; }; - D6B17CFA609D4F1BCEB6CD952F6BDF8D /* NSObject+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSObject+OCMAdditions.h"; path = "Source/OCMock/NSObject+OCMAdditions.h"; sourceTree = ""; }; + CE92382320DDEE9158D74990E3956AE2 /* OCMLocation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMLocation.m; path = Source/OCMock/OCMLocation.m; sourceTree = ""; }; + D1F1BDA14548B452E79BA387D85DFFF9 /* NSArray+LDEventSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSArray+LDEventSource.h"; path = "LDEventSource/NSArray+LDEventSource.h"; sourceTree = ""; }; + D298344BF37A3B7C5700A48D47C8C21E /* OCMVerifier.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMVerifier.h; path = Source/OCMock/OCMVerifier.h; sourceTree = ""; }; + D538EFF2D40359DD88DB4A2F6183882B /* OCMock.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = OCMock.modulemap; sourceTree = ""; }; + D54674DE86AAD19E0C6774A1964D17D2 /* DarklyEventSource-tvOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; name = "DarklyEventSource-tvOS.modulemap"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS.modulemap"; sourceTree = ""; }; D6F942D294D8E9D9CA6E50ACF143CCFC /* Pods-DarklyTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-DarklyTests-acknowledgements.markdown"; sourceTree = ""; }; + D7872C42E57DED130958BF19CB6F53E7 /* OCMArg.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMArg.h; path = Source/OCMock/OCMArg.h; sourceTree = ""; }; D922F2D72779A1967524BBBAECEF3120 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/CFNetwork.framework; sourceTree = DEVELOPER_DIR; }; - D924AEF6CECEB1D0756A6D21740A2925 /* DarklyEventSource-iOS.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "DarklyEventSource-iOS.xcconfig"; sourceTree = ""; }; DA744B745981F85B4555A78005CA3DB7 /* DarklyEventSource.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = DarklyEventSource.framework; path = "DarklyEventSource-tvOS.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; - DC4DBFCCC16AAA757348A0C17B7A32D2 /* OHHTTPStubs-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OHHTTPStubs-prefix.pch"; sourceTree = ""; }; - DC67523FE012358DE75EE4A668A5A664 /* OHHTTPStubsResponse+JSON.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "OHHTTPStubsResponse+JSON.h"; path = "OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h"; sourceTree = ""; }; + DB3E908E5778B6FED03EF1CAAA1298C5 /* NSInvocation+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSInvocation+OCMAdditions.m"; path = "Source/OCMock/NSInvocation+OCMAdditions.m"; sourceTree = ""; }; DC7B3FB5C3808BF903624018B25BA375 /* Pods-Darkly_osx-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Darkly_osx-umbrella.h"; sourceTree = ""; }; - DDE2AD1F28739DDD3342A292E311A197 /* DarklyEventSource-iOS-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "DarklyEventSource-iOS-prefix.pch"; sourceTree = ""; }; - DE7B054EF1D513FB1CDAF343DF48E695 /* OCPartialMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCPartialMockObject.m; path = Source/OCMock/OCPartialMockObject.m; sourceTree = ""; }; + DF368D68A25D4124776D48F7C58AA92E /* OCMock.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = OCMock.xcconfig; sourceTree = ""; }; + E10DACFD9E8FFDF19636BBA48FA53AE9 /* DarklyEventSource-macOS.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "DarklyEventSource-macOS.xcconfig"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS.xcconfig"; sourceTree = ""; }; + E15BA3BA7678D2BB853AF7106C10E12C /* OCMArgAction.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMArgAction.h; path = Source/OCMock/OCMArgAction.h; sourceTree = ""; }; + E1F03D99AD09DAEA70CA34975D9B60C1 /* NSString+LDEventSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSString+LDEventSource.h"; path = "LDEventSource/NSString+LDEventSource.h"; sourceTree = ""; }; + E239BC788138F289F7F1023EA0B9F908 /* OHHTTPStubs-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "OHHTTPStubs-dummy.m"; sourceTree = ""; }; + E3C4D269564C17B7A722842261A3DCFF /* DarklyEventSource-watchOS-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-watchOS-prefix.pch"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS-prefix.pch"; sourceTree = ""; }; E4610A695477BBAF084EBA2328D57A80 /* Pods-Darkly_iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Darkly_iOS.debug.xcconfig"; sourceTree = ""; }; - E736DD7FFDD3BE1C73A56D0CCAE7AD54 /* OCMVerifier.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMVerifier.h; path = Source/OCMock/OCMVerifier.h; sourceTree = ""; }; - E7EEA70FD196A9350783D5F26267EA36 /* OCMock.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMock.h; path = Source/OCMock/OCMock.h; sourceTree = ""; }; - E9F043D9D4D153EF80F85834A3D728DE /* OCMReturnValueProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMReturnValueProvider.m; path = Source/OCMock/OCMReturnValueProvider.m; sourceTree = ""; }; - EA932F95D294477A15F2195FAA819A89 /* OCMock-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "OCMock-umbrella.h"; sourceTree = ""; }; - EAD198096F351A1A1FBAFD2D6B8FF082 /* OCMLocation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMLocation.h; path = Source/OCMock/OCMLocation.h; sourceTree = ""; }; - ECCFC10038A1B1EDC39E7F558EBF050B /* OCMArg.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMArg.h; path = Source/OCMock/OCMArg.h; sourceTree = ""; }; - EEF775C9BCEB8619F891533C3F4D5442 /* OCMInvocationExpectation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMInvocationExpectation.h; path = Source/OCMock/OCMInvocationExpectation.h; sourceTree = ""; }; - EF760A95F22C50A6B60AECA5D320903C /* OHHTTPStubs.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OHHTTPStubs.h; path = OHHTTPStubs/Sources/OHHTTPStubs.h; sourceTree = ""; }; - F0E59B23BCEE7B4288CE96404C873232 /* DarklyEventSource-tvOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; name = "DarklyEventSource-tvOS.modulemap"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS.modulemap"; sourceTree = ""; }; - F0ECB43B373A36914B537E7B9B076175 /* OCMIndirectReturnValueProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMIndirectReturnValueProvider.m; path = Source/OCMock/OCMIndirectReturnValueProvider.m; sourceTree = ""; }; + E649A16E0C0CC9794FADEB73D226FD04 /* OCClassMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCClassMockObject.m; path = Source/OCMock/OCClassMockObject.m; sourceTree = ""; }; + E7C2A9E6F1B39A2F18618A62B47E88A3 /* NSInvocation+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSInvocation+OCMAdditions.h"; path = "Source/OCMock/NSInvocation+OCMAdditions.h"; sourceTree = ""; }; + E8A2F29D2CBBFD05E8C0957B415BD244 /* OCMBoxedReturnValueProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMBoxedReturnValueProvider.h; path = Source/OCMock/OCMBoxedReturnValueProvider.h; sourceTree = ""; }; + E99375BDAA9C578B2D765A92929C150D /* NSNotificationCenter+OCMAdditions.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSNotificationCenter+OCMAdditions.h"; path = "Source/OCMock/NSNotificationCenter+OCMAdditions.h"; sourceTree = ""; }; + E99EDCB4BF6E43D1BB8BF9809189FADC /* DarklyEventSource-iOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "DarklyEventSource-iOS-dummy.m"; sourceTree = ""; }; + E9A4009B6F747D00F9B42B9D986E98D6 /* DarklyEventSource-tvOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "DarklyEventSource-tvOS-dummy.m"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS-dummy.m"; sourceTree = ""; }; + E9E985CC46AF6BFB841D0106DF8831D9 /* OCMConstraint.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMConstraint.m; path = Source/OCMock/OCMConstraint.m; sourceTree = ""; }; + EA636CDA22A32949D3F7E1D3CD5DDBC4 /* OCMLocation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMLocation.h; path = Source/OCMock/OCMLocation.h; sourceTree = ""; }; + EADBD9D42E2BA673C654C69A62A9A456 /* OCObserverMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCObserverMockObject.m; path = Source/OCMock/OCObserverMockObject.m; sourceTree = ""; }; + EB220CE5C49A8E1654877D5DEC917754 /* OCMInvocationStub.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMInvocationStub.h; path = Source/OCMock/OCMInvocationStub.h; sourceTree = ""; }; + EC2E8A1FEB1BD595CEC105DF1FC6D551 /* OCMInvocationMatcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMInvocationMatcher.h; path = Source/OCMock/OCMInvocationMatcher.h; sourceTree = ""; }; + ECD1DF4E7C8DB57E5E4BE48FDDAF3091 /* OCMockObject.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMockObject.m; path = Source/OCMock/OCMockObject.m; sourceTree = ""; }; + F1B09AAA2C17F712826EF600D86DC883 /* OCMPassByRefSetter.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMPassByRefSetter.h; path = Source/OCMock/OCMPassByRefSetter.h; sourceTree = ""; }; + F39D8D66CA8C5F94886F8314A00290CF /* Compatibility.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Compatibility.h; path = OHHTTPStubs/Sources/Compatibility.h; sourceTree = ""; }; F3D6D576A18C95622C205C717C11AFD6 /* Pods-Darkly_osx.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Darkly_osx.release.xcconfig"; sourceTree = ""; }; F3FDD2EE95173021BEF192DE3D6CF6D4 /* Pods-Darkly_iOS-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Darkly_iOS-dummy.m"; sourceTree = ""; }; - F4B0192087B27BECEC0F7E1799558045 /* OCMStubRecorder.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMStubRecorder.h; path = Source/OCMock/OCMStubRecorder.h; sourceTree = ""; }; F5CFEFA7732EA70ECA1D934CF3040808 /* Pods-Darkly_iOS-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Darkly_iOS-resources.sh"; sourceTree = ""; }; F89624173D1A9B55EB00CAEAC060836D /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F90968080A13E2B5A51548EB12B78716 /* NSInvocation+OCMAdditions.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSInvocation+OCMAdditions.m"; path = "Source/OCMock/NSInvocation+OCMAdditions.m"; sourceTree = ""; }; - FB101544D209AA66FB3E8EC07343FD31 /* DarklyEventSource-watchOS-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "DarklyEventSource-watchOS-prefix.pch"; path = "../DarklyEventSource-watchOS/DarklyEventSource-watchOS-prefix.pch"; sourceTree = ""; }; - FB1A891B992A9061F78813B1AA7D2F04 /* DarklyEventSource-macOS.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "DarklyEventSource-macOS.xcconfig"; path = "../DarklyEventSource-macOS/DarklyEventSource-macOS.xcconfig"; sourceTree = ""; }; + FAB8ABEA5FDD50C6707CDCDBD3AFAEA0 /* OHPathHelpers.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OHPathHelpers.m; path = OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m; sourceTree = ""; }; + FB4F92F13F88313EC2B1F88227D626B2 /* DarklyEventSource-tvOS.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "DarklyEventSource-tvOS.xcconfig"; path = "../DarklyEventSource-tvOS/DarklyEventSource-tvOS.xcconfig"; sourceTree = ""; }; + FBDDFF74F07A19E7D9FD71218146378B /* LDEventSource.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = LDEventSource.h; path = LDEventSource/LDEventSource.h; sourceTree = ""; }; FBFEAC80AB2E00CFC60743201EA79E8E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.2.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; FC625021C19302529D54CDCF201E24F0 /* Pods-Darkly_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Darkly_iOS.release.xcconfig"; sourceTree = ""; }; - FF27A232F4D681E7DEE0C59628C61BC0 /* DarklyEventSource-iOS.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "DarklyEventSource-iOS.modulemap"; sourceTree = ""; }; - FF53F1EAB9D81A6DED2CC5DDC68998B5 /* OCMMacroState.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = OCMMacroState.h; path = Source/OCMock/OCMMacroState.h; sourceTree = ""; }; + FCB99FA900D325BA0BB2195CED057DF8 /* OCMInvocationMatcher.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = OCMInvocationMatcher.m; path = Source/OCMock/OCMInvocationMatcher.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 13A8CCE889D2F8BEA8CE6BA3B4921CC1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5F708D4CBB9F343B7A61F048C15396EE /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 31B6443FA5E51738BF4D810BE629E7AA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -378,27 +426,27 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 3B4F4B0BCDD1B2C21722641A17FC491A /* Frameworks */ = { + 464902CB027C46CF3556CCC31A71DF14 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2CE09E2B1AC5D86B66F237C58C1C612B /* Foundation.framework in Frameworks */, + ABC47FD397187DA12E4CB4C46F125957 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 464902CB027C46CF3556CCC31A71DF14 /* Frameworks */ = { + 5C1FF83D7AF573314D3ACD0DFA879C50 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - ABC47FD397187DA12E4CB4C46F125957 /* Foundation.framework in Frameworks */, + 6FA55259B9EA9D1852CD39C13B8F7DDB /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; - 70228B8E8F114C0FB2F9CA7F5C5F65C2 /* Frameworks */ = { + 63476FC136C65391B39A0616BCDD650E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7B3D75F6DB24A43FB02D2818D4864958 /* Foundation.framework in Frameworks */, + 54385A4080A43BD8EF31C38CD229E445 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -410,11 +458,11 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 916374B3DC408166DFE76F136EF886A4 /* Frameworks */ = { + 7FB39C65E37FDAF623E3E2844F78E49B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CFFD80CB651FDC49953FB5D8672038AC /* Foundation.framework in Frameworks */, + 9FD2E9450E00FC312D4848508887D0F0 /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -434,14 +482,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BC0C0D8E30296A60E074A7FDF19F3FA3 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 2F55F80110FF9D204BCFB846A8270508 /* Cocoa.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; C362CFB624187F609FA54DB341605E90 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -494,42 +534,81 @@ name = watchOS; sourceTree = ""; }; - 1AA50DB809BB674F868C5A0AF0EBD4C5 /* DarklyEventSource */ = { + 15F6C58B22795C85AAA776EE8A5EDB46 /* JSON */ = { isa = PBXGroup; children = ( - 8D5DD619EF039D6A394E253938F85A12 /* DarklyEventSource.h */, - AA49AFC7E73CAB7B63A97FC6963BD84B /* LDEventSource.h */, - 27467853512D2003F1639D1425ABA364 /* LDEventSource.m */, - CD7CF6AAB530C65C26EB25C94A79AB8D /* Support Files */, + CC4AA6DBB4820C6C213B32F52ACC75F3 /* OHHTTPStubsResponse+JSON.h */, + 17F5930E82A640BA89F9A60B548EE79C /* OHHTTPStubsResponse+JSON.m */, ); - name = DarklyEventSource; - path = DarklyEventSource; + name = JSON; sourceTree = ""; }; - 29CBC31C65965EED523F6F7B12F0F6F2 /* Pods */ = { + 16763A74BC38C1A4974227391178AD07 /* NSURLSession */ = { isa = PBXGroup; children = ( - 1AA50DB809BB674F868C5A0AF0EBD4C5 /* DarklyEventSource */, - C898B66ABA4D7EFEE45ABB3A5A7B0E84 /* OCMock */, - 62F41E4FDE6867AD1C3F660B11AEB7B3 /* OHHTTPStubs */, + CA23B66702EE0C700A3FA460633677D1 /* OHHTTPStubs+NSURLSessionConfiguration.m */, ); - name = Pods; + name = NSURLSession; sourceTree = ""; }; - 523D5199236E035EAB2B098BDE259C12 /* Support Files */ = { + 2BF24F86659444E92E660933B899E029 /* Support Files */ = { isa = PBXGroup; children = ( - 9614AA8C47626D166394777219CA6313 /* Info.plist */, - A2F25E21642A11CD0BC591D37DF2F00D /* OHHTTPStubs.modulemap */, - B8BF5256A9C8C89D14B7E5583327B291 /* OHHTTPStubs.xcconfig */, - CABB23078EEF17F61B31494DB75B7A83 /* OHHTTPStubs-dummy.m */, - DC4DBFCCC16AAA757348A0C17B7A32D2 /* OHHTTPStubs-prefix.pch */, - 0A0CD449DFE4111D9DA40063F59D0F1C /* OHHTTPStubs-umbrella.h */, + C8AE966F631635520F76CD2027348FBE /* DarklyEventSource-iOS.modulemap */, + 6D1989826B44821C35BA9D1771779DB0 /* DarklyEventSource-iOS.xcconfig */, + E99EDCB4BF6E43D1BB8BF9809189FADC /* DarklyEventSource-iOS-dummy.m */, + A5F28D54A402CF22C945A53B609AA298 /* DarklyEventSource-iOS-prefix.pch */, + 1A684FF78260E2892869AB05307F8A12 /* DarklyEventSource-iOS-umbrella.h */, + 05839ABAB9A0D60510F4B5F84D0E89CD /* DarklyEventSource-macOS.modulemap */, + E10DACFD9E8FFDF19636BBA48FA53AE9 /* DarklyEventSource-macOS.xcconfig */, + ABFB220046434E2FE02BBBA6E93B1F6B /* DarklyEventSource-macOS-dummy.m */, + 7B7B1514B90CBC53B52DD4DCECE8299E /* DarklyEventSource-macOS-prefix.pch */, + CB4C1F3AA83AE758A49096AAA2C34AF1 /* DarklyEventSource-macOS-umbrella.h */, + D54674DE86AAD19E0C6774A1964D17D2 /* DarklyEventSource-tvOS.modulemap */, + FB4F92F13F88313EC2B1F88227D626B2 /* DarklyEventSource-tvOS.xcconfig */, + E9A4009B6F747D00F9B42B9D986E98D6 /* DarklyEventSource-tvOS-dummy.m */, + AFE61AE662DB83FA37127D3A73FA9C0E /* DarklyEventSource-tvOS-prefix.pch */, + B8936874B33B59D837A5068B80AE063A /* DarklyEventSource-tvOS-umbrella.h */, + 71A7FFF7124ECB11D8DA8949F5BF69FF /* DarklyEventSource-watchOS.modulemap */, + 826FD6C5044ECF8A5CB7355F77BB3E3C /* DarklyEventSource-watchOS.xcconfig */, + 901EB4B981DFBB59BBB5CD4EA34AD42A /* DarklyEventSource-watchOS-dummy.m */, + E3C4D269564C17B7A722842261A3DCFF /* DarklyEventSource-watchOS-prefix.pch */, + BD6D790929BCF50491E14B828FDCF732 /* DarklyEventSource-watchOS-umbrella.h */, + 5CD52D204894A6629ECD216D949E2AE2 /* Info.plist */, + B3D22F2E5065CC534CFDCFFB70C49330 /* Info.plist */, + 1F99E36BBF1EB4E987E3359B92F7DD07 /* Info.plist */, + 70D0377CCE42B5A35DB52DF03FC3C5C8 /* Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/DarklyEventSource-iOS"; + sourceTree = ""; + }; + 41319C1BA187DFADE47831D2F31C6092 /* Support Files */ = { + isa = PBXGroup; + children = ( + 72CB2F44FF1F40E925087A112FD20F53 /* Info.plist */, + 1CF3530E70708E5FE428B724474D2232 /* OHHTTPStubs.modulemap */, + 13EF633664B9DF304A8EF579DC80553C /* OHHTTPStubs.xcconfig */, + E239BC788138F289F7F1023EA0B9F908 /* OHHTTPStubs-dummy.m */, + B1135E2A827CEB63AADE591470E7CB6E /* OHHTTPStubs-prefix.pch */, + 3F9F37AD5F918763317FD22F67056E6D /* OHHTTPStubs-umbrella.h */, ); name = "Support Files"; path = "../Target Support Files/OHHTTPStubs"; sourceTree = ""; }; + 5774D035C1AECC5EF1A87F1E5AE5EE05 /* Core */ = { + isa = PBXGroup; + children = ( + F39D8D66CA8C5F94886F8314A00290CF /* Compatibility.h */, + 2F7263EB5FDBA9374A691926217D7C8E /* OHHTTPStubs.h */, + 68B5CFA8361696B34926AC638CF51858 /* OHHTTPStubs.m */, + 00C177350037FC007B07D98EFBFC21D3 /* OHHTTPStubsResponse.h */, + 8B74E3BE6886528BD7236293763EC298 /* OHHTTPStubsResponse.m */, + ); + name = Core; + sourceTree = ""; + }; 57B617F7C27F540484B07FFE717F784C /* Targets Support Files */ = { isa = PBXGroup; children = ( @@ -542,17 +621,38 @@ name = "Targets Support Files"; sourceTree = ""; }; - 62F41E4FDE6867AD1C3F660B11AEB7B3 /* OHHTTPStubs */ = { + 57C6283A3067B91632B020C318A9AFD9 /* Support Files */ = { isa = PBXGroup; children = ( - FBF9A2F34CD3FAD1FCA04DC8A93FFECC /* Core */, - 7DF5F6BAF622D11737D3A473EB0BF524 /* JSON */, - 7C4ECFC2B31EF99725AC148FCEB4593C /* NSURLSession */, - 7BFCC42DCBA5430CAD60997577D6D91E /* OHPathHelpers */, - 523D5199236E035EAB2B098BDE259C12 /* Support Files */, + B50FEB3145A28BA92F3208017D485EA8 /* Info.plist */, + D538EFF2D40359DD88DB4A2F6183882B /* OCMock.modulemap */, + DF368D68A25D4124776D48F7C58AA92E /* OCMock.xcconfig */, + 47B73DDE334F6D4A01A21D8194861C85 /* OCMock-dummy.m */, + 1705667676E63E509AFEFFECAD13126D /* OCMock-prefix.pch */, + 784CBECF8028CC4AC5B0B14D7E2CB0B9 /* OCMock-umbrella.h */, ); - name = OHHTTPStubs; - path = OHHTTPStubs; + name = "Support Files"; + path = "../Target Support Files/OCMock"; + sourceTree = ""; + }; + 77F4238802BE854B64EAEBB7F8796DE7 /* DarklyEventSource */ = { + isa = PBXGroup; + children = ( + 35E6AA2AC1F3A30B8D20CE8B588A25F8 /* DarklyEventSource.h */, + C225A59880C3017D042E0BEFF60A9520 /* LDEventParser.h */, + 3C50DAE28B8386CDA6B51492071510AE /* LDEventParser.m */, + FBDDFF74F07A19E7D9FD71218146378B /* LDEventSource.h */, + BABC81EC3CA732F91FFAA9E8C91200E8 /* LDEventSource.m */, + A313E7D0C6F134B251DD75F2871B57D1 /* LDEventStringAccumulator.h */, + 3BD336159F272CBBE1150A6F6C01B8A8 /* LDEventStringAccumulator.m */, + D1F1BDA14548B452E79BA387D85DFFF9 /* NSArray+LDEventSource.h */, + 663B2E9A93F8EE36BF7D622EBE9F5D64 /* NSArray+LDEventSource.m */, + E1F03D99AD09DAEA70CA34975D9B60C1 /* NSString+LDEventSource.h */, + 18969F2E878C55DACB8FC8858D2C5100 /* NSString+LDEventSource.m */, + 2BF24F86659444E92E660933B899E029 /* Support Files */, + ); + name = DarklyEventSource; + path = DarklyEventSource; sourceTree = ""; }; 7BAA8C27B2B7463A2869FAABB150C7CB /* Pods-Darkly_watchOS */ = { @@ -572,21 +672,81 @@ path = "Target Support Files/Pods-Darkly_watchOS"; sourceTree = ""; }; - 7BFCC42DCBA5430CAD60997577D6D91E /* OHPathHelpers */ = { + 7C9AFB853B35A28FC3D67FE64606BEB8 /* OCMock */ = { isa = PBXGroup; children = ( - A9CF35EF466BC4B915F48EF27C1846AB /* OHPathHelpers.h */, - 3B6647FF28FFA097E8F2269CFD4713C8 /* OHPathHelpers.m */, + E7C2A9E6F1B39A2F18618A62B47E88A3 /* NSInvocation+OCMAdditions.h */, + DB3E908E5778B6FED03EF1CAAA1298C5 /* NSInvocation+OCMAdditions.m */, + BC8860E68463F92438DD36F071B29A55 /* NSMethodSignature+OCMAdditions.h */, + 66406B161168717D6C0C3F5C10922505 /* NSMethodSignature+OCMAdditions.m */, + E99375BDAA9C578B2D765A92929C150D /* NSNotificationCenter+OCMAdditions.h */, + 8B6F2C53A8B680B9712FCADA894F7A99 /* NSNotificationCenter+OCMAdditions.m */, + 9FD8121492A6334FDA00330D0DA407E3 /* NSObject+OCMAdditions.h */, + 3D28BAC6EEBBFD2F187B880C2293F3A9 /* NSObject+OCMAdditions.m */, + 9269E4DB085E9703969EFCB1CD2DE757 /* NSValue+OCMAdditions.h */, + 635E8F6D068B917C59F86E7C9038EC04 /* NSValue+OCMAdditions.m */, + 23A9B1BAFAFED46EF4C338455C3593D2 /* OCClassMockObject.h */, + E649A16E0C0CC9794FADEB73D226FD04 /* OCClassMockObject.m */, + D7872C42E57DED130958BF19CB6F53E7 /* OCMArg.h */, + 67467940FD2C95EC4D20DCABECABCD81 /* OCMArg.m */, + E15BA3BA7678D2BB853AF7106C10E12C /* OCMArgAction.h */, + B0E8EB42A65A20CFE56B5F7703728D4D /* OCMArgAction.m */, + 58A94BF01E22C379CEB90FDCD1FD0726 /* OCMBlockArgCaller.h */, + B53B2A149FB90B83E32D568BF1BBF7F3 /* OCMBlockArgCaller.m */, + 434B5DF5224FEDADE75A5E689D146E90 /* OCMBlockCaller.h */, + AC1AA489B5E1706BC7BDA2BF73EE987D /* OCMBlockCaller.m */, + E8A2F29D2CBBFD05E8C0957B415BD244 /* OCMBoxedReturnValueProvider.h */, + 256289EC71769D11C26C75322CA9E14E /* OCMBoxedReturnValueProvider.m */, + C1753C4E5A7538431EFC5243A309A458 /* OCMConstraint.h */, + E9E985CC46AF6BFB841D0106DF8831D9 /* OCMConstraint.m */, + 37E3A9B1DB35CBA2E6E3446BE49773A9 /* OCMExceptionReturnValueProvider.h */, + 0F24EA101E361D393A8F3A7BF72E9D05 /* OCMExceptionReturnValueProvider.m */, + B2E760A6F1FCCA6AE1FEFC1C3C88BF43 /* OCMExpectationRecorder.h */, + 19753520BE3CA6C03AA17595F381533A /* OCMExpectationRecorder.m */, + 64E7CE8152263383C56E51268DC58D63 /* OCMFunctions.h */, + 9028A0B4BC6EC42D3737F48BD89B8BC6 /* OCMFunctions.m */, + BE2F248E5EE9A96D4804C2AFC12FD878 /* OCMFunctionsPrivate.h */, + 003AB641624DD367808954F05509B17A /* OCMIndirectReturnValueProvider.h */, + A585C385272D6EB0580E2DD3C756C161 /* OCMIndirectReturnValueProvider.m */, + C272E53CB64FE49AF2832CB69221D896 /* OCMInvocationExpectation.h */, + A89DB22242D0EA1FE21D35BEE5FED120 /* OCMInvocationExpectation.m */, + EC2E8A1FEB1BD595CEC105DF1FC6D551 /* OCMInvocationMatcher.h */, + FCB99FA900D325BA0BB2195CED057DF8 /* OCMInvocationMatcher.m */, + EB220CE5C49A8E1654877D5DEC917754 /* OCMInvocationStub.h */, + 17E0CF03259C28A0039DB6847945CD9D /* OCMInvocationStub.m */, + EA636CDA22A32949D3F7E1D3CD5DDBC4 /* OCMLocation.h */, + CE92382320DDEE9158D74990E3956AE2 /* OCMLocation.m */, + 0EBE790C1C098221D320D3E63EB2BC60 /* OCMMacroState.h */, + 231E38E10643D323C59757FCA0F1996C /* OCMMacroState.m */, + A9DF034B73D13BDA81F88F70F50913C8 /* OCMNotificationPoster.h */, + B61614B63DFF88EDF160FEBF7EB05484 /* OCMNotificationPoster.m */, + B3193F9B76AC15F23E75418697A0ADBD /* OCMObserverRecorder.h */, + 1723599B03030AB02D5CFFD698FA836A /* OCMObserverRecorder.m */, + 090B89A18D929EA6CEFD35331DFFBA55 /* OCMock.h */, + 03186D06B7B71AE137317BC60A6CE121 /* OCMockObject.h */, + ECD1DF4E7C8DB57E5E4BE48FDDAF3091 /* OCMockObject.m */, + F1B09AAA2C17F712826EF600D86DC883 /* OCMPassByRefSetter.h */, + 6352107EC0A782D74D3BF44A85D3A476 /* OCMPassByRefSetter.m */, + 1A97493D9FBA59B4575383AB9A15B287 /* OCMRealObjectForwarder.h */, + 4A5EF931F06BF1C1F76D5CE2C1BBC704 /* OCMRealObjectForwarder.m */, + 0599C4609A3264BE0A6DA170D4520C40 /* OCMRecorder.h */, + 55B5741CCC432EC8DCFD5BE5F72FBB9F /* OCMRecorder.m */, + BDCB316A69C3F852581FF6CC53E64652 /* OCMReturnValueProvider.h */, + 1B372630F618271420D4D0488EB06082 /* OCMReturnValueProvider.m */, + 41B77BDCF056039A9C354161666FCED7 /* OCMStubRecorder.h */, + 70098D1C0228050C3D6F1E89DAD2685B /* OCMStubRecorder.m */, + D298344BF37A3B7C5700A48D47C8C21E /* OCMVerifier.h */, + 4927BE6EEC04B10C75C1FA438914CB8E /* OCMVerifier.m */, + A4541F05BE15B48B91C0C05BB3A2E351 /* OCObserverMockObject.h */, + EADBD9D42E2BA673C654C69A62A9A456 /* OCObserverMockObject.m */, + 1CC7AD574057A73DA9297797A75CEFBB /* OCPartialMockObject.h */, + 507069B5285158B9E9B93D9F5B2456FB /* OCPartialMockObject.m */, + 310F89F7AFD4AD6A0ACF5D36DE44757B /* OCProtocolMockObject.h */, + 6CAA365947ED1983438715063E164BCE /* OCProtocolMockObject.m */, + 57C6283A3067B91632B020C318A9AFD9 /* Support Files */, ); - name = OHPathHelpers; - sourceTree = ""; - }; - 7C4ECFC2B31EF99725AC148FCEB4593C /* NSURLSession */ = { - isa = PBXGroup; - children = ( - B73316F0AB2ADA478403780E72C212FD /* OHHTTPStubs+NSURLSessionConfiguration.m */, - ); - name = NSURLSession; + name = OCMock; + path = OCMock; sourceTree = ""; }; 7D5045F076AA19688475A0F95C420CC3 /* Pods-Darkly_osx */ = { @@ -611,21 +771,12 @@ children = ( 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, A69115D6F216CED414AB012A95729459 /* Frameworks */, - 29CBC31C65965EED523F6F7B12F0F6F2 /* Pods */, + FFC78B0E3E8759DED79A6535AB9F4E5C /* Pods */, D6223665E4F2C93E833F6AE49BE745FB /* Products */, 57B617F7C27F540484B07FFE717F784C /* Targets Support Files */, ); sourceTree = ""; }; - 7DF5F6BAF622D11737D3A473EB0BF524 /* JSON */ = { - isa = PBXGroup; - children = ( - DC67523FE012358DE75EE4A668A5A664 /* OHHTTPStubsResponse+JSON.h */, - 22DBC6DB853E340179B4940B05979383 /* OHHTTPStubsResponse+JSON.m */, - ); - name = JSON; - sourceTree = ""; - }; 8A4F74D66C850821228E8BB28B089BC4 /* OS X */ = { isa = PBXGroup; children = ( @@ -645,127 +796,13 @@ name = Frameworks; sourceTree = ""; }; - C898B66ABA4D7EFEE45ABB3A5A7B0E84 /* OCMock */ = { + A93B8D94802E91A05E2DBC41588A95EE /* OHPathHelpers */ = { isa = PBXGroup; children = ( - CA0B8765C7CF08D814754C0A0F8C1203 /* NSInvocation+OCMAdditions.h */, - F90968080A13E2B5A51548EB12B78716 /* NSInvocation+OCMAdditions.m */, - 0FA9BAC5F5AA8C7555A63F5E968C150C /* NSMethodSignature+OCMAdditions.h */, - 4DFF803BFA99780D0C3AFFBD95CBA6BF /* NSMethodSignature+OCMAdditions.m */, - A4ECE4E53237844F4AE3D13B41379C45 /* NSNotificationCenter+OCMAdditions.h */, - 6BEB5371A9B9E754AC32F232018AE56C /* NSNotificationCenter+OCMAdditions.m */, - D6B17CFA609D4F1BCEB6CD952F6BDF8D /* NSObject+OCMAdditions.h */, - 02335183C4ABC789319BEC9F361F888E /* NSObject+OCMAdditions.m */, - 44E13D1AB2457923E89EDABB9FE7D5C5 /* NSValue+OCMAdditions.h */, - 819D8ECA744A54362D07EA95D9379062 /* NSValue+OCMAdditions.m */, - 9E649BE19BCA5516709BC755EC9FEEC7 /* OCClassMockObject.h */, - 904227180DDE68F8E40C85777E8B4A62 /* OCClassMockObject.m */, - ECCFC10038A1B1EDC39E7F558EBF050B /* OCMArg.h */, - 18C457218CF6FB6FB940AF05DA169610 /* OCMArg.m */, - 0CDDBBF20B9E8B60CCF52A35F23C2768 /* OCMArgAction.h */, - 1DD72F9E4A9CF0C4E710F30B8C337243 /* OCMArgAction.m */, - 3054A0C1BA1392D2CFA6E4540AF0C3CF /* OCMBlockArgCaller.h */, - 518214EDCE7B01015C2BB6271BB772FD /* OCMBlockArgCaller.m */, - D530BD02AFFFAC8355740377AFEE3DA7 /* OCMBlockCaller.h */, - 0E025880705C042E53B065C6BF916CB5 /* OCMBlockCaller.m */, - 83C7FB0444707E5FEEB30DACDFD164A0 /* OCMBoxedReturnValueProvider.h */, - A99F324709D15E97D9B7E7EFD390B810 /* OCMBoxedReturnValueProvider.m */, - 46D954933A68BCA132BA80E0347290D5 /* OCMConstraint.h */, - 7BD1F6749A3FA26D87844207325DE6A1 /* OCMConstraint.m */, - 60700428BE87830C6BA702CCD3998F89 /* OCMExceptionReturnValueProvider.h */, - 0DDCE1AC39485BC4C5E1642371CBC5BD /* OCMExceptionReturnValueProvider.m */, - 1541B7DBA1AD0A7B65F8FFC607CBD5B5 /* OCMExpectationRecorder.h */, - 79234D9DB87E5C7FB6B8F7FAC6C79FCA /* OCMExpectationRecorder.m */, - 45C88E7B8B282A7217B488B0529A4485 /* OCMFunctions.h */, - 9EB28608957FFF3FE5D9F73B3FBFA7F3 /* OCMFunctions.m */, - CF4197454D6EDFC7E82D1585BA642113 /* OCMFunctionsPrivate.h */, - 463DD1D3C40C8339739DF808CF0C84D2 /* OCMIndirectReturnValueProvider.h */, - F0ECB43B373A36914B537E7B9B076175 /* OCMIndirectReturnValueProvider.m */, - EEF775C9BCEB8619F891533C3F4D5442 /* OCMInvocationExpectation.h */, - 20086391A8D8523A58D226AAEA76DA06 /* OCMInvocationExpectation.m */, - 159BA483E378F5A628582FF68CE1783E /* OCMInvocationMatcher.h */, - 8BBF7B3840A36892D14C230FC55B3B64 /* OCMInvocationMatcher.m */, - 258EB08B29B8B6225E55EF8CFD05B229 /* OCMInvocationStub.h */, - A252F33D314FA7E7ECBDF0DF9A105B26 /* OCMInvocationStub.m */, - EAD198096F351A1A1FBAFD2D6B8FF082 /* OCMLocation.h */, - 786D1550089C847D816C575010098672 /* OCMLocation.m */, - FF53F1EAB9D81A6DED2CC5DDC68998B5 /* OCMMacroState.h */, - 69AC1833240D28BD3D8461ED1935EDB1 /* OCMMacroState.m */, - 52DECA100A517BF5699E9107D127C358 /* OCMNotificationPoster.h */, - 49DBE0583AD97FC7AFD40E44B6ADD0C3 /* OCMNotificationPoster.m */, - A8A9B0ADD858339D3D15CABD60856848 /* OCMObserverRecorder.h */, - 8F51EF986FA63F62D8ABE46B5A0EF12E /* OCMObserverRecorder.m */, - E7EEA70FD196A9350783D5F26267EA36 /* OCMock.h */, - 2AC64AA2A942523B078445719823594D /* OCMockObject.h */, - 8F1B9C604582097FDE56BFE0E5DBD7D8 /* OCMockObject.m */, - A28DA38325D7E02D324E31F1806EF191 /* OCMPassByRefSetter.h */, - 5D02AD8B359F811CAEC7E5C2A272E5DE /* OCMPassByRefSetter.m */, - 4FBF42418B3DBA6C8DFFF85F037DBDFF /* OCMRealObjectForwarder.h */, - A528657BFE7C565C8EE90EC0CDCDE385 /* OCMRealObjectForwarder.m */, - 66AB82BF52B8DE62BC2A72C2592E23F1 /* OCMRecorder.h */, - 65C0919CA083CA15AE160F0D4A37025B /* OCMRecorder.m */, - 5C3D6B768DE17A3E7C6D7B12C635477B /* OCMReturnValueProvider.h */, - E9F043D9D4D153EF80F85834A3D728DE /* OCMReturnValueProvider.m */, - F4B0192087B27BECEC0F7E1799558045 /* OCMStubRecorder.h */, - 64E4BC5A9889E0DB1F19CDE2935049BB /* OCMStubRecorder.m */, - E736DD7FFDD3BE1C73A56D0CCAE7AD54 /* OCMVerifier.h */, - C9EDE1211E7858C38AF1D41AB7FEC624 /* OCMVerifier.m */, - 1AFC0F519CA2ABEEF80E7347EF34382C /* OCObserverMockObject.h */, - 588831B3C11013BFB9C663AE9D5DE56F /* OCObserverMockObject.m */, - 9FA926717A94389A01F7FE7C87C4FDC8 /* OCPartialMockObject.h */, - DE7B054EF1D513FB1CDAF343DF48E695 /* OCPartialMockObject.m */, - 96715FCBD819314A9A12ADE3BEFE83EC /* OCProtocolMockObject.h */, - 6312591EA5F95D2A26205C563B9FC872 /* OCProtocolMockObject.m */, - D517A4E59AC8588D080D365D2084CA7E /* Support Files */, + 01D31133F0D3AEF0B3A66C21F9DFAE2B /* OHPathHelpers.h */, + FAB8ABEA5FDD50C6707CDCDBD3AFAEA0 /* OHPathHelpers.m */, ); - name = OCMock; - path = OCMock; - sourceTree = ""; - }; - CD7CF6AAB530C65C26EB25C94A79AB8D /* Support Files */ = { - isa = PBXGroup; - children = ( - FF27A232F4D681E7DEE0C59628C61BC0 /* DarklyEventSource-iOS.modulemap */, - D924AEF6CECEB1D0756A6D21740A2925 /* DarklyEventSource-iOS.xcconfig */, - BCF6BBBE88BA653337EDA896BDA90286 /* DarklyEventSource-iOS-dummy.m */, - DDE2AD1F28739DDD3342A292E311A197 /* DarklyEventSource-iOS-prefix.pch */, - 08FF4A78E367C4F9B25AE240A893A6F4 /* DarklyEventSource-iOS-umbrella.h */, - 5E0A94E4BDADF3C941DD3C42FE7CBEEA /* DarklyEventSource-macOS.modulemap */, - FB1A891B992A9061F78813B1AA7D2F04 /* DarklyEventSource-macOS.xcconfig */, - AF29C88DAD0388B63CC569E3BF29E5E5 /* DarklyEventSource-macOS-dummy.m */, - CD1EE456601A5EB7D58613A43F503E4D /* DarklyEventSource-macOS-prefix.pch */, - A61A859375079B183E1F4F6E8F4466A7 /* DarklyEventSource-macOS-umbrella.h */, - F0E59B23BCEE7B4288CE96404C873232 /* DarklyEventSource-tvOS.modulemap */, - 69E471AA3D416E30075E8211F8F18658 /* DarklyEventSource-tvOS.xcconfig */, - 7D94DD2D5D1140DF93748DB6D561CB1F /* DarklyEventSource-tvOS-dummy.m */, - A9BE3017575270B4AA55F3CCED88D45C /* DarklyEventSource-tvOS-prefix.pch */, - 2CA2FFC0331CABB7FE9E51E64057D4F1 /* DarklyEventSource-tvOS-umbrella.h */, - 53C639006DD65A1AB87E8294614015A8 /* DarklyEventSource-watchOS.modulemap */, - 5AE7493F94D2FBC5B5F66688C6D9F366 /* DarklyEventSource-watchOS.xcconfig */, - 05F3FA1F97CBD4005BEF2CF650BEBB66 /* DarklyEventSource-watchOS-dummy.m */, - FB101544D209AA66FB3E8EC07343FD31 /* DarklyEventSource-watchOS-prefix.pch */, - 551E4D0696B6AAC86FC5B613618BF934 /* DarklyEventSource-watchOS-umbrella.h */, - 727D176E2322A0F07FE730B07254293F /* Info.plist */, - B4C08EC41761484BD661B45AE7FCBE69 /* Info.plist */, - 2E208BFC2495D241F777F565DF350CC8 /* Info.plist */, - 0C6A85CE7D53722B586097905D107537 /* Info.plist */, - ); - name = "Support Files"; - path = "../Target Support Files/DarklyEventSource-iOS"; - sourceTree = ""; - }; - D517A4E59AC8588D080D365D2084CA7E /* Support Files */ = { - isa = PBXGroup; - children = ( - B345DF905AABDA409B1830831D150BF0 /* Info.plist */, - 7BB9C703084B753AC26EBB8FCEA4D80A /* OCMock.modulemap */, - 01C36B32B951D33CF0C2806C2DA582DA /* OCMock.xcconfig */, - 525390A8E371A083C74C84FA87DA1B8C /* OCMock-dummy.m */, - BCCC2502B43C367FD6F0198B4CD2537F /* OCMock-prefix.pch */, - EA932F95D294477A15F2195FAA819A89 /* OCMock-umbrella.h */, - ); - name = "Support Files"; - path = "../Target Support Files/OCMock"; + name = OHPathHelpers; sourceTree = ""; }; D6223665E4F2C93E833F6AE49BE745FB /* Products */ = { @@ -821,6 +858,19 @@ path = "Target Support Files/Pods-Darkly_tvOS"; sourceTree = ""; }; + EF5DAB81B67A02CF44AF894DBEC124BA /* OHHTTPStubs */ = { + isa = PBXGroup; + children = ( + 5774D035C1AECC5EF1A87F1E5AE5EE05 /* Core */, + 15F6C58B22795C85AAA776EE8A5EDB46 /* JSON */, + 16763A74BC38C1A4974227391178AD07 /* NSURLSession */, + A93B8D94802E91A05E2DBC41588A95EE /* OHPathHelpers */, + 41319C1BA187DFADE47831D2F31C6092 /* Support Files */, + ); + name = OHHTTPStubs; + path = OHHTTPStubs; + sourceTree = ""; + }; F1E099E5B288576D1BCE8B7F197C01D1 /* iOS */ = { isa = PBXGroup; children = ( @@ -830,41 +880,19 @@ name = iOS; sourceTree = ""; }; - FBF9A2F34CD3FAD1FCA04DC8A93FFECC /* Core */ = { + FFC78B0E3E8759DED79A6535AB9F4E5C /* Pods */ = { isa = PBXGroup; children = ( - 36EF2A58593920A528EB346E42F18C19 /* Compatibility.h */, - EF760A95F22C50A6B60AECA5D320903C /* OHHTTPStubs.h */, - D183ABD8F5D0BF9ED235E3E57FF77187 /* OHHTTPStubs.m */, - B309EA5ED7C7F03F08835B7C48F3B1E8 /* OHHTTPStubsResponse.h */, - 5DDB0218B64211CC03C9DC1FF617998F /* OHHTTPStubsResponse.m */, + 77F4238802BE854B64EAEBB7F8796DE7 /* DarklyEventSource */, + 7C9AFB853B35A28FC3D67FE64606BEB8 /* OCMock */, + EF5DAB81B67A02CF44AF894DBEC124BA /* OHHTTPStubs */, ); - name = Core; + name = Pods; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 23B97A2B9DBBD4579377008590BEC955 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - BE82F80E7D6D501CF46925D6E25B15E1 /* DarklyEventSource-watchOS-umbrella.h in Headers */, - C12846B090288AFA42CEA099276F8437 /* DarklyEventSource.h in Headers */, - A537068C9D2B7475B22CB50D9C44C76B /* LDEventSource.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 53E8A31E294E297ED9DA54E7CC5910CD /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 076CC6AEFAE06B5E31AE2CA3860FD54A /* DarklyEventSource-macOS-umbrella.h in Headers */, - C744661BA7AA467154A805FDC2C6B63C /* DarklyEventSource.h in Headers */, - 64EAAFD6C90AB106CC975DB79853F924 /* LDEventSource.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 58BCA11C9FD2C5E4AA937799598679BC /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -873,13 +901,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 78DF58640276B9E16FCF7C1747BE15F0 /* Headers */ = { + 7FA555AD18D3D50544ADCB739CCA1739 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - A992DC3E5A593C9641D59707DBF9CF0E /* DarklyEventSource-iOS-umbrella.h in Headers */, - AF8EFDAD5DAF96A49EE0BC9A948280F3 /* DarklyEventSource.h in Headers */, - 35675FF5ABBB5D07872C3F48DA96ED6D /* LDEventSource.h in Headers */, + 4F6D69615A0C04979C46224CBDA40248 /* DarklyEventSource-iOS-umbrella.h in Headers */, + F14B9BCDC8C388965C9F2EA4CBEFC332 /* DarklyEventSource.h in Headers */, + 0402FEBC05924668535DB83CFBC838FA /* LDEventParser.h in Headers */, + D492EBF6E985107A62B877B98B22DCA9 /* LDEventSource.h in Headers */, + 7948015575C406A1165344B9B8040970 /* LDEventStringAccumulator.h in Headers */, + A92799D357ECEF8522109331DDB1E776 /* NSArray+LDEventSource.h in Headers */, + 975D664B5CE539645C2398648083A0BF /* NSString+LDEventSource.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -942,6 +974,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + AA141FA4183AB2ADEA272B78190D2C7C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 44064E11259641498118404EC4CD3FB3 /* DarklyEventSource-watchOS-umbrella.h in Headers */, + 12DC5FE2A5D12F52C6F46B3A01B653E9 /* DarklyEventSource.h in Headers */, + 1DBBF95199F3F19C49EACB655ACA342C /* LDEventParser.h in Headers */, + 8B03DF7981B4BF251F279EAA48E7A442 /* LDEventSource.h in Headers */, + 04F38EEB14F5F1762A4C39256951BA3A /* LDEventStringAccumulator.h in Headers */, + 9362C21CB9FD8EED2DFE9CBC526D3468 /* NSArray+LDEventSource.h in Headers */, + 1688C178D208025491DB68DC91C7D3E5 /* NSString+LDEventSource.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; BBF6AFEFD86537978D878A0C2BE81C0A /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -950,13 +996,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C3C0AD3DC999A9A82D8A91249B568A65 /* Headers */ = { + E435DFC620B2FAC4D1CA812795E42E23 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 368CB50FFBFF7DB7F817A4F36572BBCB /* DarklyEventSource-tvOS-umbrella.h in Headers */, - F90421F35E91B0A952234BEF474030B6 /* DarklyEventSource.h in Headers */, - 905A8D955AE02C6B8D2DBBFD1FF33427 /* LDEventSource.h in Headers */, + ACF5BA5D185CFEDD532E64EC32492949 /* DarklyEventSource-macOS-umbrella.h in Headers */, + 11DE0D9009566837614C175C449D7A30 /* DarklyEventSource.h in Headers */, + F9DACB363D43317F77E29E6EA9EB0C0F /* LDEventParser.h in Headers */, + 9A911861498FDE14AEC7BFC78272C2D1 /* LDEventSource.h in Headers */, + F58B2C0727FA65E03CF6E16F35732B36 /* LDEventStringAccumulator.h in Headers */, + 2B2D0FCD965DA9CA9F738633D9121EA1 /* NSArray+LDEventSource.h in Headers */, + 236353C203FBF8AA4F6BBE247917E0DF /* NSString+LDEventSource.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -973,6 +1023,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E60B9404B42CDE61EC028BE66D77F6BE /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D8E669F4646F84CAB8F6A945E11085A2 /* DarklyEventSource-tvOS-umbrella.h in Headers */, + D25C35F5B0196C1AFFAE6E77FC03BF7C /* DarklyEventSource.h in Headers */, + 83AF9E0B3681FAC1077E0147AFEA4DC6 /* LDEventParser.h in Headers */, + AB04782D2FF294BAA37A65D874BE2D62 /* LDEventSource.h in Headers */, + 764D2C2F0BD1E022E03FA591E7BE8448 /* LDEventStringAccumulator.h in Headers */, + 7302D41CB0CC8A04F2A872F9A9215060 /* NSArray+LDEventSource.h in Headers */, + C8040A454AC476D244C119D9209BE885 /* NSString+LDEventSource.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F27667E68C34E32F7F75D29A8FC4F67C /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1004,23 +1068,6 @@ productReference = 555FD603D5365F6C3C41E35AC29D8B8F /* Pods_DarklyTests.framework */; productType = "com.apple.product-type.framework"; }; - 23353DF7BDC41C3D6012FB12E34C8BC2 /* DarklyEventSource-macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = D7B271B7D4F112D06EB557C0364814C0 /* Build configuration list for PBXNativeTarget "DarklyEventSource-macOS" */; - buildPhases = ( - 82EF9B7BCD461F6D5A3E450C24A74BCF /* Sources */, - BC0C0D8E30296A60E074A7FDF19F3FA3 /* Frameworks */, - 53E8A31E294E297ED9DA54E7CC5910CD /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "DarklyEventSource-macOS"; - productName = "DarklyEventSource-macOS"; - productReference = 4806A26F6F06A60EDC216E00AD3F6069 /* DarklyEventSource.framework */; - productType = "com.apple.product-type.framework"; - }; 300BC2962080EE2E26EB19804423F8F6 /* Pods-Darkly_watchOS */ = { isa = PBXNativeTarget; buildConfigurationList = 85C050E44AACD798A929BA139AADB209 /* Build configuration list for PBXNativeTarget "Pods-Darkly_watchOS" */; @@ -1039,6 +1086,23 @@ productReference = A4424D4B5E382A3ECC8EEFECD5134053 /* Pods_Darkly_watchOS.framework */; productType = "com.apple.product-type.framework"; }; + 4CB4D69CF5144447B5EDA7EEB0CF8FB2 /* DarklyEventSource-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0EEE11367C18E4D1E4F21A5854F438F8 /* Build configuration list for PBXNativeTarget "DarklyEventSource-iOS" */; + buildPhases = ( + 89E347AA3121BC742A20F5C8BD3B1552 /* Sources */, + 7FB39C65E37FDAF623E3E2844F78E49B /* Frameworks */, + 7FA555AD18D3D50544ADCB739CCA1739 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "DarklyEventSource-iOS"; + productName = "DarklyEventSource-iOS"; + productReference = BBC5EE03E32233D0CB90090019393036 /* DarklyEventSource.framework */; + productType = "com.apple.product-type.framework"; + }; 4EBACACD2D000AD0FC81268F91F37592 /* OHHTTPStubs */ = { isa = PBXNativeTarget; buildConfigurationList = D730011A32AA30B718AEB0127136E17F /* Build configuration list for PBXNativeTarget "OHHTTPStubs" */; @@ -1056,13 +1120,13 @@ productReference = B67F188C9F7107615562C35BE204FB48 /* OHHTTPStubs.framework */; productType = "com.apple.product-type.framework"; }; - 71346A8B51F5CEE42752F393F365D76C /* DarklyEventSource-tvOS */ = { + 5BEC0EEA943679FEADECF05233863088 /* DarklyEventSource-tvOS */ = { isa = PBXNativeTarget; - buildConfigurationList = ABCBF2C9686D687471766A52054194B0 /* Build configuration list for PBXNativeTarget "DarklyEventSource-tvOS" */; + buildConfigurationList = AD4D21147BCFC923BB2938068EB2B482 /* Build configuration list for PBXNativeTarget "DarklyEventSource-tvOS" */; buildPhases = ( - 76BB1397CF8EE309176AFE836DBDAF4E /* Sources */, - 70228B8E8F114C0FB2F9CA7F5C5F65C2 /* Frameworks */, - C3C0AD3DC999A9A82D8A91249B568A65 /* Headers */, + 6D5C7B1C8C3BBA6BED9CBC62D6145D30 /* Sources */, + 63476FC136C65391B39A0616BCDD650E /* Frameworks */, + E60B9404B42CDE61EC028BE66D77F6BE /* Headers */, ); buildRules = ( ); @@ -1073,13 +1137,30 @@ productReference = DA744B745981F85B4555A78005CA3DB7 /* DarklyEventSource.framework */; productType = "com.apple.product-type.framework"; }; - 8E061107B742D8EF4D21D5B1ACD3767D /* DarklyEventSource-watchOS */ = { + 68D1B6C8D28C8A9C0D8496A5D39F2BB2 /* DarklyEventSource-macOS */ = { isa = PBXNativeTarget; - buildConfigurationList = 4A7EBCCAB18AD4C0DEA71EF436FD82AF /* Build configuration list for PBXNativeTarget "DarklyEventSource-watchOS" */; + buildConfigurationList = 9F65D338A8F448BF326CED53D3CDF787 /* Build configuration list for PBXNativeTarget "DarklyEventSource-macOS" */; buildPhases = ( - 14A326FE282AE43D27C8009ACF07E0B6 /* Sources */, - 3B4F4B0BCDD1B2C21722641A17FC491A /* Frameworks */, - 23B97A2B9DBBD4579377008590BEC955 /* Headers */, + 94D5AF7DA92F63C803AB5F4A1B0A2624 /* Sources */, + 5C1FF83D7AF573314D3ACD0DFA879C50 /* Frameworks */, + E435DFC620B2FAC4D1CA812795E42E23 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "DarklyEventSource-macOS"; + productName = "DarklyEventSource-macOS"; + productReference = 4806A26F6F06A60EDC216E00AD3F6069 /* DarklyEventSource.framework */; + productType = "com.apple.product-type.framework"; + }; + AC7A2FCACC2CADFC962A30DC28D71677 /* DarklyEventSource-watchOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 82FE95A49E3A920CC33790AF43F8B6AA /* Build configuration list for PBXNativeTarget "DarklyEventSource-watchOS" */; + buildPhases = ( + 376D31466E08B24CDE08AB65F87559AB /* Sources */, + 13A8CCE889D2F8BEA8CE6BA3B4921CC1 /* Frameworks */, + AA141FA4183AB2ADEA272B78190D2C7C /* Headers */, ); buildRules = ( ); @@ -1125,23 +1206,6 @@ productReference = 24B720F4F66D5399540949C2350A7494 /* OCMock.framework */; productType = "com.apple.product-type.framework"; }; - DEDCD49950C45F16C07E6D98D2C96B27 /* DarklyEventSource-iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0EC5D409CF5C3667C25963881EA5AF7C /* Build configuration list for PBXNativeTarget "DarklyEventSource-iOS" */; - buildPhases = ( - 8A4BF30E94048788E8FC7D3BF92DB782 /* Sources */, - 916374B3DC408166DFE76F136EF886A4 /* Frameworks */, - 78DF58640276B9E16FCF7C1747BE15F0 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "DarklyEventSource-iOS"; - productName = "DarklyEventSource-iOS"; - productReference = BBC5EE03E32233D0CB90090019393036 /* DarklyEventSource.framework */; - productType = "com.apple.product-type.framework"; - }; E2F871F28BFDAAE9ACE8B6D49F6D79A4 /* Pods-Darkly_osx */ = { isa = PBXNativeTarget; buildConfigurationList = 0137BC0884EEE2A9B71A6E364D622B91 /* Build configuration list for PBXNativeTarget "Pods-Darkly_osx" */; @@ -1199,10 +1263,10 @@ projectDirPath = ""; projectRoot = ""; targets = ( - DEDCD49950C45F16C07E6D98D2C96B27 /* DarklyEventSource-iOS */, - 23353DF7BDC41C3D6012FB12E34C8BC2 /* DarklyEventSource-macOS */, - 71346A8B51F5CEE42752F393F365D76C /* DarklyEventSource-tvOS */, - 8E061107B742D8EF4D21D5B1ACD3767D /* DarklyEventSource-watchOS */, + 4CB4D69CF5144447B5EDA7EEB0CF8FB2 /* DarklyEventSource-iOS */, + 68D1B6C8D28C8A9C0D8496A5D39F2BB2 /* DarklyEventSource-macOS */, + 5BEC0EEA943679FEADECF05233863088 /* DarklyEventSource-tvOS */, + AC7A2FCACC2CADFC962A30DC28D71677 /* DarklyEventSource-watchOS */, DC54C98851E54D349520C786701A1845 /* OCMock */, 4EBACACD2D000AD0FC81268F91F37592 /* OHHTTPStubs */, CD84857868E686FF20FDA2FF9F2FB39A /* Pods-Darkly_iOS */, @@ -1231,20 +1295,24 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 14A326FE282AE43D27C8009ACF07E0B6 /* Sources */ = { + 25C1F883AC1FECD35AA5BCF537F0B47D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 95687EEBD9E7FCE1BFDBAF31A3397B1F /* DarklyEventSource-watchOS-dummy.m in Sources */, - 132CF79716773FE77D67268D06A35978 /* LDEventSource.m in Sources */, + 8F30CAC67C96350CFF1E9722246D0E16 /* Pods-Darkly_osx-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 25C1F883AC1FECD35AA5BCF537F0B47D /* Sources */ = { + 376D31466E08B24CDE08AB65F87559AB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8F30CAC67C96350CFF1E9722246D0E16 /* Pods-Darkly_osx-dummy.m in Sources */, + 5C84D78343D338348A6B022472839715 /* DarklyEventSource-watchOS-dummy.m in Sources */, + 58BCAEFDBA55841E4F906B01CF49C52F /* LDEventParser.m in Sources */, + 7B8249528D562F3778CAA304EBAF2E48 /* LDEventSource.m in Sources */, + 7DF516CC95FFABF24F5B3F3AACAFA16C /* LDEventStringAccumulator.m in Sources */, + 42B089CA2DA1147F0EDDAD112688A8DB /* NSArray+LDEventSource.m in Sources */, + E259E475E1415923B893619ACB07EBE3 /* NSString+LDEventSource.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1256,30 +1324,42 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 76BB1397CF8EE309176AFE836DBDAF4E /* Sources */ = { + 6D5C7B1C8C3BBA6BED9CBC62D6145D30 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - F22E6173EA55588F0007F80E936A13F5 /* DarklyEventSource-tvOS-dummy.m in Sources */, - 31DE30A78E0C0F9440A2B571B9E62F23 /* LDEventSource.m in Sources */, + 516BF1D058670F046085DB4A05AA7A16 /* DarklyEventSource-tvOS-dummy.m in Sources */, + 56510998D6857BE91A0B3EFD76591175 /* LDEventParser.m in Sources */, + A634F7839279355FC428FBC9F8EAE69F /* LDEventSource.m in Sources */, + 9EEF57CBD78D08F318622E44C56AF195 /* LDEventStringAccumulator.m in Sources */, + 54F85174C22EDF0D7D6085FC56ADEC8A /* NSArray+LDEventSource.m in Sources */, + CD8E222FA718C250BF148F4AEDB2D830 /* NSString+LDEventSource.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 82EF9B7BCD461F6D5A3E450C24A74BCF /* Sources */ = { + 89E347AA3121BC742A20F5C8BD3B1552 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E17BB96FF8264DADDCAE9B5932C94BF3 /* DarklyEventSource-macOS-dummy.m in Sources */, - 3DC778E2C31E48A95C709B00CA42B6E2 /* LDEventSource.m in Sources */, + 52AD26246A4D997309909632620EE74A /* DarklyEventSource-iOS-dummy.m in Sources */, + E95D294A9543055B453867A057AD02A8 /* LDEventParser.m in Sources */, + 513E960012E60C5C27E3E81C27530BFE /* LDEventSource.m in Sources */, + 0AE40A1E851BE5D309075E387BDBA9EB /* LDEventStringAccumulator.m in Sources */, + CEE265209CB926C5500EA652FC50D71C /* NSArray+LDEventSource.m in Sources */, + CE514D77DA72D78D9C132E139DBFDF65 /* NSString+LDEventSource.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 8A4BF30E94048788E8FC7D3BF92DB782 /* Sources */ = { + 94D5AF7DA92F63C803AB5F4A1B0A2624 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7AD8CDC8F994F9A30A70576930F1EF95 /* DarklyEventSource-iOS-dummy.m in Sources */, - 42624ACF24F9C1BB0BB53C47F081AD55 /* LDEventSource.m in Sources */, + 37E93D19B89637966E0112DD9318BC78 /* DarklyEventSource-macOS-dummy.m in Sources */, + A04AC375476D11A1389172A9299D7823 /* LDEventParser.m in Sources */, + 1D96772AA2887C52505E87AF89AB5959 /* LDEventSource.m in Sources */, + 9D4E7A33AAC08041F4C84C6CBFE481EE /* LDEventStringAccumulator.m in Sources */, + 347D74A81D0C344A8E45EFCA3AC2059C /* NSArray+LDEventSource.m in Sources */, + AD216A93F5B1E3984DB3E04AE877A797 /* NSString+LDEventSource.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1351,19 +1431,19 @@ 0B2E146CF2C970853419820AFCF49DF3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "DarklyEventSource-tvOS"; - target = 71346A8B51F5CEE42752F393F365D76C /* DarklyEventSource-tvOS */; + target = 5BEC0EEA943679FEADECF05233863088 /* DarklyEventSource-tvOS */; targetProxy = FFA32DB3EB1843333366A8DC0D3F02BD /* PBXContainerItemProxy */; }; 245B27BBBD712CCD91D393983821106D /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "DarklyEventSource-watchOS"; - target = 8E061107B742D8EF4D21D5B1ACD3767D /* DarklyEventSource-watchOS */; + target = AC7A2FCACC2CADFC962A30DC28D71677 /* DarklyEventSource-watchOS */; targetProxy = 76626172BA93E6933CC14D453CA0965F /* PBXContainerItemProxy */; }; 3CBC1C33825D3D1A1812132214790A11 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "DarklyEventSource-macOS"; - target = 23353DF7BDC41C3D6012FB12E34C8BC2 /* DarklyEventSource-macOS */; + target = 68D1B6C8D28C8A9C0D8496A5D39F2BB2 /* DarklyEventSource-macOS */; targetProxy = 9493013B3B7ECFD46F186E1136A8C45F /* PBXContainerItemProxy */; }; 3FE312A3610795A9A003CFA75EFAA355 /* PBXTargetDependency */ = { @@ -1375,7 +1455,7 @@ 7BDD98A8C685DE63BD2FBDBEAAC2FB4C /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "DarklyEventSource-iOS"; - target = DEDCD49950C45F16C07E6D98D2C96B27 /* DarklyEventSource-iOS */; + target = 4CB4D69CF5144447B5EDA7EEB0CF8FB2 /* DarklyEventSource-iOS */; targetProxy = C9E80F6ED2CD63F802164E64C32AADEB /* PBXContainerItemProxy */; }; CF762704330DDA09C99C96845952193D /* PBXTargetDependency */ = { @@ -1387,15 +1467,15 @@ E4163809E55FD6C6775ECA69B9A51910 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = "DarklyEventSource-iOS"; - target = DEDCD49950C45F16C07E6D98D2C96B27 /* DarklyEventSource-iOS */; + target = 4CB4D69CF5144447B5EDA7EEB0CF8FB2 /* DarklyEventSource-iOS */; targetProxy = EAB2DE532572D85409AA6D1ED76A00CB /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 01B0A2CE48C6B2ABA62F60779E37676E /* Debug */ = { + 2D201B8FF7AC8B13FF4FF1DAEA7385D5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5AE7493F94D2FBC5B5F66688C6D9F366 /* DarklyEventSource-watchOS.xcconfig */; + baseConfigurationReference = 0CC4F03DA9E9B1D48355E1E15BC481D8 /* Pods-Darkly_tvOS.release.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; @@ -1407,25 +1487,29 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/DarklyEventSource-watchOS/Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-Darkly_tvOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS.modulemap"; - PRODUCT_NAME = DarklyEventSource; - SDKROOT = watchos; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Darkly_tvOS/Pods-Darkly_tvOS.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_Darkly_tvOS; + SDKROOT = appletvos; SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - TARGETED_DEVICE_FAMILY = 4; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.0; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; }; - name = Debug; + name = Release; }; - 2D201B8FF7AC8B13FF4FF1DAEA7385D5 /* Release */ = { + 4197F0452032BAB78D7A35F5C8CB1D9E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0CC4F03DA9E9B1D48355E1E15BC481D8 /* Pods-Darkly_tvOS.release.xcconfig */; + baseConfigurationReference = FB4F92F13F88313EC2B1F88227D626B2 /* DarklyEventSource-tvOS.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; @@ -1437,18 +1521,15 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-Darkly_tvOS/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/DarklyEventSource-tvOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Darkly_tvOS/Pods-Darkly_tvOS.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_Darkly_tvOS; + MODULEMAP_FILE = "Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS.modulemap"; + PRODUCT_NAME = DarklyEventSource; SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; @@ -1457,9 +1538,9 @@ }; name = Release; }; - 540284FC9266A0EF666026BF11068037 /* Release */ = { + 5127A2BF5FD2D128B3F070E61AD7A07B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FC625021C19302529D54CDCF201E24F0 /* Pods-Darkly_iOS.release.xcconfig */; + baseConfigurationReference = 826FD6C5044ECF8A5CB7355F77BB3E3C /* DarklyEventSource-watchOS.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; @@ -1471,30 +1552,27 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-Darkly_iOS/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/DarklyEventSource-watchOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-Darkly_iOS/Pods-Darkly_iOS.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_Darkly_iOS; - SDKROOT = iphoneos; + MODULEMAP_FILE = "Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS.modulemap"; + PRODUCT_NAME = DarklyEventSource; + SDKROOT = watchos; SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = 4; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; - name = Release; + name = Debug; }; - 559EA4974ACBF6ED4CD1D77022DD73B8 /* Release */ = { + 540284FC9266A0EF666026BF11068037 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B8BF5256A9C8C89D14B7E5583327B291 /* OHHTTPStubs.xcconfig */; + baseConfigurationReference = FC625021C19302529D54CDCF201E24F0 /* Pods-Darkly_iOS.release.xcconfig */; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; @@ -1504,16 +1582,19 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/OHHTTPStubs/Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-Darkly_iOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap"; - PRODUCT_NAME = OHHTTPStubs; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Darkly_iOS/Pods-Darkly_iOS.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_Darkly_iOS; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -1521,11 +1602,10 @@ }; name = Release; }; - 56DAED87C61EEF4C26CCC05DD9A7A23A /* Release */ = { + 559EA4974ACBF6ED4CD1D77022DD73B8 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D924AEF6CECEB1D0756A6D21740A2925 /* DarklyEventSource-iOS.xcconfig */; + baseConfigurationReference = 13EF633664B9DF304A8EF579DC80553C /* OHHTTPStubs.xcconfig */; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; @@ -1535,13 +1615,13 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/DarklyEventSource-iOS/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/OHHTTPStubs/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS.modulemap"; - PRODUCT_NAME = DarklyEventSource; + MODULEMAP_FILE = "Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap"; + PRODUCT_NAME = OHHTTPStubs; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; @@ -1585,40 +1665,40 @@ }; name = Debug; }; - 5CD7EE2C9CE4F211AB4C5386A53C3EC0 /* Debug */ = { + 68B98E48D846C66AEA228EE0D701C359 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FB1A891B992A9061F78813B1AA7D2F04 /* DarklyEventSource-macOS.xcconfig */; + baseConfigurationReference = 6D1989826B44821C35BA9D1771779DB0 /* DarklyEventSource-iOS.xcconfig */; buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/DarklyEventSource-macOS/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/DarklyEventSource-iOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - MODULEMAP_FILE = "Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS.modulemap"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS.modulemap"; PRODUCT_NAME = DarklyEventSource; - SDKROOT = macosx; + SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; 6FD3F1132C038BFFB4C38C3B3EF21966 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 01C36B32B951D33CF0C2806C2DA582DA /* OCMock.xcconfig */; + baseConfigurationReference = DF368D68A25D4124776D48F7C58AA92E /* OCMock.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -1646,6 +1726,37 @@ }; name = Release; }; + 8074B2E26C15D533E29285598C7691C2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E10DACFD9E8FFDF19636BBA48FA53AE9 /* DarklyEventSource-macOS.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/DarklyEventSource-macOS/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MODULEMAP_FILE = "Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS.modulemap"; + PRODUCT_NAME = DarklyEventSource; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; 85087B77170DCC24B2FC2411A101D160 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 92A0D6A6BF74D42B98D2D372CD1DBB72 /* Pods-Darkly_tvOS.debug.xcconfig */; @@ -1681,7 +1792,7 @@ }; 8F71F8D7CD0F3FF748ED93E10C98C84F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 01C36B32B951D33CF0C2806C2DA582DA /* OCMock.xcconfig */; + baseConfigurationReference = DF368D68A25D4124776D48F7C58AA92E /* OCMock.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -1708,37 +1819,6 @@ }; name = Debug; }; - 93B6F0E437E164A43B94B692C503BAE5 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5AE7493F94D2FBC5B5F66688C6D9F366 /* DarklyEventSource-watchOS.xcconfig */; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CODE_SIGN_IDENTITY = ""; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/DarklyEventSource-watchOS/Info.plist"; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS.modulemap"; - PRODUCT_NAME = DarklyEventSource; - SDKROOT = watchos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - TARGETED_DEVICE_FAMILY = 4; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; - }; - name = Release; - }; 96A2C7C46357B841B0AE348E6CAD21CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1805,9 +1885,9 @@ }; name = Debug; }; - C1A2EBA401172237C09F831AAA6B8732 /* Debug */ = { + A5B5A8F13D711F2754A62CF84842B41F /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 69E471AA3D416E30075E8211F8F18658 /* DarklyEventSource-tvOS.xcconfig */; + baseConfigurationReference = 826FD6C5044ECF8A5CB7355F77BB3E3C /* DarklyEventSource-watchOS.xcconfig */; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; @@ -1819,26 +1899,28 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/DarklyEventSource-tvOS/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/DarklyEventSource-watchOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS.modulemap"; + MODULEMAP_FILE = "Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS.modulemap"; PRODUCT_NAME = DarklyEventSource; - SDKROOT = appletvos; + SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; + TARGETED_DEVICE_FAMILY = 4; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 2.0; }; - name = Debug; + name = Release; }; - C26283719F897ADDC58A81AE56B8D7D8 /* Release */ = { + A83DAE37D17EBEA0FBE957EE04D1459F /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8451E511B5A2EB7FEDD129E4940CC68A /* Pods-DarklyTests.release.xcconfig */; + baseConfigurationReference = 6D1989826B44821C35BA9D1771779DB0 /* DarklyEventSource-iOS.xcconfig */; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; @@ -1848,93 +1930,89 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = "Target Support Files/Pods-DarklyTests/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/DarklyEventSource-iOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MODULEMAP_FILE = "Target Support Files/Pods-DarklyTests/Pods-DarklyTests.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_DarklyTests; + MODULEMAP_FILE = "Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS.modulemap"; + PRODUCT_NAME = DarklyEventSource; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Debug; }; - C4D949D3DAFA9D9AECA84E7FE083C686 /* Debug */ = { + B47ED0A1784F706FC60EDD3D999039AC /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D924AEF6CECEB1D0756A6D21740A2925 /* DarklyEventSource-iOS.xcconfig */; + baseConfigurationReference = E10DACFD9E8FFDF19636BBA48FA53AE9 /* DarklyEventSource-macOS.xcconfig */; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_OBJC_WEAK = NO; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/DarklyEventSource-iOS/Info.plist"; + FRAMEWORK_VERSION = A; + GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/DarklyEventSource-macOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS.modulemap"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MODULEMAP_FILE = "Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS.modulemap"; PRODUCT_NAME = DarklyEventSource; - SDKROOT = iphoneos; + SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Debug; + name = Release; }; - C70A38866A9341D720D50F6C81B6B587 /* Release */ = { + C26283719F897ADDC58A81AE56B8D7D8 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F3D6D576A18C95622C205C717C11AFD6 /* Pods-Darkly_osx.release.xcconfig */; + baseConfigurationReference = 8451E511B5A2EB7FEDD129E4940CC68A /* Pods-DarklyTests.release.xcconfig */; buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Target Support Files/Pods-Darkly_osx/Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-DarklyTests/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACH_O_TYPE = staticlib; - MACOSX_DEPLOYMENT_TARGET = 10.10; - MODULEMAP_FILE = "Target Support Files/Pods-Darkly_osx/Pods-Darkly_osx.modulemap"; + MODULEMAP_FILE = "Target Support Files/Pods-DarklyTests/Pods-DarklyTests.modulemap"; OTHER_LDFLAGS = ""; OTHER_LIBTOOLFLAGS = ""; PODS_ROOT = "$(SRCROOT)"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_Darkly_osx; - SDKROOT = macosx; + PRODUCT_NAME = Pods_DarklyTests; + SDKROOT = iphoneos; SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; - C78FBA5DAB560EBC990F906ADA798786 /* Release */ = { + C70A38866A9341D720D50F6C81B6B587 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FB1A891B992A9061F78813B1AA7D2F04 /* DarklyEventSource-macOS.xcconfig */; + baseConfigurationReference = F3D6D576A18C95622C205C717C11AFD6 /* Pods-Darkly_osx.release.xcconfig */; buildSettings = { CLANG_ENABLE_OBJC_WEAK = NO; CODE_SIGN_IDENTITY = ""; @@ -1948,16 +2026,19 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; - GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/DarklyEventSource-macOS/Info.plist"; + INFOPLIST_FILE = "Target Support Files/Pods-Darkly_osx/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.10; - MODULEMAP_FILE = "Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS.modulemap"; - PRODUCT_NAME = DarklyEventSource; + MODULEMAP_FILE = "Target Support Files/Pods-Darkly_osx/Pods-Darkly_osx.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_Darkly_osx; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -2058,66 +2139,65 @@ }; name = Release; }; - DE0EFE356817C9BFFC410B99D9B61D39 /* Release */ = { + E54004D5DF21F2795460A4F69BA0944E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 69E471AA3D416E30075E8211F8F18658 /* DarklyEventSource-tvOS.xcconfig */; + baseConfigurationReference = 3236760AC617A093E12C2210F6458195 /* Pods-Darkly_osx.debug.xcconfig */; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ENABLE_OBJC_WEAK = NO; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS-prefix.pch"; - INFOPLIST_FILE = "Target Support Files/DarklyEventSource-tvOS/Info.plist"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = "Target Support Files/Pods-Darkly_osx/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = "Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS.modulemap"; - PRODUCT_NAME = DarklyEventSource; - SDKROOT = appletvos; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MODULEMAP_FILE = "Target Support Files/Pods-Darkly_osx/Pods-Darkly_osx.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = Pods_Darkly_osx; + SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; - VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Debug; }; - E54004D5DF21F2795460A4F69BA0944E /* Debug */ = { + E57C4280737A28A74B6B1DFCA8F4D7CF /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3236760AC617A093E12C2210F6458195 /* Pods-Darkly_osx.debug.xcconfig */; + baseConfigurationReference = FB4F92F13F88313EC2B1F88227D626B2 /* DarklyEventSource-tvOS.xcconfig */; buildSettings = { - CLANG_ENABLE_OBJC_WEAK = NO; + APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_VERSION = A; - INFOPLIST_FILE = "Target Support Files/Pods-Darkly_osx/Info.plist"; + GCC_PREFIX_HEADER = "Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/DarklyEventSource-tvOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACH_O_TYPE = staticlib; - MACOSX_DEPLOYMENT_TARGET = 10.10; - MODULEMAP_FILE = "Target Support Files/Pods-Darkly_osx/Pods-Darkly_osx.modulemap"; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - PRODUCT_NAME = Pods_Darkly_osx; - SDKROOT = macosx; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS.modulemap"; + PRODUCT_NAME = DarklyEventSource; + SDKROOT = appletvos; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -2190,7 +2270,7 @@ }; F6CB40680738A222484E9390A04C86DC /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B8BF5256A9C8C89D14B7E5583327B291 /* OHHTTPStubs.xcconfig */; + baseConfigurationReference = 13EF633664B9DF304A8EF579DC80553C /* OHHTTPStubs.xcconfig */; buildSettings = { CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; @@ -2229,11 +2309,11 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 0EC5D409CF5C3667C25963881EA5AF7C /* Build configuration list for PBXNativeTarget "DarklyEventSource-iOS" */ = { + 0EEE11367C18E4D1E4F21A5854F438F8 /* Build configuration list for PBXNativeTarget "DarklyEventSource-iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - C4D949D3DAFA9D9AECA84E7FE083C686 /* Debug */, - 56DAED87C61EEF4C26CCC05DD9A7A23A /* Release */, + A83DAE37D17EBEA0FBE957EE04D1459F /* Debug */, + 68B98E48D846C66AEA228EE0D701C359 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2256,20 +2336,20 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 4A7EBCCAB18AD4C0DEA71EF436FD82AF /* Build configuration list for PBXNativeTarget "DarklyEventSource-watchOS" */ = { + 54AF477E97B164FB18A6CD45FB222B85 /* Build configuration list for PBXNativeTarget "Pods-DarklyTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 01B0A2CE48C6B2ABA62F60779E37676E /* Debug */, - 93B6F0E437E164A43B94B692C503BAE5 /* Release */, + EC90C208E716EEBAFEE67C20F5B9A21F /* Debug */, + C26283719F897ADDC58A81AE56B8D7D8 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 54AF477E97B164FB18A6CD45FB222B85 /* Build configuration list for PBXNativeTarget "Pods-DarklyTests" */ = { + 82FE95A49E3A920CC33790AF43F8B6AA /* Build configuration list for PBXNativeTarget "DarklyEventSource-watchOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - EC90C208E716EEBAFEE67C20F5B9A21F /* Debug */, - C26283719F897ADDC58A81AE56B8D7D8 /* Release */, + 5127A2BF5FD2D128B3F070E61AD7A07B /* Debug */, + A5B5A8F13D711F2754A62CF84842B41F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -2292,38 +2372,38 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - ABCBF2C9686D687471766A52054194B0 /* Build configuration list for PBXNativeTarget "DarklyEventSource-tvOS" */ = { + 9F65D338A8F448BF326CED53D3CDF787 /* Build configuration list for PBXNativeTarget "DarklyEventSource-macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - C1A2EBA401172237C09F831AAA6B8732 /* Debug */, - DE0EFE356817C9BFFC410B99D9B61D39 /* Release */, + 8074B2E26C15D533E29285598C7691C2 /* Debug */, + B47ED0A1784F706FC60EDD3D999039AC /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - AE8BA48E64C0BE4DC719C4BAE69F06A8 /* Build configuration list for PBXNativeTarget "OCMock" */ = { + AD4D21147BCFC923BB2938068EB2B482 /* Build configuration list for PBXNativeTarget "DarklyEventSource-tvOS" */ = { isa = XCConfigurationList; buildConfigurations = ( - 8F71F8D7CD0F3FF748ED93E10C98C84F /* Debug */, - 6FD3F1132C038BFFB4C38C3B3EF21966 /* Release */, + E57C4280737A28A74B6B1DFCA8F4D7CF /* Debug */, + 4197F0452032BAB78D7A35F5C8CB1D9E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D730011A32AA30B718AEB0127136E17F /* Build configuration list for PBXNativeTarget "OHHTTPStubs" */ = { + AE8BA48E64C0BE4DC719C4BAE69F06A8 /* Build configuration list for PBXNativeTarget "OCMock" */ = { isa = XCConfigurationList; buildConfigurations = ( - F6CB40680738A222484E9390A04C86DC /* Debug */, - 559EA4974ACBF6ED4CD1D77022DD73B8 /* Release */, + 8F71F8D7CD0F3FF748ED93E10C98C84F /* Debug */, + 6FD3F1132C038BFFB4C38C3B3EF21966 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D7B271B7D4F112D06EB557C0364814C0 /* Build configuration list for PBXNativeTarget "DarklyEventSource-macOS" */ = { + D730011A32AA30B718AEB0127136E17F /* Build configuration list for PBXNativeTarget "OHHTTPStubs" */ = { isa = XCConfigurationList; buildConfigurations = ( - 5CD7EE2C9CE4F211AB4C5386A53C3EC0 /* Debug */, - C78FBA5DAB560EBC990F906ADA798786 /* Release */, + F6CB40680738A222484E9390A04C86DC /* Debug */, + 559EA4974ACBF6ED4CD1D77022DD73B8 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/Pods/Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS-umbrella.h b/Pods/Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS-umbrella.h index 1500560c..2cea053d 100644 --- a/Pods/Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS-umbrella.h +++ b/Pods/Target Support Files/DarklyEventSource-iOS/DarklyEventSource-iOS-umbrella.h @@ -11,7 +11,11 @@ #endif #import "DarklyEventSource.h" +#import "LDEventParser.h" #import "LDEventSource.h" +#import "LDEventStringAccumulator.h" +#import "NSArray+LDEventSource.h" +#import "NSString+LDEventSource.h" FOUNDATION_EXPORT double DarklyEventSourceVersionNumber; FOUNDATION_EXPORT const unsigned char DarklyEventSourceVersionString[]; diff --git a/Pods/Target Support Files/DarklyEventSource-iOS/Info.plist b/Pods/Target Support Files/DarklyEventSource-iOS/Info.plist index 806ba0d4..b6e86dd8 100644 --- a/Pods/Target Support Files/DarklyEventSource-iOS/Info.plist +++ b/Pods/Target Support Files/DarklyEventSource-iOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.2.3 + 3.2.4 CFBundleSignature ???? CFBundleVersion diff --git a/Pods/Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS-umbrella.h b/Pods/Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS-umbrella.h index 1602ef9d..16950cd9 100644 --- a/Pods/Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS-umbrella.h +++ b/Pods/Target Support Files/DarklyEventSource-macOS/DarklyEventSource-macOS-umbrella.h @@ -11,7 +11,11 @@ #endif #import "DarklyEventSource.h" +#import "LDEventParser.h" #import "LDEventSource.h" +#import "LDEventStringAccumulator.h" +#import "NSArray+LDEventSource.h" +#import "NSString+LDEventSource.h" FOUNDATION_EXPORT double DarklyEventSourceVersionNumber; FOUNDATION_EXPORT const unsigned char DarklyEventSourceVersionString[]; diff --git a/Pods/Target Support Files/DarklyEventSource-macOS/Info.plist b/Pods/Target Support Files/DarklyEventSource-macOS/Info.plist index 806ba0d4..b6e86dd8 100644 --- a/Pods/Target Support Files/DarklyEventSource-macOS/Info.plist +++ b/Pods/Target Support Files/DarklyEventSource-macOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.2.3 + 3.2.4 CFBundleSignature ???? CFBundleVersion diff --git a/Pods/Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS-umbrella.h b/Pods/Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS-umbrella.h index 1500560c..2cea053d 100644 --- a/Pods/Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS-umbrella.h +++ b/Pods/Target Support Files/DarklyEventSource-tvOS/DarklyEventSource-tvOS-umbrella.h @@ -11,7 +11,11 @@ #endif #import "DarklyEventSource.h" +#import "LDEventParser.h" #import "LDEventSource.h" +#import "LDEventStringAccumulator.h" +#import "NSArray+LDEventSource.h" +#import "NSString+LDEventSource.h" FOUNDATION_EXPORT double DarklyEventSourceVersionNumber; FOUNDATION_EXPORT const unsigned char DarklyEventSourceVersionString[]; diff --git a/Pods/Target Support Files/DarklyEventSource-tvOS/Info.plist b/Pods/Target Support Files/DarklyEventSource-tvOS/Info.plist index 806ba0d4..b6e86dd8 100644 --- a/Pods/Target Support Files/DarklyEventSource-tvOS/Info.plist +++ b/Pods/Target Support Files/DarklyEventSource-tvOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.2.3 + 3.2.4 CFBundleSignature ???? CFBundleVersion diff --git a/Pods/Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS-umbrella.h b/Pods/Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS-umbrella.h index 7f12e052..b7ffc578 100644 --- a/Pods/Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS-umbrella.h +++ b/Pods/Target Support Files/DarklyEventSource-watchOS/DarklyEventSource-watchOS-umbrella.h @@ -11,7 +11,11 @@ #endif #import "DarklyEventSource.h" +#import "LDEventParser.h" #import "LDEventSource.h" +#import "LDEventStringAccumulator.h" +#import "NSArray+LDEventSource.h" +#import "NSString+LDEventSource.h" FOUNDATION_EXPORT double DarklyEventSourceVersionNumber; FOUNDATION_EXPORT const unsigned char DarklyEventSourceVersionString[]; diff --git a/Pods/Target Support Files/DarklyEventSource-watchOS/Info.plist b/Pods/Target Support Files/DarklyEventSource-watchOS/Info.plist index 806ba0d4..b6e86dd8 100644 --- a/Pods/Target Support Files/DarklyEventSource-watchOS/Info.plist +++ b/Pods/Target Support Files/DarklyEventSource-watchOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.2.3 + 3.2.4 CFBundleSignature ???? CFBundleVersion diff --git a/README.md b/README.md index fb7d8b9e..542c8a30 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ $ brew install carthage To integrate LaunchDarkly into your Xcode project using Carthage, specify it in your `Cartfile`: ```ogdl -github "launchdarkly/ios-client" +github "launchdarkly/ios-client" "2.13.0" ``` Run `carthage` to build the framework and drag the built `Darkly.framework` into your Xcode project. @@ -61,11 +61,11 @@ Quick setup 1. Add the SDK to your `Podfile`: - pod `LaunchDarkly` + pod 'LaunchDarkly', '2.13.0' 2. Import the LaunchDarkly client: - #import "LDClient.h" + #import "Darkly.h" 3. Instantiate a new LDClient with your mobile key and user: