Skip to content

Commit

Permalink
feat: local-evaluation (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
bgiori authored Oct 6, 2023
1 parent 387a8e1 commit ebe1188
Show file tree
Hide file tree
Showing 35 changed files with 7,932 additions and 340 deletions.
2 changes: 1 addition & 1 deletion AmplitudeExperiment.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Pod::Spec.new do |spec|
spec.osx.deployment_target = '10.10'
spec.osx.source_files = 'sources/Experiment/**/*.{h,swift}'

spec.tvos.deployment_target = '9.0'
spec.tvos.deployment_target = '10.0'
spec.tvos.source_files = 'sources/Experiment/**/*.{h,swift}'

spec.watchos.deployment_target = '3.0'
Expand Down
94 changes: 74 additions & 20 deletions Experiment.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<EnvironmentVariables>
<EnvironmentVariable
key = "OS_ACTIVITY_MODE"
value = "disable"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let package = Package(
platforms: [
.iOS(.v10),
.macOS(.v10_10),
.tvOS(.v9),
.tvOS(.v10),
.watchOS(.v3)
],
products: [
Expand Down
138 changes: 138 additions & 0 deletions Sources/Experiment/AnyCodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// AnyCodable.swift
// Experiment
//
// Created by Brian Giori on 9/20/23.
//

import Foundation

internal struct AnyDecodable: Decodable {

let value: Any?

init<T>(_ value: T?) {
if value is NSNull {
self.value = nil
} else {
self.value = value
}
}

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()

if container.decodeNil() {
self.init(NSNull())
} else if let bool = try? container.decode(Bool.self) {
self.init(bool)
} else if let int = try? container.decode(Int.self) {
self.init(int)
} else if let uint = try? container.decode(UInt.self) {
self.init(uint)
} else if let double = try? container.decode(Double.self) {
self.init(double)
} else if let string = try? container.decode(String.self) {
self.init(string)
} else if let array = try? container.decode([AnyDecodable].self) {
self.init(array.map { $0.value })
} else if let dictionary = try? container.decode([String: AnyDecodable].self) {
self.init(dictionary.mapValues { $0.value })
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyDecodable value cannot be decoded")
}
}
}

internal struct AnyEncodable: Encodable {

let value: Any

init<T>(_ value: T?) {
self.value = value ?? ()
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()

switch value {
case is NSNull:
try container.encodeNil()
case is Void:
try container.encodeNil()
case let bool as Bool:
try container.encode(bool)
case let int as Int:
try container.encode(int)
case let int8 as Int8:
try container.encode(int8)
case let int16 as Int16:
try container.encode(int16)
case let int32 as Int32:
try container.encode(int32)
case let int64 as Int64:
try container.encode(int64)
case let uint as UInt:
try container.encode(uint)
case let uint8 as UInt8:
try container.encode(uint8)
case let uint16 as UInt16:
try container.encode(uint16)
case let uint32 as UInt32:
try container.encode(uint32)
case let uint64 as UInt64:
try container.encode(uint64)
case let float as Float:
try container.encode(float)
case let double as Double:
try container.encode(double)
case let string as String:
try container.encode(string)
case let number as NSNumber:
try encode(nsnumber: number, into: &container)
case let date as Date:
try container.encode(date)
case let url as URL:
try container.encode(url)
case let array as [Any?]:
try container.encode(array.map { AnyEncodable($0) })
case let dictionary as [String: Any?]:
try container.encode(dictionary.mapValues { AnyEncodable($0) })
case let encodable as Encodable:
try encodable.encode(to: encoder)
default:
let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyEncodable value cannot be encoded")
throw EncodingError.invalidValue(value, context)
}
}

private func encode(nsnumber: NSNumber, into container: inout SingleValueEncodingContainer) throws {
switch Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee))) {
case "B":
try container.encode(nsnumber.boolValue)
case "c":
try container.encode(nsnumber.int8Value)
case "s":
try container.encode(nsnumber.int16Value)
case "i", "l":
try container.encode(nsnumber.int32Value)
case "q":
try container.encode(nsnumber.int64Value)
case "C":
try container.encode(nsnumber.uint8Value)
case "S":
try container.encode(nsnumber.uint16Value)
case "I", "L":
try container.encode(nsnumber.uint32Value)
case "Q":
try container.encode(nsnumber.uint64Value)
case "f":
try container.encode(nsnumber.floatValue)
case "d":
try container.encode(nsnumber.doubleValue)
default:
let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "NSNumber cannot be encoded because its type is not handled")
throw EncodingError.invalidValue(nsnumber, context)
}
}
}
5 changes: 3 additions & 2 deletions Sources/Experiment/Backoff.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@ internal class Backoff {

// Dispatch
private let lock = DispatchSemaphore(value: 1)
private let fetchQueue = DispatchQueue(label: "com.amplitude.experiment.fetch.backoff", qos: .default)
private let fetchQueue: DispatchQueue

// State
private var started: Bool = false
private var cancelled: Bool = false
private var fetchTask: URLSessionTask? = nil

init(attempts: Int, min: Int, max: Int, scalar: Float) {
init(attempts: Int, min: Int, max: Int, scalar: Float, queue: DispatchQueue = DispatchQueue(label: "com.amplitude.experiment.backoff", qos: .default)) {
self.attempts = attempts
self.min = min
self.max = max
self.scalar = scalar
self.fetchQueue = queue
}

func start(
Expand Down
Loading

0 comments on commit ebe1188

Please sign in to comment.