Skip to content
2 changes: 1 addition & 1 deletion .github/workflows/source_clear_cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
source_clear:
runs-on: macos-latest
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- name: Source clear scan
Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
TRAVIS_COM_TOKEN: ${{ secrets.TRAVIS_COM_TOKEN }}

lint:
runs-on: macos-latest
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- uses: maxim-lobanov/setup-xcode@v1
Expand All @@ -47,10 +47,12 @@ jobs:

unittests:
if: "${{ github.event.inputs.PREP == '' && github.event.inputs.RELEASE == '' }}"
uses: optimizely/swift-sdk/.github/workflows/unit_tests.yml@master
# restore back to master after merging.
#uses: optimizely/swift-sdk/.github/workflows/unit_tests.yml@master
uses: optimizely/swift-sdk/.github/workflows/unit_tests.yml@jae/ats-timeout

prepare_for_release:
runs-on: macos-latest
runs-on: macos-12
if: "${{ github.event.inputs.PREP == 'true' && github.event_name == 'workflow_dispatch' }}"
steps:
- uses: actions/checkout@v3
Expand All @@ -74,7 +76,7 @@ jobs:

release:
if: "${{github.event.inputs.RELEASE == 'true' && github.event_name == 'workflow_dispatch' }}"
runs-on: macos-latest
runs-on: macos-12
steps:
- uses: actions/checkout@v3
- uses: maxim-lobanov/setup-xcode@v1
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ jobs:
- uses: actions/checkout@v3
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 14.1.0
# macos version and supported simulator_xcode_versions are all related to this xcode_version, so be careful when you upgrade this.
xcode-version: 12.4
- name: set SDK Branch if PR
if: ${{ github.event_name == 'pull_request' }}
run: |
Expand Down
20 changes: 16 additions & 4 deletions Sources/ODP/OdpEventApiManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ import Foundation
*/

class OdpEventApiManager {

let resourceTimeoutInSecs: Int?

/// OdpEventApiManager init
/// - Parameters:
/// - timeout: timeout for segment fetch
init(timeout: Int? = nil) {
self.resourceTimeoutInSecs = timeout
}

func sendOdpEvents(apiKey: String,
apiHost: String,
events: [OdpEvent],
Expand Down Expand Up @@ -99,8 +107,12 @@ class OdpEventApiManager {
task.resume()
}

func getSession() -> URLSession {
return URLSession(configuration: .ephemeral)
open func getSession() -> URLSession {
let config = URLSessionConfiguration.ephemeral
if let timeout = resourceTimeoutInSecs, timeout > 0 {
config.timeoutIntervalForResource = TimeInterval(timeout)
}
return URLSession(configuration: config)
}

}
13 changes: 11 additions & 2 deletions Sources/ODP/OdpEventManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,18 @@ class OdpEventManager {

let logger = OPTLoggerFactory.getLogger()

init(sdkKey: String, odpConfig: OdpConfig? = nil, apiManager: OdpEventApiManager? = nil) {
/// OdpEventManager init
/// - Parameters:
/// - sdkKey: datafile sdkKey
/// - odpConfig: ODP config (apiKey, apiHost, ...)
/// - apiManager: OdpEventApiManager
/// - resourceTimeoutInSecs: timeout for event dispatch
init(sdkKey: String,
odpConfig: OdpConfig? = nil,
apiManager: OdpEventApiManager? = nil,
resourceTimeoutInSecs: Int? = nil) {
self.odpConfig = odpConfig ?? OdpConfig()
self.apiMgr = apiManager ?? OdpEventApiManager()
self.apiMgr = apiManager ?? OdpEventApiManager(timeout: resourceTimeoutInSecs)

self.queueLock = DispatchQueue(label: "event")

Expand Down
19 changes: 17 additions & 2 deletions Sources/ODP/OdpManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,22 @@ class OdpManager {
return vuidManager.vuid
}

/// OdpManager init
/// - Parameters:
/// - sdkKey: datafile sdkKey
/// - disable: disable ODP
/// - cacheSize: segment cache size
/// - cacheTimeoutInSecs: segment cache timeout
/// - timeoutForSegmentFetchInSecs: timeout for segment fetch
/// - timeoutForEventDispatchInSecs: timeout for event dispatch
/// - segmentManager: ODPSegmentManager
/// - eventManager: ODPEventManager
init(sdkKey: String,
disable: Bool,
cacheSize: Int,
cacheTimeoutInSecs: Int,
timeoutForSegmentFetchInSecs: Int? = nil,
timeoutForEventDispatchInSecs: Int? = nil,
segmentManager: OdpSegmentManager? = nil,
eventManager: OdpEventManager? = nil) {

Expand All @@ -53,14 +65,17 @@ class OdpManager {
} else {
self.segmentManager = OdpSegmentManager(cacheSize: cacheSize,
cacheTimeoutInSecs: cacheTimeoutInSecs,
odpConfig: odpConfig)
odpConfig: odpConfig,
resourceTimeoutInSecs: timeoutForSegmentFetchInSecs)
}

if let eventManager = eventManager {
eventManager.odpConfig = odpConfig
self.eventManager = eventManager
} else {
self.eventManager = OdpEventManager(sdkKey: sdkKey, odpConfig: odpConfig)
self.eventManager = OdpEventManager(sdkKey: sdkKey,
odpConfig: odpConfig,
resourceTimeoutInSecs: timeoutForEventDispatchInSecs)
}

self.eventManager.registerVUID(vuid: self.vuidManager.vuid)
Expand Down
16 changes: 14 additions & 2 deletions Sources/ODP/OdpSegmentApiManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ import Foundation

class OdpSegmentApiManager {
let logger = OPTLoggerFactory.getLogger()
let resourceTimeoutInSecs: Int?

/// OdpSegmentApiManager init
/// - Parameters:
/// - timeout: timeout for segment fetch
init(timeout: Int? = nil) {
self.resourceTimeoutInSecs = timeout
}

func fetchSegments(apiKey: String,
apiHost: String,
Expand Down Expand Up @@ -175,8 +183,12 @@ class OdpSegmentApiManager {
task.resume()
}

func getSession() -> URLSession {
return URLSession(configuration: .ephemeral)
open func getSession() -> URLSession {
let config = URLSessionConfiguration.ephemeral
if let timeout = resourceTimeoutInSecs, timeout > 0 {
config.timeoutIntervalForResource = TimeInterval(timeout)
}
return URLSession(configuration: config)
}

func makeQuery(userKey: String, userValue: String, segmentsToCheck: [String]) -> [String: Any] {
Expand Down
12 changes: 10 additions & 2 deletions Sources/ODP/OdpSegmentManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ class OdpSegmentManager {

let logger = OPTLoggerFactory.getLogger()

/// OdpSegmentManager init
/// - Parameters:
/// - cacheSize: segment cache size
/// - cacheTimeoutInSecs: segment cache timeout
/// - odpConfig: ODP config (apiKey, apiHost, ...)
/// - apiManager: OdpSegmentApiManager
/// - resourceTimeoutInSecs: timeout for segment fetch
init(cacheSize: Int,
cacheTimeoutInSecs: Int,
odpConfig: OdpConfig? = nil,
apiManager: OdpSegmentApiManager? = nil) {
apiManager: OdpSegmentApiManager? = nil,
resourceTimeoutInSecs: Int? = nil) {
self.odpConfig = odpConfig ?? OdpConfig()
self.apiMgr = apiManager ?? OdpSegmentApiManager()
self.apiMgr = apiManager ?? OdpSegmentApiManager(timeout: resourceTimeoutInSecs)

self.segmentsCache = LruCache<String, [String]>(size: cacheSize,
timeoutInSecs: cacheTimeoutInSecs)
Expand Down
10 changes: 10 additions & 0 deletions Sources/ODP/OptimizelySdkSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public struct OptimizelySdkSettings {
let segmentsCacheSize: Int
/// The timeout in seconds of audience segments cache - timeout is disabled if this is set to zero.
let segmentsCacheTimeoutInSecs: Int
/// The timeout in seconds of odp segment fetch - OS default timeout will be used if this is set to zero.
let timeoutForSegmentFetchInSecs: Int
/// The timeout in seconds of odp event dispatch - OS default timeout will be used if this is set to zero.
let timeoutForOdpEventInSecs: Int
/// ODP features are disabled if this is set to true.
let disableOdp: Bool

Expand All @@ -29,12 +33,18 @@ public struct OptimizelySdkSettings {
/// - Parameters:
/// - segmentsCacheSize: The maximum size of audience segments cache (optional. default = 100). Set to zero to disable caching.
/// - segmentsCacheTimeoutInSecs: The timeout in seconds of audience segments cache (optional. default = 600). Set to zero to disable timeout.
/// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero.
/// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero.
/// - disableOdp: Set this flag to true (default = false) to disable ODP features
public init(segmentsCacheSize: Int = 100,
segmentsCacheTimeoutInSecs: Int = 600,
timeoutForSegmentFetchInSecs: Int = 10,
timeoutForOdpEventInSecs: Int = 10,
disableOdp: Bool = false) {
self.segmentsCacheSize = segmentsCacheSize
self.segmentsCacheTimeoutInSecs = segmentsCacheTimeoutInSecs
self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs
self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs
self.disableOdp = disableOdp
}
}
4 changes: 3 additions & 1 deletion Sources/Optimizely/OptimizelyClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ open class OptimizelyClient: NSObject {
self.odpManager = OdpManager(sdkKey: sdkKey,
disable: sdkSettings.disableOdp,
cacheSize: sdkSettings.segmentsCacheSize,
cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs)
cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs,
timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs,
timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs)

super.init()

Expand Down
15 changes: 12 additions & 3 deletions Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,29 @@ class OptimizelyClientTests_ODP: XCTestCase {

// MARK: - ODP configuration

func testConfigurableSettings_default() {
func testSdkSettings_default() {
let optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey)

XCTAssertEqual(100, optimizely.odpManager.segmentManager?.segmentsCache.maxSize)
XCTAssertEqual(600, optimizely.odpManager.segmentManager?.segmentsCache.timeoutInSecs)
XCTAssertEqual(10, optimizely.odpManager.segmentManager?.apiMgr.resourceTimeoutInSecs)
XCTAssertEqual(10, optimizely.odpManager.eventManager?.apiMgr.resourceTimeoutInSecs)
XCTAssertEqual(true, optimizely.odpManager.enabled)
}

func testConfigurableSettings_custom() {
var sdkSettings = OptimizelySdkSettings(segmentsCacheSize: 12, segmentsCacheTimeoutInSecs: 345)
func testSdkSettings_custom() {
var sdkSettings = OptimizelySdkSettings(segmentsCacheSize: 12,
segmentsCacheTimeoutInSecs: 345)
var optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings)
XCTAssertEqual(12, optimizely.odpManager.segmentManager?.segmentsCache.maxSize)
XCTAssertEqual(345, optimizely.odpManager.segmentManager?.segmentsCache.timeoutInSecs)

sdkSettings = OptimizelySdkSettings(timeoutForSegmentFetchInSecs: 34,
timeoutForOdpEventInSecs: 45)
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings)
XCTAssertEqual(34, optimizely.odpManager.segmentManager?.apiMgr.resourceTimeoutInSecs)
XCTAssertEqual(45, optimizely.odpManager.eventManager?.apiMgr.resourceTimeoutInSecs)

sdkSettings = OptimizelySdkSettings(disableOdp: true)
optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: sdkSettings)
XCTAssertEqual(false, optimizely.odpManager.enabled)
Expand Down
12 changes: 12 additions & 0 deletions Tests/OptimizelyTests-Common/OdpEventApiManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ class OdpEventApiManagerTests: XCTestCase {
XCTAssert(jsonDispatched.contains("\"key3\":null"))
}

// MARK: - timeout

func testTimeout() {
let api = OdpEventApiManager(timeout: 3)
XCTAssertEqual(3, api.getSession().configuration.timeoutIntervalForResource)
}

func testTimeout_useOSDefaultIfTimeoutIsNotProvided() {
let api = OdpEventApiManager()
XCTAssertEqual(604800, api.getSession().configuration.timeoutIntervalForResource)
}

}

// MARK: - Tests with live ODP server
Expand Down
1 change: 0 additions & 1 deletion Tests/OptimizelyTests-Common/OdpManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class OdpManagerTests: XCTestCase {
disable: true,
cacheSize: cacheSize,
cacheTimeoutInSecs: cacheTimeout)

XCTAssertTrue(manager.vuid.starts(with: "vuid_"), "vuid should be serverved even when ODP is disabled.")

let sem = DispatchSemaphore(value: 0)
Expand Down
12 changes: 12 additions & 0 deletions Tests/OptimizelyTests-Common/OdpSegmentApiManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,18 @@ class OdpSegmentApiManagerTests: XCTestCase {
XCTAssertEqual(.success, sem.wait(timeout: .now() + .seconds(1)))
}

// MARK: - timeout

func testTimeout() {
let api = OdpSegmentApiManager(timeout: 3)
XCTAssertEqual(3, api.getSession().configuration.timeoutIntervalForResource)
}

func testTimeout_useOSDefaultIfTimeoutIsNotProvided() {
let api = OdpSegmentApiManager()
XCTAssertEqual(604800, api.getSession().configuration.timeoutIntervalForResource)
}

// MARK: - Others

func testMakeQuery() {
Expand Down