Skip to content

Commit

Permalink
Update Objective C bindings to work with contexts (#213)
Browse files Browse the repository at this point in the history
  • Loading branch information
keelerm84 authored Jun 22, 2022
1 parent c25f75c commit 790603f
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .jazzy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ custom_categories:
- ObjcLDClient
- ObjcLDConfig
- ObjcLDUser
- ObjcLDReference
- ObjcLDContext
- ObjcLDChangedFlag
- ObjcLDValue
- ObjcLDValueType
Expand Down
20 changes: 20 additions & 0 deletions LaunchDarkly.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@
A31088282837DCA900184942 /* ReferenceSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A31088252837DCA900184942 /* ReferenceSpec.swift */; };
A31088292837DCA900184942 /* KindSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A31088262837DCA900184942 /* KindSpec.swift */; };
A33A5F7A28466D04000C29C7 /* LDContextStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A33A5F7928466D04000C29C7 /* LDContextStub.swift */; };
A36EDFC82853883400D91B05 /* ObjcLDReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36EDFC72853883400D91B05 /* ObjcLDReference.swift */; };
A36EDFC92853883400D91B05 /* ObjcLDReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36EDFC72853883400D91B05 /* ObjcLDReference.swift */; };
A36EDFCA2853883400D91B05 /* ObjcLDReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36EDFC72853883400D91B05 /* ObjcLDReference.swift */; };
A36EDFCB2853883400D91B05 /* ObjcLDReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36EDFC72853883400D91B05 /* ObjcLDReference.swift */; };
A36EDFCD2853C50B00D91B05 /* ObjcLDContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36EDFCC2853C50B00D91B05 /* ObjcLDContext.swift */; };
A36EDFCE2853C50B00D91B05 /* ObjcLDContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36EDFCC2853C50B00D91B05 /* ObjcLDContext.swift */; };
A36EDFCF2853C50B00D91B05 /* ObjcLDContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36EDFCC2853C50B00D91B05 /* ObjcLDContext.swift */; };
A36EDFD02853C50B00D91B05 /* ObjcLDContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36EDFCC2853C50B00D91B05 /* ObjcLDContext.swift */; };
B40B419C249ADA6B00CD0726 /* DiagnosticCacheSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40B419B249ADA6B00CD0726 /* DiagnosticCacheSpec.swift */; };
B4265EB124E7390C001CFD2C /* TestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4265EB024E7390C001CFD2C /* TestUtil.swift */; };
B467791324D8AEEC00897F00 /* LDSwiftEventSourceStatic in Frameworks */ = {isa = PBXBuildFile; productRef = B467791224D8AEEC00897F00 /* LDSwiftEventSourceStatic */; };
Expand Down Expand Up @@ -416,6 +424,8 @@
A31088252837DCA900184942 /* ReferenceSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReferenceSpec.swift; sourceTree = "<group>"; };
A31088262837DCA900184942 /* KindSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KindSpec.swift; sourceTree = "<group>"; };
A33A5F7928466D04000C29C7 /* LDContextStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDContextStub.swift; sourceTree = "<group>"; };
A36EDFC72853883400D91B05 /* ObjcLDReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjcLDReference.swift; sourceTree = "<group>"; };
A36EDFCC2853C50B00D91B05 /* ObjcLDContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjcLDContext.swift; sourceTree = "<group>"; };
B40B419B249ADA6B00CD0726 /* DiagnosticCacheSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiagnosticCacheSpec.swift; sourceTree = "<group>"; };
B4265EB024E7390C001CFD2C /* TestUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUtil.swift; sourceTree = "<group>"; };
B468E70F24B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjcLDEvaluationDetail.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -634,12 +644,14 @@
835E1D341F63332C00184DB4 /* ObjectiveC */ = {
isa = PBXGroup;
children = (
A36EDFCC2853C50B00D91B05 /* ObjcLDContext.swift */,
835E1D3C1F63450A00184DB4 /* ObjcLDClient.swift */,
835E1D3D1F63450A00184DB4 /* ObjcLDConfig.swift */,
835E1D3E1F63450A00184DB4 /* ObjcLDUser.swift */,
835E1D421F685AC900184DB4 /* ObjcLDChangedFlag.swift */,
B468E70F24B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift */,
29F9D19D2812E005008D12C0 /* ObjcLDValue.swift */,
A36EDFC72853883400D91B05 /* ObjcLDReference.swift */,
);
path = ObjectiveC;
sourceTree = "<group>";
Expand Down Expand Up @@ -1150,6 +1162,7 @@
831188572113AE0B00D77CB5 /* FlagChangeNotifier.swift in Sources */,
8311884D2113ADE200D77CB5 /* FlagsUnchangedObserver.swift in Sources */,
8311885F2113AE2D00D77CB5 /* HTTPURLRequest.swift in Sources */,
A36EDFD02853C50B00D91B05 /* ObjcLDContext.swift in Sources */,
B4C9D4362489C8FD004A9B03 /* DiagnosticCache.swift in Sources */,
831188452113ADC500D77CB5 /* LDClient.swift in Sources */,
A310881E2837DC0400184942 /* Kind.swift in Sources */,
Expand Down Expand Up @@ -1184,6 +1197,7 @@
8354AC732243166900CDE602 /* FeatureFlagCache.swift in Sources */,
8311885B2113AE1D00D77CB5 /* Throttler.swift in Sources */,
8311884E2113ADE500D77CB5 /* Event.swift in Sources */,
A36EDFCB2853883400D91B05 /* ObjcLDReference.swift in Sources */,
832D68A5224A38FC005F052A /* CacheConverter.swift in Sources */,
831188432113ADBE00D77CB5 /* LDCommon.swift in Sources */,
B4C9D4312489B5FF004A9B03 /* DiagnosticEvent.swift in Sources */,
Expand All @@ -1202,6 +1216,7 @@
buildActionMask = 2147483647;
files = (
B468E71224B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift in Sources */,
A36EDFCF2853C50B00D91B05 /* ObjcLDContext.swift in Sources */,
831EF34320655E730001C643 /* LDCommon.swift in Sources */,
831EF34420655E730001C643 /* LDConfig.swift in Sources */,
A31088212837DC0400184942 /* LDContext.swift in Sources */,
Expand All @@ -1212,6 +1227,7 @@
831EF34A20655E730001C643 /* FeatureFlag.swift in Sources */,
C443A40C2315AA4D00145710 /* NetworkReporter.swift in Sources */,
831EF34B20655E730001C643 /* LDChangedFlag.swift in Sources */,
A36EDFCA2853883400D91B05 /* ObjcLDReference.swift in Sources */,
8354AC722243166900CDE602 /* FeatureFlagCache.swift in Sources */,
A310881D2837DC0400184942 /* Kind.swift in Sources */,
C443A40423145FBE00145710 /* ConnectionInformation.swift in Sources */,
Expand Down Expand Up @@ -1267,6 +1283,7 @@
83FEF8DD1F266742001CF12C /* FlagSynchronizer.swift in Sources */,
830BF933202D188E006DF9B1 /* HTTPURLRequest.swift in Sources */,
B4C9D4332489C8FD004A9B03 /* DiagnosticCache.swift in Sources */,
A36EDFCD2853C50B00D91B05 /* ObjcLDContext.swift in Sources */,
8354EFE51F263DAC00C05156 /* FeatureFlag.swift in Sources */,
8372668C20D4439600BD1088 /* DateFormatter.swift in Sources */,
A310881B2837DC0400184942 /* Kind.swift in Sources */,
Expand Down Expand Up @@ -1301,6 +1318,7 @@
8347BB0C21F147E100E56BCD /* LDTimer.swift in Sources */,
B468E71024B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift in Sources */,
8354AC702243166900CDE602 /* FeatureFlagCache.swift in Sources */,
A36EDFC82853883400D91B05 /* ObjcLDReference.swift in Sources */,
8358F2621F47747F00ECE1AF /* FlagChangeObserver.swift in Sources */,
832D68A2224A38FC005F052A /* CacheConverter.swift in Sources */,
835E1D401F63450A00184DB4 /* ObjcLDConfig.swift in Sources */,
Expand Down Expand Up @@ -1372,6 +1390,7 @@
83D9EC782062DEAB004D7FA6 /* LDUser.swift in Sources */,
B4C9D4342489C8FD004A9B03 /* DiagnosticCache.swift in Sources */,
83D9EC7C2062DEAB004D7FA6 /* FeatureFlag.swift in Sources */,
A36EDFCE2853C50B00D91B05 /* ObjcLDContext.swift in Sources */,
8372668D20D4439600BD1088 /* DateFormatter.swift in Sources */,
83D9EC7D2062DEAB004D7FA6 /* LDChangedFlag.swift in Sources */,
A310881C2837DC0400184942 /* Kind.swift in Sources */,
Expand Down Expand Up @@ -1406,6 +1425,7 @@
C443A40323145FB700145710 /* ConnectionInformation.swift in Sources */,
B4C9D4392489E20A004A9B03 /* DiagnosticReporter.swift in Sources */,
B468E71124B3C3AC00E0C883 /* ObjcLDEvaluationDetail.swift in Sources */,
A36EDFC92853883400D91B05 /* ObjcLDReference.swift in Sources */,
83D9EC952062DEAB004D7FA6 /* Date.swift in Sources */,
832D68A3224A38FC005F052A /* CacheConverter.swift in Sources */,
83D9EC972062DEAB004D7FA6 /* Thread.swift in Sources */,
Expand Down
10 changes: 5 additions & 5 deletions LaunchDarkly/LaunchDarkly/Models/Context/Reference.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

enum ReferenceError: Codable, Equatable, Error {
public enum ReferenceError: Codable, Equatable, Error {
case empty
case doubleSlash
case invalidEscapeSequence
Expand All @@ -25,7 +25,7 @@ enum ReferenceError: Codable, Equatable, Error {
}

extension ReferenceError: CustomStringConvertible {
var description: String {
public var description: String {
switch self {
case .empty: return "empty"
case .doubleSlash: return "doubleSlash"
Expand Down Expand Up @@ -129,19 +129,19 @@ public struct Reference: Codable, Equatable, Hashable {
return error == nil
}

internal func getError() -> ReferenceError? {
public func getError() -> ReferenceError? {
return error
}

public func depth() -> Int {
internal func depth() -> Int {
return components.count
}

internal func raw() -> String {
return rawPath
}

public func component(_ index: Int) -> (String, Int?)? {
internal func component(_ index: Int) -> (String, Int?)? {
if index >= self.depth() {
return nil
}
Expand Down
28 changes: 12 additions & 16 deletions LaunchDarkly/LaunchDarkly/ObjectiveC/ObjcLDClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,9 @@ public final class ObjcLDClient: NSObject {
- parameter user: The ObjcLDUser set with the desired user.
*/
// TODO(mmk) Come back to this
// @objc public func identify(context: ObjcLDContext) {
// ldClient.identify(context: context.context, completion: nil)
// }
@objc public func identify(context: ObjcLDContext) {
ldClient.identify(context: context.context, completion: nil)
}

/**
The LDUser set into the LDClient may affect the set of feature flags returned by the LaunchDarkly server, and ties event tracking to the user. See `LDUser` for details about what information can be retained.
Expand All @@ -127,10 +126,9 @@ public final class ObjcLDClient: NSObject {
- parameter user: The ObjcLDUser set with the desired user.
- parameter completion: Closure called when the embedded `setOnlineIdentify` call completes, subject to throttling delays. (Optional)
*/
// TODO(mmk) Come back to this
// @objc public func identify(context: ObjcLDContext, completion: (() -> Void)? = nil) {
// ldClient.identify(context: context.context, completion: completion)
// }
@objc public func identify(context: ObjcLDContext, completion: (() -> Void)? = nil) {
ldClient.identify(context: context.context, completion: completion)
}

/**
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.
Expand Down Expand Up @@ -552,10 +550,9 @@ public final class ObjcLDClient: NSObject {
- parameter completion: Closure called when the embedded `setOnline` call completes. (Optional)
*/
/// - Tag: start
// TODO(mmk) Come back to this
// @objc public static func start(configuration: ObjcLDConfig, context: ObjcLDContext, completion: (() -> Void)? = nil) {
// LDClient.start(config: configuration.config, context: context.context, completion: completion)
// }
@objc public static func start(configuration: ObjcLDConfig, context: ObjcLDContext, completion: (() -> Void)? = nil) {
LDClient.start(config: configuration.config, context: context.context, completion: completion)
}

/**
See [start](x-source-tag://start) for more information on starting the SDK.
Expand All @@ -565,10 +562,9 @@ public final class ObjcLDClient: NSObject {
- parameter startWaitSeconds: A TimeInterval that determines when the completion will return if no flags have been returned from the network.
- parameter completion: Closure called when the embedded `setOnline` call completes. Takes a Bool that indicates whether the completion timedout as a parameter. (Optional)
*/
// TODO(mmk) Come back to this
// @objc public static func start(configuration: ObjcLDConfig, context: ObjcLDContext, startWaitSeconds: TimeInterval, completion: ((_ timedOut: Bool) -> Void)? = nil) {
// LDClient.start(config: configuration.config, context: context.context, startWaitSeconds: startWaitSeconds, completion: completion)
// }
@objc public static func start(configuration: ObjcLDConfig, context: ObjcLDContext, startWaitSeconds: TimeInterval, completion: ((_ timedOut: Bool) -> Void)? = nil) {
LDClient.start(config: configuration.config, context: context.context, startWaitSeconds: startWaitSeconds, completion: completion)
}

private init(client: LDClient) {
ldClient = client
Expand Down
110 changes: 110 additions & 0 deletions LaunchDarkly/LaunchDarkly/ObjectiveC/ObjcLDContext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import Foundation

@objc(LDContext)
public final class ObjcLDContext: NSObject {
var context: LDContext

init(_ context: LDContext) {
self.context = context
}

@objc public func fullyQualifiedKey() -> String { context.fullyQualifiedKey() }
@objc public func isMulti() -> Bool { context.isMulti() }
@objc public func contextKeys() -> [String: String] { context.contextKeys() }
@objc public func getValue(reference: ObjcLDReference) -> ObjcLDValue? {
guard let value = context.getValue(reference.reference)
else { return nil }

return ObjcLDValue(wrappedValue: value)
}
}

@objc(LDContextBuilder)
public final class ObjcLDContextBuilder: NSObject {
var builder: LDContextBuilder

@objc public init(key: String) {
builder = LDContextBuilder(key: key)
}

// Initializer to wrap the Swift LDContextBuilder into ObjcLDContextBuilder for use in
// Objective-C apps.
init(_ builder: LDContextBuilder) {
self.builder = builder
}

@objc public func kind(kind: String) { builder.kind(kind) }
@objc public func key(key: String) { builder.key(key) }
@objc public func name(name: String) { builder.name(name) }
@objc public func secondary(secondary: String) { builder.secondary(secondary) }
@objc public func transient(transient: Bool) { builder.transient(transient) }
@objc public func addPrivateAttribute(reference: ObjcLDReference) { builder.addPrivateAttribute(reference.reference) }
@objc public func removePrivateAttribute(reference: ObjcLDReference) { builder.removePrivateAttribute(reference.reference) }

@discardableResult
@objc public func trySetValue(name: String, value: ObjcLDValue) -> Bool {
builder.trySetValue(name, value.wrappedValue)
}

@objc public func build() -> ContextBuilderResult {
switch builder.build() {
case .success(let context):
return ContextBuilderResult.fromSuccess(context)
case .failure(let error):
return ContextBuilderResult.fromError(error)
}
}
}

@objc(LDMultiContextBuilder)
public final class ObjcLDMultiContextBuilder: NSObject {
var builder: LDMultiContextBuilder

@objc public override init() {
builder = LDMultiContextBuilder()
}

@objc public func addContext(context: ObjcLDContext) {
builder.addContext(context.context)
}

// Initializer to wrap the Swift LDMultiContextBuilder into ObjcLDMultiContextBuilder for use in
// Objective-C apps.
init(_ builder: LDMultiContextBuilder) {
self.builder = builder
}

@objc public func build() -> ContextBuilderResult {
switch builder.build() {
case .success(let context):
return ContextBuilderResult.fromSuccess(context)
case .failure(let error):
return ContextBuilderResult.fromError(error)
}
}
}

@objc public class ContextBuilderResult: NSObject {
@objc public private(set) var success: ObjcLDContext?
@objc public private(set) var failure: NSError?

private override init() {
super.init()
success = nil
failure = nil
}

public static func fromSuccess(_ success: LDContext) -> ContextBuilderResult {
ContextBuilderResult(success, nil)
}

public static func fromError(_ error: ContextBuilderError) -> ContextBuilderResult {
ContextBuilderResult(nil, error)
}

private convenience init(_ arg1: LDContext?, _ arg2: ContextBuilderError?) {
self.init()
success = arg1.map { ObjcLDContext($0) }
failure = arg2.map { $0 as NSError }
}
}
38 changes: 38 additions & 0 deletions LaunchDarkly/LaunchDarkly/ObjectiveC/ObjcLDReference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Foundation

@objc(Reference)
public final class ObjcLDReference: NSObject {
var reference: Reference

@objc public init(value: String) {
reference = Reference(value)
}

// Initializer to wrap the Swift Reference into ObjcLDReference for use in
// Objective-C apps.
init(_ reference: Reference) {
self.reference = reference
}

@objc public func isValid() -> Bool { reference.isValid() }

@objc public func getError() -> NSError? {
guard let error = reference.getError()
else { return nil }

return error as NSError
}
}

@objc(ReferenceError)
public final class ObjcLDReferenceError: NSObject {
var error: ReferenceError

// Initializer to wrap the Swift ReferenceError into ObjcLDReferenceError for use in
// Objective-C apps.
init(_ error: ReferenceError) {
self.error = error
}

override public var description: String { self.error.description }
}
15 changes: 13 additions & 2 deletions LaunchDarkly/LaunchDarklyTests/Models/Context/LDContextSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import XCTest
@testable import LaunchDarkly

final class LDContextSpec: XCTestCase {
// TOOD(mmk) Make sure we cannot make a context with a kind of kind

func testBuildCanCreateSimpleContext() throws {
var builder = LDContextBuilder(key: "context-key")
builder.name("Name")
Expand All @@ -15,6 +13,19 @@ final class LDContextSpec: XCTestCase {
XCTAssertFalse(context.isMulti())
}

func testBuilderWillNotAcceptKindOfTypeKind() {
var builder = LDContextBuilder(key: "context-key")
builder.kind("kind")

guard case .failure(let error) = builder.build()
else {
XCTFail("Builder should not create context with kind 'kind'")
return
}

XCTAssertEqual(error, ContextBuilderError.invalidKind)
}

func testBuilderCanHandleMissingKind() throws {
var builder = LDContextBuilder(key: "key")

Expand Down

0 comments on commit 790603f

Please sign in to comment.