1 // Copyright © 2019 650 Industries. All rights reserved. 2 3 import Foundation 4 5 /** 6 * A ReaperSelectionPolicy which keeps a predefined maximum number of updates across all scopes, 7 * and, once that number is surpassed, selects the updates least recently accessed (and then least 8 * recently published) to delete. Ignores filters and scopes. 9 * 10 * Uses the `lastAccessed` property to determine ordering of updates. 11 */ 12 @objc(EXUpdatesReaperSelectionPolicyDevelopmentClient) 13 @objcMembers 14 public final class ReaperSelectionPolicyDevelopmentClient: NSObject, ReaperSelectionPolicy { 15 private let maxUpdatesToKeep: Int 16 17 public override init() { 18 self.maxUpdatesToKeep = 10 19 } 20 21 public init(maxUpdatesToKeep: Int) { 22 self.maxUpdatesToKeep = maxUpdatesToKeep 23 24 if maxUpdatesToKeep <= 0 { 25 NSException.init( 26 name: .invalidArgumentException, 27 reason: "Cannot initiailize ReaperSelectionPolicy with maxUpdatesToKeep <= 0" 28 ) 29 .raise() 30 } 31 } 32 public func updatesToDelete(withLaunchedUpdate launchedUpdate: Update, updates: [Update], filters: [String: Any]?) -> [Update] { 33 if updates.count < maxUpdatesToKeep { 34 return [] 35 } 36 37 var updatesMutable = updates.sorted { update1, update2 in 38 if update1.lastAccessed.compare(update2.lastAccessed) == .orderedSame { 39 return update1.commitTime < update2.commitTime 40 } 41 return update1.lastAccessed < update2.lastAccessed 42 } 43 44 var updatesToDelete: [Update] = [] 45 var hasFoundLaunchedUpdate = false 46 47 while updatesMutable.count > maxUpdatesToKeep { 48 let oldest = updatesMutable.first! 49 updatesMutable.remove(at: 0) 50 51 if launchedUpdate.updateId == oldest.updateId { 52 if hasFoundLaunchedUpdate { 53 // avoid infinite loop 54 NSException.init( 55 name: .internalInconsistencyException, 56 reason: "Multiple updates with the same ID were passed into ReaperSelectionPolicyDevelopmentClient" 57 ) 58 .raise() 59 } 60 61 // we don't want to delete launchedUpdate, so put it back on the end of the stack 62 updatesMutable.append(oldest) 63 hasFoundLaunchedUpdate = true 64 } else { 65 updatesToDelete.append(oldest) 66 } 67 } 68 69 return updatesToDelete 70 } 71 } 72