RIBs/Classes/LeakDetector/Executor.swift (27 lines of code) (raw):
//
// Copyright (c) 2017. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Dispatch
import Foundation
import RxSwift
public class Executor {
/// Execute the given logic after the given delay assuming the given maximum frame duration.
///
/// This allows excluding the time elapsed due to breakpoint pauses.
///
/// - note: The logic closure is not guaranteed to be performed exactly after the given delay. It may be performed
/// later if the actual frame duration exceeds the given maximum frame duration.
///
/// - parameter delay: The delay to perform the logic, excluding any potential elapsed time due to breakpoint
/// pauses.
/// - parameter maxFrameDuration: The maximum duration a single frame should take. Defaults to 33ms.
/// - parameter logic: The closure logic to perform.
public static func execute(withDelay delay: TimeInterval, maxFrameDuration: Int = 33, logic: @escaping () -> ()) {
let period = DispatchTimeInterval.milliseconds(maxFrameDuration / 3)
var lastRunLoopTime = Date().timeIntervalSinceReferenceDate
var properFrameTime = 0.0
var didExecute = false
_ = Observable<Int>
.timer(DispatchTimeInterval.milliseconds(0), period: period, scheduler: MainScheduler.instance)
.take(while: { _ in
!didExecute
})
.subscribe(onNext: { _ in
let currentTime = Date().timeIntervalSinceReferenceDate
let trueElapsedTime = currentTime - lastRunLoopTime
lastRunLoopTime = currentTime
// If we did drop frame, we under-count the frame duration, which is fine. It
// just means the logic is performed slightly later.
let boundedElapsedTime = min(trueElapsedTime, Double(maxFrameDuration) / 1000)
properFrameTime += boundedElapsedTime
if properFrameTime > delay {
didExecute = true
logic()
}
})
}
}