Skip to content

fix: remove accumulated polling when app comes back to foreground #301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 11 additions & 19 deletions Sources/Implementation/DefaultDatafileHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,18 +168,6 @@ class DefaultDatafileHandler: OPTDatafileHandler {
}
}

@objc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not used anywhere. probably leftover from previous changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leftover from ios 9 support. got it. :)

func timerFired(timer: Timer) {
if let info = timer.userInfo as? [String: Any],
let sdkKey = info["sdkKey"] as? String,
let updateInterval = info["updateInterval"] as? Int,
let startDate = info["startTime"] as? Date,
let datafileChangeNotification = info["datafileChangeNotification"] as? ((Data) -> Void) {
self.performPerodicDownload(sdkKey: sdkKey, startTime: startDate, updateInterval: updateInterval, datafileChangeNotification: datafileChangeNotification)
}
timer.invalidate()
}

func hasPeriodUpdates(sdkKey: String) -> Bool {
var restart = true
self.timers.performAtomic(atomicOperation: { (timers) in
Expand All @@ -195,6 +183,7 @@ class DefaultDatafileHandler: OPTDatafileHandler {
startTime: Date,
updateInterval: Int,
datafileChangeNotification: ((Data) -> Void)?) {
let beginDownloading = Date()
self.downloadDatafile(sdkKey: sdkKey) { (result) in
switch result {
case .success(let data):
Expand All @@ -207,15 +196,18 @@ class DefaultDatafileHandler: OPTDatafileHandler {
}

if self.hasPeriodUpdates(sdkKey: sdkKey) {
let interval = self.timers.property?[sdkKey]?.interval ?? updateInterval
let actualDiff = (Int(abs(startTime.timeIntervalSinceNow)) - updateInterval)
var nextInterval = interval
if actualDiff > 0 {
nextInterval -= actualDiff
// adjust the next fire time so that events will be fired at fixed interval regardless of the download latency
// if latency is too big (or returning from background mode), fire the next event immediately once

var interval = self.timers.property?[sdkKey]?.interval ?? updateInterval
let delay = Int(Date().timeIntervalSince(beginDownloading))
interval -= delay
if interval < 0 {
interval = 0
}

self.logger.d("next datafile download is \(nextInterval) seconds \(Date())")
self.startPeriodicUpdates(sdkKey: sdkKey, updateInterval: nextInterval, datafileChangeNotification: datafileChangeNotification)
self.logger.d("next datafile download is \(interval) seconds \(Date())")
self.startPeriodicUpdates(sdkKey: sdkKey, updateInterval: interval, datafileChangeNotification: datafileChangeNotification)
}
}
}
Expand Down
75 changes: 74 additions & 1 deletion Tests/OptimizelyTests-Common/DatafileHandlerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,83 @@ class DatafileHandlerTests: XCTestCase {

XCTAssert(count == 10)
XCTAssert(seconds == 10)
}

func testPeriodicDownload_PollingShouldNotBeAccumulatedWhileInBackground() {
class FakeDatafileHandler: DefaultDatafileHandler {
let data = Data()
override func downloadDatafile(sdkKey: String,
returnCacheIfNoChange: Bool,
resourceTimeoutInterval: Double?,
completionHandler: @escaping DatafileDownloadCompletionHandler) {
completionHandler(.success(data))
}
}

let expectation = XCTestExpectation(description: "polling")
let handler = FakeDatafileHandler()
let now = Date()

let updateInterval = 1
let idleTime = 5
var count = 0
var seconds = 0
handler.startPeriodicUpdates(sdkKey: "notrealkey", updateInterval: updateInterval) { _ in
// simulate going to background and coming back to foreground after 5secs
if count == 0 {
sleep(UInt32(idleTime))
}

count += 1

// check if delayed polling not accumulated and completed back-to-back
if count == 5 {
handler.stopPeriodicUpdates()
expectation.fulfill()
seconds = Int(abs(now.timeIntervalSinceNow))
}
}

wait(for: [expectation], timeout: 30)

XCTAssert(seconds >= idleTime + 3) // 3 instead of 5 for tolerating timer inaccuracy
}

func testPeriodicDownload_PollingPeriodAdjustedByDelay() {
class FakeDatafileHandler: DefaultDatafileHandler {
let data = Data()
override func downloadDatafile(sdkKey: String,
returnCacheIfNoChange: Bool,
resourceTimeoutInterval: Double?,
completionHandler: @escaping DatafileDownloadCompletionHandler) {
sleep(1)
completionHandler(.success(data))
}
}

let expectation = XCTestExpectation(description: "polling")
let handler = FakeDatafileHandler()
let now = Date()

let updateInterval = 2
let maxCount = 5
var count = 0
var seconds = 0
handler.startPeriodicUpdates(sdkKey: "notrealkey", updateInterval: updateInterval) { _ in
count += 1

if count == maxCount {
handler.stopPeriodicUpdates()
expectation.fulfill()
seconds = Int(abs(now.timeIntervalSinceNow))
}
}

wait(for: [expectation], timeout: 30)
XCTAssert(seconds <= updateInterval * (maxCount + 1))
}


func testPeriodicDownloadWithOptimizlyClient() {
class FakeDatafileHandler: DefaultDatafileHandler {
let data = OTUtils.loadJSONDatafile("typed_audience_datafile")
Expand Down Expand Up @@ -228,7 +302,6 @@ class DatafileHandlerTests: XCTestCase {
wait(for: [expection], timeout: 10)

XCTAssert(count == 9)

}

func testDownloadTimeout() {
Expand Down