Further work on the JobRunner

Moved the JobRunner into SessionUtilitiesKit so it can be used by SessionSnodeKit
Exposed a 'sharedLokiProject' value on UserDefaults to remove the hard-coded group name used everywhere
Added "blocking" job support for 'OnLaunch' and 'OnActive' jobs to the JobRunner (will retry until it succeeds)
Added the UpdateProfilePicture and RetrieveDefaultOpenGroupRooms jobs
This commit is contained in:
Morgan Pretty 2022-04-27 10:48:54 +10:00
parent 94742c80ec
commit 3baeb981d9
40 changed files with 519 additions and 205 deletions

View file

@ -33,6 +33,8 @@ public enum SNMessagingKit { // Just to make the external API nice
JobRunner.add(executor: DisappearingMessagesJob.self, for: .disappearingMessages) JobRunner.add(executor: DisappearingMessagesJob.self, for: .disappearingMessages)
JobRunner.add(executor: FailedMessagesJob.self, for: .failedMessages) JobRunner.add(executor: FailedMessagesJob.self, for: .failedMessages)
JobRunner.add(executor: FailedAttachmentDownloadsJob.self, for: .failedAttachmentDownloads) JobRunner.add(executor: FailedAttachmentDownloadsJob.self, for: .failedAttachmentDownloads)
JobRunner.add(executor: UpdateProfilePictureJob.self, for: .updateProfilePicture)
JobRunner.add(executor: RetrieveDefaultOpenGroupRoomsJob.self, for: .retrieveDefaultOpenGroupRooms)
JobRunner.add(executor: MessageSendJob.self, for: .messageSend) JobRunner.add(executor: MessageSendJob.self, for: .messageSend)
JobRunner.add(executor: MessageReceiveJob.self, for: .messageReceive) JobRunner.add(executor: MessageReceiveJob.self, for: .messageReceive)
JobRunner.add(executor: NotifyPushServerJob.self, for: .notifyPushServer) JobRunner.add(executor: NotifyPushServerJob.self, for: .notifyPushServer)

View file

@ -751,7 +751,7 @@
FD17D7A027F40CC800122BE0 /* _001_InitialSetupMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D79F27F40CC800122BE0 /* _001_InitialSetupMigration.swift */; }; FD17D7A027F40CC800122BE0 /* _001_InitialSetupMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D79F27F40CC800122BE0 /* _001_InitialSetupMigration.swift */; };
FD17D7A127F40D2500122BE0 /* GRDBStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD28A4F527EAD44C00FF65E7 /* GRDBStorage.swift */; }; FD17D7A127F40D2500122BE0 /* GRDBStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD28A4F527EAD44C00FF65E7 /* GRDBStorage.swift */; };
FD17D7A227F40F0500122BE0 /* _001_InitialSetupMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D79527F3E04600122BE0 /* _001_InitialSetupMigration.swift */; }; FD17D7A227F40F0500122BE0 /* _001_InitialSetupMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D79527F3E04600122BE0 /* _001_InitialSetupMigration.swift */; };
FD17D7A427F40F8100122BE0 /* _002_YDBToGRDBMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7A327F40F8100122BE0 /* _002_YDBToGRDBMigration.swift */; }; FD17D7A427F40F8100122BE0 /* _003_YDBToGRDBMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7A327F40F8100122BE0 /* _003_YDBToGRDBMigration.swift */; };
FD17D7A727F41AF000122BE0 /* SSKLegacyModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7A627F41AF000122BE0 /* SSKLegacyModels.swift */; }; FD17D7A727F41AF000122BE0 /* SSKLegacyModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7A627F41AF000122BE0 /* SSKLegacyModels.swift */; };
FD17D7AA27F41BF500122BE0 /* SnodeSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7A927F41BF500122BE0 /* SnodeSet.swift */; }; FD17D7AA27F41BF500122BE0 /* SnodeSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7A927F41BF500122BE0 /* SnodeSet.swift */; };
FD17D7AE27F41C4300122BE0 /* SnodeReceivedMessageInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7AD27F41C4300122BE0 /* SnodeReceivedMessageInfo.swift */; }; FD17D7AE27F41C4300122BE0 /* SnodeReceivedMessageInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7AD27F41C4300122BE0 /* SnodeReceivedMessageInfo.swift */; };
@ -772,7 +772,7 @@
FD17D7D827F658E200122BE0 /* SSKDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7D727F658E200122BE0 /* SSKDestination.swift */; }; FD17D7D827F658E200122BE0 /* SSKDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7D727F658E200122BE0 /* SSKDestination.swift */; };
FD17D7E127F67BD400122BE0 /* SnodeReceivedMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7E027F67BD400122BE0 /* SnodeReceivedMessage.swift */; }; FD17D7E127F67BD400122BE0 /* SnodeReceivedMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7E027F67BD400122BE0 /* SnodeReceivedMessage.swift */; };
FD17D7E527F6A09900122BE0 /* Identity.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7E427F6A09900122BE0 /* Identity.swift */; }; FD17D7E527F6A09900122BE0 /* Identity.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7E427F6A09900122BE0 /* Identity.swift */; };
FD17D7E727F6A16700122BE0 /* _002_YDBToGRDBMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7E627F6A16700122BE0 /* _002_YDBToGRDBMigration.swift */; }; FD17D7E727F6A16700122BE0 /* _003_YDBToGRDBMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7E627F6A16700122BE0 /* _003_YDBToGRDBMigration.swift */; };
FD17D7EA27F6A1C600122BE0 /* SUKLegacyModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7E927F6A1C600122BE0 /* SUKLegacyModels.swift */; }; FD17D7EA27F6A1C600122BE0 /* SUKLegacyModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD17D7E927F6A1C600122BE0 /* SUKLegacyModels.swift */; };
FD28A4F227E990E800FF65E7 /* BlockingManagerRemovalMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD28A4F127E990E800FF65E7 /* BlockingManagerRemovalMigration.swift */; }; FD28A4F227E990E800FF65E7 /* BlockingManagerRemovalMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD28A4F127E990E800FF65E7 /* BlockingManagerRemovalMigration.swift */; };
FD28A4F427EA79F800FF65E7 /* BlockListUIUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD28A4F327EA79F800FF65E7 /* BlockListUIUtils.swift */; }; FD28A4F427EA79F800FF65E7 /* BlockListUIUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD28A4F327EA79F800FF65E7 /* BlockListUIUtils.swift */; };
@ -781,6 +781,9 @@
FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */; }; FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */; };
FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */; }; FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */; };
FD659AC027A7649600F12C02 /* MessageRequestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */; }; FD659AC027A7649600F12C02 /* MessageRequestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */; };
FD6A7A692818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A682818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift */; };
FD6A7A6B2818C17C00035AC1 /* UpdateProfilePictureJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A6A2818C17C00035AC1 /* UpdateProfilePictureJob.swift */; };
FD6A7A6D2818C61500035AC1 /* _002_SetupStandardJobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A6C2818C61500035AC1 /* _002_SetupStandardJobs.swift */; };
FD705A8C278CDB5600F16121 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */; }; FD705A8C278CDB5600F16121 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */; };
FD705A8E278CE29800F16121 /* String+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8D278CE29800F16121 /* String+Localization.swift */; }; FD705A8E278CE29800F16121 /* String+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8D278CE29800F16121 /* String+Localization.swift */; };
FD705A90278CEBBC00F16121 /* ShareAppExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8F278CEBBC00F16121 /* ShareAppExtensionContext.swift */; }; FD705A90278CEBBC00F16121 /* ShareAppExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8F278CEBBC00F16121 /* ShareAppExtensionContext.swift */; };
@ -790,6 +793,11 @@
FD859F0027C4691300510D0C /* MockDataGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFF27C4691300510D0C /* MockDataGenerator.swift */; }; FD859F0027C4691300510D0C /* MockDataGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFF27C4691300510D0C /* MockDataGenerator.swift */; };
FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; }; FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; };
FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; }; FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; };
FD90040F2818AB6D00ABAAF6 /* GetSnodePoolJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD90040E2818AB6D00ABAAF6 /* GetSnodePoolJob.swift */; };
FD9004122818ABDC00ABAAF6 /* Job.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B73F280402C4004C14C5 /* Job.swift */; };
FD9004142818AD0B00ABAAF6 /* _002_SetupStandardJobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9004132818AD0B00ABAAF6 /* _002_SetupStandardJobs.swift */; };
FD9004152818B46300ABAAF6 /* JobRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7432804EF1B004C14C5 /* JobRunner.swift */; };
FD9004162818B46700ABAAF6 /* JobRunnerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */; };
FDA8EAFE280E8B78002B68E5 /* FailedMessagesJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EAFD280E8B78002B68E5 /* FailedMessagesJob.swift */; }; FDA8EAFE280E8B78002B68E5 /* FailedMessagesJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EAFD280E8B78002B68E5 /* FailedMessagesJob.swift */; };
FDA8EB00280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */; }; FDA8EB00280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */; };
FDA8EB09280E90FB002B68E5 /* AppSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF287255B6D85007E1867 /* AppSetup.m */; }; FDA8EB09280E90FB002B68E5 /* AppSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF287255B6D85007E1867 /* AppSetup.m */; };
@ -798,12 +806,9 @@
FDC4389E27BA2B8A00C60D73 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; }; FDC4389E27BA2B8A00C60D73 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
FDCDB8DE2810F73B00352A0C /* Differentiable+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCDB8DD2810F73B00352A0C /* Differentiable+Utilities.swift */; }; FDCDB8DE2810F73B00352A0C /* Differentiable+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCDB8DD2810F73B00352A0C /* Differentiable+Utilities.swift */; };
FDCDB8E02811007F00352A0C /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCDB8DF2811007F00352A0C /* HomeViewModel.swift */; }; FDCDB8E02811007F00352A0C /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCDB8DF2811007F00352A0C /* HomeViewModel.swift */; };
FDE77F69280F9EDA002CFC5D /* JobRunnerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */; };
FDE77F6B280FEB28002CFC5D /* ControlMessageProcessRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */; }; FDE77F6B280FEB28002CFC5D /* ControlMessageProcessRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */; };
FDF0B73C27FFD3D6004C14C5 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */; }; FDF0B73C27FFD3D6004C14C5 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */; };
FDF0B740280402C4004C14C5 /* Job.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B73F280402C4004C14C5 /* Job.swift */; };
FDF0B7422804EA4F004C14C5 /* _002_SetupStandardJobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7412804EA4F004C14C5 /* _002_SetupStandardJobs.swift */; }; FDF0B7422804EA4F004C14C5 /* _002_SetupStandardJobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7412804EA4F004C14C5 /* _002_SetupStandardJobs.swift */; };
FDF0B7442804EF1B004C14C5 /* JobRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7432804EF1B004C14C5 /* JobRunner.swift */; };
FDF0B7472804F0CE004C14C5 /* DisappearingMessagesJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7462804F0CE004C14C5 /* DisappearingMessagesJob.swift */; }; FDF0B7472804F0CE004C14C5 /* DisappearingMessagesJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7462804F0CE004C14C5 /* DisappearingMessagesJob.swift */; };
FDF0B74928060D13004C14C5 /* QuotedReplyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B74828060D13004C14C5 /* QuotedReplyModel.swift */; }; FDF0B74928060D13004C14C5 /* QuotedReplyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B74828060D13004C14C5 /* QuotedReplyModel.swift */; };
FDF0B74B28061F7A004C14C5 /* InteractionAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B74A28061F7A004C14C5 /* InteractionAttachment.swift */; }; FDF0B74B28061F7A004C14C5 /* InteractionAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B74A28061F7A004C14C5 /* InteractionAttachment.swift */; };
@ -1815,7 +1820,7 @@
FD17D79827F40AB800122BE0 /* _003_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _003_YDBToGRDBMigration.swift; sourceTree = "<group>"; }; FD17D79827F40AB800122BE0 /* _003_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _003_YDBToGRDBMigration.swift; sourceTree = "<group>"; };
FD17D79B27F40B2E00122BE0 /* SMKLegacyModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMKLegacyModels.swift; sourceTree = "<group>"; }; FD17D79B27F40B2E00122BE0 /* SMKLegacyModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMKLegacyModels.swift; sourceTree = "<group>"; };
FD17D79F27F40CC800122BE0 /* _001_InitialSetupMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _001_InitialSetupMigration.swift; sourceTree = "<group>"; }; FD17D79F27F40CC800122BE0 /* _001_InitialSetupMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _001_InitialSetupMigration.swift; sourceTree = "<group>"; };
FD17D7A327F40F8100122BE0 /* _002_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _002_YDBToGRDBMigration.swift; sourceTree = "<group>"; }; FD17D7A327F40F8100122BE0 /* _003_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _003_YDBToGRDBMigration.swift; sourceTree = "<group>"; };
FD17D7A627F41AF000122BE0 /* SSKLegacyModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSKLegacyModels.swift; sourceTree = "<group>"; }; FD17D7A627F41AF000122BE0 /* SSKLegacyModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSKLegacyModels.swift; sourceTree = "<group>"; };
FD17D7A927F41BF500122BE0 /* SnodeSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnodeSet.swift; sourceTree = "<group>"; }; FD17D7A927F41BF500122BE0 /* SnodeSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnodeSet.swift; sourceTree = "<group>"; };
FD17D7AD27F41C4300122BE0 /* SnodeReceivedMessageInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnodeReceivedMessageInfo.swift; sourceTree = "<group>"; }; FD17D7AD27F41C4300122BE0 /* SnodeReceivedMessageInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnodeReceivedMessageInfo.swift; sourceTree = "<group>"; };
@ -1836,7 +1841,7 @@
FD17D7D727F658E200122BE0 /* SSKDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSKDestination.swift; sourceTree = "<group>"; }; FD17D7D727F658E200122BE0 /* SSKDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSKDestination.swift; sourceTree = "<group>"; };
FD17D7E027F67BD400122BE0 /* SnodeReceivedMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnodeReceivedMessage.swift; sourceTree = "<group>"; }; FD17D7E027F67BD400122BE0 /* SnodeReceivedMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnodeReceivedMessage.swift; sourceTree = "<group>"; };
FD17D7E427F6A09900122BE0 /* Identity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identity.swift; sourceTree = "<group>"; }; FD17D7E427F6A09900122BE0 /* Identity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identity.swift; sourceTree = "<group>"; };
FD17D7E627F6A16700122BE0 /* _002_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _002_YDBToGRDBMigration.swift; sourceTree = "<group>"; }; FD17D7E627F6A16700122BE0 /* _003_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _003_YDBToGRDBMigration.swift; sourceTree = "<group>"; };
FD17D7E927F6A1C600122BE0 /* SUKLegacyModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SUKLegacyModels.swift; sourceTree = "<group>"; }; FD17D7E927F6A1C600122BE0 /* SUKLegacyModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SUKLegacyModels.swift; sourceTree = "<group>"; };
FD28A4F127E990E800FF65E7 /* BlockingManagerRemovalMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockingManagerRemovalMigration.swift; sourceTree = "<group>"; }; FD28A4F127E990E800FF65E7 /* BlockingManagerRemovalMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockingManagerRemovalMigration.swift; sourceTree = "<group>"; };
FD28A4F327EA79F800FF65E7 /* BlockListUIUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListUIUtils.swift; sourceTree = "<group>"; }; FD28A4F327EA79F800FF65E7 /* BlockListUIUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockListUIUtils.swift; sourceTree = "<group>"; };
@ -1846,6 +1851,9 @@
FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestResponse.swift; sourceTree = "<group>"; }; FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestResponse.swift; sourceTree = "<group>"; };
FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; }; FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsViewController.swift; sourceTree = "<group>"; }; FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsViewController.swift; sourceTree = "<group>"; };
FD6A7A682818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetrieveDefaultOpenGroupRoomsJob.swift; sourceTree = "<group>"; };
FD6A7A6A2818C17C00035AC1 /* UpdateProfilePictureJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateProfilePictureJob.swift; sourceTree = "<group>"; };
FD6A7A6C2818C61500035AC1 /* _002_SetupStandardJobs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _002_SetupStandardJobs.swift; sourceTree = "<group>"; };
FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; }; FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
FD705A8D278CE29800F16121 /* String+Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localization.swift"; sourceTree = "<group>"; }; FD705A8D278CE29800F16121 /* String+Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localization.swift"; sourceTree = "<group>"; };
FD705A8F278CEBBC00F16121 /* ShareAppExtensionContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAppExtensionContext.swift; sourceTree = "<group>"; }; FD705A8F278CEBBC00F16121 /* ShareAppExtensionContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAppExtensionContext.swift; sourceTree = "<group>"; };
@ -1855,6 +1863,8 @@
FD859EFF27C4691300510D0C /* MockDataGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDataGenerator.swift; sourceTree = "<group>"; }; FD859EFF27C4691300510D0C /* MockDataGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDataGenerator.swift; sourceTree = "<group>"; };
FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = "<group>"; }; FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = "<group>"; };
FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = "<group>"; }; FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = "<group>"; };
FD90040E2818AB6D00ABAAF6 /* GetSnodePoolJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSnodePoolJob.swift; sourceTree = "<group>"; };
FD9004132818AD0B00ABAAF6 /* _002_SetupStandardJobs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _002_SetupStandardJobs.swift; sourceTree = "<group>"; };
FD9039443F7CB729CF71350E /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; sourceTree = "<group>"; }; FD9039443F7CB729CF71350E /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; sourceTree = "<group>"; };
FDA8EAFD280E8B78002B68E5 /* FailedMessagesJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedMessagesJob.swift; sourceTree = "<group>"; }; FDA8EAFD280E8B78002B68E5 /* FailedMessagesJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedMessagesJob.swift; sourceTree = "<group>"; };
FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedAttachmentDownloadsJob.swift; sourceTree = "<group>"; }; FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedAttachmentDownloadsJob.swift; sourceTree = "<group>"; };
@ -2853,8 +2863,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FDF0B7452804F0A8004C14C5 /* Types */, FDF0B7452804F0A8004C14C5 /* Types */,
FDF0B7432804EF1B004C14C5 /* JobRunner.swift */,
FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */,
C352A2F425574B4700338F3E /* LegacyJob.swift */, C352A2F425574B4700338F3E /* LegacyJob.swift */,
C352A3922557883D00338F3E /* JobDelegate.swift */, C352A3922557883D00338F3E /* JobDelegate.swift */,
C352A3882557876500338F3E /* JobQueue.swift */, C352A3882557876500338F3E /* JobQueue.swift */,
@ -3272,6 +3280,7 @@
C3C2A5BA255385ED00C340D1 /* OnionRequestAPI.swift */, C3C2A5BA255385ED00C340D1 /* OnionRequestAPI.swift */,
C3C2A5BB255385ED00C340D1 /* OnionRequestAPI+Encryption.swift */, C3C2A5BB255385ED00C340D1 /* OnionRequestAPI+Encryption.swift */,
C3C2A5BE255385EE00C340D1 /* SnodeAPI.swift */, C3C2A5BE255385EE00C340D1 /* SnodeAPI.swift */,
FD90040E2818AB6D00ABAAF6 /* GetSnodePoolJob.swift */,
C3C2A5B6255385EC00C340D1 /* SnodeMessage.swift */, C3C2A5B6255385EC00C340D1 /* SnodeMessage.swift */,
C3C2A5CD255385F300C340D1 /* Utilities */, C3C2A5CD255385F300C340D1 /* Utilities */,
); );
@ -3306,6 +3315,7 @@
B8A582AC258C653C00AFD84C /* Crypto */, B8A582AC258C653C00AFD84C /* Crypto */,
B8A582AB258C64E800AFD84C /* Database */, B8A582AB258C64E800AFD84C /* Database */,
B8A582B0258C66C900AFD84C /* General */, B8A582B0258C66C900AFD84C /* General */,
FD9004102818ABB000ABAAF6 /* JobRunner */,
B8A582AF258C665E00AFD84C /* Media */, B8A582AF258C665E00AFD84C /* Media */,
B8A582B9258C696200AFD84C /* Messaging */, B8A582B9258C696200AFD84C /* Messaging */,
B8A582AE258C65D000AFD84C /* Networking */, B8A582AE258C65D000AFD84C /* Networking */,
@ -3331,7 +3341,6 @@
C3C2A7802553AA6300C340D1 /* Protos */, C3C2A7802553AA6300C340D1 /* Protos */,
C3C2A70A25539DF900C340D1 /* Meta */, C3C2A70A25539DF900C340D1 /* Meta */,
C32C5BB9256DC7C4003C73A2 /* To Do */, C32C5BB9256DC7C4003C73A2 /* To Do */,
C3BBE0752554CDA60050F1E3 /* Configuration.swift */,
C3BBE07F2554CDD70050F1E3 /* Storage.swift */, C3BBE07F2554CDD70050F1E3 /* Storage.swift */,
C32C5BCB256DC818003C73A2 /* Database */, C32C5BCB256DC818003C73A2 /* Database */,
C300A5BB2554AFFB00555489 /* Messages */, C300A5BB2554AFFB00555489 /* Messages */,
@ -3520,6 +3529,7 @@
C33FD9AC255A548A00E217F9 /* SignalUtilitiesKit */, C33FD9AC255A548A00E217F9 /* SignalUtilitiesKit */,
C331FF1C2558F9D300070591 /* SessionUIKit */, C331FF1C2558F9D300070591 /* SessionUIKit */,
C3C2A6F125539DE700C340D1 /* SessionMessagingKit */, C3C2A6F125539DE700C340D1 /* SessionMessagingKit */,
C3BBE0752554CDA60050F1E3 /* Configuration.swift */,
C3C2A5A0255385C100C340D1 /* SessionSnodeKit */, C3C2A5A0255385C100C340D1 /* SessionSnodeKit */,
C3C2A67A255388CC00C340D1 /* SessionUtilitiesKit */, C3C2A67A255388CC00C340D1 /* SessionUtilitiesKit */,
D221A08C169C9E5E00537ABF /* Frameworks */, D221A08C169C9E5E00537ABF /* Frameworks */,
@ -3648,7 +3658,6 @@
FD09799A27FFC82D00936362 /* Quote.swift */, FD09799A27FFC82D00936362 /* Quote.swift */,
FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */, FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */,
FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */, FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */,
FDF0B73F280402C4004C14C5 /* Job.swift */,
); );
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3686,7 +3695,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FD17D79F27F40CC800122BE0 /* _001_InitialSetupMigration.swift */, FD17D79F27F40CC800122BE0 /* _001_InitialSetupMigration.swift */,
FD17D7A327F40F8100122BE0 /* _002_YDBToGRDBMigration.swift */, FD6A7A6C2818C61500035AC1 /* _002_SetupStandardJobs.swift */,
FD17D7A327F40F8100122BE0 /* _003_YDBToGRDBMigration.swift */,
); );
path = Migrations; path = Migrations;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3744,7 +3754,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FD17D7C927F546D900122BE0 /* _001_InitialSetupMigration.swift */, FD17D7C927F546D900122BE0 /* _001_InitialSetupMigration.swift */,
FD17D7E627F6A16700122BE0 /* _002_YDBToGRDBMigration.swift */, FD9004132818AD0B00ABAAF6 /* _002_SetupStandardJobs.swift */,
FD17D7E627F6A16700122BE0 /* _003_YDBToGRDBMigration.swift */,
); );
path = Migrations; path = Migrations;
sourceTree = "<group>"; sourceTree = "<group>";
@ -3753,6 +3764,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FD17D7E427F6A09900122BE0 /* Identity.swift */, FD17D7E427F6A09900122BE0 /* Identity.swift */,
FDF0B73F280402C4004C14C5 /* Job.swift */,
FD17D7CC27F546FF00122BE0 /* Setting.swift */, FD17D7CC27F546FF00122BE0 /* Setting.swift */,
); );
path = Models; path = Models;
@ -3801,12 +3813,23 @@
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
FD9004102818ABB000ABAAF6 /* JobRunner */ = {
isa = PBXGroup;
children = (
FDF0B7432804EF1B004C14C5 /* JobRunner.swift */,
FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */,
);
path = JobRunner;
sourceTree = "<group>";
};
FDF0B7452804F0A8004C14C5 /* Types */ = { FDF0B7452804F0A8004C14C5 /* Types */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FDF0B7462804F0CE004C14C5 /* DisappearingMessagesJob.swift */, FDF0B7462804F0CE004C14C5 /* DisappearingMessagesJob.swift */,
FDA8EAFD280E8B78002B68E5 /* FailedMessagesJob.swift */, FDA8EAFD280E8B78002B68E5 /* FailedMessagesJob.swift */,
FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */, FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */,
FD6A7A6A2818C17C00035AC1 /* UpdateProfilePictureJob.swift */,
FD6A7A682818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift */,
C352A2FE25574B6300338F3E /* MessageSendJob.swift */, C352A2FE25574B6300338F3E /* MessageSendJob.swift */,
C352A31225574F5200338F3E /* MessageReceiveJob.swift */, C352A31225574F5200338F3E /* MessageReceiveJob.swift */,
C352A32E2557549C00338F3E /* NotifyPushServerJob.swift */, C352A32E2557549C00338F3E /* NotifyPushServerJob.swift */,
@ -4792,7 +4815,7 @@
C3C2A5C7255385EE00C340D1 /* SnodeAPI.swift in Sources */, C3C2A5C7255385EE00C340D1 /* SnodeAPI.swift in Sources */,
C3C2A5C6255385EE00C340D1 /* Notification+OnionRequestAPI.swift in Sources */, C3C2A5C6255385EE00C340D1 /* Notification+OnionRequestAPI.swift in Sources */,
FD17D7AA27F41BF500122BE0 /* SnodeSet.swift in Sources */, FD17D7AA27F41BF500122BE0 /* SnodeSet.swift in Sources */,
FD17D7A427F40F8100122BE0 /* _002_YDBToGRDBMigration.swift in Sources */, FD17D7A427F40F8100122BE0 /* _003_YDBToGRDBMigration.swift in Sources */,
C3C2A5DC2553860B00C340D1 /* Promise+Threading.swift in Sources */, C3C2A5DC2553860B00C340D1 /* Promise+Threading.swift in Sources */,
C3C2A5C4255385EE00C340D1 /* OnionRequestAPI+Encryption.swift in Sources */, C3C2A5C4255385EE00C340D1 /* OnionRequestAPI+Encryption.swift in Sources */,
FD17D7D227F5797A00122BE0 /* SSKEndpoint.swift in Sources */, FD17D7D227F5797A00122BE0 /* SSKEndpoint.swift in Sources */,
@ -4802,9 +4825,11 @@
FD17D7A727F41AF000122BE0 /* SSKLegacyModels.swift in Sources */, FD17D7A727F41AF000122BE0 /* SSKLegacyModels.swift in Sources */,
C3C2A5C2255385EE00C340D1 /* Configuration.swift in Sources */, C3C2A5C2255385EE00C340D1 /* Configuration.swift in Sources */,
FD17D7D827F658E200122BE0 /* SSKDestination.swift in Sources */, FD17D7D827F658E200122BE0 /* SSKDestination.swift in Sources */,
FD6A7A6D2818C61500035AC1 /* _002_SetupStandardJobs.swift in Sources */,
FD17D7B327F51E5B00122BE0 /* SSKSetting.swift in Sources */, FD17D7B327F51E5B00122BE0 /* SSKSetting.swift in Sources */,
FD17D7AE27F41C4300122BE0 /* SnodeReceivedMessageInfo.swift in Sources */, FD17D7AE27F41C4300122BE0 /* SnodeReceivedMessageInfo.swift in Sources */,
C3C2A5C3255385EE00C340D1 /* OnionRequestAPI.swift in Sources */, C3C2A5C3255385EE00C340D1 /* OnionRequestAPI.swift in Sources */,
FD90040F2818AB6D00ABAAF6 /* GetSnodePoolJob.swift in Sources */,
FD17D7D427F6584600122BE0 /* SSKError.swift in Sources */, FD17D7D427F6584600122BE0 /* SSKError.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -4832,6 +4857,7 @@
B8856E09256F1676001CE70E /* UIDevice+featureSupport.swift in Sources */, B8856E09256F1676001CE70E /* UIDevice+featureSupport.swift in Sources */,
B8856DEF256F161F001CE70E /* NSString+SSK.m in Sources */, B8856DEF256F161F001CE70E /* NSString+SSK.m in Sources */,
FD09796727F6B0B600936362 /* Sodium+Conversion.swift in Sources */, FD09796727F6B0B600936362 /* Sodium+Conversion.swift in Sources */,
FD9004122818ABDC00ABAAF6 /* Job.swift in Sources */,
FDF0B74D280664E9004C14C5 /* PersistableRecord+Utilities.swift in Sources */, FDF0B74D280664E9004C14C5 /* PersistableRecord+Utilities.swift in Sources */,
FD09797927FAB7E800936362 /* ImageFormat.swift in Sources */, FD09797927FAB7E800936362 /* ImageFormat.swift in Sources */,
C32C5DC9256DD935003C73A2 /* ProxiedContentDownloader.swift in Sources */, C32C5DC9256DD935003C73A2 /* ProxiedContentDownloader.swift in Sources */,
@ -4839,6 +4865,7 @@
C3D9E43125676D3D0040E4F3 /* Configuration.swift in Sources */, C3D9E43125676D3D0040E4F3 /* Configuration.swift in Sources */,
C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */, C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */,
FD705A94278D052B00F16121 /* UITableView+ReusableView.swift in Sources */, FD705A94278D052B00F16121 /* UITableView+ReusableView.swift in Sources */,
FD9004152818B46300ABAAF6 /* JobRunner.swift in Sources */,
C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */, C3A7211A2558BCA10043A11F /* DiffieHellman.swift in Sources */,
C32C5FA1256DFED5003C73A2 /* NSArray+Functional.m in Sources */, C32C5FA1256DFED5003C73A2 /* NSArray+Functional.m in Sources */,
C3A7225E2558C38D0043A11F /* Promise+Retaining.swift in Sources */, C3A7225E2558C38D0043A11F /* Promise+Retaining.swift in Sources */,
@ -4886,16 +4913,18 @@
C32C5A48256DB8F0003C73A2 /* BuildConfiguration.swift in Sources */, C32C5A48256DB8F0003C73A2 /* BuildConfiguration.swift in Sources */,
FD17D7BF27F51F8200122BE0 /* ColumnExpressible.swift in Sources */, FD17D7BF27F51F8200122BE0 /* ColumnExpressible.swift in Sources */,
FD17D7E527F6A09900122BE0 /* Identity.swift in Sources */, FD17D7E527F6A09900122BE0 /* Identity.swift in Sources */,
FD9004142818AD0B00ABAAF6 /* _002_SetupStandardJobs.swift in Sources */,
B87EF18126377A1D00124B3C /* Features.swift in Sources */, B87EF18126377A1D00124B3C /* Features.swift in Sources */,
FD09797727FAB7A600936362 /* Data+Image.swift in Sources */, FD09797727FAB7A600936362 /* Data+Image.swift in Sources */,
C300A60D2554B31900555489 /* Logging.swift in Sources */, C300A60D2554B31900555489 /* Logging.swift in Sources */,
B8FF8EA625C11FEF004D1F22 /* IPv4.swift in Sources */, B8FF8EA625C11FEF004D1F22 /* IPv4.swift in Sources */,
C3D9E35525675EE10040E4F3 /* MIMETypeUtil.m in Sources */, C3D9E35525675EE10040E4F3 /* MIMETypeUtil.m in Sources */,
FD705A8E278CE29800F16121 /* String+Localization.swift in Sources */, FD705A8E278CE29800F16121 /* String+Localization.swift in Sources */,
FD9004162818B46700ABAAF6 /* JobRunnerError.swift in Sources */,
FD09797227FAA2F500936362 /* Optional+Utilities.swift in Sources */, FD09797227FAA2F500936362 /* Optional+Utilities.swift in Sources */,
C3A7219A2558C1660043A11F /* AnyPromise+Conversion.swift in Sources */, C3A7219A2558C1660043A11F /* AnyPromise+Conversion.swift in Sources */,
C300A6322554B6D100555489 /* NSDate+Timestamp.mm in Sources */, C300A6322554B6D100555489 /* NSDate+Timestamp.mm in Sources */,
FD17D7E727F6A16700122BE0 /* _002_YDBToGRDBMigration.swift in Sources */, FD17D7E727F6A16700122BE0 /* _003_YDBToGRDBMigration.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -4909,7 +4938,6 @@
C3A3A156256E1B91004D228D /* ProtoUtils.m in Sources */, C3A3A156256E1B91004D228D /* ProtoUtils.m in Sources */,
FD09799927FFC1A300936362 /* Attachment.swift in Sources */, FD09799927FFC1A300936362 /* Attachment.swift in Sources */,
C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */, C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */,
FDE77F69280F9EDA002CFC5D /* JobRunnerError.swift in Sources */,
FDF0B74928060D13004C14C5 /* QuotedReplyModel.swift in Sources */, FDF0B74928060D13004C14C5 /* QuotedReplyModel.swift in Sources */,
C352A32F2557549C00338F3E /* NotifyPushServerJob.swift in Sources */, C352A32F2557549C00338F3E /* NotifyPushServerJob.swift in Sources */,
7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */, 7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */,
@ -4919,6 +4947,7 @@
FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */, FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */,
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */, C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */, C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
FD6A7A6B2818C17C00035AC1 /* UpdateProfilePictureJob.swift in Sources */,
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */, C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */, C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
C3A3A108256E1A5C004D228D /* OWSIncomingMessageFinder.m in Sources */, C3A3A108256E1A5C004D228D /* OWSIncomingMessageFinder.m in Sources */,
@ -4937,7 +4966,6 @@
C32C5CA4256DD1DC003C73A2 /* TSAccountManager.m in Sources */, C32C5CA4256DD1DC003C73A2 /* TSAccountManager.m in Sources */,
C352A3892557876500338F3E /* JobQueue.swift in Sources */, C352A3892557876500338F3E /* JobQueue.swift in Sources */,
C3BBE0B52554F0E10050F1E3 /* ProofOfWork.swift in Sources */, C3BBE0B52554F0E10050F1E3 /* ProofOfWork.swift in Sources */,
FDF0B740280402C4004C14C5 /* Job.swift in Sources */,
C32C59C1256DB41F003C73A2 /* TSGroupThread.m in Sources */, C32C59C1256DB41F003C73A2 /* TSGroupThread.m in Sources */,
C3A3A08F256E1728004D228D /* FullTextSearchFinder.swift in Sources */, C3A3A08F256E1728004D228D /* FullTextSearchFinder.swift in Sources */,
FDF0B7472804F0CE004C14C5 /* DisappearingMessagesJob.swift in Sources */, FDF0B7472804F0CE004C14C5 /* DisappearingMessagesJob.swift in Sources */,
@ -4990,6 +5018,7 @@
C32C5E15256DDC78003C73A2 /* SSKPreferences.swift in Sources */, C32C5E15256DDC78003C73A2 /* SSKPreferences.swift in Sources */,
C32C5D9C256DD6DC003C73A2 /* OWSOutgoingReceiptManager.m in Sources */, C32C5D9C256DD6DC003C73A2 /* OWSOutgoingReceiptManager.m in Sources */,
C32C5C4F256DCC36003C73A2 /* Storage+OpenGroups.swift in Sources */, C32C5C4F256DCC36003C73A2 /* Storage+OpenGroups.swift in Sources */,
FD6A7A692818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift in Sources */,
C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */, C3DA9C0725AE7396008F7C7E /* ConfigurationMessage.swift in Sources */,
B8856CEE256F1054001CE70E /* OWSAudioPlayer.m in Sources */, B8856CEE256F1054001CE70E /* OWSAudioPlayer.m in Sources */,
FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */, FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */,
@ -5018,7 +5047,6 @@
C3A3A0EC256E1949004D228D /* OWSRecipientIdentity.m in Sources */, C3A3A0EC256E1949004D228D /* OWSRecipientIdentity.m in Sources */,
FDF0B74B28061F7A004C14C5 /* InteractionAttachment.swift in Sources */, FDF0B74B28061F7A004C14C5 /* InteractionAttachment.swift in Sources */,
B8F5F56525EC8453003BF8D4 /* Notification+Contacts.swift in Sources */, B8F5F56525EC8453003BF8D4 /* Notification+Contacts.swift in Sources */,
FDF0B7442804EF1B004C14C5 /* JobRunner.swift in Sources */,
C32C5AB2256DBE8F003C73A2 /* TSMessage.m in Sources */, C32C5AB2256DBE8F003C73A2 /* TSMessage.m in Sources */,
FD09796E27FA6D0000936362 /* Contact.swift in Sources */, FD09796E27FA6D0000936362 /* Contact.swift in Sources */,
C3A3A0FE256E1A3C004D228D /* TSDatabaseSecondaryIndexes.m in Sources */, C3A3A0FE256E1A3C004D228D /* TSDatabaseSecondaryIndexes.m in Sources */,

View file

@ -165,16 +165,10 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve
} }
} }
// Re-populate snode pool if needed
SnodeAPI.getSnodePool().retainUntilComplete()
// Onion request path countries cache // Onion request path countries cache
DispatchQueue.global(qos: .utility).sync { DispatchQueue.global(qos: .utility).sync {
let _ = IP2Country.shared.populateCacheIfNeeded() let _ = IP2Country.shared.populateCacheIfNeeded()
} }
// Get default open group rooms if needed
OpenGroupAPIV2.getDefaultRoomsIfNeeded()
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {

View file

@ -162,9 +162,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func applicationDidBecomeActive(_ application: UIApplication) { func applicationDidBecomeActive(_ application: UIApplication) {
guard !CurrentAppContext().isRunningTests else { return } guard !CurrentAppContext().isRunningTests else { return }
// FIXME: We should move this somewhere to prevent typos from breaking it UserDefaults.sharedLokiProject?[.isMainAppActive] = true
let sharedUserDefaults: UserDefaults? = UserDefaults(suiteName: "group.com.loki-project.loki-messenger")
sharedUserDefaults?[.isMainAppActive] = true
ensureRootViewController() ensureRootViewController()
adapt(appMode: AppModeManager.getAppModeOrSystemDefault()) adapt(appMode: AppModeManager.getAppModeOrSystemDefault())
@ -186,8 +184,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func applicationWillResignActive(_ application: UIApplication) { func applicationWillResignActive(_ application: UIApplication) {
clearAllNotificationsAndRestoreBadgeCount() clearAllNotificationsAndRestoreBadgeCount()
let sharedUserDefaults: UserDefaults? = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") UserDefaults.sharedLokiProject?[.isMainAppActive] = false
sharedUserDefaults?[.isMainAppActive] = false
DDLog.flushLog() DDLog.flushLog()
} }
@ -258,7 +255,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
enableBackgroundRefreshIfNecessary() enableBackgroundRefreshIfNecessary()
JobRunner.appDidBecomeActive() JobRunner.appDidBecomeActive()
SnodeAPI.getSnodePool().retainUntilComplete()
startPollersIfNeeded() startPollersIfNeeded()
if CurrentAppContext().isMainApp { if CurrentAppContext().isMainApp {

View file

@ -226,9 +226,8 @@ NSString *const ReportedApplicationStateDidChangeNotification = @"ReportedApplic
- (void)setMainAppBadgeNumber:(NSInteger)value - (void)setMainAppBadgeNumber:(NSInteger)value
{ {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:value]; [[UIApplication sharedApplication] setApplicationIconBadgeNumber:value];
NSUserDefaults *sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.loki-project.loki-messenger"]; [[NSUserDefaults sharedLokiProject] setInteger:value forKey:@"currentBadgeNumber"];
[sharedUserDefaults setInteger:value forKey:@"currentBadgeNumber"]; [[NSUserDefaults sharedLokiProject] synchronize];
[sharedUserDefaults synchronize];
} }
- (nullable UIViewController *)frontmostViewController - (nullable UIViewController *)frontmostViewController

View file

@ -8,7 +8,7 @@ import SessionMessagingKit
import SessionUtilitiesKit import SessionUtilitiesKit
public enum SyncPushTokensJob: JobExecutor { public enum SyncPushTokensJob: JobExecutor {
public static let maxFailureCount: UInt = 0 public static let maxFailureCount: Int = -1
public static let requiresThreadId: Bool = false public static let requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false public static let requiresInteractionId: Bool = false
@ -18,12 +18,8 @@ public enum SyncPushTokensJob: JobExecutor {
failure: @escaping (Job, Error?, Bool) -> (), failure: @escaping (Job, Error?, Bool) -> (),
deferred: @escaping (Job) -> () deferred: @escaping (Job) -> ()
) { ) {
// Don't schedule run when inactive or not in main app // Don't run when inactive or not in main app
var isMainAppActive = false guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
if let sharedUserDefaults = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") {
isMainAppActive = sharedUserDefaults[.isMainAppActive]
}
guard isMainAppActive else {
deferred(job) // Don't need to do anything if it's not the main app deferred(job) // Don't need to do anything if it's not the main app
return return
} }

View file

@ -278,29 +278,5 @@ enum _001_InitialSetupMigration: Migration {
t.uniqueKey([.threadId, .sentTimestampMs, .serverHash, .openGroupMessageServerId]) t.uniqueKey([.threadId, .sentTimestampMs, .serverHash, .openGroupMessageServerId])
} }
try db.create(table: Job.self) { t in
t.column(.id, .integer)
.notNull()
.primaryKey(autoincrement: true)
t.column(.failureCount, .integer)
.notNull()
.defaults(to: 0)
t.column(.variant, .integer)
.notNull()
.indexed() // Quicker querying
t.column(.behaviour, .integer).notNull() // TODO: Indexed???
t.column(.nextRunTimestamp, .double)
.notNull() // TODO: Should this just be nullable??? (or do we want to fetch by this?)
.indexed() // Quicker querying
.defaults(to: 0)
t.column(.threadId, .text)
.indexed() // Quicker querying
.references(SessionThread.self, onDelete: .cascade) // Delete if thread deleted
t.column(.interactionId, .text)
.indexed() // Quicker querying
.references(Interaction.self, onDelete: .cascade) // Delete if interaction deleted
t.column(.details, .blob)
}
} }
} }

View file

@ -17,32 +17,28 @@ enum _002_SetupStandardJobs: Migration {
try autoreleasepool { try autoreleasepool {
// TODO: Add additional jobs from the AppDelegate // TODO: Add additional jobs from the AppDelegate
_ = try Job( _ = try Job(
failureCount: 0,
variant: .disappearingMessages, variant: .disappearingMessages,
behaviour: .recurringOnLaunch, behaviour: .recurringOnLaunchBlockingOncePerSession
nextRunTimestamp: 0
).inserted(db) ).inserted(db)
_ = try Job( _ = try Job(
failureCount: 0,
variant: .failedMessages, variant: .failedMessages,
behaviour: .recurringOnLaunch, behaviour: .recurringOnLaunchBlocking
nextRunTimestamp: 0
).inserted(db) ).inserted(db)
_ = try Job( _ = try Job(
failureCount: 0,
variant: .failedAttachmentDownloads, variant: .failedAttachmentDownloads,
behaviour: .recurringOnLaunch, behaviour: .recurringOnLaunchBlocking
nextRunTimestamp: 0
).inserted(db) ).inserted(db)
// Note: This job exists in the 'Session' target but that doesn't have it's own migrations
_ = try Job( _ = try Job(
failureCount: 0, variant: .updateProfilePicture,
variant: .syncPushTokens, behaviour: .recurringOnActive
behaviour: .recurringOnLaunch, ).inserted(db)
nextRunTimestamp: 0
_ = try Job(
variant: .retrieveDefaultOpenGroupRooms,
behaviour: .recurringOnActive
).inserted(db) ).inserted(db)
} }
} }

View file

@ -334,6 +334,11 @@ public struct Interaction: Codable, Identifiable, Equatable, FetchableRecord, Mu
} }
} }
// Delete any jobs associated to this interaction
try Job
.filter(Job.Columns.interactionId == id)
.deleteAll(db)
return try performDelete(db) return try performDelete(db)
} }
} }

View file

@ -120,6 +120,17 @@ public struct SessionThread: Codable, Identifiable, Equatable, FetchableRecord,
self.notificationSound = notificationSound self.notificationSound = notificationSound
self.mutedUntilTimestamp = mutedUntilTimestamp self.mutedUntilTimestamp = mutedUntilTimestamp
} }
// MARK: - Custom Database Interaction
public func delete(_ db: Database) throws -> Bool {
// Delete any jobs associated to this thread
try Job
.filter(Job.Columns.threadId == id)
.deleteAll(db)
return try performDelete(db)
}
} }
// MARK: - GRDB Interactions // MARK: - GRDB Interactions

View file

@ -7,7 +7,7 @@ import SessionSnodeKit
import SignalCoreKit import SignalCoreKit
public enum AttachmentDownloadJob: JobExecutor { public enum AttachmentDownloadJob: JobExecutor {
public static var maxFailureCount: UInt = 10 public static var maxFailureCount: Int = 10
public static var requiresThreadId: Bool = true public static var requiresThreadId: Bool = true
public static let requiresInteractionId: Bool = true public static let requiresInteractionId: Bool = true

View file

@ -5,7 +5,7 @@ import GRDB
import SessionUtilitiesKit import SessionUtilitiesKit
public enum DisappearingMessagesJob: JobExecutor { public enum DisappearingMessagesJob: JobExecutor {
public static let maxFailureCount: UInt = 0 public static let maxFailureCount: Int = -1
public static let requiresThreadId: Bool = false public static let requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false public static let requiresInteractionId: Bool = false
@ -25,8 +25,15 @@ public enum DisappearingMessagesJob: JobExecutor {
.filter(sql: "(\(Interaction.Columns.expiresStartedAtMs) + (\(Interaction.Columns.expiresInSeconds) * 1000) <= \(timestampNowMs)") .filter(sql: "(\(Interaction.Columns.expiresStartedAtMs) + (\(Interaction.Columns.expiresInSeconds) * 1000) <= \(timestampNowMs)")
.deleteAll(db) .deleteAll(db)
// Update the next run timestamp for the DisappearingMessagesJob // Update the next run timestamp for the DisappearingMessagesJob (if the call
// to 'updateNextRunIfNeeded' returns 'nil' then it doesn't need to re-run so
// should have it's 'nextRunTimestamp' cleared)
return updateNextRunIfNeeded(db) return updateNextRunIfNeeded(db)
.defaulting(
to: try job
.with(nextRunTimestamp: 0)
.saved(db)
)
} }
success(updatedJob ?? job, false) success(updatedJob ?? job, false)
@ -40,12 +47,8 @@ public enum DisappearingMessagesJob: JobExecutor {
public extension DisappearingMessagesJob { public extension DisappearingMessagesJob {
@discardableResult static func updateNextRunIfNeeded(_ db: Database) -> Job? { @discardableResult static func updateNextRunIfNeeded(_ db: Database) -> Job? {
// Don't schedule run when inactive or not in main app // Don't run when inactive or not in main app
var isMainAppActive = false guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else { return nil }
if let sharedUserDefaults = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") {
isMainAppActive = sharedUserDefaults[.isMainAppActive]
}
guard isMainAppActive else { return nil }
// If there is another expiring message then update the job to run 1 second after it's meant to expire // If there is another expiring message then update the job to run 1 second after it's meant to expire
let nextExpirationTimestampMs: Double? = try? Double let nextExpirationTimestampMs: Double? = try? Double

View file

@ -6,7 +6,7 @@ import SignalCoreKit
import SessionUtilitiesKit import SessionUtilitiesKit
public enum FailedAttachmentDownloadsJob: JobExecutor { public enum FailedAttachmentDownloadsJob: JobExecutor {
public static let maxFailureCount: UInt = 0 public static let maxFailureCount: Int = -1
public static let requiresThreadId: Bool = false public static let requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false public static let requiresInteractionId: Bool = false

View file

@ -6,7 +6,7 @@ import SignalCoreKit
import SessionUtilitiesKit import SessionUtilitiesKit
public enum FailedMessagesJob: JobExecutor { public enum FailedMessagesJob: JobExecutor {
public static let maxFailureCount: UInt = 0 public static let maxFailureCount: Int = -1
public static let requiresThreadId: Bool = false public static let requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false public static let requiresInteractionId: Bool = false

View file

@ -5,7 +5,7 @@ import PromiseKit
import SessionUtilitiesKit import SessionUtilitiesKit
public enum MessageReceiveJob: JobExecutor { public enum MessageReceiveJob: JobExecutor {
public static var maxFailureCount: UInt = 10 public static var maxFailureCount: Int = 10
public static var requiresThreadId: Bool = true public static var requiresThreadId: Bool = true
public static let requiresInteractionId: Bool = false public static let requiresInteractionId: Bool = false

View file

@ -7,7 +7,7 @@ import SessionUtilitiesKit
import SessionSnodeKit import SessionSnodeKit
public enum MessageSendJob: JobExecutor { public enum MessageSendJob: JobExecutor {
public static var maxFailureCount: UInt = 10 public static var maxFailureCount: Int = 10
public static var requiresThreadId: Bool = true public static var requiresThreadId: Bool = true
public static let requiresInteractionId: Bool = false // Some messages don't have interactions public static let requiresInteractionId: Bool = false // Some messages don't have interactions

View file

@ -6,7 +6,7 @@ import SessionSnodeKit
import SessionUtilitiesKit import SessionUtilitiesKit
public enum NotifyPushServerJob: JobExecutor { public enum NotifyPushServerJob: JobExecutor {
public static var maxFailureCount: UInt = 20 public static var maxFailureCount: Int = 20
public static var requiresThreadId: Bool = false public static var requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false public static let requiresInteractionId: Bool = false

View file

@ -0,0 +1,30 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import SignalCoreKit
import SessionUtilitiesKit
public enum RetrieveDefaultOpenGroupRoomsJob: JobExecutor {
public static let maxFailureCount: Int = -1
public static let requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false
public static func run(
_ job: Job,
success: @escaping (Job, Bool) -> (),
failure: @escaping (Job, Error?, Bool) -> (),
deferred: @escaping (Job) -> ()
) {
// Don't run when inactive or not in main app
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
deferred(job) // Don't need to do anything if it's not the main app
return
}
OpenGroupAPIV2.getDefaultRoomsIfNeeded()
.done { _ in success(job, false) }
.catch { error in failure(job, error, false) }
.retainUntilComplete()
}
}

View file

@ -6,7 +6,7 @@ import PromiseKit
import SessionUtilitiesKit import SessionUtilitiesKit
public enum SendReadReceiptsJob: JobExecutor { public enum SendReadReceiptsJob: JobExecutor {
public static let maxFailureCount: UInt = 0 public static let maxFailureCount: Int = -1
public static let requiresThreadId: Bool = false public static let requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false public static let requiresInteractionId: Bool = false
private static let minRunFrequency: TimeInterval = 3 private static let minRunFrequency: TimeInterval = 3

View file

@ -0,0 +1,46 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import SignalCoreKit
import SessionUtilitiesKit
public enum UpdateProfilePictureJob: JobExecutor {
public static let maxFailureCount: Int = -1
public static let requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false
public static func run(
_ job: Job,
success: @escaping (Job, Bool) -> (),
failure: @escaping (Job, Error?, Bool) -> (),
deferred: @escaping (Job) -> ()
) {
// Don't run when inactive or not in main app
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
deferred(job) // Don't need to do anything if it's not the main app
return
}
// Only re-upload the profile picture if enough time has passed since the last upload
guard
let lastProfilePictureUpload: Date = UserDefaults.standard[.lastProfilePictureUpload],
Date().timeIntervalSince(lastProfilePictureUpload) > (14 * 24 * 60 * 60)
else {
deferred(job)
return
}
// Note: The user defaults flag is updated in ProfileManager
let profile: Profile = Profile.fetchOrCreateCurrentUser()
let profilePicture: UIImage? = ProfileManager.profileAvatar(id: profile.id)
ProfileManager.updateLocal(
profileName: profile.name,
avatarImage: profilePicture,
requiredSync: true,
success: { success(job, false) },
failure: { error in failure(job, error, false) }
)
}
}

View file

@ -11,9 +11,4 @@ extension OpenGroupAPIV2 {
public static func objc_isUserModerator(_ publicKey: String, for room: String, on server: String) -> Bool { public static func objc_isUserModerator(_ publicKey: String, for room: String, on server: String) -> Bool {
return isUserModerator(publicKey, for: room, on: server) return isUserModerator(publicKey, for: room, on: server)
} }
@objc(getDefaultRoomsIfNeeded)
public static func objc_getDefaultRoomsIfNeeded() {
getDefaultRoomsIfNeeded()
}
} }

View file

@ -13,7 +13,7 @@ extension MessageReceiver {
/// ///
/// **Note:** This is a slightly optimised version of the `decryptWithSessionProtocol` function which just skips /// **Note:** This is a slightly optimised version of the `decryptWithSessionProtocol` function which just skips
/// the validation (handled when the job actually runs) and doesn't throw /// the validation (handled when the job actually runs) and doesn't throw
internal static func extractSenderPublicKey(_ db: Database, from envelope: SNProtoEnvelope) -> String? { public static func extractSenderPublicKey(_ db: Database, from envelope: SNProtoEnvelope) -> String? {
guard guard
let ciphertext: Data = envelope.content, let ciphertext: Data = envelope.content,
let userX25519KeyPair: Box.KeyPair = Identity.fetchUserKeyPair(db) let userX25519KeyPair: Box.KeyPair = Identity.fetchUserKeyPair(db)

View file

@ -29,11 +29,7 @@ extension MessageReceiver {
default: fatalError() default: fatalError()
} }
var isMainAppActive = false guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else { return }
if let sharedUserDefaults = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") {
isMainAppActive = sharedUserDefaults[.isMainAppActive]
}
guard isMainAppActive else { return }
// Touch the thread to update the home screen preview // Touch the thread to update the home screen preview
let storage = SNMessagingKitConfiguration.shared.storage let storage = SNMessagingKitConfiguration.shared.storage
@ -399,11 +395,7 @@ extension MessageReceiver {
// Note: `message.sentTimestamp` is in ms // Note: `message.sentTimestamp` is in ms
let messageSentTimestamp: TimeInterval = TimeInterval((message.sentTimestamp ?? 0) / 1000) let messageSentTimestamp: TimeInterval = TimeInterval((message.sentTimestamp ?? 0) / 1000)
let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false)
var isMainAppActive = false
if let sharedUserDefaults = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") {
isMainAppActive = sharedUserDefaults[.isMainAppActive]
}
// Parse & persist attachments // Parse & persist attachments

View file

@ -89,10 +89,7 @@ public final class MessageSender : NSObject {
let (promise, seal) = Promise<Void>.pending() let (promise, seal) = Promise<Void>.pending()
let userPublicKey: String = getUserHexEncodedPublicKey(db) let userPublicKey: String = getUserHexEncodedPublicKey(db)
var isMainAppActive = false let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false)
if let sharedUserDefaults = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") {
isMainAppActive = sharedUserDefaults[.isMainAppActive]
}
// Set the timestamp, sender and recipient // Set the timestamp, sender and recipient
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set

View file

@ -124,31 +124,39 @@ public final class ClosedGroupPoller : NSObject {
SNLog("Received \(messages.count) new message(s) in closed group with public key: \(groupPublicKey).") SNLog("Received \(messages.count) new message(s) in closed group with public key: \(groupPublicKey).")
GRDBStorage.shared.write { db in GRDBStorage.shared.write { db in
var jobDetailMessages: [MessageReceiveJob.Details.MessageInfo] = []
messages.forEach { message in messages.forEach { message in
guard let envelope = SNProtoEnvelope.from(message) else { return } guard let envelope = SNProtoEnvelope.from(message) else { return }
do { do {
JobRunner.add( jobDetailMessages.append(
db, MessageReceiveJob.Details.MessageInfo(
job: Job( data: try envelope.serializedData(),
variant: .messageReceive, serverHash: message.info.hash
behaviour: .runOnce,
threadId: groupPublicKey,
details: MessageReceiveJob.Details(
data: try envelope.serializedData(),
serverHash: message.info.hash,
isBackgroundPoll: false
)
) )
) )
// Persist the received message after the MessageReceiveJob is created // Persist the received message after the MessageReceiveJob is created
try message.info.save(db) _ = try message.info.saved(db)
} }
catch { catch {
SNLog("Failed to deserialize envelope due to error: \(error).") SNLog("Failed to deserialize envelope due to error: \(error).")
} }
} }
JobRunner.add(
db,
job: Job(
variant: .messageReceive,
behaviour: .runOnce,
threadId: groupPublicKey,
details: MessageReceiveJob.Details(
messages: jobDetailMessages,
isBackgroundPoll: false
)
)
)
} }
} }
} }

View file

@ -95,7 +95,9 @@ public final class Poller : NSObject {
private func poll(_ snode: Snode, seal longTermSeal: Resolver<Void>) -> Promise<Void> { private func poll(_ snode: Snode, seal longTermSeal: Resolver<Void>) -> Promise<Void> {
guard isPolling else { return Promise { $0.fulfill(()) } } guard isPolling else { return Promise { $0.fulfill(()) } }
let userPublicKey = getUserHexEncodedPublicKey() let userPublicKey = getUserHexEncodedPublicKey()
return SnodeAPI.getRawMessages(from: snode, associatedWith: userPublicKey) return SnodeAPI.getRawMessages(from: snode, associatedWith: userPublicKey)
.then(on: Threading.pollerQueue) { [weak self] rawResponse -> Promise<Void> in .then(on: Threading.pollerQueue) { [weak self] rawResponse -> Promise<Void> in
guard let strongSelf = self, strongSelf.isPolling else { return Promise { $0.fulfill(()) } } guard let strongSelf = self, strongSelf.isPolling else { return Promise { $0.fulfill(()) } }
@ -106,6 +108,8 @@ public final class Poller : NSObject {
SNLog("Received \(messages.count) new message(s).") SNLog("Received \(messages.count) new message(s).")
GRDBStorage.shared.write { db in GRDBStorage.shared.write { db in
var threadMessages: [String: [MessageReceiveJob.Details.MessageInfo]] = [:]
messages.forEach { message in messages.forEach { message in
guard let envelope = SNProtoEnvelope.from(message) else { return } guard let envelope = SNProtoEnvelope.from(message) else { return }
@ -117,27 +121,36 @@ public final class Poller : NSObject {
} }
do { do {
JobRunner.add( threadMessages[threadId ?? ""] = (threadMessages[threadId ?? ""] ?? [])
db, .appending(
job: Job( MessageReceiveJob.Details.MessageInfo(
variant: .messageReceive,
behaviour: .runOnce,
threadId: threadId,
details: MessageReceiveJob.Details(
data: try envelope.serializedData(), data: try envelope.serializedData(),
serverHash: message.info.hash, serverHash: message.info.hash
isBackgroundPoll: false
) )
) )
)
// Persist the received message after the MessageReceiveJob is created // Persist the received message after the MessageReceiveJob is created
try message.info.save(db) _ = try message.info.saved(db)
} }
catch { catch {
SNLog("Failed to deserialize envelope due to error: \(error).") SNLog("Failed to deserialize envelope due to error: \(error).")
} }
} }
threadMessages.forEach { threadId, threadMessages in
JobRunner.add(
db,
job: Job(
variant: .messageReceive,
behaviour: .runOnce,
threadId: threadId,
details: MessageReceiveJob.Details(
messages: threadMessages,
isBackgroundPoll: false
)
)
)
}
} }
} }

View file

@ -19,11 +19,9 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension
self.notificationContent = request.content.mutableCopy() as? UNMutableNotificationContent self.notificationContent = request.content.mutableCopy() as? UNMutableNotificationContent
// Abort if the main app is running // Abort if the main app is running
var isMainAppActive = false guard !(UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
if let sharedUserDefaults = UserDefaults(suiteName: "group.com.loki-project.loki-messenger") { return self.completeSilenty()
isMainAppActive = sharedUserDefaults.bool(forKey: "isMainAppActive")
} }
guard !isMainAppActive else { return self.completeSilenty() }
// Perform main setup // Perform main setup
DispatchQueue.main.sync { self.setUpIfNecessary() { } } DispatchQueue.main.sync { self.setUpIfNecessary() { } }

View file

@ -13,15 +13,18 @@ public enum SNSnodeKit { // Just to make the external API nice
identifier: .snodeKit, identifier: .snodeKit,
migrations: [ migrations: [
[ [
_001_InitialSetupMigration.self _001_InitialSetupMigration.self,
_002_SetupStandardJobs.self
], ],
[ [
_002_YDBToGRDBMigration.self _003_YDBToGRDBMigration.self
] ]
] ]
) )
} }
public static func configure() { public static func configure() {
// Configure the job executors
JobRunner.add(executor: GetSnodePoolJob.self, for: .getSnodePool)
} }
} }

View file

@ -0,0 +1,20 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import SessionUtilitiesKit
/// This migration sets up the standard jobs, since we want these jobs to run before any "once-off" jobs we do this migration
/// before running the `YDBToGRDBMigration`
enum _002_SetupStandardJobs: Migration {
static let identifier: String = "SetupStandardJobs"
static func migrate(_ db: Database) throws {
try autoreleasepool {
_ = try Job(
variant: .getSnodePool,
behaviour: .recurringOnActiveBlocking
).inserted(db)
}
}
}

View file

@ -4,7 +4,7 @@ import Foundation
import GRDB import GRDB
import SessionUtilitiesKit import SessionUtilitiesKit
enum _002_YDBToGRDBMigration: Migration { enum _003_YDBToGRDBMigration: Migration {
static let identifier: String = "YDBToGRDBMigration" static let identifier: String = "YDBToGRDBMigration"
static func migrate(_ db: Database) throws { static func migrate(_ db: Database) throws {
@ -103,6 +103,7 @@ enum _002_YDBToGRDBMigration: Migration {
var lastMessageResults: [String: (hash: String, json: JSON)] = [:] var lastMessageResults: [String: (hash: String, json: JSON)] = [:]
var receivedMessageResults: [String: Set<String>] = [:] var receivedMessageResults: [String: Set<String>] = [:]
// TODO: Move into the top read block???
Storage.read { transaction in Storage.read { transaction in
// Extract the received message hashes // Extract the received message hashes
transaction.enumerateKeysAndObjects(inCollection: Legacy.receivedMessagesCollection) { key, object, _ in transaction.enumerateKeysAndObjects(inCollection: Legacy.receivedMessagesCollection) { key, object, _ in

View file

@ -0,0 +1,24 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import SignalCoreKit
import SessionUtilitiesKit
public enum GetSnodePoolJob: JobExecutor {
public static let maxFailureCount: Int = -1
public static let requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false
public static func run(
_ job: Job,
success: @escaping (Job, Bool) -> (),
failure: @escaping (Job, Error?, Bool) -> (),
deferred: @escaping (Job) -> ()
) {
SnodeAPI.getSnodePool()
.done { _ in success(job, false) }
.catch { error in failure(job, error, false) }
.retainUntilComplete()
}
}

View file

@ -258,7 +258,9 @@ public final class SnodeAPI : NSObject {
public static func getSnodePool() -> Promise<Set<Snode>> { public static func getSnodePool() -> Promise<Set<Snode>> {
loadSnodePoolIfNeeded() loadSnodePoolIfNeeded()
let now = Date() let now = Date()
let hasSnodePoolExpired = given(GRDBStorage.shared[.lastSnodePoolRefreshDate]) { now.timeIntervalSince($0) > 2 * 60 * 60 } ?? true let hasSnodePoolExpired = given(GRDBStorage.shared[.lastSnodePoolRefreshDate]) {
now.timeIntervalSince($0) > 2 * 60 * 60
}.defaulting(to: true)
let snodePool = SnodeAPI.snodePool let snodePool = SnodeAPI.snodePool
let hasInsufficientSnodes = (snodePool.count < minSnodePoolCount) let hasInsufficientSnodes = (snodePool.count < minSnodePoolCount)
@ -441,6 +443,7 @@ public final class SnodeAPI : NSObject {
// "pubkey_ed25519" : ed25519PublicKey, // "pubkey_ed25519" : ed25519PublicKey,
// "signature" : signature.toBase64()! // "signature" : signature.toBase64()!
] ]
return invoke(.getMessages, on: snode, associatedWith: publicKey, parameters: parameters) return invoke(.getMessages, on: snode, associatedWith: publicKey, parameters: parameters)
} }

View file

@ -18,11 +18,12 @@ public enum SNUtilitiesKit { // Just to make the external API nice
identifier: .utilitiesKit, identifier: .utilitiesKit,
migrations: [ migrations: [
[ [
// Intentionally including the '_002_YDBToGRDBMigration' in the first migration // Intentionally including the '_003_YDBToGRDBMigration' in the first migration
// set to ensure the 'Identity' data is migrated before any other migrations are // set to ensure the 'Identity' data is migrated before any other migrations are
// run (some need access to the users publicKey) // run (some need access to the users publicKey)
_001_InitialSetupMigration.self, _001_InitialSetupMigration.self,
_002_YDBToGRDBMigration.self _002_SetupStandardJobs.self,
_003_YDBToGRDBMigration.self
] ]
] ]
) )

View file

@ -15,6 +15,30 @@ enum _001_InitialSetupMigration: Migration {
t.column(.data, .blob).notNull() t.column(.data, .blob).notNull()
} }
try db.create(table: Job.self) { t in
t.column(.id, .integer)
.notNull()
.primaryKey(autoincrement: true)
t.column(.failureCount, .integer)
.notNull()
.defaults(to: 0)
t.column(.variant, .integer)
.notNull()
.indexed() // Quicker querying
t.column(.behaviour, .integer)
.notNull()
.indexed() // Quicker querying
t.column(.nextRunTimestamp, .double)
.notNull()
.indexed() // Quicker querying
.defaults(to: 0)
t.column(.threadId, .text)
.indexed() // Quicker querying
t.column(.interactionId, .text)
.indexed() // Quicker querying
t.column(.details, .blob)
}
try db.create(table: Setting.self) { t in try db.create(table: Setting.self) { t in
t.column(.key, .text) t.column(.key, .text)
.notNull() .notNull()

View file

@ -0,0 +1,21 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import Curve25519Kit
/// This migration sets up the standard jobs, since we want these jobs to run before any "once-off" jobs we do this migration
/// before running the `YDBToGRDBMigration`
enum _002_SetupStandardJobs: Migration {
static let identifier: String = "SetupStandardJobs"
static func migrate(_ db: Database) throws {
try autoreleasepool {
// Note: This job exists in the 'Session' target but that doesn't have it's own migrations
_ = try Job(
variant: .syncPushTokens,
behaviour: .recurringOnLaunch
).inserted(db)
}
}
}

View file

@ -3,7 +3,7 @@
import Foundation import Foundation
import GRDB import GRDB
enum _002_YDBToGRDBMigration: Migration { enum _003_YDBToGRDBMigration: Migration {
static let identifier: String = "YDBToGRDBMigration" static let identifier: String = "YDBToGRDBMigration"
static func migrate(_ db: Database) throws { static func migrate(_ db: Database) throws {
@ -65,6 +65,7 @@ enum _002_YDBToGRDBMigration: Migration {
throw GRDBStorageError.migrationFailed throw GRDBStorageError.migrationFailed
} }
print("RAWR publicKey \(userX25519KeyPair.publicKey.toHexString())")
try autoreleasepool { try autoreleasepool {
// Insert the data into GRDB // Insert the data into GRDB
try Identity( try Identity(

View file

@ -2,21 +2,9 @@
import Foundation import Foundation
import GRDB import GRDB
import SessionUtilitiesKit
import SwiftProtobuf
public struct Job: Codable, Equatable, Identifiable, FetchableRecord, MutablePersistableRecord, TableRecord, ColumnExpressible { public struct Job: Codable, Equatable, Identifiable, FetchableRecord, MutablePersistableRecord, TableRecord, ColumnExpressible {
public static var databaseTableName: String { "job" } public static var databaseTableName: String { "job" }
internal static let threadForeignKey = ForeignKey(
[Columns.threadId],
to: [SessionThread.Columns.id]
)
internal static let interactionForeignKey = ForeignKey(
[Columns.interactionId],
to: [Interaction.Columns.id]
)
internal static let thread = hasOne(SessionThread.self, using: Job.threadForeignKey)
internal static let interaction = hasOne(Interaction.self, using: Job.interactionForeignKey)
public typealias Columns = CodingKeys public typealias Columns = CodingKeys
public enum CodingKeys: String, CodingKey, ColumnExpression { public enum CodingKeys: String, CodingKey, ColumnExpression {
@ -35,13 +23,31 @@ public struct Job: Codable, Equatable, Identifiable, FetchableRecord, MutablePer
/// at the timestamp of the next disappearing message /// at the timestamp of the next disappearing message
case disappearingMessages case disappearingMessages
/// This is a recurring job that ensures the app retrieves a service node pool on active
///
/// **Note:** This is a blocking job so it will run before any other jobs and prevent them from
/// running until it's complete
case getSnodePool
/// This is a recurring job that checks if the user needs to update their profile picture on launch, and if so
/// attempt to download the latest
case updateProfilePicture
/// This is a recurring job that ensures the app fetches the default open group rooms on launch
case retrieveDefaultOpenGroupRooms
/// This is a recurring job that runs on launch and flags any messages marked as 'sending' to /// This is a recurring job that runs on launch and flags any messages marked as 'sending' to
/// be in their 'failed' state /// be in their 'failed' state
///
/// **Note:** This is a blocking job so it will run before any other jobs and prevent them from
/// running until it's complete
case failedMessages = 1000 case failedMessages = 1000
/// This is a recurring job that runs on launch and flags any attachments marked as 'uploading' to /// This is a recurring job that runs on launch and flags any attachments marked as 'uploading' to
/// be in their 'failed' state /// be in their 'failed' state
///
/// **Note:** This is a blocking job so it will run before any other jobs and prevent them from
/// running until it's complete
case failedAttachmentDownloads case failedAttachmentDownloads
/// This is a recurring job that runs on return from background and registeres and uploads the /// This is a recurring job that runs on return from background and registeres and uploads the
@ -84,11 +90,26 @@ public struct Job: Codable, Equatable, Identifiable, FetchableRecord, MutablePer
/// the future) in order to be run again /// the future) in order to be run again
case recurring case recurring
/// This job will run once each launch /// This job will run once each launch and may run again during the same session if `nextRunTimestamp`
/// gets set
case recurringOnLaunch case recurringOnLaunch
/// This job will run once each whenever the app becomes active (launch and return from background) /// This job will run once each launch and may run again during the same session if `nextRunTimestamp`
/// gets set, it also must complete before any other jobs can run
case recurringOnLaunchBlocking
/// This job will run once each launch and may run again during the same session if `nextRunTimestamp`
/// gets set, it also must complete before any other jobs can run
case recurringOnLaunchBlockingOncePerSession
/// This job will run once each whenever the app becomes active (launch and return from background) and
/// may run again during the same session if `nextRunTimestamp` gets set
case recurringOnActive case recurringOnActive
/// This job will run once each whenever the app becomes active (launch and return from background) and
/// may run again during the same session if `nextRunTimestamp` gets set, it also must complete before
/// any other jobs can run
case recurringOnActiveBlocking
} }
/// The `id` value is auto incremented by the database, if the `Job` hasn't been inserted into /// The `id` value is auto incremented by the database, if the `Job` hasn't been inserted into
@ -122,16 +143,6 @@ public struct Job: Codable, Equatable, Identifiable, FetchableRecord, MutablePer
/// JSON encoded data required for the job /// JSON encoded data required for the job
public let details: Data? public let details: Data?
// MARK: - Relationships
public var thread: QueryInterfaceRequest<SessionThread> {
request(for: Job.thread)
}
public var interaction: QueryInterfaceRequest<Interaction> {
request(for: Job.interaction)
}
// MARK: - Initialization // MARK: - Initialization
fileprivate init( fileprivate init(
@ -201,26 +212,72 @@ public struct Job: Codable, Equatable, Identifiable, FetchableRecord, MutablePer
} }
} }
// MARK: - GRDB Interactions
extension Job {
internal static func filterPendingJobs(excludeFutureJobs: Bool = true) -> QueryInterfaceRequest<Job> {
let query: QueryInterfaceRequest<Job> = Job
.filter(
// TODO: Should this include other behaviours? (what happens if one of the other types fails???? Just leave it until the next launch/active???) Set a 'failureCount' and use that to determine if it should run? (reset on success)
// Retrieve all 'runOnce' and 'recurring' jobs
[
Job.Behaviour.runOnce,
Job.Behaviour.recurring
].contains(Job.Columns.behaviour) || (
// Retrieve any 'recurringOnLaunch' and 'recurringOnActive' jobs that have a
// 'nextRunTimestamp'
[
Job.Behaviour.recurringOnLaunch,
Job.Behaviour.recurringOnLaunchBlocking,
Job.Behaviour.recurringOnActive,
Job.Behaviour.recurringOnActiveBlocking
].contains(Job.Columns.behaviour) &&
Job.Columns.nextRunTimestamp > 0
)
)
.order(Job.Columns.nextRunTimestamp)
.order(Job.Columns.id)
guard excludeFutureJobs else {
return query
}
return query
.filter(Job.Columns.nextRunTimestamp <= Date().timeIntervalSince1970)
}
}
// MARK: - Convenience // MARK: - Convenience
public extension Job { public extension Job {
internal func with( var isBlocking: Bool {
switch self.behaviour {
case .recurringOnLaunchBlocking,
.recurringOnLaunchBlockingOncePerSession,
.recurringOnActiveBlocking:
return true
default: return false
}
}
func with(
failureCount: UInt = 0, failureCount: UInt = 0,
nextRunTimestamp: TimeInterval? nextRunTimestamp: TimeInterval
) -> Job { ) -> Job {
return Job( return Job(
id: id, id: id,
failureCount: failureCount, failureCount: failureCount,
variant: variant, variant: variant,
behaviour: behaviour, behaviour: behaviour,
nextRunTimestamp: (nextRunTimestamp ?? self.nextRunTimestamp), nextRunTimestamp: nextRunTimestamp,
threadId: threadId, threadId: threadId,
interactionId: interactionId, interactionId: interactionId,
details: details details: details
) )
} }
internal func with<T: Encodable>(details: T) -> Job? { func with<T: Encodable>(details: T) -> Job? {
guard let detailsData: Data = try? JSONEncoder().encode(details) else { return nil } guard let detailsData: Data = try? JSONEncoder().encode(details) else { return nil }
return Job( return Job(

View file

@ -34,6 +34,9 @@ public enum SNUserDefaults {
} }
public extension UserDefaults { public extension UserDefaults {
@objc static var sharedLokiProject: UserDefaults? {
UserDefaults(suiteName: "group.com.loki-project.loki-messenger")
}
subscript(bool: SNUserDefaults.Bool) -> Bool { subscript(bool: SNUserDefaults.Bool) -> Bool {
get { return self.bool(forKey: bool.rawValue) } get { return self.bool(forKey: bool.rawValue) }

View file

@ -3,10 +3,12 @@
import Foundation import Foundation
import GRDB import GRDB
import SignalCoreKit import SignalCoreKit
import SessionUtilitiesKit
public protocol JobExecutor { public protocol JobExecutor {
static var maxFailureCount: UInt { get } /// The maximum number of times the job can fail before it fails permanently
///
/// **Note:** A value of `-1` means it will retry indefinitely
static var maxFailureCount: Int { get }
static var requiresThreadId: Bool { get } static var requiresThreadId: Bool { get }
static var requiresInteractionId: Bool { get } static var requiresInteractionId: Bool { get }
@ -57,8 +59,8 @@ public final class JobRunner {
} }
} }
// TODO: Could this be a bottleneck? (single serial queue to process all these jobs? Group by thread?) // TODO: Could this be a bottleneck? (single serial queue to process all these jobs? Group by thread?).
// TODO: Multi-thread support // TODO: Multi-thread support.
private static let queueKey: DispatchSpecificKey = DispatchSpecificKey<String>() private static let queueKey: DispatchSpecificKey = DispatchSpecificKey<String>()
private static let queueContext: String = "JobRunner" private static let queueContext: String = "JobRunner"
private static let internalQueue: DispatchQueue = { private static let internalQueue: DispatchQueue = {
@ -74,6 +76,7 @@ public final class JobRunner {
private static var jobQueue: Atomic<[Job]> = Atomic([]) private static var jobQueue: Atomic<[Job]> = Atomic([])
private static var jobsCurrentlyRunning: Atomic<Set<Int64>> = Atomic([]) private static var jobsCurrentlyRunning: Atomic<Set<Int64>> = Atomic([])
private static var perSessionJobsCompleted: Atomic<Set<Int64>> = Atomic([])
// MARK: - Configuration // MARK: - Configuration
@ -182,27 +185,64 @@ public final class JobRunner {
.filter( .filter(
[ [
Job.Behaviour.recurringOnLaunch, Job.Behaviour.recurringOnLaunch,
Job.Behaviour.recurringOnLaunchBlocking,
Job.Behaviour.recurringOnLaunchBlockingOncePerSession,
Job.Behaviour.runOnceNextLaunch Job.Behaviour.runOnceNextLaunch
].contains(Job.Columns.behaviour) ].contains(Job.Columns.behaviour)
) )
.order(Job.Columns.id)
.fetchAll(db) .fetchAll(db)
} }
guard let jobsToRun: [Job] = maybeJobsToRun else { return } guard let jobsToRun: [Job] = maybeJobsToRun else { return }
jobQueue.mutate { $0.append(contentsOf: jobsToRun) } jobQueue.mutate {
// Insert any blocking jobs after any existing blocking jobs then add
// the remaining jobs to the end of the queue
let lastBlockingIndex = $0.lastIndex(where: { $0.isBlocking })
.defaulting(to: $0.startIndex.advanced(by: -1))
.advanced(by: 1)
$0.insert(
contentsOf: jobsToRun.filter { $0.isBlocking },
at: lastBlockingIndex
)
$0.append(
contentsOf: jobsToRun.filter { !$0.isBlocking }
)
}
} }
public static func appDidBecomeActive() { public static func appDidBecomeActive() {
let maybeJobsToRun: [Job]? = GRDBStorage.shared.read { db in let maybeJobsToRun: [Job]? = GRDBStorage.shared.read { db in
try Job try Job
.filter(Job.Columns.behaviour == Job.Behaviour.recurringOnActive) .filter(
[
Job.Behaviour.recurringOnActive,
Job.Behaviour.recurringOnActiveBlocking
].contains(Job.Columns.behaviour)
)
.order(Job.Columns.id)
.fetchAll(db) .fetchAll(db)
} }
guard let jobsToRun: [Job] = maybeJobsToRun else { return } guard let jobsToRun: [Job] = maybeJobsToRun else { return }
jobQueue.mutate { $0.append(contentsOf: jobsToRun) } jobQueue.mutate {
// Insert any blocking jobs after any existing blocking jobs then add
// the remaining jobs to the end of the queue
let lastBlockingIndex = $0.lastIndex(where: { $0.isBlocking })
.defaulting(to: $0.startIndex.advanced(by: -1))
.advanced(by: 1)
$0.insert(
contentsOf: jobsToRun.filter { $0.isBlocking },
at: lastBlockingIndex
)
$0.append(
contentsOf: jobsToRun.filter { !$0.isBlocking }
)
}
// Start the job runner if needed // Start the job runner if needed
if !isRunning.wrappedValue { if !isRunning.wrappedValue {
@ -228,21 +268,14 @@ public final class JobRunner {
guard DispatchQueue.getSpecific(key: queueKey) == queueContext else { guard DispatchQueue.getSpecific(key: queueKey) == queueContext else {
internalQueue.async { internalQueue.async {
start() start()
} }// TODO: Want to have multiple threads for this (attachment download should be separate - do we even use attachment upload anymore???)
return return
} }
// Get any pending jobs // Get any pending jobs
let maybeJobsToRun: [Job]? = GRDBStorage.shared.read { db in let maybeJobsToRun: [Job]? = GRDBStorage.shared.read { db in
try Job try Job// TODO: Test this
.filter( .filterPendingJobs()
[
Job.Behaviour.runOnce,
Job.Behaviour.recurring
].contains(Job.Columns.behaviour)
)
.filter(Job.Columns.nextRunTimestamp <= Date().timeIntervalSince1970)
.order(Job.Columns.nextRunTimestamp)
.fetchAll(db) .fetchAll(db)
} }
@ -300,6 +333,12 @@ public final class JobRunner {
return return
} }
// If the 'nextRunTimestamp' for the job is in the future then don't run it yet
guard nextJob.nextRunTimestamp <= Date().timeIntervalSince1970 else {
handleJobDeferred(nextJob)
return
}
// Update the state to indicate it's running // Update the state to indicate it's running
// //
// Note: We need to store 'numJobsRemaining' in it's own variable because // Note: We need to store 'numJobsRemaining' in it's own variable because
@ -324,21 +363,17 @@ public final class JobRunner {
try TimeInterval try TimeInterval
.fetchOne( .fetchOne(
db, db,
Job Job// TODO: Test this works as expected
.filterPendingJobs(excludeFutureJobs: false)
.select(Job.Columns.nextRunTimestamp) .select(Job.Columns.nextRunTimestamp)
.filter(
[
Job.Behaviour.runOnce,
Job.Behaviour.recurring
].contains(Job.Columns.behaviour)
)
.order(Job.Columns.nextRunTimestamp)
) )
} }
guard let nextJobTimestamp: TimeInterval = nextJobTimestamp else { return } guard let nextJobTimestamp: TimeInterval = nextJobTimestamp else { return }
// If the next job isn't scheduled in the future then just restart the JobRunner immediately // If the next job isn't scheduled in the future then just restart the JobRunner immediately
let secondsUntilNextJob: TimeInterval = (nextJobTimestamp - Date().timeIntervalSince1970) let secondsUntilNextJob: TimeInterval = (nextJobTimestamp - Date().timeIntervalSince1970)
guard secondsUntilNextJob > 0 else { guard secondsUntilNextJob > 0 else {
SNLog("[JobRunner] Restarting immediately for job scheduled \(Int(ceil(abs(secondsUntilNextJob)))) second\(Int(ceil(abs(secondsUntilNextJob))) == 1 ? "" : "s")) ago") SNLog("[JobRunner] Restarting immediately for job scheduled \(Int(ceil(abs(secondsUntilNextJob)))) second\(Int(ceil(abs(secondsUntilNextJob))) == 1 ? "" : "s")) ago")
@ -378,6 +413,9 @@ public final class JobRunner {
.saved(db) .saved(db)
} }
case .recurringOnLaunchBlockingOncePerSession:
perSessionJobsCompleted.mutate { $0 = $0.inserting(job.id) }
default: break default: break
} }
@ -393,17 +431,48 @@ public final class JobRunner {
guard GRDBStorage.shared.read({ db in try Job.exists(db, id: job.id ?? -1) }) == true else { guard GRDBStorage.shared.read({ db in try Job.exists(db, id: job.id ?? -1) }) == true else {
SNLog("[JobRunner] \(job.variant) job canceled") SNLog("[JobRunner] \(job.variant) job canceled")
jobsCurrentlyRunning.mutate { $0 = $0.removing(job.id) } jobsCurrentlyRunning.mutate { $0 = $0.removing(job.id) }
runNextJob()
internalQueue.async {
runNextJob()
}
return return
} }
switch job.behaviour {
// If a "blocking" job failed then rerun it immediately
case .recurringOnLaunchBlocking, .recurringOnActiveBlocking:
SNLog("[JobRunner] blocking \(job.variant) job failed; retrying immediately")
jobQueue.mutate({ $0.insert(job, at: 0) })
internalQueue.async {
runNextJob()
}
return
// For "blocking once per session" jobs only rerun it immediately if it hasn't already
// run this session
case .recurringOnLaunchBlockingOncePerSession:
guard !perSessionJobsCompleted.wrappedValue.contains(job.id ?? -1) else { break }
SNLog("[JobRunner] blocking \(job.variant) job failed; retrying immediately")
perSessionJobsCompleted.mutate { $0 = $0.inserting(job.id) }
jobQueue.mutate({ $0.insert(job, at: 0) })
internalQueue.async {
runNextJob()
}
return
default: break
}
GRDBStorage.shared.write { db in GRDBStorage.shared.write { db in
// Check if the job has a 'maxFailureCount' (a value of '0' means it will always retry) // Get the max failure count for the job (a value of '-1' means it will retry indefinitely)
let maxFailureCount: UInt = (executorMap.wrappedValue[job.variant]?.maxFailureCount ?? 0) let maxFailureCount: Int = (executorMap.wrappedValue[job.variant]?.maxFailureCount ?? 0)
guard guard
!permanentFailure && !permanentFailure &&
maxFailureCount > 0 && maxFailureCount >= 0 &&
job.failureCount + 1 < maxFailureCount job.failureCount + 1 < maxFailureCount
else { else {
// If the job permanently failed or we have performed all of our retry attempts // If the job permanently failed or we have performed all of our retry attempts
@ -422,7 +491,9 @@ public final class JobRunner {
} }
jobsCurrentlyRunning.mutate { $0 = $0.removing(job.id) } jobsCurrentlyRunning.mutate { $0 = $0.removing(job.id) }
runNextJob() internalQueue.async {
runNextJob()
}
} }
/// This function is called when a job neither succeeds or fails (this should only occur if the job has specific logic that makes it dependant /// This function is called when a job neither succeeds or fails (this should only occur if the job has specific logic that makes it dependant