extension Storage { public func persist(_ job: Job, using transaction: Any) { (transaction as! YapDatabaseReadWriteTransaction).setObject(job, forKey: job.id!, inCollection: type(of: job).collection) } public func markJobAsSucceeded(_ job: Job, using transaction: Any) { (transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: job.id!, inCollection: type(of: job).collection) } public func markJobAsFailed(_ job: Job, using transaction: Any) { (transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: job.id!, inCollection: type(of: job).collection) } public func getAllPendingJobs(of type: Job.Type) -> [Job] { var result: [Job] = [] Storage.read { transaction in transaction.enumerateRows(inCollection: type.collection) { key, object, _, x in guard let job = object as? Job else { return } result.append(job) } } return result } public func cancelAllPendingJobs(of type: Job.Type, using transaction: YapDatabaseReadWriteTransaction) { transaction.removeAllObjects(inCollection: type.collection) } @objc(cancelPendingMessageSendJobIfNeededForMessage:using:) public func cancelPendingMessageSendJobIfNeeded(for tsMessageTimestamp: UInt64, using transaction: YapDatabaseReadWriteTransaction) { var attachmentUploadJobKeys: [String] = [] transaction.enumerateRows(inCollection: AttachmentUploadJob.collection) { key, object, _, _ in guard let job = object as? AttachmentUploadJob, job.message.sentTimestamp == tsMessageTimestamp else { return } attachmentUploadJobKeys.append(key) } var messageSendJobKeys: [String] = [] transaction.enumerateRows(inCollection: MessageSendJob.collection) { key, object, _, _ in guard let job = object as? MessageSendJob, job.message.sentTimestamp == tsMessageTimestamp else { return } messageSendJobKeys.append(key) } transaction.removeObjects(forKeys: attachmentUploadJobKeys, inCollection: AttachmentUploadJob.collection) transaction.removeObjects(forKeys: messageSendJobKeys, inCollection: MessageSendJob.collection) } @objc public func cancelPendingMessageSendJobs(for threadID: String, using transaction: YapDatabaseReadWriteTransaction) { var attachmentUploadJobKeys: [String] = [] transaction.enumerateRows(inCollection: AttachmentUploadJob.collection) { key, object, _, _ in guard let job = object as? AttachmentUploadJob, job.threadID == threadID else { return } attachmentUploadJobKeys.append(key) } var messageSendJobKeys: [String] = [] transaction.enumerateRows(inCollection: MessageSendJob.collection) { key, object, _, _ in guard let job = object as? MessageSendJob, job.message.threadID == threadID else { return } messageSendJobKeys.append(key) } transaction.removeObjects(forKeys: attachmentUploadJobKeys, inCollection: AttachmentUploadJob.collection) transaction.removeObjects(forKeys: messageSendJobKeys, inCollection: MessageSendJob.collection) } public func getAttachmentUploadJob(for attachmentID: String) -> AttachmentUploadJob? { var result: [AttachmentUploadJob] = [] Storage.read { transaction in transaction.enumerateRows(inCollection: AttachmentUploadJob.collection) { _, object, _, _ in guard let job = object as? AttachmentUploadJob, job.attachmentID == attachmentID else { return } result.append(job) } } #if DEBUG assert(result.isEmpty || result.count == 1) #endif return result.first } public func getAttachmentDownloadJob(for attachmentID: String, with transaction: YapDatabaseReadTransaction) -> AttachmentDownloadJob? { var result: [AttachmentDownloadJob] = [] transaction.enumerateRows(inCollection: AttachmentDownloadJob.collection) { _, object, _, _ in guard let job = object as? AttachmentDownloadJob, job.attachmentID == attachmentID else { return } result.append(job) } #if DEBUG assert(result.isEmpty || result.count == 1) #endif return result.first } public func getAttachmentDownloadJobs(for threadID: String) -> [AttachmentDownloadJob] { var result: [AttachmentDownloadJob] = [] Storage.read { transaction in transaction.enumerateRows(inCollection: AttachmentDownloadJob.collection) { _, object, _, _ in guard let job = object as? AttachmentDownloadJob, job.threadID == threadID else { return } result.append(job) } } return result } public func resumeAttachmentDownloadJobsIfNeeded(for threadID: String) { let jobs = getAttachmentDownloadJobs(for: threadID) jobs.forEach { job in job.delegate = JobQueue.shared job.isDeferred = false job.execute() } } public func getMessageSendJob(for messageSendJobID: String) -> MessageSendJob? { var result: MessageSendJob? Storage.read { transaction in result = transaction.object(forKey: messageSendJobID, inCollection: MessageSendJob.collection) as? MessageSendJob } return result } public func resumeMessageSendJobIfNeeded(_ messageSendJobID: String) { guard let job = getMessageSendJob(for: messageSendJobID) else { return } job.delegate = JobQueue.shared job.execute() } public func isJobCanceled(_ job: Job) -> Bool { var result = true Storage.read { transaction in result = !transaction.hasObject(forKey: job.id!, inCollection: type(of: job).collection) } return result } }