Skip to content

Commit

Permalink
Merge commit 'bc1313d3266c77d6e83da1d5294eb9a35e9fba6f'
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucas Nelaupe committed May 26, 2020
1 parent b96454d commit 2698fc5
Show file tree
Hide file tree
Showing 29 changed files with 687 additions and 761 deletions.
4 changes: 2 additions & 2 deletions Sources/SwiftQueue/Constraint+Charging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ internal final class BatteryChargingConstraint: JobConstraint {
return true
}

operation.logger.log(.verbose, jobId: operation.info.uuid, message: "Unsatisfied charging requirement")
operation.logger.log(.verbose, jobId: operation.name, message: "Unsatisfied charging requirement")

/// Keep actual job
actual = operation
Expand All @@ -73,6 +73,6 @@ internal final class BatteryChargingConstraint: JobConstraint {

#else

internal final class BatteryChargingConstraint: DefaultNoConstraint {}
internal final class BatteryChargingConstraint: SimpleConstraint {}

#endif
19 changes: 18 additions & 1 deletion Sources/SwiftQueue/Constraint+Deadline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import Foundation

internal final class DeadlineConstraint: JobConstraint {

private let deadline: Date
/// Cancel the job after a certain date
internal let deadline: Date

init(deadline: Date) {
self.deadline = deadline
Expand Down Expand Up @@ -53,4 +54,20 @@ internal final class DeadlineConstraint: JobConstraint {
throw SwiftQueueError.deadline
}
}

private enum DeadlineConstraintKey: String, CodingKey {
case deadline = "deadline"
}

func decode(builder: ConstraintBuilder, from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DeadlineConstraintKey.self)
let deadline: Date = try container.decode(Date.self, forKey: .deadline)
builder.register(constraint: DeadlineConstraint(deadline: deadline))
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: DeadlineConstraintKey.self)
try container.encode(deadline, forKey: .deadline)
}

}
33 changes: 21 additions & 12 deletions Sources/SwiftQueue/Constraint+Delay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,16 @@

import Foundation

internal final class DelayConstraint: JobConstraint {
internal final class DelayConstraint: SimpleConstraint {

private let delay: TimeInterval
/// Delay for the first execution of the job
internal let delay: TimeInterval

init(delay: TimeInterval) {
self.delay = delay
}

func willSchedule(queue: SqOperationQueue, operation: SqOperation) throws {
// Nothing to do
}

func willRun(operation: SqOperation) throws {
// Nothing to do
}

func run(operation: SqOperation) -> Bool {
override func run(operation: SqOperation) -> Bool {
let epoch = Date().timeIntervalSince(operation.info.createTime)
guard epoch < delay else {
// Epoch already greater than delay
Expand All @@ -55,7 +48,23 @@ internal final class DelayConstraint: JobConstraint {
operation?.run()
})

operation.logger.log(.verbose, jobId: operation.info.uuid, message: "Job delayed by \(time)s")
operation.logger.log(.verbose, jobId: operation.name, message: "Job delayed by \(time)s")
return false
}

private enum delayConstraintKey: String, CodingKey {
case delay = "delay"
}

override func decode(builder: ConstraintBuilder, from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: delayConstraintKey.self)
let delay: TimeInterval = try container.decode(TimeInterval.self, forKey: .delay)
builder.register(constraint: DelayConstraint(delay: delay))
}

override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: delayConstraintKey.self)
try container.encode(delay, forKey: .delay)
}

}
46 changes: 0 additions & 46 deletions Sources/SwiftQueue/Constraint+Executor.swift

This file was deleted.

66 changes: 43 additions & 23 deletions Sources/SwiftQueue/Constraint+Network.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,43 +38,48 @@ public enum NetworkType: Int, Codable {
#if os(iOS) || os(macOS) || os(tvOS)
internal final class NetworkConstraint: JobConstraint {

var reachability: Reachability?
/// Require a certain connectivity type
internal let networkType: NetworkType

private var reachability: Reachability!

init(networkType: NetworkType) {
self.networkType = networkType
}

func willSchedule(queue: SqOperationQueue, operation: SqOperation) throws {
if operation.info.requireNetwork.rawValue > NetworkType.any.rawValue {
do {
try self.reachability = Reachability(targetQueue: operation.dispatchQueue, notificationQueue: operation.dispatchQueue)
} catch {
operation.logger.log(.error, jobId: operation.info.uuid, message: error.localizedDescription)
}
}
assert(operation.dispatchQueue != .main)
self.reachability = try Reachability(targetQueue: operation.dispatchQueue, notificationQueue: operation.dispatchQueue)
}

func willRun(operation: SqOperation) throws {
guard let reachability = reachability else { return }
guard hasCorrectNetwork(reachability: reachability, required: operation.info.requireNetwork) else {
guard hasCorrectNetwork(reachability: reachability, required: networkType) else {
try reachability.startNotifier()
return
}
}

func run(operation: SqOperation) -> Bool {
guard let reachability = reachability else {
return true
}
guard hasCorrectNetwork(reachability: reachability, required: networkType) else {
reachability.whenReachable = { reachability in
reachability.stopNotifier()
reachability.whenReachable = nil
operation.run()
}

if hasCorrectNetwork(reachability: reachability, required: operation.info.requireNetwork) {
return true
}
operation.logger.log(.verbose, jobId: operation.name, message: "Unsatisfied network requirement")

do {
try reachability.startNotifier()
} catch {
operation.logger.log(.verbose, jobId: operation.name, message: "Unable to start network listener. Job will run.")
operation.logger.log(.error, jobId: operation.name, message: error.localizedDescription)
}

reachability.whenReachable = { reachability in
reachability.stopNotifier()
reachability.whenReachable = nil
operation.run()
return false
}

operation.logger.log(.verbose, jobId: operation.info.uuid, message: "Unsatisfied network requirement")
return false
return true
}

private func hasCorrectNetwork(reachability: Reachability, required: NetworkType) -> Bool {
Expand All @@ -88,9 +93,24 @@ internal final class NetworkConstraint: JobConstraint {
}
}

private enum networkConstraintKey: String, CodingKey {
case requireNetwork = "requireNetwork"
}

func decode(builder: ConstraintBuilder, from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: networkConstraintKey.self)
let networkType: NetworkType = try container.decode(NetworkType.self, forKey: .requireNetwork)
builder.register(constraint: NetworkConstraint(networkType: networkType))
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: networkConstraintKey.self)
try container.encode(networkType, forKey: .requireNetwork)
}

}
#else

internal final class NetworkConstraint: DefaultNoConstraint {}
internal final class NetworkConstraint: SimpleConstraint {}

#endif
28 changes: 28 additions & 0 deletions Sources/SwiftQueue/Constraint+Persister.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Created by Lucas Nelaupe on 25/5/20.
//

import Foundation

internal class PersisterConstraint: SimpleConstraint {

private let serializer: JobInfoSerializer

private let persister: JobPersister

init(serializer: JobInfoSerializer, persister: JobPersister) {
self.serializer = serializer
self.persister = persister
}

override func willSchedule(queue: SqOperationQueue, operation: SqOperation) throws {
let data = try serializer.serialize(info: operation.info)
persister.put(queueName: queue.name!, taskId: "job.info.uuid", data: data)
}

func remove(queueName: String, taskId: String) {

persister.remove(queueName: queueName, taskId: taskId)
}

}
109 changes: 109 additions & 0 deletions Sources/SwiftQueue/Constraint+Repeat.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//
// Created by Lucas Nelaupe on 26/5/20.
//

import Foundation

internal final class RepeatConstraint: SimpleConstraint {

/// Number of run maximum
public let maxRun: Limit

/// Time between each repetition of the job
public let interval: TimeInterval

/// Executor to run job in foreground or background
public let executor: Executor

/// Current number of run
public var runCount: Double = 0

init(maxRun: Limit, interval: TimeInterval, executor: Executor) {
self.maxRun = maxRun
self.interval = interval
self.executor = executor
}

override func run(operation: SqOperation) -> Bool {
switch executor {
case .background:
return false
case .foreground:
return true
case.any:
return true
}
}

func completionSuccess(sqOperation: SqOperation) {
if case .limited(let limit) = maxRun {
// Reached run limit
guard runCount + 1 < limit else {
sqOperation.onTerminate()
return
}
}

guard interval > 0 else {
// Run immediately
runCount += 1
sqOperation.run()
return
}

// Schedule run after interval
sqOperation.nextRunSchedule = Date().addingTimeInterval(interval)
sqOperation.dispatchQueue.runAfter(interval, callback: {
self.runCount += 1 // TODO weak
sqOperation.run()
})
}

}

/// Enum to specify background and foreground restriction
public enum Executor: Int {

/// Job will only run only when the app is in foreground
case foreground = 0

/// Job will only run only when the app is in background
case background = 1

/// Job can run in both background and foreground
case any = 2

}

internal extension Executor {

static func fromRawValue(value: Int) -> Executor {
assert(value == 0 || value == 1 || value == 2)
switch value {
case 1:
return Executor.background
case 2:
return Executor.any
default:
return Executor.foreground
}
}

}

extension Executor: Codable {

private enum CodingKeys: String, CodingKey { case value }

public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let value = try values.decode(Int.self, forKey: .value)
self = Executor.fromRawValue(value: value)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.rawValue, forKey: .value)
}

}
Loading

0 comments on commit 2698fc5

Please sign in to comment.