This documentation is for migrating from the v2.x or v3.x Objective-C implementations of the SDK to the previous major version of the SDK. For information on upgrading from 4.x to the 5.0 major release, see the Migration Guide on the documentation site.
Follow the steps below to migrate your app from v2.x or v3.x to v4.x. Most of these steps will be required, your IDE will give you build errors if you don't take them. We recommend reading through the guide before starting to get a feel for what you will need to do to migrate your app to v4.x.
Version 4.1 does not support multiple environments. If you use version 2.14.0 or later and set LDConfig's secondaryMobileKeys
you will not be able to migrate to version 4.1. Multiple Environments will be added in a future release to the Swift SDK.
Version 4.x is built on Swift 5 using Xcode 10. The SDK will not build using previous Swift versions, and therefore you must use Xcode 10 or later to build.
API Differences from v2.x or v3.x lists all of the changes to the API.
- Integrate the v4.x SDK into your app. (e.g. CocoaPods podfile or Carthage cartfile change to point to v4.1.1). See Integrate the Swift SDK into your app using either CocoaPods or Carthage for details.
- Clean, and delete Derived Data.
- Update imports to
LaunchDarkly
. See Update imports toLaunchDarkly
for details. - In Swift code, replace instances of
LDClient.sharedInstance()
withLDClient.shared
. Do not change Objective-C[LDClient sharedInstance]
occurrences. - Update
LDUser
and properties. See UpdateLDUser
and properties for details. - Update
LDConfig
and properties. See UpdateLDConfig
and properties for details. - Update
LDClient
Controls. See UpdateLDClient
Controls for details. - Update
LDClient
Feature Flag access. See UpdateLDClient
Feature Flag access for details. - Install
LDClient
Feature Flag Observers. See InstallLDClient
Feature Flag Observers for details. - Remove
LDClientDelegate
methods if they were not re-used. See RemoveLDClientDelegate
methods for details. - Install exception handling for
LDClient
trackEvent
calls. See InstallLDClient.trackEvent
exception handling for details.
- Add
use_frameworks!
to either the root or to any targets that includeLaunchDarkly
- Replace the
Darkly.framework
for each target withLaunchDarkly.framework
. Non-iOS platform frameworks include the platform in the framework name, e.g.LaunchDarkly_tvOS.framework
. TheDarklyEventSource.framework
should also be present, just as with v2.x. - For Objective-C apps using Carthage, turn on
Always Embed Swift Standard Libraries
in Build Settings - If you use the copy frameworks carthage script, replace
Darkly.framework
withLaunchDarkly.framework
(or the framework with the platform name) in both the Input and Output files entries.
The module was renamed from Darkly
to LaunchDarkly
.
- Objective-C apps replace any
#import
that has a LD header with a single@import LaunchDarkly;
- Swift apps replace
import Darkly
withimport LaunchDarkly
. - CocoaPods users import
LaunchDarkly
for all platforms, similar to prior releases. - For non-iOS platforms built outside of CocoaPods, append the platform name to LaunchDarkly, e.g.
@import LaunchDarkly_macOS;
orimport LaunchDarkly_watchOS
.
- Replace any references to
LDUserBuilder
orLDUserModel
withLDUser
- Replace constructor calls to use the
withKey
constructor - Replace references to
<someUser>.ip
with<someUser>.ipAddress
- Replace references to
<someUser>.custom<jsonType>
with<someUser>.custom
, and convert the object set into a[String: Any]
See Custom users with LDUser
for details.
- Change lines that set
baseUrl
,eventsUrl
, andstreamUrl
with strings to instead set these properties withURL
objects. - Change lines that set
capacity
to seteventCapacity
- Change lines that set time-based properties (
connectionTimeout
,flushInterval
,pollingInterval
, andbackgroundFetchInterval
) to theirTimeInterval
property (connectionTimeout
,eventFlushInterval
,flagPollingInterval
, andbackgroundFlagPollingInterval
). - Change lines that set
streaming
to setstreamingMode
. For Swift apps, use the enum.streaming
or.polling
. For Objective-C apps, set toYES
for streaming, andNO
for polling as with the v2.x or v3.x SDK. Note that if you do not have this property set, LDConfig sets it to streaming mode for you. - Change lines that set
debugEnabled
to setdebugMode
instead.
See Configuration with LDConfig for details.
- Replace references to
LDClient.sharedInstance()
in Swift code toLDClient.shared
. Do not change references tosharedInstance
in Objective-C code. - Replace references to
ldUser
touser
. - Replace references to
ldConfig
toconfig
. - Remove any references to
delegate
. - Change calls to
start
to use the parameter nameconfig
. - Change calls to
start
to use the parameter nameuser
. - Change calls to
start
to not expect a return value. If desired, capture the SDK's online status usingisOnline
. - Change calls to
stopClient
tostop
See LDClient
Controls for details.
- For Objective-C apps, add
ForKey
to eachvariation
method call. e.g.[[LDClient sharedInstance] boolVariationForKey:some-key fallback:fallback-value]
- For Swift apps remove the type in the variation call, and add
forKey:
to the first parameter. e.g.LDClient.shared.variation(forKey: some-key, fallback: fallback-value)
. - Replace calls to
numberVariation
withintegerVariation
ordoubleVariation
as appropriate. Replace the type of theintegerVariation
call withInt
(Swift) orNSInteger
(Objective-C). For Objective-C apps, the client can wrap the result into an NSNumber if needed.
See Getting Feature Flag Values for details.
For any object (Swift enum and struct cannot be feature flag observers) that conformed to LDClientDelegate
, set observers on the LDClient
that correspond to the implemented delegate methods. Use the steps below as a guide.
featureFlagDidUpdate
was called whenever any feature flag was updated. Assess the feature flags the former delegate was interested in.- If the object watches a single feature flag, use
observe(key:, owner:, handler:)
. You can call the original delegate method from thehandler
, or copy the code in the delegate method into the handler. TheLDChangedFlag
passed into the handler has thekey
for the changed flag. - If the object watches a set of feature flags, but not all of them in the environment, use
observe(keys:, owner:, handler:)
. As with 1 above, you can call the original delegate method by looping through the keys in the[LDFlagKey, LDChangedFlag]
passed into the handler. Or you may copy the delegate method code into the handler. Each changed feature flag has an entry inchangedFlags
that contains aLDChangedFlag
you can use to update the client app. - If the object watches all of the feature flags in an environment (available with a specific
mobileKey
) useobserveAll(owner: , handler:)
. Follow the guidance in #2 above to unpack changed feature flag details.
- If the object watches a single feature flag, use
userDidUpdate
was called whenever any feature flag changed. Follow the guidance underfeatureFlagDidUpdate
to decide which observer method to call on the client.userUnchanged
was called in.polling
mode when the response to a flag request did not change any feature flag value. If your app uses.streaming
mode (whether you set it explicitly or accept the default value inLDConfig
) you can ignore this method. If using.polling
mode, callobserveFlagsUnchanged
and either call the delegate method from within the handler, or copy the delegate method code into the handler. 4.serverConnectionUnavailable
was called when the SDK could not connect to LaunchDarkly servers. CallobserveError(owner:, handler:)
with a handler to execute when an error occurs.
See Monitoring Feature Flags for changes for details.
If they were not re-used when implementing observers, you can delete the former LDClientDelegate
methods.
See Event Controls for more details.
Wrap calls to trackEvent
into do-catch statements. If desired, catch JSONSerialization.JSONError.invalidJsonObject
errors. Alternatively, add throws
to any method calls trackEvent
to allow the calling method to handle the error.
Calls to trackEvent
include a 3rd parameter error
, which the SDK sets when a call receives invalid JSON data. To verify the error
object set by trackEvents
threw a JSONSerialization.JSONError.invalidJsonObject
error, compare the domain
to LaunchDarklyJSONErrorDomain
and the code
to LaunchDarklyJSONErrorInvalidJsonObject
.
This section details the changes between the v2.x or v3.x and v4.x APIs.
LDConfig has changed to a struct
, and therefore uses value semantics.
These properties have changed to URL
objects. Set these properties by converting URL strings to a URL using:
ldconfig.baseUrl = URL(string: "https://custom.url.string")!
This property has changed to eventCapacity
.
Time based properties (connectionTimeout
, flushInterval
, pollingInterval
, and backgroundFetchInterval
)
These properties have changed to TimeInterval
properties.
flushInterval
has changed toeventFlushInterval
.pollingInterval
has changed toflagPollingInterval
.backgroundFetchInterval
has changed tobackgroundFlagPollingInterval
This property has changed to streamingMode
and to an enum type LDStreamingMode
. Swift apps use the new API. The default remains .streaming
. To set polling mode, set this property to .polling
. Objective-C apps continue to use streaming
set to YES
or NO
This property has changed to debugMode
.
Set this property to true
to allow the SDK to poll while running in the background.
NOTE: Background polling requires additional client app support.
Set this property to false
if you want the SDK to remain offline after you call start()
We created the Minima struct and defined polling and background polling minima there. This allows the client app to ensure the values set into the corresponding properties meet the requirements for those properties. Access these via the minima
property on your LDConfig struct.
LDConfig conforms to Equatable
.
Since Objective-C does not represent struct
items, the SDK wraps the LDConfig into a NSObject
based wrapper with all the same properties as the Swift struct
. The class ObjcLDConfig
encapsulates the wrapper class. Objective-C client apps should refer to LDConfig
and allow the Swift runtime to handle the conversion. Mixed apps can use the LDConfig
var objcLdConfig
to vend the Objective-C wrapper if needed to pass the LDConfig
to an Objective-C method.
The type changes mentioned above all apply to the Objective-C LDConfig. Int
types become NSInteger
types in Objective-C, replacing NSNumber
objects from v2.x or v3.x.
An Objective-C isEqual
method provides LDConfig object comparison capability.
LDUser
replaces LDUserBuilder
and LDUserModel
from v2.x or v3.x. LDUser
is a Swift struct
, and therefore uses value semantics.
Since the only required property is key
, all other properties are Optional. While this is not really a change from v2.x or v3.x, it is more explicit in Swift and may require some Optional handling that was not required in v2.x or v3.x.
This property has changed to ipAddress
.
This property has changed to custom
and its type has also changed to [String: Any]?.
We added coding keys for all of the user properties. If you add your own custom attributes, you might want to extend CodingKeys
to include your custom attribute keys.
This new static property contains a [String]
with the attributes that can be made private. This list is used if the LDConfig has the flag allUserAttributesPrivate
set.
The SDK sets this property with the system provided device string.
The SDK sets this property with the system provided operating system string.
These methods allows you to pass in a [String: Any]
to create a user. Any other object passed in returns a nil
. Use the CodingKeys
to set user properties in the dictionary.
LDUser conforms to Equatable
.
Since Objective-C does not represent struct
items, the SDK wraps the LDUser into a NSObject
based wrapper with all the same properties as the Swift struct
. The class ObjcLDUser
encapsulates the wrapper class. Objective-C client apps should refer to LDUser
and allow the Swift runtime to handle the conversion. Mixed apps can use the LDUser
var objcLdUser
to vend the Objective-C wrapper if needed to pass the LDUser
to an Objective-C method.
An Objective-C isEqual
method provides LDConfig object comparison capability.
Since CodingKeys
is not accessible to Objective-C, we defined class vars for attribute names, allowing you to define a user dictionary that you can pass into constructors.
The new constructors added to Swift were translated to Objective-C also. Use [[LDUser alloc] initWithObject:]
and [[LDUser alloc] initWithUserDictionary:]
to access them.
An Objective-C isEqual
method provides LDUser
object comparison capability.
This property has changed to shared
.
This property has changed to user
and its type has changed to LDUser
. Client apps can set the user
directly.
This property has changed to config
. Client apps can set the config
directly.
This property was removed. See Replacing LDClient delegate methods
inputConfig
has changed toconfig
.withUserBuilder
has changed touser
and its type changed toLDUser
completion
was added to get a callback when the SDK has completed starting- The return value has been removed. Use
isOnline
to determine the SDK's online status.
This method was renamed to stop()
This method was removed. Set the user
property instead.
LDClient
does not inherit from NSObject, and is therefore not directly available to Objective-C. Instead, the class ObjcLDClient
wraps the LDClient. Since the wrapper inherits from NSObject, Objective-C apps can access the LDClient. We have defined the Objective-C name for ObjcLDClient
to LDClient
, so you access the client through LDClient
just as before.
shared
isn't used with Objective-C, continue to use sharedInstance
.
Swift Generics allowed us to combine the variation
methods that were used in the v2.x or v3.x SDK. v4.x has one variation
method that returns a non-Optional type that matches the non-Optional type the client app provides in the fallback
parameter. A second variation
method allows the client app to use an Optional as the fallback
. This method requires the client to specify the Optional type when passing nil
as the fallback
. For this second method, the fallback parameter is defaulted to nil
. When using this second method, set the type on the item holding the return value, e.g.
let boolFlagValue: Bool? = LDClient.shared.variation(forKey: "bool-flag-key")
A new variationAndSource()
method returns a tuple (value, source)
that allows the client app to see what the source of the value was. source
is an LDFlagValueSource
enum with .server
, .cache
, and .fallback
. As with the variation
methods, there are two variationAndSource
methods. The first returns a non-Optional type as the tuple's value
, while the second returns an Optional type as the tuple's value
. This second method requires the client to specify the Optional type when passing nil
as the fallback
. For this second method, the fallback parameter is defaulted to nil
. When using this second method, set the type on the item holding the return value, e.g.
let (boolFlagValue, boolFlagSource): (Bool?, LDFlagValueSource) = LDClient.shared.variationAndSource(forKey: "bool-flag-key")
A new computed property allFlagValues
returns a [LDFlagKey: Any]
that has all feature flag keys and their values. This dictionary is a snapshot taken when allFlagValues
was requested. The SDK does not try to keep these values up-to-date, and does not record any events when accessing the dictionary.
Swift generic functions cannot operate in Objective-C. The ObjcLDClient
wrapper retains the type-based variation methods used in v2.x or v3.x, except for numberVariation
. A new integerVariation
method reports NSInteger feature flags. NSNumbers that were decimal numbers should use doubleVariation
.
The wrapper also includes new type-based variationAndSource
methods that return a type-based VariationValue
object (e.g. LDBoolVariationValue
) that encapsulates the value
and source
. source
is an ObjcLDFlagValueSource
Objective-C int backed enum (accessed in Objective-C via LDFlagValueSource
). In addition to server
, cache
, and fallback
, other possible values could be nilSource
and typeMismatch
. Feature Flag types that are object types have value types that are nullable in the wrapper. Take care to verify a value exists before using it.
v4.x removes the LDClientDelegate
, which included featureFlagDidUpdate
and userDidUpdate
that the SDK called to notify client apps of changes in the set of feature flags for a given mobile key (called the environment), and the userUnchanged
that the SDK called in .polling
mode when the response to a feature flag request did not change any feature flag value. In order to have the SDK notify the client app when feature flags change, we have provided a closure based observer API.
To monitor a single feature flag, set a callback handler using observe(key:, owner:, handler:)
. The SDK will keep a weak reference to the owner
. When an observed feature flag changes, the SDK executes the closure, passing into it an LDChangedFlag
that provides the key
, oldValue
, oldValueSource
, newValue
, and newValueSource
. The client app can use this to update itself with the new value, or use the closure as a trigger to make another variation
request from the LDClient.
To monitor a set of feature flags, set a callback handler using observe(keys: owner: handler:)
. This functions similar to the single feature flag observer. When any of the observed feature flags change, the SDK will call the closure one time. The closure takes a [LDFlagKey: LDChangedFlag]
which the client app can use to update itself with the new values.
To monitor all feature flags in an environment, set a callback handler using observeAll()
. This functions similar to the multiple-key feature flag observer. When any feature flag in the environment changes, the SDK will call the closure one time.
To monitor when a polling request completes with no changes to the environment, set a callback handler using observeFlagsUnchanged()
. If the SDK is in .polling
mode, and a flag request did not change any flag values, the SDK will call this closure. (NOTE: In .streaming
mode, there is no event that signals flags are unchanged. Therefore this callback will be ignored in .streaming
mode). This method effectively replaces the LDClientDelegate method userUnchanged
.
To discontinue monitoring all feature flags for a given object, call this method. Note that this is not required, the SDK will only keep a weak reference to observers. When the observer goes out of scope, the SDK reference will be nil'd out, and the SDK will no longer call that handler.
The LDClient wrapper provides type-based single-key observer methods that function as described above. The only difference is that the object passed into the observer block will contain type-based Objective-C wrappers for LDChangedFlag
. observeKeys
provides multiple-key observing, and observeAllKeys
provides all-key observing. These function as described above, except that the dictionary passed into the handler will contain Objective-C type-based wrappers that encapsulate the LDChangedFlag properties.
This method has changed to reportEvents()
.
This method has changed to trackEvent
. trackEvent
can now throw if the data
parameter does not contain a valid JSON Object, or nil
. This check is made at run-time. For Objective-C client apps, the method has an additional error
parameter, which the SDK populates when the data is not a valid JSON object.
A new enum JSONError
has cases the SDK uses when a JSON object does not meet expectations. notADictionary
and invalidJsonObject
cases were added. The SDK does not throw notADictionary
to client apps. Client apps should handle invalidJsonObject
errors thrown from trackEvent
A new string constant LaunchDarklyJSONErrorDomain
provides the ability for Objective-C apps to verify the NSError
domain
. The SDK sets this domain for JSONError
s reported via trackEvent
in Objective-C only.
The observe
methods provide the ability to monitor feature flags individually, as a collection, or the whole environment. The SDK will release these observers when they go out of scope, so you can set and forget them. Of course if you need to stop observing you can do that also.
The observeFlagsUnchanged
method sets an observer called in .polling
mode when a flag request leaves the flags unchanged, effectively replacing userUnchanged
.
The observeError(owner:, handler:)
method sets an observer called when an error occurs during flag processing. Replace the delegate method with a call to this method, or call the former delegate method from within the handler set with this method.