From c0cf57239ab4d957242a8c5ed31d3f4daede7c95 Mon Sep 17 00:00:00 2001 From: Gavin Whelan Date: Tue, 4 Aug 2020 22:11:26 +0000 Subject: [PATCH] prepare 5.1.0 release (#219) ## [5.1.0] - 2020-08-04 ### Added - The ability to specify additional headers to be included on HTTP requests to LaunchDarkly services using `LDConfig.additionalHeaders`. This feature is to enable certain proxy configurations, and is not needed for normal use. - Support for building docs with [jazzy](https://github.com/realm/jazzy). These docs will be available through [GitHub Pages](https://launchdarkly.github.io/ios-client-sdk/). ### Fixed - SDK causing nested bundles in archived product when including the SDK through Carthage. This caused rejections when submitted to the App Store. Thanks to @spr for reporting ([#217](https://github.com/launchdarkly/ios-client-sdk/issues/217)). - SDK causing application to expect LDSwiftEventSource dynamic framework when built with SwiftPM, which does not include the dynamic framework in the resulting application. This causes the application to be rejected when submitted to the App Store. Thanks to @spr for reporting ([#216](https://github.com/launchdarkly/ios-client-sdk/issues/216)). --- .circleci/config.yml | 6 + .jazzy.yaml | 73 +++++------ .swiftlint.yml | 66 +++++----- CHANGELOG.md | 9 ++ LaunchDarkly.podspec | 2 +- LaunchDarkly.xcodeproj/project.pbxproj | 96 +++++++-------- .../GeneratedCode/mocks.generated.swift | 20 ++-- .../LaunchDarkly/Extensions/Dictionary.swift | 23 ---- LaunchDarkly/LaunchDarkly/LDClient.swift | 113 +++++++++--------- .../Cache/CacheableUserEnvironmentFlags.swift | 2 +- .../Models/ConnectionInformation.swift | 2 + LaunchDarkly/LaunchDarkly/Models/Event.swift | 5 +- .../Models/FeatureFlag/FeatureFlag.swift | 2 +- .../FlagRequestTracking/FlagCounter.swift | 9 +- .../FlagValue/LDFlagBaseTypeConvertible.swift | 2 - .../FlagValue/LDFlagValueConvertible.swift | 22 ++-- .../FeatureFlag/LDEvaluationDetail.swift | 9 +- .../LaunchDarkly/Models/LDConfig.swift | 12 +- .../Networking/DarklyService.swift | 10 +- .../LaunchDarkly/Networking/HTTPHeaders.swift | 14 ++- .../ObjectiveC/ObjcLDEvaluationDetail.swift | 2 +- .../Cache/DeprecatedCache.swift | 1 + .../Cache/DiagnosticCache.swift | 3 +- .../Cache/UserEnvironmentFlagCache.swift | 1 + .../Service Objects/EnvironmentReporter.swift | 2 +- .../Service Objects/EventReporter.swift | 9 +- .../Service Objects/FlagChangeNotifier.swift | 3 +- .../Service Objects/FlagStore.swift | 1 + .../Service Objects/FlagSynchronizer.swift | 10 +- .../LaunchDarkly/Service Objects/Log.swift | 3 + LaunchDarkly/LaunchDarklyTests/.swiftlint.yml | 68 +++-------- .../Extensions/AnyComparerSpec.swift | 1 - .../Extensions/DictionarySpec.swift | 89 +++++++------- .../LaunchDarklyTests/LDClientSpec.swift | 3 - .../Mocks/DarklyServiceMock.swift | 2 +- .../Models/DiagnosticEventSpec.swift | 1 - .../Models/FeatureFlag/FeatureFlagSpec.swift | 2 +- .../FlagChange/FlagChangeObserverSpec.swift | 28 ++--- .../Models/LDConfigSpec.swift | 7 +- .../Networking/HTTPHeadersSpec.swift | 81 ++++++++++--- .../Cache/UserEnvironmentFlagCacheSpec.swift | 6 +- .../Service Objects/EventReporterSpec.swift | 1 - Mintfile | 2 +- Package.swift | 2 +- README.md | 6 +- 45 files changed, 407 insertions(+), 424 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index eea749de..3be503e1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -55,6 +55,12 @@ jobs: command: swift test -v 2>&1 | tee 'artifacts/raw-logs-swiftpm.txt' | xcpretty -r junit -o 'test-results/swiftpm/junit.xml' when: always + - run: + name: Build Documentation + command: | + sudo gem install jazzy + jazzy -o artifacts/docs + - run: name: CocoaPods spec lint command: | diff --git a/.jazzy.yaml b/.jazzy.yaml index 91c046b0..4b73ab27 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -1,75 +1,64 @@ module: LaunchDarkly author: LaunchDarkly -author_url: http://launchdarkly.com +author_url: https://launchdarkly.com github_url: https://github.com/launchdarkly/ios-client-sdk clean: true -include: - - "LaunchDarkly/LaunchDarkly/**" -exclude: - - "LaunchDarkly/LaunchDarkly/User/CacheableUserFlags.swift" - - "LaunchDarkly/LaunchDarkly/FeatureFlag/*" - - "LaunchDarkly/LaunchDarkly/FeatureFlag/FlagChange/Flag*" - - "LaunchDarkly/LaunchDarkly/FeatureFlag/FlagRequestTracking/*" - - "LaunchDarkly/LaunchDarkly/FeatureFlag/FlagValue/*" - - "LaunchDarkly/LaunchDarkly/ServiceObjects/*" - - "LaunchDarkly/LaunchDarkly/Networking/*" - - "LaunchDarkly/LaunchDarkly/Extensions/*" +swift_build_tool: spm +readme: README.md +documentation: + - CHANGELOG.md + - CONTRIBUTING.md + - LICENSE.txt copyright: 'Copyright © 2019 Catamorphic Co.' -theme: fullwidth - custom_categories: - - name: LaunchDarkly + - name: Core Classes children: - LDClient - LDConfig - LDUser - - LDFlagKey - - - name: Enumerations - children: - - LDStreamingMode - - LDFlagValueSource - - LDFlagValue + - LDEvaluationDetail - - name: Flag Changes + - name: Flag Change Observers children: + - LDObserverOwner - LDChangedFlag - - LDFlagChangeOwner - LDFlagChangeHandler - LDFlagCollectionChangeHandler - LDFlagsUnchangedHandler - - LDErrorHandler - - name: LD Protocols + - name: Connection Information + children: + - ConnectionInformation + - LDConnectionModeChangedHandler + + - name: Other Types children: + - LDStreamingMode - LDFlagValueConvertible + - LDFlagKey + - LDInvalidArgumentError + - LDErrorHandler - - name: Objective-C + - name: Objective-C Core Interfaces children: - ObjcLDClient - ObjcLDConfig - ObjcLDUser + - ObjcLDChangedFlag - - name: Obj-C Enumerations - children: - - ObjcLDFlagValue - - ObjcLDFlagValueSource - - NSString - - - name: Obj-C Variation Values + - name: Objective-C EvaluationDetail Wrappers children: - - ObjcLDBoolVariationValue - - ObjcLDIntegerVariationValue - - ObjcLDDoubleVariationValue - - ObjcLDStringVariationValue - - ObjcLDArrayVariationValue - - ObjcLDDictionaryVariationValue + - ObjcLDBoolEvaluationDetail + - ObjcLDIntegerEvaluationDetail + - ObjcLDDoubleEvaluationDetail + - ObjcLDStringEvaluationDetail + - ObjcLDArrayEvaluationDetail + - ObjcLDDictionaryEvaluationDetail - - name: Obj-C Changed Flags + - name: Objective-C ChangedFlag Wrappers children: - - ObjcLDChangedFlag - ObjcLDBoolChangedFlag - ObjcLDIntegerChangedFlag - ObjcLDDoubleChangedFlag diff --git a/.swiftlint.yml b/.swiftlint.yml index b2c2401e..8f35130b 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,61 +1,52 @@ -disabled_rules: # rule identifiers to exclude from running +# See test subconfiguration at `LaunchDarkly/LaunchDarklyTests/.swiftlint.yml` + +disabled_rules: - line_length - trailing_whitespace - - todo # temporarily disabled. In general they should be treated as warnings -# - type_body_length # temporarily disabled. -# - file_length # temporarily disabled - - statement_position # wish we could support both uncuddled_else and default (but not others) - - trailing_comma - - syntactic_sugar - - redundant_optional_initialization - - redundant_void_return - - large_tuple # this is flagging closure argument declarations, seems like a mistake -opt_in_rules: # some rules are only opt-in +opt_in_rules: + - contains_over_filter_count + - contains_over_filter_is_empty + - contains_over_first_not_nil + - contains_over_range_nil_comparison - empty_count - # - missing_docs - # - force_unwrapping - # Find all the available rules by running: - # swiftlint rules + - first_where + - flatmap_over_map_reduce + - implicitly_unwrapped_optional + - let_var_whitespace + - missing_docs + - redundant_nil_coalescing + - sorted_first_last + - trailing_closure + - unused_declaration + - unused_import + - vertical_whitespace_closing_braces included: - LaunchDarkly/LaunchDarkly - LaunchDarkly/LaunchDarklyTests -excluded: # paths to ignore during linting. Takes precedence over `included`. - -# configurable rules can be customized from this configuration file -# binary rules can set their severity level - -# force_cast: warning # implicitly -# force_try: -# severity: warning # explicitly -# # rules that have both warning and error levels, can set just the warning level -# # implicitly -# # line_length: 110 -# # they can set both implicitly with an array -# - -# Set doesn't have isEmpty so using count seems to be our ownly option -empty_count: warning +excluded: function_body_length: warning: 50 error: 70 + type_body_length: - - 300 # warning - - 500 # error -# or they can set both explicitly + warning: 300 + error: 500 + file_length: warning: 1000 error: 1500 + identifier_name: min_length: # only min_length warning: 3 # only warning - max_length: # warning and error + max_length: warning: 50 error: 60 - excluded: # excluded via string array + excluded: - id - URL - url @@ -65,4 +56,5 @@ identifier_name: - tag - lhs - rhs -reporter: "xcode" # reporter type (xcode, json, csv, checkstyle) \ No newline at end of file + +reporter: "xcode" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7db48345..8f3faa36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to the LaunchDarkly iOS SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org). +## [5.1.0] - 2020-08-04 + +### Added +- The ability to specify additional headers to be included on HTTP requests to LaunchDarkly services using `LDConfig.additionalHeaders`. This feature is to enable certain proxy configurations, and is not needed for normal use. +- Support for building docs with [jazzy](https://github.com/realm/jazzy). These docs will be available through [GitHub Pages](https://launchdarkly.github.io/ios-client-sdk/). +### Fixed +- SDK causing nested bundles in archived product when including the SDK through Carthage. This caused rejections when submitted to the App Store. Thanks to @spr for reporting ([#217](https://github.com/launchdarkly/ios-client-sdk/issues/217)). +- SDK causing application to expect LDSwiftEventSource dynamic framework when built with SwiftPM, which does not include the dynamic framework in the resulting application. This causes the application to be rejected when submitted to the App Store. Thanks to @spr for reporting ([#216](https://github.com/launchdarkly/ios-client-sdk/issues/216)). + ## [5.0.1] - 2020-07-23 **Note that this release contains the notes for the 5.0.0 release, which should not be used.** diff --git a/LaunchDarkly.podspec b/LaunchDarkly.podspec index 42f8c0f6..6cf8eca7 100644 --- a/LaunchDarkly.podspec +++ b/LaunchDarkly.podspec @@ -2,7 +2,7 @@ Pod::Spec.new do |ld| ld.name = "LaunchDarkly" - ld.version = "5.0.1" + ld.version = "5.1.0" ld.summary = "iOS SDK for LaunchDarkly" ld.description = <<-DESC diff --git a/LaunchDarkly.xcodeproj/project.pbxproj b/LaunchDarkly.xcodeproj/project.pbxproj index adf5173a..67c1fc9a 100644 --- a/LaunchDarkly.xcodeproj/project.pbxproj +++ b/LaunchDarkly.xcodeproj/project.pbxproj @@ -273,16 +273,10 @@ 83FEF8DD1F266742001CF12C /* FlagSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FEF8DC1F266742001CF12C /* FlagSynchronizer.swift */; }; 83FEF8DF1F2667E4001CF12C /* EventReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83FEF8DE1F2667E4001CF12C /* EventReporter.swift */; }; B40B419C249ADA6B00CD0726 /* DiagnosticCacheSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40B419B249ADA6B00CD0726 /* DiagnosticCacheSpec.swift */; }; - B445A6EC24C0D210000BAD6D /* LDSwiftEventSource in Frameworks */ = {isa = PBXBuildFile; productRef = B445A6EB24C0D210000BAD6D /* LDSwiftEventSource */; }; - B445A6ED24C0D210000BAD6D /* LDSwiftEventSource in Embed Frameworks */ = {isa = PBXBuildFile; productRef = B445A6EB24C0D210000BAD6D /* LDSwiftEventSource */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - B445A6EF24C0D218000BAD6D /* LDSwiftEventSource in Frameworks */ = {isa = PBXBuildFile; productRef = B445A6EE24C0D218000BAD6D /* LDSwiftEventSource */; }; - B445A6F024C0D218000BAD6D /* LDSwiftEventSource in Embed Frameworks */ = {isa = PBXBuildFile; productRef = B445A6EE24C0D218000BAD6D /* LDSwiftEventSource */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - B445A6F524C0D22C000BAD6D /* LDSwiftEventSource in Frameworks */ = {isa = PBXBuildFile; productRef = B445A6F424C0D22C000BAD6D /* LDSwiftEventSource */; }; - B445A6F624C0D22C000BAD6D /* LDSwiftEventSource in Embed Frameworks */ = {isa = PBXBuildFile; productRef = B445A6F424C0D22C000BAD6D /* LDSwiftEventSource */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - B445A70124C0D64C000BAD6D /* LDSwiftEventSource in Frameworks */ = {isa = PBXBuildFile; productRef = B445A70024C0D64C000BAD6D /* LDSwiftEventSource */; }; - B445A70224C0D64C000BAD6D /* LDSwiftEventSource in Embed Frameworks */ = {isa = PBXBuildFile; productRef = B445A70024C0D64C000BAD6D /* LDSwiftEventSource */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - B445A70424C0D6E8000BAD6D /* LDSwiftEventSource in Frameworks */ = {isa = PBXBuildFile; productRef = B445A70324C0D6E8000BAD6D /* LDSwiftEventSource */; }; - B445A70524C0D6E8000BAD6D /* LDSwiftEventSource in Embed Frameworks */ = {isa = PBXBuildFile; productRef = B445A70324C0D6E8000BAD6D /* LDSwiftEventSource */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + B467791324D8AEEC00897F00 /* LDSwiftEventSourceStatic in Frameworks */ = {isa = PBXBuildFile; productRef = B467791224D8AEEC00897F00 /* LDSwiftEventSourceStatic */; }; + B467791524D8AEF300897F00 /* LDSwiftEventSourceStatic in Frameworks */ = {isa = PBXBuildFile; productRef = B467791424D8AEF300897F00 /* LDSwiftEventSourceStatic */; }; + B467791724D8AEF800897F00 /* LDSwiftEventSourceStatic in Frameworks */ = {isa = PBXBuildFile; productRef = B467791624D8AEF800897F00 /* LDSwiftEventSourceStatic */; }; + B467791924D8AEFC00897F00 /* LDSwiftEventSourceStatic in Frameworks */ = {isa = PBXBuildFile; productRef = B467791824D8AEFC00897F00 /* LDSwiftEventSourceStatic */; }; B468E71024B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = B468E70F24B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift */; }; B468E71124B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = B468E70F24B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift */; }; B468E71224B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = B468E70F24B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift */; }; @@ -343,7 +337,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - B445A6ED24C0D210000BAD6D /* LDSwiftEventSource in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -354,7 +347,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - B445A6F024C0D218000BAD6D /* LDSwiftEventSource in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -365,7 +357,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - B445A6F624C0D22C000BAD6D /* LDSwiftEventSource in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -376,7 +367,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - B445A70524C0D6E8000BAD6D /* LDSwiftEventSource in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -387,7 +377,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - B445A70224C0D64C000BAD6D /* LDSwiftEventSource in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -523,7 +512,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B445A70424C0D6E8000BAD6D /* LDSwiftEventSource in Frameworks */, + B467791924D8AEFC00897F00 /* LDSwiftEventSourceStatic in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -531,7 +520,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B445A6F524C0D22C000BAD6D /* LDSwiftEventSource in Frameworks */, + B467791724D8AEF800897F00 /* LDSwiftEventSourceStatic in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -539,7 +528,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B445A6EC24C0D210000BAD6D /* LDSwiftEventSource in Frameworks */, + B467791324D8AEEC00897F00 /* LDSwiftEventSourceStatic in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -547,7 +536,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B445A70124C0D64C000BAD6D /* LDSwiftEventSource in Frameworks */, B4903D9E24BD61EF00F087C4 /* Quick in Frameworks */, B4903D9B24BD61D000F087C4 /* Nimble in Frameworks */, B4903D9824BD61B200F087C4 /* OHHTTPStubsSwift in Frameworks */, @@ -559,7 +547,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B445A6EF24C0D218000BAD6D /* LDSwiftEventSource in Frameworks */, + B467791524D8AEF300897F00 /* LDSwiftEventSourceStatic in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -681,6 +669,7 @@ 8354EFCF1F22491C00C05156 /* LaunchDarklyTests */, 83411A5C1FABDA5E00E5CF39 /* GeneratedCode */, 8354EFC31F22491C00C05156 /* Products */, + B467790E24D8AECA00897F00 /* Frameworks */, ); sourceTree = ""; }; @@ -928,6 +917,13 @@ path = "Service Objects"; sourceTree = ""; }; + B467790E24D8AECA00897F00 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -984,7 +980,7 @@ ); name = LaunchDarkly_tvOS; packageProductDependencies = ( - B445A70324C0D6E8000BAD6D /* LDSwiftEventSource */, + B467791824D8AEFC00897F00 /* LDSwiftEventSourceStatic */, ); productName = Darkly_tvOS; productReference = 831188382113A16900D77CB5 /* LaunchDarkly_tvOS.framework */; @@ -1008,7 +1004,7 @@ ); name = LaunchDarkly_macOS; packageProductDependencies = ( - B445A6F424C0D22C000BAD6D /* LDSwiftEventSource */, + B467791624D8AEF800897F00 /* LDSwiftEventSourceStatic */, ); productName = Darkly_macOS; productReference = 831EF33B20655D700001C643 /* LaunchDarkly_macOS.framework */; @@ -1032,7 +1028,7 @@ ); name = LaunchDarkly_iOS; packageProductDependencies = ( - B445A6EB24C0D210000BAD6D /* LDSwiftEventSource */, + B467791224D8AEEC00897F00 /* LDSwiftEventSourceStatic */, ); productName = Darkly; productReference = 8354EFC21F22491C00C05156 /* LaunchDarkly.framework */; @@ -1058,7 +1054,6 @@ B4903D9724BD61B200F087C4 /* OHHTTPStubsSwift */, B4903D9A24BD61D000F087C4 /* Nimble */, B4903D9D24BD61EF00F087C4 /* Quick */, - B445A70024C0D64C000BAD6D /* LDSwiftEventSource */, ); productName = DarklyTests; productReference = 8354EFCB1F22491C00C05156 /* LaunchDarklyTests.xctest */; @@ -1082,7 +1077,7 @@ ); name = LaunchDarkly_watchOS; packageProductDependencies = ( - B445A6EE24C0D218000BAD6D /* LDSwiftEventSource */, + B467791424D8AEF300897F00 /* LDSwiftEventSourceStatic */, ); productName = "Darkly-watchOS"; productReference = 83D9EC6B2062DBB7004D7FA6 /* LaunchDarkly_watchOS.framework */; @@ -1638,7 +1633,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "$(PROJECT_DIR)/LaunchDarkly/LaunchDarkly/Support/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - MARKETING_VERSION = 5.0.1; + MARKETING_VERSION = 5.1.0; MODULEMAP_FILE = ""; PRODUCT_BUNDLE_IDENTIFIER = "com.launchdarkly.Darkly-tvOS"; PRODUCT_NAME = LaunchDarkly_tvOS; @@ -1661,7 +1656,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "$(PROJECT_DIR)/LaunchDarkly/LaunchDarkly/Support/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - MARKETING_VERSION = 5.0.1; + MARKETING_VERSION = 5.1.0; MODULEMAP_FILE = ""; PRODUCT_BUNDLE_IDENTIFIER = "com.launchdarkly.Darkly-tvOS"; PRODUCT_NAME = LaunchDarkly_tvOS; @@ -1684,7 +1679,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "$(PROJECT_DIR)/LaunchDarkly/LaunchDarkly/Support/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - MARKETING_VERSION = 5.0.1; + MARKETING_VERSION = 5.1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.launchdarkly.Darkly-macOS"; PRODUCT_NAME = LaunchDarkly_macOS; SDKROOT = macosx; @@ -1705,7 +1700,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "$(PROJECT_DIR)/LaunchDarkly/LaunchDarkly/Support/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - MARKETING_VERSION = 5.0.1; + MARKETING_VERSION = 5.1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.launchdarkly.Darkly-macOS"; PRODUCT_NAME = LaunchDarkly_macOS; SDKROOT = macosx; @@ -1748,8 +1743,8 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; - DYLIB_COMPATIBILITY_VERSION = 5.0.1; - DYLIB_CURRENT_VERSION = 5.0.1; + DYLIB_COMPATIBILITY_VERSION = 5.1.0; + DYLIB_CURRENT_VERSION = 5.1.0; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; FRAMEWORK_VERSION = B; @@ -1819,8 +1814,8 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_COMPATIBILITY_VERSION = 5.0.1; - DYLIB_CURRENT_VERSION = 5.0.1; + DYLIB_COMPATIBILITY_VERSION = 5.1.0; + DYLIB_CURRENT_VERSION = 5.1.0; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_VERSION = B; @@ -1859,7 +1854,7 @@ INFOPLIST_FILE = "$(PROJECT_DIR)/LaunchDarkly/LaunchDarkly/Support/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)"; - MARKETING_VERSION = 5.0.1; + MARKETING_VERSION = 5.1.0; MODULEMAP_FILE = "$(PROJECT_DIR)/Framework/module.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = com.launchdarkly.Darkly; PRODUCT_NAME = LaunchDarkly; @@ -1879,7 +1874,7 @@ INFOPLIST_FILE = "$(PROJECT_DIR)/LaunchDarkly/LaunchDarkly/Support/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)"; - MARKETING_VERSION = 5.0.1; + MARKETING_VERSION = 5.1.0; MODULEMAP_FILE = "$(PROJECT_DIR)/Framework/module.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = com.launchdarkly.Darkly; PRODUCT_NAME = LaunchDarkly; @@ -1921,7 +1916,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "$(PROJECT_DIR)/LaunchDarkly/LaunchDarkly/Support/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - MARKETING_VERSION = 5.0.1; + MARKETING_VERSION = 5.1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.launchdarkly.Darkly-watchOS"; PRODUCT_NAME = LaunchDarkly_watchOS; SDKROOT = watchos; @@ -1943,7 +1938,7 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = "$(PROJECT_DIR)/LaunchDarkly/LaunchDarkly/Support/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - MARKETING_VERSION = 5.0.1; + MARKETING_VERSION = 5.1.0; PRODUCT_BUNDLE_IDENTIFIER = "com.launchdarkly.Darkly-watchOS"; PRODUCT_NAME = LaunchDarkly_watchOS; SDKROOT = watchos; @@ -2052,45 +2047,40 @@ package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; productName = LDSwiftEventSource; }; - B445A6EB24C0D210000BAD6D /* LDSwiftEventSource */ = { - isa = XCSwiftPackageProductDependency; - package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; - productName = LDSwiftEventSource; - }; - B445A6EE24C0D218000BAD6D /* LDSwiftEventSource */ = { + B445A6F224C0D21E000BAD6D /* LDSwiftEventSource */ = { isa = XCSwiftPackageProductDependency; package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; productName = LDSwiftEventSource; }; - B445A6F224C0D21E000BAD6D /* LDSwiftEventSource */ = { + B445A6F824C0D232000BAD6D /* LDSwiftEventSource */ = { isa = XCSwiftPackageProductDependency; package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; productName = LDSwiftEventSource; }; - B445A6F424C0D22C000BAD6D /* LDSwiftEventSource */ = { + B445A6FA24C0D237000BAD6D /* LDSwiftEventSource */ = { isa = XCSwiftPackageProductDependency; package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; productName = LDSwiftEventSource; }; - B445A6F824C0D232000BAD6D /* LDSwiftEventSource */ = { + B467791224D8AEEC00897F00 /* LDSwiftEventSourceStatic */ = { isa = XCSwiftPackageProductDependency; package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; - productName = LDSwiftEventSource; + productName = LDSwiftEventSourceStatic; }; - B445A6FA24C0D237000BAD6D /* LDSwiftEventSource */ = { + B467791424D8AEF300897F00 /* LDSwiftEventSourceStatic */ = { isa = XCSwiftPackageProductDependency; package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; - productName = LDSwiftEventSource; + productName = LDSwiftEventSourceStatic; }; - B445A70024C0D64C000BAD6D /* LDSwiftEventSource */ = { + B467791624D8AEF800897F00 /* LDSwiftEventSourceStatic */ = { isa = XCSwiftPackageProductDependency; package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; - productName = LDSwiftEventSource; + productName = LDSwiftEventSourceStatic; }; - B445A70324C0D6E8000BAD6D /* LDSwiftEventSource */ = { + B467791824D8AEFC00897F00 /* LDSwiftEventSourceStatic */ = { isa = XCSwiftPackageProductDependency; package = B445A6DE24C0D1CD000BAD6D /* XCRemoteSwiftPackageReference "swift-eventsource" */; - productName = LDSwiftEventSource; + productName = LDSwiftEventSourceStatic; }; B4903D9724BD61B200F087C4 /* OHHTTPStubsSwift */ = { isa = XCSwiftPackageProductDependency; diff --git a/LaunchDarkly/GeneratedCode/mocks.generated.swift b/LaunchDarkly/GeneratedCode/mocks.generated.swift index e344bb57..f92d5ac0 100644 --- a/LaunchDarkly/GeneratedCode/mocks.generated.swift +++ b/LaunchDarkly/GeneratedCode/mocks.generated.swift @@ -308,16 +308,6 @@ final class EventReportingMock: EventReporting { } } - // MARK: lastEventResponseDate - var lastEventResponseDateSetCount = 0 - var setLastEventResponseDateCallback: (() -> Void)? - var lastEventResponseDate: Date? = nil { - didSet { - lastEventResponseDateSetCount += 1 - setLastEventResponseDateCallback?() - } - } - // MARK: service var serviceSetCount = 0 var setServiceCallback: (() -> Void)? @@ -328,6 +318,16 @@ final class EventReportingMock: EventReporting { } } + // MARK: lastEventResponseDate + var lastEventResponseDateSetCount = 0 + var setLastEventResponseDateCallback: (() -> Void)? + var lastEventResponseDate: Date? = nil { + didSet { + lastEventResponseDateSetCount += 1 + setLastEventResponseDateCallback?() + } + } + // MARK: record var recordCallCount = 0 var recordCallback: (() -> Void)? diff --git a/LaunchDarkly/LaunchDarkly/Extensions/Dictionary.swift b/LaunchDarkly/LaunchDarkly/Extensions/Dictionary.swift index b72f793a..d16ffb2f 100644 --- a/LaunchDarkly/LaunchDarkly/Extensions/Dictionary.swift +++ b/LaunchDarkly/LaunchDarkly/Extensions/Dictionary.swift @@ -59,26 +59,3 @@ extension Dictionary where Key == String, Value == Any { } } } - -extension Optional where Wrapped == [String: Any] { - static func == (lhs: [String: Any]?, rhs: [String: Any]?) -> Bool { - guard let lhs = lhs - else { - // swiftlint:disable unused_optional_binding - guard let _ = rhs - else { - return true - } - return false - } - guard let rhs = rhs - else { - return false - } - return lhs.isEqual(to: rhs) - } - - static func != (lhs: [String: Any]?, rhs: [String: Any]?) -> Bool { - !(lhs == rhs) - } -} diff --git a/LaunchDarkly/LaunchDarkly/LDClient.swift b/LaunchDarkly/LaunchDarkly/LDClient.swift index 423c698a..68207cb5 100644 --- a/LaunchDarkly/LaunchDarkly/LDClient.swift +++ b/LaunchDarkly/LaunchDarkly/LDClient.swift @@ -11,6 +11,9 @@ enum LDClientRunMode { case foreground, background } +// swiftlint:disable type_body_length +// swiftlint:disable file_length + /** The LDClient is the heart of the SDK, providing client apps running iOS, watchOS, macOS, or tvOS access to LaunchDarkly services. This singleton provides the ability to set a configuration (LDConfig) that controls how the LDClient talks to LaunchDarkly servers, and a user (LDUser) that provides finer control on the feature flag values delivered to LDClient. Once the LDClient has started, it connects to LaunchDarkly's servers to get the feature flag values you set in the Dashboard. ## Usage @@ -42,13 +45,11 @@ enum LDClientRunMode { ```` The `changedFlag` passed in to the closure contains the old and new value of the flag. */ -// swiftlint:disable type_body_length -// swiftlint:disable file_length public class LDClient { // MARK: - State Controls and Indicators - private static var instances: [String: LDClient]? = nil + private static var instances: [String: LDClient]? /** Reports the online/offline state of the LDClient. @@ -82,20 +83,6 @@ public class LDClient { private var _isOnline = false private var isOnlineQueue = DispatchQueue(label: "com.launchdarkly.LDClient.isOnlineQueue") - // Stores ConnectionInformation in UserDefaults on change - var connectionInformation: ConnectionInformation { - didSet { - Log.debug(connectionInformation.description) - ConnectionInformationStore.storeConnectionInformation(connectionInformation: connectionInformation) - if connectionInformation.currentConnectionMode != oldValue.currentConnectionMode { - flagChangeNotifier.notifyConnectionModeChangedObservers(connectionMode: connectionInformation.currentConnectionMode) - } - } - } - - // Returns an object containing information about successful and/or failed polling or streaming connections to LaunchDarkly - public func getConnectionInformation() -> ConnectionInformation { connectionInformation } - /** Set the LDClient online/offline. @@ -208,7 +195,37 @@ public class LDClient { } } - // MARK: - Foreground / Background notification + // Stores ConnectionInformation in UserDefaults on change + var connectionInformation: ConnectionInformation { + didSet { + Log.debug(connectionInformation.description) + ConnectionInformationStore.storeConnectionInformation(connectionInformation: connectionInformation) + if connectionInformation.currentConnectionMode != oldValue.currentConnectionMode { + flagChangeNotifier.notifyConnectionModeChangedObservers(connectionMode: connectionInformation.currentConnectionMode) + } + } + } + + /// Returns an object containing information about successful and/or failed polling or streaming connections to LaunchDarkly + public func getConnectionInformation() -> ConnectionInformation { connectionInformation } + + /** + Stops the LDClient. Stopping the client means the LDClient goes offline and stops recording events. LDClient will no longer provide feature flag values, only returning default values. + + There is almost no reason to stop the LDClient. Normally, set the LDClient offline to stop communication with the LaunchDarkly servers. Stop the LDClient to stop recording events. There is no need to stop the LDClient prior to suspending, moving to the background, or terminating the app. The SDK will respond to these events as the system requires and as configured in LDConfig. + */ + public func close() { + LDClient.instances?.forEach { $1.internalClose() } + LDClient.instances = nil + } + + private func internalClose() { + Log.debug(typeName(and: #function, appending: "- ") + "stopping") + internalFlush() + internalSetOnline(false) + hasStarted = false + Log.debug(typeName(and: #function, appending: "- ") + "stopped") + } @objc private func didEnterBackground() { Log.debug(typeName(and: #function)) @@ -293,40 +310,7 @@ public class LDClient { } } - /** - Stops the LDClient. Stopping the client means the LDClient goes offline and stops recording events. LDClient will no longer provide feature flag values, only returning default values. - - There is almost no reason to stop the LDClient. Normally, set the LDClient offline to stop communication with the LaunchDarkly servers. Stop the LDClient to stop recording events. There is no need to stop the LDClient prior to suspending, moving to the background, or terminating the app. The SDK will respond to these events as the system requires and as configured in LDConfig. - */ - public func close() { - LDClient.instances?.forEach { $1.internalClose() } - LDClient.instances = nil - } - - private func internalClose() { - Log.debug(typeName(and: #function, appending: "- ") + "stopping") - internalFlush() - internalSetOnline(false) - hasStarted = false - Log.debug(typeName(and: #function, appending: "- ") + "stopped") - } - - /** - Returns the LDClient instance for a given environment. - - - parameter environment: The name of an environment provided in LDConfig.secondaryMobileKeys, defaults to `LDConfig.Constants.primaryEnvironmentName` which is always associated with the `LDConfig.mobileKey` environment. - - - returns: The requested LDClient instance. - */ - public static func get(environment: String = LDConfig.Constants.primaryEnvironmentName) -> LDClient? { - guard let internalInstances = LDClient.instances else { - Log.debug("LDClient.get() was called before init()!") - return nil - } - return internalInstances[environment] - } - - // MARK: Feature Flag values + // MARK: Retrieving Flag Values /* FF Value Requests Conceptual Model @@ -392,7 +376,7 @@ public class LDClient { return LDEvaluationDetail(value: value ?? defaultValue, variationIndex: featureFlag?.variation, reason: reason) } - private func checkErrorKinds(featureFlag: FeatureFlag?) -> Dictionary? { + private func checkErrorKinds(featureFlag: FeatureFlag?) -> [String: Any]? { if !hasStarted { return ["kind": "ERROR", "errorKind": "CLIENT_NOT_READY"] } else if featureFlag == nil { @@ -519,7 +503,7 @@ public class LDClient { return user.flagStore.featureFlags.allFlagValues } - // MARK: Feature Flag Updates + // MARK: Observing Updates /* FF Change Notification Conceptual Model @@ -731,7 +715,7 @@ public class LDClient { flagChangeNotifier.notifyObservers(user: user, oldFlags: oldFlags) } - // MARK: - Events + // MARK: Events /* Event tracking Conceptual model @@ -797,7 +781,9 @@ public class LDClient { Log.debug(typeName(and: #function)) self.connectionInformation = ConnectionInformation.lastSuccessfulConnectionCheck(connectionInformation: self.connectionInformation) } - + + // MARK: Initializing and Accessing + /** Starts the LDClient using the passed in `config` & `user`. Call this before requesting feature flag values. The LDClient will not go online until you call this method. Starting the LDClient means setting the `config` & `user`, setting the client online if `config.startOnline` is true (the default setting), and starting event recording. The client app must start the LDClient before it will report feature flag values. If a client does not call `start`, no methods will work. @@ -876,6 +862,21 @@ public class LDClient { } } } + + /** + Returns the LDClient instance for a given environment. + + - parameter environment: The name of an environment provided in LDConfig.secondaryMobileKeys, defaults to `LDConfig.Constants.primaryEnvironmentName` which is always associated with the `LDConfig.mobileKey` environment. + + - returns: The requested LDClient instance. + */ + public static func get(environment: String = LDConfig.Constants.primaryEnvironmentName) -> LDClient? { + guard let internalInstances = LDClient.instances else { + Log.debug("LDClient.get() was called before init()!") + return nil + } + return internalInstances[environment] + } // MARK: - Private private(set) var serviceFactory: ClientServiceCreating = ClientServiceFactory() diff --git a/LaunchDarkly/LaunchDarkly/Models/Cache/CacheableUserEnvironmentFlags.swift b/LaunchDarkly/LaunchDarkly/Models/Cache/CacheableUserEnvironmentFlags.swift index 1ab68bd9..af214547 100644 --- a/LaunchDarkly/LaunchDarkly/Models/Cache/CacheableUserEnvironmentFlags.swift +++ b/LaunchDarkly/LaunchDarkly/Models/Cache/CacheableUserEnvironmentFlags.swift @@ -74,7 +74,7 @@ struct CacheableUserEnvironmentFlags { var dictionaryValue: [String: Any] { [CodingKeys.userKey.rawValue: userKey, CodingKeys.lastUpdated.rawValue: lastUpdated.stringValue, - CodingKeys.environmentFlags.rawValue: environmentFlags.compactMapValues({ $0.dictionaryValue }) ] + CodingKeys.environmentFlags.rawValue: environmentFlags.compactMapValues { $0.dictionaryValue } ] } } diff --git a/LaunchDarkly/LaunchDarkly/Models/ConnectionInformation.swift b/LaunchDarkly/LaunchDarkly/Models/ConnectionInformation.swift index da2c1629..6629faa3 100644 --- a/LaunchDarkly/LaunchDarkly/Models/ConnectionInformation.swift +++ b/LaunchDarkly/LaunchDarkly/Models/ConnectionInformation.swift @@ -162,6 +162,7 @@ extension ConnectionInformation.LastConnectionFailureReason { case type, payload } + /// Decode a ConnectionInformation.LastConnectionFailureReason enum using Codable public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(String.self, forKey: .type) @@ -180,6 +181,7 @@ extension ConnectionInformation.LastConnectionFailureReason { } } + /// Encode a ConnectionInformation.LastConnectionFailureReason enum using Codable public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) diff --git a/LaunchDarkly/LaunchDarkly/Models/Event.swift b/LaunchDarkly/LaunchDarkly/Models/Event.swift index aa0a5a41..0d353f42 100644 --- a/LaunchDarkly/LaunchDarkly/Models/Event.swift +++ b/LaunchDarkly/LaunchDarkly/Models/Event.swift @@ -70,13 +70,14 @@ struct Event { //sdk internal, not publically accessible self.metricValue = metricValue } - // swiftlint:disable function_parameter_count + // swiftlint:disable:next function_parameter_count static func featureEvent(key: String, value: Any?, defaultValue: Any?, featureFlag: FeatureFlag?, user: LDUser, includeReason: Bool) -> Event { Log.debug(typeName(and: #function) + "key: " + key + ", value: \(String(describing: value)), " + "defaultValue: \(String(describing: defaultValue) + "reason: \(String(describing: includeReason))"), " + "featureFlag: \(String(describing: featureFlag))") return Event(kind: .feature, key: key, user: user, value: value, defaultValue: defaultValue, featureFlag: featureFlag, includeReason: includeReason) } + // swiftlint:disable:next function_parameter_count static func debugEvent(key: String, value: Any?, defaultValue: Any?, featureFlag: FeatureFlag, user: LDUser, includeReason: Bool) -> Event { Log.debug(typeName(and: #function) + "key: " + key + ", value: \(String(describing: value)), " + "defaultValue: \(String(describing: defaultValue) + "reason: \(String(describing: includeReason))"), " + "featureFlag: \(String(describing: featureFlag))") @@ -153,7 +154,7 @@ extension Array where Element == [String: Any] { } func contains(_ eventDictionary: [String: Any]) -> Bool { - !self.filter { $0.matches(eventDictionary: eventDictionary) }.isEmpty + self.contains { $0.matches(eventDictionary: eventDictionary) } } } diff --git a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FeatureFlag.swift b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FeatureFlag.swift index d96703a8..f84568b4 100644 --- a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FeatureFlag.swift +++ b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FeatureFlag.swift @@ -79,7 +79,7 @@ extension FeatureFlag: Equatable { lhs.flagKey == rhs.flagKey && lhs.variation == rhs.variation && lhs.version == rhs.version && - lhs.reason == rhs.reason && + AnyComparer.isEqual(lhs.reason, to: rhs.reason) && lhs.trackReason == rhs.trackReason } } diff --git a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagRequestTracking/FlagCounter.swift b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagRequestTracking/FlagCounter.swift index 0f5fba34..96bf83ab 100644 --- a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagRequestTracking/FlagCounter.swift +++ b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagRequestTracking/FlagCounter.swift @@ -2,7 +2,6 @@ // FlagCounter.swift // LaunchDarkly // -// Created by Mark Pokorny on 6/19/18. +JMJ // Copyright © 2018 Catamorphic Co. All rights reserved. // @@ -13,7 +12,7 @@ final class FlagCounter { case defaultValue = "default", counters } - var defaultValue: Any? = nil + var defaultValue: Any? var flagValueCounters = [FlagValueCounter]() func trackRequest(reportedValue: Any?, featureFlag: FeatureFlag?, defaultValue: Any?) { @@ -116,11 +115,7 @@ extension Array where Element == FlagValueCounter { return selectedFlagValueCounters.first } - var dictionaryValues: [[String: Any]] { - return map { (flagValueCounter) in - return flagValueCounter.dictionaryValue - } - } + var dictionaryValues: [[String: Any]] { map { $0.dictionaryValue } } } extension Array: TypeIdentifying { } diff --git a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagValue/LDFlagBaseTypeConvertible.swift b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagValue/LDFlagBaseTypeConvertible.swift index 20471e55..b866f71a 100644 --- a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagValue/LDFlagBaseTypeConvertible.swift +++ b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagValue/LDFlagBaseTypeConvertible.swift @@ -43,7 +43,6 @@ extension Bool: LDFlagBaseTypeConvertible { extension Int: LDFlagBaseTypeConvertible { init?(_ flag: LDFlagValue?) { - //TODO: Assess whether we need to initialize with a double or string too guard case let .int(value) = flag else { return nil } self = value @@ -54,7 +53,6 @@ extension Int: LDFlagBaseTypeConvertible { extension Double: LDFlagBaseTypeConvertible { init?(_ flag: LDFlagValue?) { - //TODO: Assess whether we need to initialize with an int or string too guard case let .double(value) = flag else { return nil } self = value diff --git a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagValue/LDFlagValueConvertible.swift b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagValue/LDFlagValueConvertible.swift index d9459834..5834e3cb 100644 --- a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagValue/LDFlagValueConvertible.swift +++ b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/FlagValue/LDFlagValueConvertible.swift @@ -7,47 +7,41 @@ import Foundation -//Protocol to convert base LDFlagType into an LDFlagValue ///Protocol used by the SDK to limit feature flag types to those representable on LaunchDarkly servers. Client app developers should not need to use this protocol. The protocol is public because `LDClient.variation(forKey:defaultValue:)` and `LDClient.variationDetail(forKey:defaultValue:)` return a type that conforms to this protocol. See `LDFlagValue` for types that LaunchDarkly feature flags can take. public protocol LDFlagValueConvertible { // This commented out code here and in each extension will be used to support automatic typing. Version `4.0.0` does not support that capability. When that capability is added, uncomment this code. // func toLDFlagValue() -> LDFlagValue } -// MARK: - Bool - +/// :nodoc: extension Bool: LDFlagValueConvertible { // public func toLDFlagValue() -> LDFlagValue { // return .bool(self) // } } -// MARK: - Int - +/// :nodoc: extension Int: LDFlagValueConvertible { // public func toLDFlagValue() -> LDFlagValue { // return .int(self) // } } -// MARK: - Double - +/// :nodoc: extension Double: LDFlagValueConvertible { // public func toLDFlagValue() -> LDFlagValue { // return .double(self) // } } -// MARK: - String - +/// :nodoc: extension String: LDFlagValueConvertible { // public func toLDFlagValue() -> LDFlagValue { // return .string(self) // } } -// MARK: - Array - +/// :nodoc: extension Array where Element: LDFlagValueConvertible { // func toLDFlagValue() -> LDFlagValue { // let flagValues = self.map { (element) in @@ -57,6 +51,7 @@ extension Array where Element: LDFlagValueConvertible { // } } +/// :nodoc: extension Array: LDFlagValueConvertible { // public func toLDFlagValue() -> LDFlagValue { // guard let flags = self as? [LDFlagValueConvertible] @@ -70,8 +65,7 @@ extension Array: LDFlagValueConvertible { // } } -// MARK: - Dictionary - +/// :nodoc: extension Dictionary where Value: LDFlagValueConvertible { // func toLDFlagValue() -> LDFlagValue { // var flagValues = [LDFlagKey: LDFlagValue]() @@ -82,6 +76,7 @@ extension Dictionary where Value: LDFlagValueConvertible { // } } +/// :nodoc: extension Dictionary: LDFlagValueConvertible { // public func toLDFlagValue() -> LDFlagValue { // if let flagValueDictionary = self as? [LDFlagKey: LDFlagValue] { @@ -107,6 +102,7 @@ extension Dictionary: LDFlagValueConvertible { // } } +/// :nodoc: extension NSNull: LDFlagValueConvertible { // public func toLDFlagValue() -> LDFlagValue { // return .null diff --git a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/LDEvaluationDetail.swift b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/LDEvaluationDetail.swift index 85d701f7..749a971d 100644 --- a/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/LDEvaluationDetail.swift +++ b/LaunchDarkly/LaunchDarkly/Models/FeatureFlag/LDEvaluationDetail.swift @@ -7,11 +7,18 @@ import Foundation +/** + An object returned by the SDK's `variationDetail` methods, combining the result of a flag evaluation with an + explanation of how it is calculated. + */ public final class LDEvaluationDetail { + /// The value of the flag for the current user. public internal(set) var value: T + /// The index of the returned value within the flag's list of variations, or `nil` if the default was returned. public internal(set) var variationIndex: Int? + /// A structure representing the main factor that influenced the resultant flag evaluation value. public internal(set) var reason: [String: Any]? - + internal init(value: T, variationIndex: Int?, reason: [String: Any]?) { self.value = value self.variationIndex = variationIndex diff --git a/LaunchDarkly/LaunchDarkly/Models/LDConfig.swift b/LaunchDarkly/LaunchDarkly/Models/LDConfig.swift index d7da2b67..c4edc0e3 100644 --- a/LaunchDarkly/LaunchDarkly/Models/LDConfig.swift +++ b/LaunchDarkly/LaunchDarkly/Models/LDConfig.swift @@ -86,10 +86,14 @@ public struct LDConfig { /// The default secondary mobile keys. ([:]) static let secondaryMobileKeys: [String: String] = [:] + + /// The default additional headers that should be added to all HTTP requests from SDK components to LaunchDarkly services + static let additionalHeaders: [String: String] = [:] } + /// Constants relevant to setting up an `LDConfig` public struct Constants { - /// The dafault environment name that must be present in a single or multiple environment configuration + /// The default environment name that must be present in a single or multiple environment configuration public static let primaryEnvironmentName = "default" } @@ -138,8 +142,6 @@ public struct LDConfig { /// The maximum number of analytics events the LDClient can store. When the LDClient event store reaches the eventCapacity, the SDK discards events until it successfully reports them to LaunchDarkly. (Default: 100) public var eventCapacity: Int = Defaults.eventCapacity - // MARK: Time configuration - /// The timeout interval for flag requests and event reports. (Default: 10 seconds) public var connectionTimeout: TimeInterval = Defaults.connectionTimeout /// The time interval between event reports (Default: 30 seconds) @@ -233,6 +235,9 @@ public struct LDConfig { /// For use by wrapper libraries to report the version of the library in use. If the `wrapperName` has not been set this field will be ignored. Otherwise the version string will be included with the `wrapperName` in the "X-LaunchDarkly-Wrapper" header on requests to the LaunchDarkly servers. (Default: nil) public var wrapperVersion: String? = Defaults.wrapperVersion + /// Additional headers that should be added to all HTTP requests from SDK components to LaunchDarkly services + public var additionalHeaders: [String: String] = [:] + /// LaunchDarkly defined minima for selected configurable items public let minima: Minima @@ -342,6 +347,7 @@ extension LDConfig: Equatable { && lhs.diagnosticRecordingInterval == rhs.diagnosticRecordingInterval && lhs.wrapperName == rhs.wrapperName && lhs.wrapperVersion == rhs.wrapperVersion + && lhs.additionalHeaders == rhs.additionalHeaders } } diff --git a/LaunchDarkly/LaunchDarkly/Networking/DarklyService.swift b/LaunchDarkly/LaunchDarkly/Networking/DarklyService.swift index 2c71c073..f0554477 100644 --- a/LaunchDarkly/LaunchDarkly/Networking/DarklyService.swift +++ b/LaunchDarkly/LaunchDarkly/Networking/DarklyService.swift @@ -20,14 +20,15 @@ protocol DarklyStreamingProvider: class { extension EventSource: DarklyStreamingProvider {} protocol DarklyServiceProvider: class { + var config: LDConfig { get } + var user: LDUser { get } + var diagnosticCache: DiagnosticCaching? { get } + func getFeatureFlags(useReport: Bool, completion: ServiceCompletionHandler?) func clearFlagResponseCache() func createEventSource(useReport: Bool, handler: EventHandler, errorHandler: ConnectionErrorHandler?) -> DarklyStreamingProvider func publishEventDictionaries(_ eventDictionaries: [[String: Any]], _ payloadId: String, completion: ServiceCompletionHandler?) func publishDiagnostic(diagnosticEvent: T, completion: ServiceCompletionHandler?) - var config: LDConfig { get } - var user: LDUser { get } - var diagnosticCache: DiagnosticCaching? { get } } final class DarklyService: DarklyServiceProvider { @@ -179,7 +180,6 @@ final class DarklyService: DarklyServiceProvider { .jsonData, handler: handler, errorHandler: errorHandler) - } return serviceFactory.makeStreamingProvider(url: getStreamRequestUrl, httpHeaders: httpHeaders.eventSourceHeaders, @@ -218,8 +218,8 @@ final class DarklyService: DarklyServiceProvider { private func eventRequest(eventDictionaries: [[String: Any]], payloadId: String) -> URLRequest { var request = URLRequest(url: eventUrl, cachePolicy: .useProtocolCachePolicy, timeoutInterval: config.connectionTimeout) - request.appendHeaders(httpHeaders.eventRequestHeaders) request.appendHeaders([HTTPHeaders.HeaderKey.eventPayloadIDHeader: payloadId]) + request.appendHeaders(httpHeaders.eventRequestHeaders) request.httpMethod = URLRequest.HTTPMethods.post request.httpBody = eventDictionaries.jsonData diff --git a/LaunchDarkly/LaunchDarkly/Networking/HTTPHeaders.swift b/LaunchDarkly/LaunchDarkly/Networking/HTTPHeaders.swift index 31b71565..8c97b6d1 100644 --- a/LaunchDarkly/LaunchDarkly/Networking/HTTPHeaders.swift +++ b/LaunchDarkly/LaunchDarkly/Networking/HTTPHeaders.swift @@ -37,12 +37,14 @@ struct HTTPHeaders { } private let mobileKey: String + private let additionalHeaders: [String: String] private let authKey: String private let userAgent: String private let wrapperHeaderVal: String? init(config: LDConfig, environmentReporter: EnvironmentReporting) { self.mobileKey = config.mobileKey + self.additionalHeaders = config.additionalHeaders self.userAgent = "\(environmentReporter.systemName)/\(environmentReporter.sdkVersion)" self.authKey = "\(HeaderValue.apiKey) \(config.mobileKey)" @@ -68,14 +70,14 @@ struct HTTPHeaders { return headers } - var eventSourceHeaders: [String: String] { baseHeaders } + var eventSourceHeaders: [String: String] { withAdditionalHeaders(baseHeaders) } var flagRequestHeaders: [String: String] { var headers = baseHeaders if let etag = HTTPHeaders.flagRequestEtags[mobileKey] { headers[HeaderKey.ifNoneMatch] = etag } - return headers + return withAdditionalHeaders(headers) } var hasFlagRequestEtag: Bool { @@ -87,13 +89,17 @@ struct HTTPHeaders { headers[HeaderKey.contentType] = HeaderValue.applicationJson headers[HeaderKey.accept] = HeaderValue.applicationJson headers[HeaderKey.eventSchema] = HeaderValue.eventSchema3 - return headers + return withAdditionalHeaders(headers) } var diagnosticRequestHeaders: [String: String] { var headers = baseHeaders headers[HeaderKey.contentType] = HeaderValue.applicationJson headers[HeaderKey.accept] = HeaderValue.applicationJson - return headers + return withAdditionalHeaders(headers) + } + + private func withAdditionalHeaders(_ headers: [String: String]) -> [String: String] { + headers.merging(additionalHeaders, uniquingKeysWith: { $1 }) } } diff --git a/LaunchDarkly/LaunchDarkly/ObjectiveC/ObjcLDEvaluationDetail.swift b/LaunchDarkly/LaunchDarkly/ObjectiveC/ObjcLDEvaluationDetail.swift index eecd51e7..97bd7a8e 100644 --- a/LaunchDarkly/LaunchDarkly/ObjectiveC/ObjcLDEvaluationDetail.swift +++ b/LaunchDarkly/LaunchDarkly/ObjectiveC/ObjcLDEvaluationDetail.swift @@ -78,7 +78,7 @@ public final class ObjcLDDictionaryEvaluationDetail: NSObject { @objc public let variationIndex: Int @objc public let reason: [String: Any]? - internal init(value: Dictionary?, variationIndex: Int?, reason: [String: Any]?) { + internal init(value: [String: Any]?, variationIndex: Int?, reason: [String: Any]?) { self.value = value self.variationIndex = variationIndex ?? -1 self.reason = reason diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/Cache/DeprecatedCache.swift b/LaunchDarkly/LaunchDarkly/Service Objects/Cache/DeprecatedCache.swift index 89634a45..dfce297e 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/Cache/DeprecatedCache.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/Cache/DeprecatedCache.swift @@ -10,6 +10,7 @@ import Foundation protocol DeprecatedCache { var cachedDataKey: String { get } var keyedValueCache: KeyedValueCaching { get } + func retrieveFlags(for userKey: UserKey, and mobileKey: MobileKey) -> (featureFlags: [LDFlagKey: FeatureFlag]?, lastUpdated: Date?) func userKeys(from cachedUserData: [UserKey: [String: Any]], olderThan: Date) -> [UserKey] func removeData(olderThan expirationDate: Date) //provided for testing, to allow the mock to override the protocol extension diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/Cache/DiagnosticCache.swift b/LaunchDarkly/LaunchDarkly/Service Objects/Cache/DiagnosticCache.swift index 25fa8f07..e58709d6 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/Cache/DiagnosticCache.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/Cache/DiagnosticCache.swift @@ -9,8 +9,8 @@ import Foundation //sourcery: autoMockable protocol DiagnosticCaching { - //sourcery: defaultMockValue = nil var lastStats: DiagnosticStats? { get } + func getDiagnosticId() -> DiagnosticId func getCurrentStatsAndReset() -> DiagnosticStats func incrementDroppedEventCount() @@ -47,6 +47,7 @@ final class DiagnosticCache: DiagnosticCaching { func getCurrentStatsAndReset() -> DiagnosticStats { let now = Date().millisSince1970 + //swiftlint:disable:next implicitly_unwrapped_optional var stored: StoreData! cacheQueue.sync { stored = loadOrSetup() diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/Cache/UserEnvironmentFlagCache.swift b/LaunchDarkly/LaunchDarkly/Service Objects/Cache/UserEnvironmentFlagCache.swift index 92a63e62..e84c132e 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/Cache/UserEnvironmentFlagCache.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/Cache/UserEnvironmentFlagCache.swift @@ -15,6 +15,7 @@ enum FlagCachingStoreMode: CaseIterable { protocol FeatureFlagCaching { //sourcery: defaultMockValue = 5 var maxCachedUsers: Int { get set } + func retrieveFeatureFlags(forUserWithKey userKey: String, andMobileKey mobileKey: String) -> [LDFlagKey: FeatureFlag]? func storeFeatureFlags(_ featureFlags: [LDFlagKey: FeatureFlag], forUser user: LDUser, andMobileKey mobileKey: String, lastUpdated: Date, storeMode: FlagCachingStoreMode) } diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/EnvironmentReporter.swift b/LaunchDarkly/LaunchDarkly/Service Objects/EnvironmentReporter.swift index cfb28043..cea140ad 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/EnvironmentReporter.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/EnvironmentReporter.swift @@ -135,7 +135,7 @@ struct EnvironmentReporter: EnvironmentReporting { var shouldThrottleOnlineCalls: Bool { true } #endif - let sdkVersion = "5.0.1" + let sdkVersion = "5.1.0" // Unfortunately, the following does not function in certain configurations, such as when included through SPM // var sdkVersion: String { // Bundle(for: LDClient.self).infoDictionary?["CFBundleShortVersionString"] as? String ?? "5.x" diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/EventReporter.swift b/LaunchDarkly/LaunchDarkly/Service Objects/EventReporter.swift index 840777a2..b6572d89 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/EventReporter.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/EventReporter.swift @@ -13,18 +13,18 @@ enum EventSyncResult { } typealias EventSyncCompleteClosure = ((EventSyncResult) -> Void) -//swiftlint:disable function_parameter_count //sourcery: autoMockable protocol EventReporting { //sourcery: defaultMockValue = LDConfig.stub var config: LDConfig { get set } //sourcery: defaultMockValue = false var isOnline: Bool { get set } - //sourcery: defaultMockValue = nil - var lastEventResponseDate: Date? { get } //sourcery: defaultMockValue = DarklyServiceMock() var service: DarklyServiceProvider { get set } + var lastEventResponseDate: Date? { get } + func record(_ event: Event) + // swiftlint:disable:next function_parameter_count func recordFlagEvaluationEvents(flagKey: LDFlagKey, value: Any?, defaultValue: Any?, featureFlag: FeatureFlag?, user: LDUser, includeReason: Bool) func flush(completion: CompletionClosure?) } @@ -54,7 +54,7 @@ class EventReporter: EventReporting { private var _isOnline = false private var isOnlineQueue = DispatchQueue(label: "com.launchdarkly.EventReporter.isOnlineQueue") - private (set) var lastEventResponseDate: Date? = nil + private (set) var lastEventResponseDate: Date? var service: DarklyServiceProvider @@ -87,6 +87,7 @@ class EventReporter: EventReporting { self.eventStore.append(event.dictionaryValue(config: self.config)) } + // swiftlint:disable:next function_parameter_count func recordFlagEvaluationEvents(flagKey: LDFlagKey, value: Any?, defaultValue: Any?, featureFlag: FeatureFlag?, user: LDUser, includeReason: Bool) { let recordingFeatureEvent = featureFlag?.trackEvents == true let recordingDebugEvent = featureFlag?.shouldCreateDebugEvents(lastEventReportResponseTime: lastEventResponseDate) ?? false diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/FlagChangeNotifier.swift b/LaunchDarkly/LaunchDarkly/Service Objects/FlagChangeNotifier.swift index ce253acb..75c5eff3 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/FlagChangeNotifier.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/FlagChangeNotifier.swift @@ -142,7 +142,8 @@ final class FlagChangeNotifier: FlagChangeNotifying { else { return true } - return !(oldFeatureFlag.variation == newFeatureFlag.variation && ["value": oldFeatureFlag.value as Any] == ["value": newFeatureFlag.value as Any]) + return !(oldFeatureFlag.variation == newFeatureFlag.variation && + AnyComparer.isEqual(oldFeatureFlag.value, to: newFeatureFlag.value)) } } } diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/FlagStore.swift b/LaunchDarkly/LaunchDarkly/Service Objects/FlagStore.swift index 398c616c..d1ad778f 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/FlagStore.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/FlagStore.swift @@ -10,6 +10,7 @@ import Foundation //sourcery: autoMockable protocol FlagMaintaining { var featureFlags: [LDFlagKey: FeatureFlag] { get } + func replaceStore(newFlags: [LDFlagKey: Any]?, completion: CompletionClosure?) func updateStore(updateDictionary: [String: Any], completion: CompletionClosure?) func deleteFlag(deleteDictionary: [String: Any], completion: CompletionClosure?) diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/FlagSynchronizer.swift b/LaunchDarkly/LaunchDarkly/Service Objects/FlagSynchronizer.swift index 78f646af..cc7e7768 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/FlagSynchronizer.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/FlagSynchronizer.swift @@ -203,17 +203,16 @@ class FlagSynchronizer: LDFlagSynchronizing, EventHandler { Log.debug(typeName(and: #function, appending: " - ") + "starting") let context = (useReport: useReport, logPrefix: typeName(and: #function, appending: " - ")) - service.getFeatureFlags(useReport: useReport, completion: { [weak self] serviceResponse in + service.getFeatureFlags(useReport: useReport) { [weak self] serviceResponse in if FlagSynchronizer.shouldRetryFlagRequest(useReport: context.useReport, statusCode: (serviceResponse.urlResponse as? HTTPURLResponse)?.statusCode) { Log.debug(context.logPrefix + "retrying via GET") - self?.service.getFeatureFlags(useReport: false, completion: { retryServiceResponse in + self?.service.getFeatureFlags(useReport: false) { retryServiceResponse in self?.processFlagResponse(serviceResponse: retryServiceResponse) - }) + } } else { self?.processFlagResponse(serviceResponse: serviceResponse) } - Log.debug(context.logPrefix + "complete") - }) + } } private class func shouldRetryFlagRequest(useReport: Bool, statusCode: Int?) -> Bool { @@ -351,7 +350,6 @@ class FlagSynchronizer: LDFlagSynchronizing, EventHandler { } public func onComment(comment: String) { - } public func onError(error: Error) { diff --git a/LaunchDarkly/LaunchDarkly/Service Objects/Log.swift b/LaunchDarkly/LaunchDarkly/Service Objects/Log.swift index 260de35e..535abb37 100644 --- a/LaunchDarkly/LaunchDarkly/Service Objects/Log.swift +++ b/LaunchDarkly/LaunchDarkly/Service Objects/Log.swift @@ -48,12 +48,15 @@ extension TypeIdentifying { var typeName: String { String(describing: type(of: self)) } + func typeName(and method: String, appending suffix: String = " ") -> String { typeName + "." + method + suffix } + static var typeName: String { String(describing: self) } + static func typeName(and method: String, appending suffix: String = " ") -> String { typeName + "." + method + suffix } diff --git a/LaunchDarkly/LaunchDarklyTests/.swiftlint.yml b/LaunchDarkly/LaunchDarklyTests/.swiftlint.yml index 274e51cb..12b8258f 100644 --- a/LaunchDarkly/LaunchDarklyTests/.swiftlint.yml +++ b/LaunchDarkly/LaunchDarklyTests/.swiftlint.yml @@ -1,59 +1,21 @@ -disabled_rules: # rule identifiers to exclude from running - - type_body_length - - function_body_length +disabled_rules: - cyclomatic_complexity - - identifier_name + - function_body_length - force_cast + - force_try - file_length + - identifier_name + - large_tuple + - redundant_optional_initialization + - type_body_length + # Disabled opt-in rules from .swiftlint.yml in the project root. + - implicitly_unwrapped_optional + - let_var_whitespace + - missing_docs + - trailing_closure -opt_in_rules: # some rules are only opt-in - # Find all the available rules by running: - # swiftlint rules -# included: # paths to include during linting. `--path` is ignored if present. -# # - Source - -excluded: # paths to ignore during linting. Takes precedence over `included`. - - Pods - -# configurable rules can be customized from this configuration file -# binary rules can set their severity level - -# force_cast: warning # implicitly -# force_try: -# severity: warning # explicitly -# # rules that have both warning and error levels, can set just the warning level -# # implicitly -# # line_length: 110 -# # they can set both implicitly with an array -# +opt_in_rules: -# Set doesn't have isEmpty so using count seems to be our ownly option -empty_count: warning +excluded: -function_body_length: - warning: 50 - error: 70 -type_body_length: - - 300 # warning - - 500 # error -# or they can set both explicitly -file_length: - warning: 1000 - error: 1500 -identifier_name: - min_length: # only min_length - error: 4 # only error - max_length: # warning and error - warning: 50 - error: 60 - excluded: # excluded via string array - - id - - URL - - url - - obj - - key - - all - - tag - - lhs - - rhs -reporter: "xcode" # reporter type (xcode, json, csv, checkstyle) \ No newline at end of file +reporter: "xcode" \ No newline at end of file diff --git a/LaunchDarkly/LaunchDarklyTests/Extensions/AnyComparerSpec.swift b/LaunchDarkly/LaunchDarklyTests/Extensions/AnyComparerSpec.swift index 637bae75..252e31cd 100644 --- a/LaunchDarkly/LaunchDarklyTests/Extensions/AnyComparerSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Extensions/AnyComparerSpec.swift @@ -13,7 +13,6 @@ import Nimble final class AnyComparerSpec: QuickSpec { struct Constants { - } struct Values { diff --git a/LaunchDarkly/LaunchDarklyTests/Extensions/DictionarySpec.swift b/LaunchDarkly/LaunchDarklyTests/Extensions/DictionarySpec.swift index bc11cf8d..a646d3af 100644 --- a/LaunchDarkly/LaunchDarklyTests/Extensions/DictionarySpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Extensions/DictionarySpec.swift @@ -191,60 +191,55 @@ final class DictionarySpec: QuickSpec { fileprivate extension Dictionary where Key == String, Value == Any { struct Keys { - static var bool: String { - return "bool-key" - } - static var int: String { - return "int-key" - } - static var double: String { - return "double-key" - } - static var string: String { - return "string-key" - } - static var array: String { - return "array-key" - } - static var dictionary: String { - return "dictionary-key" - } - static var null: String { - return "null-key" - } + static let bool: String = "bool-key" + static let int: String = "int-key" + static let double: String = "double-key" + static let string: String = "string-key" + static let array: String = "array-key" + static let dictionary: String = "dictionary-key" + static let null: String = "null-key" } struct Values { - static var bool: Bool { - return true - } - static var int: Int { - return 7 - } - static var double: Double { - return 3.14159 - } - static var string: String { - return "string value" - } - static var array: [Int] { - return [1, 2, 3] - } - static var dictionary: [String: Any] { - return ["sub-flag-a": false, "sub-flag-b": 3, "sub-flag-c": 2.71828] + static let bool: Bool = true + static let int: Int = 7 + static let double: Double = 3.14159 + static let string: String = "string value" + static let array: [Int] = [1, 2, 3] + static let dictionary: [String: Any] = ["sub-flag-a": false, "sub-flag-b": 3, "sub-flag-c": 2.71828] + static let null: NSNull = NSNull() + } + + static func stub() -> [String: Any] { + [Keys.bool: Values.bool, + Keys.int: Values.int, + Keys.double: Values.double, + Keys.string: Values.string, + Keys.array: Values.array, + Keys.dictionary: Values.dictionary] + } +} + +extension Optional where Wrapped == [String: Any] { + static func == (lhs: [String: Any]?, rhs: [String: Any]?) -> Bool { + guard let lhs = lhs + else { + // swiftlint:disable unused_optional_binding + guard let _ = rhs + else { + return true + } + return false } - static var null: NSNull { - return NSNull() + guard let rhs = rhs + else { + return false } + return lhs.isEqual(to: rhs) } - static func stub() -> [String: Any] { - return [Keys.bool: Values.bool, - Keys.int: Values.int, - Keys.double: Values.double, - Keys.string: Values.string, - Keys.array: Values.array, - Keys.dictionary: Values.dictionary] + static func != (lhs: [String: Any]?, rhs: [String: Any]?) -> Bool { + !(lhs == rhs) } } diff --git a/LaunchDarkly/LaunchDarklyTests/LDClientSpec.swift b/LaunchDarkly/LaunchDarklyTests/LDClientSpec.swift index 719656f9..7193987d 100644 --- a/LaunchDarkly/LaunchDarklyTests/LDClientSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/LDClientSpec.swift @@ -1226,7 +1226,6 @@ final class LDClientSpec: QuickSpec { } context("when client was started") { beforeEach { - //swiftlint:disable:next force_try try! testContext.subject.track(key: event.key!, data: event.data) } it("records a custom event") { @@ -1242,7 +1241,6 @@ final class LDClientSpec: QuickSpec { testContext.subject.close() priorRecordedEvents = testContext.eventReporterMock.recordCallCount - //swiftlint:disable:next force_try try! testContext.subject.track(key: event.key!, data: event.data) } it("does not record any more events") { @@ -1530,7 +1528,6 @@ final class LDClientSpec: QuickSpec { testContext.subject.flagChangeNotifier = ClientServiceMockFactory().makeFlagChangeNotifier() testContext.subject.observeError(owner: self, handler: { (_) in - }) } it("registers an error observer") { diff --git a/LaunchDarkly/LaunchDarklyTests/Mocks/DarklyServiceMock.swift b/LaunchDarkly/LaunchDarklyTests/Mocks/DarklyServiceMock.swift index ad35ae14..6dc8d0bc 100644 --- a/LaunchDarkly/LaunchDarklyTests/Mocks/DarklyServiceMock.swift +++ b/LaunchDarkly/LaunchDarklyTests/Mocks/DarklyServiceMock.swift @@ -193,7 +193,7 @@ final class DarklyServiceMock: DarklyServiceProvider { } return useAlternateFlagVersion ? flagVersion + 1 : flagVersion } - private static func reason(includeEvaluationReason: Bool) -> Dictionary? { + private static func reason(includeEvaluationReason: Bool) -> [String: Any]? { return includeEvaluationReason ? reason : nil } diff --git a/LaunchDarkly/LaunchDarklyTests/Models/DiagnosticEventSpec.swift b/LaunchDarkly/LaunchDarklyTests/Models/DiagnosticEventSpec.swift index 2555a73f..81936aac 100644 --- a/LaunchDarkly/LaunchDarklyTests/Models/DiagnosticEventSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Models/DiagnosticEventSpec.swift @@ -221,7 +221,6 @@ final class DiagnosticEventSpec: QuickSpec { customConfig.enableBackgroundUpdates = true customConfig.evaluationReasons = true customConfig.maxCachedUsers = -2 - // swiftlint:disable force_try try! customConfig.setSecondaryMobileKeys(["test": "foobar1", "debug": "foobar2"]) customConfig.diagnosticRecordingInterval = 600.0 customConfig.wrapperName = "ReactNative" diff --git a/LaunchDarkly/LaunchDarklyTests/Models/FeatureFlag/FeatureFlagSpec.swift b/LaunchDarkly/LaunchDarklyTests/Models/FeatureFlag/FeatureFlagSpec.swift index 6352b914..6b784b46 100644 --- a/LaunchDarkly/LaunchDarklyTests/Models/FeatureFlag/FeatureFlagSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Models/FeatureFlag/FeatureFlagSpec.swift @@ -765,7 +765,7 @@ extension FeatureFlag { && flagVersion == otherFlag.flagVersion } - init(copying featureFlag: FeatureFlag, value: Any? = nil, variation: Int? = nil, version: Int? = nil, flagVersion: Int? = nil, trackEvents: Bool? = nil, debugEventsUntilDate: Date? = nil, reason: Dictionary? = nil, trackReason: Bool? = nil) { + init(copying featureFlag: FeatureFlag, value: Any? = nil, variation: Int? = nil, version: Int? = nil, flagVersion: Int? = nil, trackEvents: Bool? = nil, debugEventsUntilDate: Date? = nil, reason: [String: Any]? = nil, trackReason: Bool? = nil) { self.init(flagKey: featureFlag.flagKey, value: value ?? featureFlag.value, variation: variation ?? featureFlag.variation, diff --git a/LaunchDarkly/LaunchDarklyTests/Models/FeatureFlag/FlagChange/FlagChangeObserverSpec.swift b/LaunchDarkly/LaunchDarklyTests/Models/FeatureFlag/FlagChange/FlagChangeObserverSpec.swift index b32045ea..79f10413 100644 --- a/LaunchDarkly/LaunchDarklyTests/Models/FeatureFlag/FlagChange/FlagChangeObserverSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Models/FeatureFlag/FlagChange/FlagChangeObserverSpec.swift @@ -23,14 +23,13 @@ final class FlagChangeObserverSpec: QuickSpec { var otherOwnerMock: FlagChangeHandlerOwnerMock! describe("equals") { - //swiftlint:disable unused_closure_parameter beforeEach { ownerMock = FlagChangeHandlerOwnerMock() otherOwnerMock = FlagChangeHandlerOwnerMock() } context("when observers are the same item") { beforeEach { - leftObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { (changedFlag) in }) + leftObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { _ in }) } it("returns true") { expect(leftObserver) == leftObserver @@ -38,8 +37,8 @@ final class FlagChangeObserverSpec: QuickSpec { } context("when observers have the same key and owner") { beforeEach { - leftObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { (changedFlag) in }) - rightObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { (changedFlag) in }) + leftObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { _ in }) + rightObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { _ in }) } it("returns true") { expect(leftObserver) == rightObserver @@ -47,8 +46,8 @@ final class FlagChangeObserverSpec: QuickSpec { } context("when observers has a different key and the same owner") { beforeEach { - leftObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { (changedFlag) in }) - rightObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.int, owner: ownerMock, flagChangeHandler: { (changedFlag) in }) + leftObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { _ in }) + rightObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.int, owner: ownerMock, flagChangeHandler: { _ in }) } it("returns false") { expect(leftObserver) != rightObserver @@ -56,8 +55,8 @@ final class FlagChangeObserverSpec: QuickSpec { } context("when observers have the same key and different owner") { beforeEach { - leftObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { (changedFlag) in }) - rightObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: otherOwnerMock, flagChangeHandler: { (changedFlag) in }) + leftObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: ownerMock, flagChangeHandler: { _ in }) + rightObserver = FlagChangeObserver(key: DarklyServiceMock.FlagKeys.bool, owner: otherOwnerMock, flagChangeHandler: { _ in }) } it("returns false") { expect(leftObserver) != rightObserver @@ -65,8 +64,8 @@ final class FlagChangeObserverSpec: QuickSpec { } context("when observers have the same keys and owner") { beforeEach { - leftObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: ownerMock, flagCollectionChangeHandler: { (changedFlags) in }) - rightObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: ownerMock, flagCollectionChangeHandler: { (changedFlags) in }) + leftObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: ownerMock, flagCollectionChangeHandler: { _ in }) + rightObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: ownerMock, flagCollectionChangeHandler: { _ in }) } it("returns true") { expect(leftObserver) == rightObserver @@ -74,8 +73,8 @@ final class FlagChangeObserverSpec: QuickSpec { } context("when observers have different keys and the same owner") { beforeEach { - leftObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: ownerMock, flagCollectionChangeHandler: { (changedFlags) in }) - rightObserver = FlagChangeObserver(keys: [DarklyServiceMock.FlagKeys.bool], owner: ownerMock, flagCollectionChangeHandler: { (changedFlags) in }) + leftObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: ownerMock, flagCollectionChangeHandler: { _ in }) + rightObserver = FlagChangeObserver(keys: [DarklyServiceMock.FlagKeys.bool], owner: ownerMock, flagCollectionChangeHandler: { _ in }) } it("returns false") { expect(leftObserver) != rightObserver @@ -83,14 +82,13 @@ final class FlagChangeObserverSpec: QuickSpec { } context("when observers have the same keys and a different owner") { beforeEach { - leftObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: ownerMock, flagCollectionChangeHandler: { (changedFlags) in }) - rightObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: otherOwnerMock, flagCollectionChangeHandler: { (changedFlags) in }) + leftObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: ownerMock, flagCollectionChangeHandler: { _ in }) + rightObserver = FlagChangeObserver(keys: DarklyServiceMock.FlagKeys.knownFlags, owner: otherOwnerMock, flagCollectionChangeHandler: { _ in }) } it("returns false") { expect(leftObserver) != rightObserver } } - //swiftlint:enable unused_closure_parameter } } } diff --git a/LaunchDarkly/LaunchDarklyTests/Models/LDConfigSpec.swift b/LaunchDarkly/LaunchDarklyTests/Models/LDConfigSpec.swift index ec93aecf..1ec9f298 100644 --- a/LaunchDarkly/LaunchDarklyTests/Models/LDConfigSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Models/LDConfigSpec.swift @@ -37,6 +37,7 @@ final class LDConfigSpec: QuickSpec { fileprivate static let diagnosticRecordingInterval: TimeInterval = 600.0 fileprivate static let wrapperName = "ReactNative" fileprivate static let wrapperVersion = "0.1.0" + fileprivate static let additionalHeaders = ["Proxy-Authorization": "creds"] } struct TestContext { @@ -97,6 +98,7 @@ final class LDConfigSpec: QuickSpec { expect(config.diagnosticRecordingInterval) == LDConfig.Defaults.diagnosticRecordingInterval expect(config.wrapperName == LDConfig.Defaults.wrapperName) == true expect(config.wrapperVersion == LDConfig.Defaults.wrapperVersion) == true + expect(config.additionalHeaders) == LDConfig.Defaults.additionalHeaders } } context("changing the config values") { @@ -129,6 +131,7 @@ final class LDConfigSpec: QuickSpec { config.diagnosticRecordingInterval = Constants.diagnosticRecordingInterval config.wrapperName = Constants.wrapperName config.wrapperVersion = Constants.wrapperVersion + config.additionalHeaders = Constants.additionalHeaders testElements.append((os, config)) } @@ -157,6 +160,7 @@ final class LDConfigSpec: QuickSpec { expect(config.diagnosticRecordingInterval) == Constants.diagnosticRecordingInterval expect(config.wrapperName) == Constants.wrapperName expect(config.wrapperVersion) == Constants.wrapperVersion + expect(config.additionalHeaders) == Constants.additionalHeaders } } } @@ -338,7 +342,8 @@ final class LDConfigSpec: QuickSpec { ("diagnostic opt out", Constants.diagnosticOptOut, { c, v in c.diagnosticOptOut = v as! Bool }), ("diagnostic recording interval", Constants.diagnosticRecordingInterval, { c, v in c.diagnosticRecordingInterval = v as! TimeInterval }), ("wrapper name", Constants.wrapperName, { c, v in c.wrapperName = v as! String? }), - ("wrapper version", Constants.wrapperVersion, { c, v in c.wrapperVersion = v as! String? })] + ("wrapper version", Constants.wrapperVersion, { c, v in c.wrapperVersion = v as! String? }), + ("additional headers", Constants.additionalHeaders, { c, v in c.additionalHeaders = v as! [String: String]})] testFields.forEach { name, otherVal, setter in context("when \(name) differs") { beforeEach { diff --git a/LaunchDarkly/LaunchDarklyTests/Networking/HTTPHeadersSpec.swift b/LaunchDarkly/LaunchDarklyTests/Networking/HTTPHeadersSpec.swift index e241b846..bc51ec1f 100644 --- a/LaunchDarkly/LaunchDarklyTests/Networking/HTTPHeadersSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Networking/HTTPHeadersSpec.swift @@ -43,9 +43,9 @@ final class HTTPHeadersSpec: QuickSpec { private func eventSourceHeadersSpec() { var testContext: TestContext! + var headers: [String: String]! describe("eventSourceHeaders") { context("with basic elements") { - var headers: [String: String]! beforeEach { testContext = TestContext() headers = testContext.httpHeaders.eventSourceHeaders @@ -56,7 +56,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperName set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperName = "test-wrapper" @@ -68,7 +67,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperVersion set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperVersion = "0.1.0" @@ -80,7 +78,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperName and wrapperVersion set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperName = "test-wrapper" @@ -92,17 +89,32 @@ final class HTTPHeadersSpec: QuickSpec { expect(headers["X-LaunchDarkly-Wrapper"]) == "test-wrapper/0.1.0" } } + context("with additional headers") { + beforeEach { + var config = LDConfig.stub + config.additionalHeaders = ["Proxy-Authorization": "token", + HTTPHeaders.HeaderKey.authorization: "feh"] + testContext = TestContext(config: config) + headers = testContext.httpHeaders.eventSourceHeaders + } + it("creates headers including new headers") { + expect(headers["Proxy-Authorization"]) == "token" + } + it("overrides SDK set headers") { + expect(headers[HTTPHeaders.HeaderKey.authorization]) == "feh" + } + } } } private func flagRequestHeadersSpec() { var testContext: TestContext! + var headers: [String: String]! describe("flagRequestHeaders") { afterEach { HTTPHeaders.removeFlagRequestEtags() } context("without etags") { - var headers: [String: String]! beforeEach { testContext = TestContext() @@ -116,7 +128,6 @@ final class HTTPHeadersSpec: QuickSpec { } context("with an etag for the selected environment") { var etag: String! - var headers: [String: String]! beforeEach { testContext = TestContext() etag = UUID().uuidString @@ -134,7 +145,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperName set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperName = "test-wrapper" @@ -146,7 +156,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperVersion set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperVersion = "0.1.0" @@ -158,7 +167,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperName and wrapperVersion set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperName = "test-wrapper" @@ -170,6 +178,21 @@ final class HTTPHeadersSpec: QuickSpec { expect(headers["X-LaunchDarkly-Wrapper"]) == "test-wrapper/0.1.0" } } + context("with additional headers") { + beforeEach { + var config = LDConfig.stub + config.additionalHeaders = ["Proxy-Authorization": "token", + HTTPHeaders.HeaderKey.authorization: "feh"] + testContext = TestContext(config: config) + headers = testContext.httpHeaders.flagRequestHeaders + } + it("creates headers including new headers") { + expect(headers["Proxy-Authorization"]) == "token" + } + it("overrides SDK set headers") { + expect(headers[HTTPHeaders.HeaderKey.authorization]) == "feh" + } + } } } @@ -262,9 +285,9 @@ final class HTTPHeadersSpec: QuickSpec { private func eventRequestHeadersSpec() { var testContext: TestContext! + var headers: [String: String]! describe("eventRequestHeaders") { context("with basic elements") { - var headers: [String: String]! beforeEach { testContext = TestContext() @@ -279,7 +302,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperName set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperName = "test-wrapper" @@ -291,7 +313,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperVersion set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperVersion = "0.1.0" @@ -303,7 +324,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperName and wrapperVersion set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperName = "test-wrapper" @@ -315,14 +335,29 @@ final class HTTPHeadersSpec: QuickSpec { expect(headers["X-LaunchDarkly-Wrapper"]) == "test-wrapper/0.1.0" } } + context("with additional headers") { + beforeEach { + var config = LDConfig.stub + config.additionalHeaders = ["Proxy-Authorization": "token", + HTTPHeaders.HeaderKey.authorization: "feh"] + testContext = TestContext(config: config) + headers = testContext.httpHeaders.flagRequestHeaders + } + it("creates headers including new headers") { + expect(headers["Proxy-Authorization"]) == "token" + } + it("overrides SDK set headers") { + expect(headers[HTTPHeaders.HeaderKey.authorization]) == "feh" + } + } } } private func diagnosticRequestHeadersSpec() { var testContext: TestContext! + var headers: [String: String]! describe("diagnosticRequestHeaders") { context("with basic elements") { - var headers: [String: String]! beforeEach { testContext = TestContext() headers = testContext.httpHeaders.diagnosticRequestHeaders @@ -336,7 +371,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperName set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperName = "test-wrapper" @@ -348,7 +382,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperVersion set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperVersion = "0.1.0" @@ -360,7 +393,6 @@ final class HTTPHeadersSpec: QuickSpec { } } context("with wrapperName and wrapperVersion set") { - var headers: [String: String]! beforeEach { var config = LDConfig.stub config.wrapperName = "test-wrapper" @@ -372,6 +404,21 @@ final class HTTPHeadersSpec: QuickSpec { expect(headers["X-LaunchDarkly-Wrapper"]) == "test-wrapper/0.1.0" } } + context("with additional headers") { + beforeEach { + var config = LDConfig.stub + config.additionalHeaders = ["Proxy-Authorization": "token", + HTTPHeaders.HeaderKey.authorization: "feh"] + testContext = TestContext(config: config) + headers = testContext.httpHeaders.flagRequestHeaders + } + it("creates headers including new headers") { + expect(headers["Proxy-Authorization"]) == "token" + } + it("overrides SDK set headers") { + expect(headers[HTTPHeaders.HeaderKey.authorization]) == "feh" + } + } } } } diff --git a/LaunchDarkly/LaunchDarklyTests/Service Objects/Cache/UserEnvironmentFlagCacheSpec.swift b/LaunchDarkly/LaunchDarklyTests/Service Objects/Cache/UserEnvironmentFlagCacheSpec.swift index 77aa3653..c5d51ab8 100644 --- a/LaunchDarkly/LaunchDarklyTests/Service Objects/Cache/UserEnvironmentFlagCacheSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Service Objects/Cache/UserEnvironmentFlagCacheSpec.swift @@ -37,9 +37,9 @@ final class UserEnvironmentFlagCacheSpec: QuickSpec { return pair2.value.isEarlierThan(pair1.value) } let oldestUserKey = sortedLastUpdatedPairs.last!.key - return users.filter { (user) in - return user.key == oldestUserKey - }.first! + return users.first { user in + user.key == oldestUserKey + }! } init(userCount: Int = 1, maxUsers: Int = 5) { diff --git a/LaunchDarkly/LaunchDarklyTests/Service Objects/EventReporterSpec.swift b/LaunchDarkly/LaunchDarklyTests/Service Objects/EventReporterSpec.swift index 74cf8bad..0a396dc7 100644 --- a/LaunchDarkly/LaunchDarklyTests/Service Objects/EventReporterSpec.swift +++ b/LaunchDarkly/LaunchDarklyTests/Service Objects/EventReporterSpec.swift @@ -331,7 +331,6 @@ final class EventReporterSpec: QuickSpec { } expect(result == testContext.serviceMock.publishedEventDictionaries!).to(beTrue()) } - } context("with events only") { beforeEach { diff --git a/Mintfile b/Mintfile index 0d1a4173..0b78b0ea 100644 --- a/Mintfile +++ b/Mintfile @@ -1,2 +1,2 @@ -realm/SwiftLint@0.33.0 +realm/SwiftLint@0.39.2 krzysztofzablocki/Sourcery@0.16.1 diff --git a/Package.swift b/Package.swift index b45669c2..a0be9904 100644 --- a/Package.swift +++ b/Package.swift @@ -24,7 +24,7 @@ let package = Package( targets: [ .target( name: "LaunchDarkly", - dependencies: ["LDSwiftEventSource"], + dependencies: ["LDSwiftEventSourceStatic"], path: "LaunchDarkly/LaunchDarkly", exclude: ["Support"]), .testTarget( diff --git a/README.md b/README.md index b25a5cc5..3423f382 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ To include LaunchDarkly in a Swift package, simply add it to the dependencies se ```swift dependencies: [ - .package(url: "https://github.com/launchdarkly/ios-client-sdk.git", .upToNextMinor(from: "5.0.1")) + .package(url: "https://github.com/launchdarkly/ios-client-sdk.git", .upToNextMinor(from: "5.1.0")) ] ``` @@ -51,7 +51,7 @@ To use the [CocoaPods](https://cocoapods.org) dependency manager to integrate La ```ruby use_frameworks! target 'YourTargetName' do - pod 'LaunchDarkly', '~> 5.0' + pod 'LaunchDarkly', '~> 5.1' end ``` @@ -62,7 +62,7 @@ To use the [Carthage](https://github.com/Carthage/Carthage) dependency manager t To integrate LaunchDarkly into your Xcode project using Carthage, specify it in your `Cartfile`: ```ogdl -github "launchdarkly/ios-client-sdk" ~> 5.0 +github "launchdarkly/ios-client-sdk" ~> 5.1 ``` ### Manual installation