Added a simple migration progress indicator and animation (need timing tweaks)
Cleaned up the creation of the GRDBStorage instance Fixed an issue where the launch screen wasn't setting it's background colour based on the system setting Renamed the GRDBStorageError to StorageError (in preparation of legacy 'Storage' relocation) Consolidated the two Environment classes (in Swift) Refactored the AppSetup class to Swift
This commit is contained in:
parent
e2ee0e94ee
commit
26c7a5022a
|
@ -184,8 +184,6 @@
|
|||
B8856D11256F112A001CE70E /* OWSAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF281255B6D84007E1867 /* OWSAudioSession.swift */; };
|
||||
B8856D1A256F114D001CE70E /* ProximityMonitoringManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */; };
|
||||
B8856D23256F116B001CE70E /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2EF255B6DBB007E1867 /* Weak.swift */; };
|
||||
B8856D34256F1192001CE70E /* Environment.m in Sources */ = {isa = PBXBuildFile; fileRef = C37F5402255BA9ED002AEA92 /* Environment.m */; };
|
||||
B8856D3D256F11B2001CE70E /* Environment.h in Headers */ = {isa = PBXBuildFile; fileRef = C37F53E8255BA9BB002AEA92 /* Environment.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B8856D60256F129B001CE70E /* OWSAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8856D5F256F129B001CE70E /* OWSAlerts.swift */; };
|
||||
B8856D69256F141F001CE70E /* OWSWindowManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF306255B6DBE007E1867 /* OWSWindowManager.m */; };
|
||||
B8856D72256F1421001CE70E /* OWSWindowManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2FB255B6DBD007E1867 /* OWSWindowManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
|
@ -421,8 +419,6 @@
|
|||
C38EF275255B6D7A007E1867 /* OWSDatabaseMigrationRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF26F255B6D79007E1867 /* OWSDatabaseMigrationRunner.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF276255B6D7A007E1867 /* OWSDatabaseMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF270255B6D79007E1867 /* OWSDatabaseMigration.m */; };
|
||||
C38EF277255B6D7A007E1867 /* OWSDatabaseMigration.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF28F255B6D86007E1867 /* VersionMigrations.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF283255B6D84007E1867 /* VersionMigrations.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C38EF292255B6D86007E1867 /* VersionMigrations.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF286255B6D85007E1867 /* VersionMigrations.m */; };
|
||||
C38EF2A5255B6D93007E1867 /* Identicon+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A2255B6D93007E1867 /* Identicon+ObjC.swift */; };
|
||||
C38EF2A6255B6D93007E1867 /* PlaceholderIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */; };
|
||||
C38EF2A7255B6D93007E1867 /* ProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */; };
|
||||
|
@ -680,6 +676,8 @@
|
|||
FD848B9328420164000E298B /* UnicodeScalar+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD848B9228420164000E298B /* UnicodeScalar+Utilities.swift */; };
|
||||
FD848B9628422A2A000E298B /* MessageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD848B86283B844B000E298B /* MessageViewModel.swift */; };
|
||||
FD848B9828422F1A000E298B /* Date+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD848B9728422F1A000E298B /* Date+Utilities.swift */; };
|
||||
FD848B9A28442CE6000E298B /* StorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD848B9928442CE6000E298B /* StorageError.swift */; };
|
||||
FD848B9C284435D7000E298B /* AppSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD848B9B284435D7000E298B /* AppSetup.swift */; };
|
||||
FD859F0027C4691300510D0C /* MockDataGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFF27C4691300510D0C /* MockDataGenerator.swift */; };
|
||||
FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; };
|
||||
FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; };
|
||||
|
@ -690,8 +688,6 @@
|
|||
FD9004162818B46700ABAAF6 /* JobRunnerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */; };
|
||||
FDA8EAFE280E8B78002B68E5 /* FailedMessageSendsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EAFD280E8B78002B68E5 /* FailedMessageSendsJob.swift */; };
|
||||
FDA8EB00280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */; };
|
||||
FDA8EB09280E90FB002B68E5 /* AppSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF287255B6D85007E1867 /* AppSetup.m */; };
|
||||
FDA8EB0A280E9103002B68E5 /* AppSetup.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF284255B6D84007E1867 /* AppSetup.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
FDA8EB10280F8238002B68E5 /* Codable+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */; };
|
||||
FDB4BBC72838B91E00B7C95D /* LinkPreviewError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB4BBC62838B91E00B7C95D /* LinkPreviewError.swift */; };
|
||||
FDB4BBC92839BEF000B7C95D /* ProfileManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB4BBC82839BEF000B7C95D /* ProfileManagerError.swift */; };
|
||||
|
@ -710,7 +706,7 @@
|
|||
FDF0B74B28061F7A004C14C5 /* InteractionAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B74A28061F7A004C14C5 /* InteractionAttachment.swift */; };
|
||||
FDF0B74F28079E5E004C14C5 /* SendReadReceiptsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B74E28079E5E004C14C5 /* SendReadReceiptsJob.swift */; };
|
||||
FDF0B7512807BA56004C14C5 /* NotificationsProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7502807BA56004C14C5 /* NotificationsProtocol.swift */; };
|
||||
FDF0B7552807C4BB004C14C5 /* SSKEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7542807C4BB004C14C5 /* SSKEnvironment.swift */; };
|
||||
FDF0B7552807C4BB004C14C5 /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7542807C4BB004C14C5 /* Environment.swift */; };
|
||||
FDF0B7582807F368004C14C5 /* MessageReceiverError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7572807F368004C14C5 /* MessageReceiverError.swift */; };
|
||||
FDF0B75A2807F3A3004C14C5 /* MessageSenderError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7592807F3A3004C14C5 /* MessageSenderError.swift */; };
|
||||
FDF0B75C2807F41D004C14C5 /* MessageSender+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B75B2807F41D004C14C5 /* MessageSender+Convenience.swift */; };
|
||||
|
@ -1337,8 +1333,6 @@
|
|||
C374EEEA25DA3CA70073A857 /* ConversationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTitleView.swift; sourceTree = "<group>"; };
|
||||
C374EEF325DB31D40073A857 /* VoiceMessageRecordingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecordingView.swift; sourceTree = "<group>"; };
|
||||
C379DCF3256735770002D4EB /* VisibleMessage+Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleMessage+Attachment.swift"; sourceTree = "<group>"; };
|
||||
C37F53E8255BA9BB002AEA92 /* Environment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Environment.h; sourceTree = "<group>"; };
|
||||
C37F5402255BA9ED002AEA92 /* Environment.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Environment.m; sourceTree = "<group>"; };
|
||||
C38D5E8C2575011E00B6A65C /* MessageSender+ClosedGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageSender+ClosedGroups.swift"; sourceTree = "<group>"; };
|
||||
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SNProtoEnvelope+Conversion.swift"; sourceTree = "<group>"; };
|
||||
C38EF224255B6D5D007E1867 /* SignalAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SignalAttachment.swift; path = "SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift"; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1364,10 +1358,6 @@
|
|||
C38EF270255B6D79007E1867 /* OWSDatabaseMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OWSDatabaseMigration.m; path = SignalUtilitiesKit/Database/Migrations/OWSDatabaseMigration.m; sourceTree = SOURCE_ROOT; };
|
||||
C38EF271255B6D79007E1867 /* OWSDatabaseMigration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OWSDatabaseMigration.h; path = SignalUtilitiesKit/Database/Migrations/OWSDatabaseMigration.h; sourceTree = SOURCE_ROOT; };
|
||||
C38EF281255B6D84007E1867 /* OWSAudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSAudioSession.swift; path = SessionMessagingKit/Utilities/OWSAudioSession.swift; sourceTree = SOURCE_ROOT; };
|
||||
C38EF283255B6D84007E1867 /* VersionMigrations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VersionMigrations.h; path = SignalUtilitiesKit/Utilities/VersionMigrations.h; sourceTree = SOURCE_ROOT; };
|
||||
C38EF284255B6D84007E1867 /* AppSetup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppSetup.h; path = SignalUtilitiesKit/Utilities/AppSetup.h; sourceTree = SOURCE_ROOT; };
|
||||
C38EF286255B6D85007E1867 /* VersionMigrations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VersionMigrations.m; path = SignalUtilitiesKit/Utilities/VersionMigrations.m; sourceTree = SOURCE_ROOT; };
|
||||
C38EF287255B6D85007E1867 /* AppSetup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppSetup.m; path = SignalUtilitiesKit/Utilities/AppSetup.m; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2A2255B6D93007E1867 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Identicon+ObjC.swift"; path = "SignalUtilitiesKit/Profile Pictures/Identicon+ObjC.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PlaceholderIcon.swift; path = "SignalUtilitiesKit/Profile Pictures/PlaceholderIcon.swift"; sourceTree = SOURCE_ROOT; };
|
||||
C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfilePictureView.swift; path = "SignalUtilitiesKit/Profile Pictures/ProfilePictureView.swift"; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1648,6 +1638,8 @@
|
|||
FD848B8E283EF2A8000E298B /* UIScrollView+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FD848B9228420164000E298B /* UnicodeScalar+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UnicodeScalar+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FD848B9728422F1A000E298B /* Date+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FD848B9928442CE6000E298B /* StorageError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageError.swift; sourceTree = "<group>"; };
|
||||
FD848B9B284435D7000E298B /* AppSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSetup.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>"; };
|
||||
FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = "<group>"; };
|
||||
|
@ -1676,7 +1668,7 @@
|
|||
FDF0B74A28061F7A004C14C5 /* InteractionAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionAttachment.swift; sourceTree = "<group>"; };
|
||||
FDF0B74E28079E5E004C14C5 /* SendReadReceiptsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendReadReceiptsJob.swift; sourceTree = "<group>"; };
|
||||
FDF0B7502807BA56004C14C5 /* NotificationsProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsProtocol.swift; sourceTree = "<group>"; };
|
||||
FDF0B7542807C4BB004C14C5 /* SSKEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSKEnvironment.swift; sourceTree = "<group>"; };
|
||||
FDF0B7542807C4BB004C14C5 /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = "<group>"; };
|
||||
FDF0B7572807F368004C14C5 /* MessageReceiverError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReceiverError.swift; sourceTree = "<group>"; };
|
||||
FDF0B7592807F3A3004C14C5 /* MessageSenderError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSenderError.swift; sourceTree = "<group>"; };
|
||||
FDF0B75B2807F41D004C14C5 /* MessageSender+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MessageSender+Convenience.swift"; sourceTree = "<group>"; };
|
||||
|
@ -2125,6 +2117,7 @@
|
|||
FD17D7CB27F546F500122BE0 /* Models */,
|
||||
FD17D7B427F51E6700122BE0 /* Types */,
|
||||
FD17D7BB27F51F5C00122BE0 /* Utilities */,
|
||||
FD848B9928442CE6000E298B /* StorageError.swift */,
|
||||
FD28A4F527EAD44C00FF65E7 /* GRDBStorage.swift */,
|
||||
C33FDBAB255A581500E217F9 /* OWSFileSystem.h */,
|
||||
C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */,
|
||||
|
@ -2873,8 +2866,7 @@
|
|||
C33FDB75255A581000E217F9 /* AppReadiness.m */,
|
||||
FD09799027FD499200936362 /* BoxKeyPair+Utilities.swift */,
|
||||
C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */,
|
||||
C37F53E8255BA9BB002AEA92 /* Environment.h */,
|
||||
C37F5402255BA9ED002AEA92 /* Environment.m */,
|
||||
FDF0B7542807C4BB004C14C5 /* Environment.swift */,
|
||||
C3BBE0C62554F1570050F1E3 /* FixedWidthInteger+BigEndian.swift */,
|
||||
C3A71D0A2558989C0043A11F /* MessageWrapper.swift */,
|
||||
C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */,
|
||||
|
@ -2894,7 +2886,6 @@
|
|||
FDB4BBC82839BEF000B7C95D /* ProfileManagerError.swift */,
|
||||
C38EF2EC255B6DBA007E1867 /* ProximityMonitoringManager.swift */,
|
||||
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
|
||||
FDF0B7542807C4BB004C14C5 /* SSKEnvironment.swift */,
|
||||
C3A3A170256E1D25004D228D /* SSKReachabilityManager.swift */,
|
||||
C3ECBF7A257056B700EA7FCE /* Threading.swift */,
|
||||
C33FDB5F255A580E00E217F9 /* YapDatabaseConnection+OWS.h */,
|
||||
|
@ -3032,8 +3023,7 @@
|
|||
C3CA3B11255CF17200F4C6D4 /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C38EF284255B6D84007E1867 /* AppSetup.h */,
|
||||
C38EF287255B6D85007E1867 /* AppSetup.m */,
|
||||
FD848B9B284435D7000E298B /* AppSetup.swift */,
|
||||
C38EF302255B6DBE007E1867 /* OWSAnyTouchGestureRecognizer.h */,
|
||||
C38EF2F0255B6DBB007E1867 /* OWSAnyTouchGestureRecognizer.m */,
|
||||
FDCDB8DD2810F73B00352A0C /* Differentiable+Utilities.swift */,
|
||||
|
@ -3071,8 +3061,6 @@
|
|||
C38EF2F2255B6DBC007E1867 /* Searcher.swift */,
|
||||
B8856D5F256F129B001CE70E /* OWSAlerts.swift */,
|
||||
C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */,
|
||||
C38EF283255B6D84007E1867 /* VersionMigrations.h */,
|
||||
C38EF286255B6D85007E1867 /* VersionMigrations.m */,
|
||||
C33FDA8B255A57FD00E217F9 /* AppVersion.m */,
|
||||
C33FDB69255A580F00E217F9 /* FeatureFlags.swift */,
|
||||
C33FDA99255A57FE00E217F9 /* OutageDetection.swift */,
|
||||
|
@ -3538,7 +3526,6 @@
|
|||
C33FDD7C255A582000E217F9 /* SSKAsserts.h in Headers */,
|
||||
C38EF3F6255B6DF7007E1867 /* OWSTextView.h in Headers */,
|
||||
C38EF24C255B6D67007E1867 /* NSAttributedString+OWS.h in Headers */,
|
||||
FDA8EB0A280E9103002B68E5 /* AppSetup.h in Headers */,
|
||||
C38EF32B255B6DBF007E1867 /* OWSFormat.h in Headers */,
|
||||
C33FDDB8255A582000E217F9 /* NSSet+Functional.h in Headers */,
|
||||
C33FDDCC255A582000E217F9 /* TSConstants.h in Headers */,
|
||||
|
@ -3562,7 +3549,6 @@
|
|||
C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */,
|
||||
C33FDC50255A582000E217F9 /* OWSDispatch.h in Headers */,
|
||||
C33FDD06255A582000E217F9 /* AppVersion.h in Headers */,
|
||||
C38EF28F255B6D86007E1867 /* VersionMigrations.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -3613,7 +3599,6 @@
|
|||
C32A026C25A801AF000ED5D4 /* NSData+messagePadding.h in Headers */,
|
||||
B8856D72256F1421001CE70E /* OWSWindowManager.h in Headers */,
|
||||
B8856CF7256F105E001CE70E /* OWSAudioPlayer.h in Headers */,
|
||||
B8856D3D256F11B2001CE70E /* Environment.h in Headers */,
|
||||
C32C5E7E256DE023003C73A2 /* YapDatabaseTransaction+OWS.h in Headers */,
|
||||
C32C5EA0256DE0D6003C73A2 /* OWSPrimaryStorage.h in Headers */,
|
||||
B8856E33256F18D5001CE70E /* OWSStorage+Subclass.h in Headers */,
|
||||
|
@ -4306,7 +4291,6 @@
|
|||
C38EF247255B6D67007E1867 /* NSAttributedString+OWS.m in Sources */,
|
||||
C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */,
|
||||
C38EF3C5255B6DE7007E1867 /* OWSViewController+ImageEditor.swift in Sources */,
|
||||
FDA8EB09280E90FB002B68E5 /* AppSetup.m in Sources */,
|
||||
C38EF2A5255B6D93007E1867 /* Identicon+ObjC.swift in Sources */,
|
||||
C38EF273255B6D7A007E1867 /* OWSDatabaseMigrationRunner.m in Sources */,
|
||||
C38EF31A255B6DBF007E1867 /* OWSAnyTouchGestureRecognizer.m in Sources */,
|
||||
|
@ -4384,7 +4368,6 @@
|
|||
C33FDC78255A582000E217F9 /* TSConstants.m in Sources */,
|
||||
C38EF324255B6DBF007E1867 /* Bench.swift in Sources */,
|
||||
FDCDB8DE2810F73B00352A0C /* Differentiable+Utilities.swift in Sources */,
|
||||
C38EF292255B6D86007E1867 /* VersionMigrations.m in Sources */,
|
||||
C38EF3F9255B6DF7007E1867 /* OWSLayerView.swift in Sources */,
|
||||
C33FDD03255A582000E217F9 /* WeakTimer.swift in Sources */,
|
||||
B8B3204E258C15C80020074B /* ContactsMigration.swift in Sources */,
|
||||
|
@ -4400,6 +4383,7 @@
|
|||
B8C2B2C82563685C00551B4D /* CircleView.swift in Sources */,
|
||||
C38EF331255B6DBF007E1867 /* UIGestureRecognizer+OWS.swift in Sources */,
|
||||
C33FDDC5255A582000E217F9 /* OWSError.m in Sources */,
|
||||
FD848B9C284435D7000E298B /* AppSetup.swift in Sources */,
|
||||
C38EF38D255B6DD2007E1867 /* AttachmentCaptionViewController.swift in Sources */,
|
||||
C38EF31C255B6DBF007E1867 /* Searcher.swift in Sources */,
|
||||
C38EF2B3255B6D9C007E1867 /* UIViewController+Utilities.swift in Sources */,
|
||||
|
@ -4501,6 +4485,7 @@
|
|||
B8856DE6256F15F2001CE70E /* String+SSK.swift in Sources */,
|
||||
FDF2220F281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift in Sources */,
|
||||
C3471ED42555386B00297E91 /* AESGCM.swift in Sources */,
|
||||
FD848B9A28442CE6000E298B /* StorageError.swift in Sources */,
|
||||
FD17D7B827F51ECA00122BE0 /* Migration.swift in Sources */,
|
||||
C32C5DDB256DD9FF003C73A2 /* ContentProxy.swift in Sources */,
|
||||
C3A71F892558BA9F0043A11F /* Mnemonic.swift in Sources */,
|
||||
|
@ -4583,12 +4568,11 @@
|
|||
FD09797F27FCFBFF00936362 /* OWSAES256Key+Utilities.swift in Sources */,
|
||||
FDB4BBC72838B91E00B7C95D /* LinkPreviewError.swift in Sources */,
|
||||
FD09798327FD1A1500936362 /* ClosedGroup.swift in Sources */,
|
||||
B8856D34256F1192001CE70E /* Environment.m in Sources */,
|
||||
B8B320B7258C30D70020074B /* HTMLMetadata.swift in Sources */,
|
||||
FD09798727FD1B7800936362 /* GroupMember.swift in Sources */,
|
||||
FD09799127FD499200936362 /* BoxKeyPair+Utilities.swift in Sources */,
|
||||
FDB4BBC92839BEF000B7C95D /* ProfileManagerError.swift in Sources */,
|
||||
FDF0B7552807C4BB004C14C5 /* SSKEnvironment.swift in Sources */,
|
||||
FDF0B7552807C4BB004C14C5 /* Environment.swift in Sources */,
|
||||
B8856ECE256F1E58001CE70E /* OWSPreferences.m in Sources */,
|
||||
FD3E0C84283B5835002A425C /* SessionThreadViewModel.swift in Sources */,
|
||||
FD09C5EC282B8F18000CE219 /* AttachmentError.swift in Sources */,
|
||||
|
|
|
@ -873,6 +873,7 @@ extension ConversationVC:
|
|||
let interaction: Interaction = try? Interaction.fetchOne(db, id: cellViewModel.id),
|
||||
let thread: SessionThread = try SessionThread.fetchOne(db, id: threadId)
|
||||
else { return }
|
||||
|
||||
try MessageSender.send(
|
||||
db,
|
||||
interaction: interaction,
|
||||
|
|
|
@ -15,7 +15,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
var window: UIWindow?
|
||||
var backgroundSnapshotBlockerWindow: UIWindow?
|
||||
var appStartupWindow: UIWindow?
|
||||
var poller: Poller = Poller()
|
||||
var hasInitialRootViewController: Bool = false
|
||||
|
||||
/// This needs to be a lazy variable to ensure it doesn't get initialized before it actually needs to be used
|
||||
lazy var poller: Poller = {
|
||||
return Poller()
|
||||
}()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
|
@ -26,8 +31,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
|
||||
AppModeManager.configure(delegate: self)
|
||||
Cryptography.seedRandom()
|
||||
|
||||
AppVersion.sharedInstance() // TODO: ???
|
||||
AppVersion.sharedInstance()
|
||||
|
||||
// Prevent the device from sleeping during database view async registration
|
||||
// (e.g. long database upgrades).
|
||||
|
@ -35,14 +39,38 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
// This block will be cleared in storageIsReady.
|
||||
DeviceSleepManager.sharedInstance.addBlock(blockObject: self)
|
||||
|
||||
let mainWindow: UIWindow = UIWindow(frame: UIScreen.main.bounds)
|
||||
let loadingViewController: LoadingViewController = LoadingViewController()
|
||||
|
||||
AppSetup.setupEnvironment(
|
||||
appSpecificSingletonBlock: {
|
||||
appSpecificBlock: {
|
||||
// Create AppEnvironment
|
||||
AppEnvironment.shared.setup()
|
||||
},
|
||||
migrationCompletion: { [weak self] successful, needsConfigSync in
|
||||
guard let strongSelf = self else { return }
|
||||
|
||||
// Note: Intentionally dispatching sync as we want to wait for these to complete before
|
||||
// continuing
|
||||
DispatchQueue.main.sync {
|
||||
OWSScreenLockUI.sharedManager().setup(withRootWindow: mainWindow)
|
||||
OWSWindowManager.shared().setup(
|
||||
withRootWindow: mainWindow,
|
||||
screenBlockingWindow: OWSScreenLockUI.sharedManager().screenBlockingWindow
|
||||
)
|
||||
OWSScreenLockUI.sharedManager().startObserving()
|
||||
}
|
||||
},
|
||||
migrationProgressChanged: { progress, minEstimatedTotalTime in
|
||||
loadingViewController.updateProgress(
|
||||
progress: progress,
|
||||
minEstimatedTotalTime: minEstimatedTotalTime
|
||||
)
|
||||
},
|
||||
migrationsCompletion: { [weak self] successful, needsConfigSync in
|
||||
guard let strongSelf = self else { return }
|
||||
guard successful else {
|
||||
return
|
||||
}
|
||||
|
||||
Configuration.performMainSetup()
|
||||
JobRunner.add(executor: SyncPushTokensJob.self, for: .syncPushTokens)
|
||||
|
||||
// Trigger any launch-specific jobs and start the JobRunner
|
||||
|
@ -56,7 +84,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
DeviceSleepManager.sharedInstance.removeBlock(blockObject: strongSelf)
|
||||
AppVersion.sharedInstance().mainAppLaunchDidComplete()
|
||||
Environment.shared.audioSession.setup()
|
||||
SSKEnvironment.shared.reachabilityManager.setup()
|
||||
Environment.shared.reachabilityManager.setup()
|
||||
|
||||
GRDBStorage.shared.writeAsync { db in
|
||||
// Disable the SAE until the main app has successfully completed launch process
|
||||
|
@ -83,18 +111,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
}
|
||||
)
|
||||
|
||||
Configuration.performMainSetup()
|
||||
SNAppearance.switchToSessionAppearance()
|
||||
|
||||
// No point continuing if we are running tests
|
||||
guard !CurrentAppContext().isRunningTests else { return true }
|
||||
|
||||
let mainWindow: UIWindow = UIWindow(frame: UIScreen.main.bounds)
|
||||
self.window = mainWindow
|
||||
CurrentAppContext().mainWindow = mainWindow
|
||||
|
||||
// Show LoadingViewController until the async database view registrations are complete.
|
||||
mainWindow.rootViewController = LoadingViewController()
|
||||
mainWindow.rootViewController = loadingViewController
|
||||
mainWindow.makeKeyAndVisible()
|
||||
|
||||
adapt(appMode: AppModeManager.getAppModeOrSystemDefault())
|
||||
|
@ -104,14 +130,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
// Setting the delegate also seems to prevent us from getting the legacy notification
|
||||
// notification callbacks upon launch e.g. 'didReceiveLocalNotification'
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
|
||||
OWSScreenLockUI.sharedManager().setup(withRootWindow: mainWindow)
|
||||
OWSWindowManager.shared().setup(
|
||||
withRootWindow: mainWindow,
|
||||
screenBlockingWindow: OWSScreenLockUI.sharedManager().screenBlockingWindow
|
||||
)
|
||||
OWSScreenLockUI.sharedManager().startObserving()
|
||||
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(registrationStateDidChange),
|
||||
|
@ -196,10 +215,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
private func verifyDBKeysAvailableBeforeBackgroundLaunch() {
|
||||
guard UIApplication.shared.applicationState == .background else { return }
|
||||
|
||||
let migrationHasRun: Bool = false
|
||||
|
||||
// Ensure both databases are accessible (as long as we are supporting the YDB migration
|
||||
// we should keep this check)
|
||||
let databasePasswordAccessible: Bool = (
|
||||
(migrationHasRun && GRDBStorage.isDatabasePasswordAccessible) || // GRDB password access
|
||||
GRDBStorage.isDatabasePasswordAccessible && // GRDB password access
|
||||
OWSStorage.isDatabasePasswordAccessible() // YapDatabase password access
|
||||
)
|
||||
|
||||
|
@ -250,8 +269,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
}
|
||||
|
||||
private func ensureRootViewController() {
|
||||
// TODO: Add 'MigrationProcessingViewController' in here as well
|
||||
guard self.window?.rootViewController is LoadingViewController else { return }
|
||||
guard AppReadiness.isAppReady() && GRDBStorage.shared.isValid && !hasInitialRootViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let navController: UINavigationController = OWSNavigationController(
|
||||
rootViewController: (Identity.userExists() ?
|
||||
|
|
|
@ -57,7 +57,7 @@ import SignalUtilitiesKit
|
|||
@objc
|
||||
public func setup() {
|
||||
// Hang certain singletons on SSKEnvironment too.
|
||||
SSKEnvironment.shared.notificationsManager.mutate {
|
||||
Environment.shared.notificationsManager.mutate {
|
||||
$0 = notificationPresenter
|
||||
}
|
||||
setupLogFiles()
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
|
@ -27,7 +28,7 @@
|
|||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.086274509803921567" green="0.086274509803921567" blue="0.086274509803921567" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="backgroundColor" name="session_navigation_bar_background"/>
|
||||
<constraints>
|
||||
<constraint firstItem="b92-yg-YYQ" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="ec1-lk-fbn"/>
|
||||
<constraint firstItem="b92-yg-YYQ" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="jLr-XH-MKf"/>
|
||||
|
@ -41,5 +42,8 @@
|
|||
</scenes>
|
||||
<resources>
|
||||
<image name="SessionGreen64" width="57.5" height="64"/>
|
||||
<namedColor name="session_navigation_bar_background">
|
||||
<color red="0.9882352941176471" green="0.9882352941176471" blue="0.9882352941176471" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
@ -1,115 +1,150 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import PromiseKit
|
||||
import SessionUIKit
|
||||
|
||||
// The initial presentation is intended to be indistinguishable from the Launch Screen.
|
||||
// After a delay we present some "loading" UI so the user doesn't think the app is frozen.
|
||||
@objc
|
||||
public class LoadingViewController: UIViewController {
|
||||
/// This value specifies the minimum expected duration which needs to be hit before the loading UI needs to be show
|
||||
private static let minExpectedDurationToShowLoading: TimeInterval = 5
|
||||
|
||||
/// This value specifies the minimum expected duration which needs to be hit before the additional "might take a few minutes"
|
||||
/// label gets shown
|
||||
private static let minExpectedDurationAdditionalLabel: TimeInterval = 15
|
||||
|
||||
private var isShowingProgress: Bool = false
|
||||
|
||||
// MARK: - UI
|
||||
|
||||
override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
return .portrait
|
||||
}
|
||||
|
||||
private var logoView: UIImageView = {
|
||||
let result: UIImageView = UIImageView(image: #imageLiteral(resourceName: "SessionGreen64"))
|
||||
result.contentMode = .scaleAspectFit
|
||||
result.layer.shadowColor = Colors.accent.cgColor
|
||||
result.layer.shadowOffset = .zero
|
||||
result.layer.shadowRadius = 3
|
||||
result.layer.shadowOpacity = 0
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private var progressBar: UIProgressView = {
|
||||
let result: UIProgressView = UIProgressView(progressViewStyle: .bar)
|
||||
result.clipsToBounds = true
|
||||
result.progress = 0
|
||||
result.tintColor = Colors.accent
|
||||
result.trackTintColor = Colors.text.withAlphaComponent(0.1)
|
||||
result.layer.cornerRadius = 6
|
||||
|
||||
var logoView: UIImageView!
|
||||
var topLabel: UILabel!
|
||||
var bottomLabel: UILabel!
|
||||
return result
|
||||
}()
|
||||
|
||||
private var topLabel: UILabel = {
|
||||
let result: UILabel = UILabel()
|
||||
result.font = UIFont.systemFont(ofSize: Values.mediumFontSize)
|
||||
result.text = "DATABASE_VIEW_OVERLAY_TITLE".localized()
|
||||
result.textColor = Colors.text
|
||||
result.textAlignment = .center
|
||||
result.numberOfLines = 0
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private var bottomLabel: UILabel = {
|
||||
let result: UILabel = UILabel()
|
||||
result.font = UIFont.systemFont(ofSize: Values.verySmallFontSize)
|
||||
result.text = "DATABASE_VIEW_OVERLAY_SUBTITLE".localized()
|
||||
result.textColor = Colors.text
|
||||
result.textAlignment = .center
|
||||
result.numberOfLines = 0
|
||||
result.lineBreakMode = .byWordWrapping
|
||||
result.isHidden = true
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var labelStack: UIStackView = {
|
||||
let result: UIStackView = UIStackView()
|
||||
result.axis = .vertical
|
||||
result.alignment = .center
|
||||
result.spacing = 20
|
||||
result.alpha = 0
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override public func loadView() {
|
||||
self.view = UIView()
|
||||
|
||||
// Loki: Set gradient background
|
||||
view.backgroundColor = .clear
|
||||
let gradient = Gradients.defaultBackground
|
||||
view.setGradient(gradient)
|
||||
self.view.backgroundColor = Colors.navigationBarBackground
|
||||
|
||||
self.logoView = UIImageView(image: #imageLiteral(resourceName: "SessionGreen64"))
|
||||
view.addSubview(logoView)
|
||||
self.view.addSubview(self.logoView)
|
||||
self.view.addSubview(self.labelStack)
|
||||
self.view.addSubview(self.bottomLabel)
|
||||
|
||||
self.labelStack.addArrangedSubview(self.progressBar)
|
||||
self.labelStack.addArrangedSubview(self.topLabel)
|
||||
|
||||
logoView.autoCenterInSuperview()
|
||||
logoView.autoSetDimension(.width, toSize: 64)
|
||||
logoView.autoSetDimension(.height, toSize: 64)
|
||||
logoView.contentMode = .scaleAspectFit
|
||||
|
||||
self.topLabel = buildLabel()
|
||||
topLabel.alpha = 0
|
||||
topLabel.font = UIFont.ows_dynamicTypeTitle2
|
||||
topLabel.text = NSLocalizedString("DATABASE_VIEW_OVERLAY_TITLE", comment: "Title shown while the app is updating its database.")
|
||||
|
||||
self.bottomLabel = buildLabel()
|
||||
bottomLabel.alpha = 0
|
||||
bottomLabel.font = UIFont.ows_dynamicTypeBody
|
||||
bottomLabel.text = NSLocalizedString("DATABASE_VIEW_OVERLAY_SUBTITLE", comment: "Subtitle shown while the app is updating its database.")
|
||||
|
||||
let labelStack = UIStackView(arrangedSubviews: [topLabel, bottomLabel])
|
||||
labelStack.axis = .vertical
|
||||
labelStack.alignment = .center
|
||||
labelStack.spacing = 8
|
||||
view.addSubview(labelStack)
|
||||
|
||||
labelStack.autoPinEdge(.top, to: .bottom, of: logoView, withOffset: 20)
|
||||
labelStack.autoPinLeadingToSuperviewMargin()
|
||||
labelStack.autoPinTrailingToSuperviewMargin()
|
||||
labelStack.setCompressionResistanceHigh()
|
||||
labelStack.setContentHuggingHigh()
|
||||
// Layout
|
||||
|
||||
self.logoView.autoCenterInSuperview()
|
||||
self.logoView.autoSetDimension(.width, toSize: 64)
|
||||
self.logoView.autoSetDimension(.height, toSize: 64)
|
||||
|
||||
self.progressBar.set(.height, to: (self.progressBar.layer.cornerRadius * 2))
|
||||
self.progressBar.set(.width, to: .width, of: self.view, multiplier: 0.5)
|
||||
|
||||
self.labelStack.pin(.top, to: .bottom, of: self.logoView, withInset: 40)
|
||||
self.labelStack.pin(.left, to: .left, of: self.view)
|
||||
self.labelStack.pin(.right, to: .right, of: self.view)
|
||||
self.labelStack.setCompressionResistanceHigh()
|
||||
self.labelStack.setContentHuggingHigh()
|
||||
|
||||
self.bottomLabel.pin(.top, to: .bottom, of: self.labelStack, withInset: 10)
|
||||
self.bottomLabel.pin(.left, to: .left, of: self.view)
|
||||
self.bottomLabel.pin(.right, to: .right, of: self.view)
|
||||
self.bottomLabel.setCompressionResistanceHigh()
|
||||
self.bottomLabel.setContentHuggingHigh()
|
||||
}
|
||||
|
||||
var isShowingTopLabel = false
|
||||
var isShowingBottomLabel = false
|
||||
override public func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
// We only show the "loading" UI if it's a slow launch. Otherwise this ViewController
|
||||
// should be indistinguishable from the launch screen.
|
||||
let kTopLabelThreshold: TimeInterval = 5
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + kTopLabelThreshold) { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
public func updateProgress(progress: CGFloat, minEstimatedTotalTime: TimeInterval) {
|
||||
guard minEstimatedTotalTime >= LoadingViewController.minExpectedDurationToShowLoading else { return }
|
||||
|
||||
if !self.isShowingProgress {
|
||||
self.isShowingProgress = true
|
||||
self.bottomLabel.isHidden = (
|
||||
minEstimatedTotalTime < LoadingViewController.minExpectedDurationAdditionalLabel
|
||||
)
|
||||
|
||||
UIView.animate(withDuration: 0.3) { [weak self] in
|
||||
self?.labelStack.alpha = 1
|
||||
}
|
||||
|
||||
guard !strongSelf.isShowingTopLabel else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isShowingTopLabel = true
|
||||
UIView.animate(withDuration: 0.1) {
|
||||
strongSelf.topLabel.alpha = 1
|
||||
}
|
||||
UIView.animate(withDuration: 0.9, delay: 2, options: [.autoreverse, .repeat, .curveEaseInOut], animations: {
|
||||
strongSelf.topLabel.alpha = 0.2
|
||||
}, completion: nil)
|
||||
|
||||
UIView.animate(
|
||||
withDuration: 1.95,
|
||||
delay: 0.05,
|
||||
options: [
|
||||
.curveEaseInOut,
|
||||
.autoreverse,
|
||||
.repeat
|
||||
],
|
||||
animations: { [weak self] in
|
||||
self?.logoView.layer.shadowOpacity = 1
|
||||
},
|
||||
completion: nil
|
||||
)
|
||||
}
|
||||
|
||||
let kBottomLabelThreshold: TimeInterval = 15
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + kBottomLabelThreshold) { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard !strongSelf.isShowingBottomLabel else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isShowingBottomLabel = true
|
||||
UIView.animate(withDuration: 0.1) {
|
||||
strongSelf.bottomLabel.alpha = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Orientation
|
||||
|
||||
override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
return .portrait
|
||||
}
|
||||
|
||||
// MARK:
|
||||
|
||||
private func buildLabel() -> UILabel {
|
||||
let label = UILabel()
|
||||
|
||||
label.textColor = .white
|
||||
label.numberOfLines = 0
|
||||
label.lineBreakMode = .byWordWrapping
|
||||
|
||||
return label
|
||||
|
||||
self.progressBar.setProgress(Float(progress), animated: true)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -348,7 +348,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return ScreenLockUIStateNone;
|
||||
}
|
||||
|
||||
if (Environment.shared.isRequestingPermission) {
|
||||
if (SMKEnvironment.shared.isRequestingPermission) {
|
||||
return ScreenLockUIStateNone;
|
||||
}
|
||||
|
||||
|
|
|
@ -486,7 +486,7 @@ public enum SMKLegacy {
|
|||
let admins: [Data] = self.admins
|
||||
else {
|
||||
SNLog("[Migration Error] Unable to decode Legacy ClosedGroupControlMessage")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
return .new(
|
||||
|
@ -504,7 +504,7 @@ public enum SMKLegacy {
|
|||
case "encryptionKeyPair":
|
||||
guard let wrappers: [_KeyPairWrapper] = self.wrappers else {
|
||||
SNLog("[Migration Error] Unable to decode Legacy ClosedGroupControlMessage")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
return .encryptionKeyPair(
|
||||
|
@ -515,7 +515,7 @@ public enum SMKLegacy {
|
|||
let encryptedKeyPair: Data = wrapper.encryptedKeyPair
|
||||
else {
|
||||
SNLog("[Migration Error] Unable to decode Legacy ClosedGroupControlMessage")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
return SessionMessagingKit.ClosedGroupControlMessage.KeyPairWrapper(
|
||||
|
@ -528,7 +528,7 @@ public enum SMKLegacy {
|
|||
case "nameChange":
|
||||
guard let name: String = self.name else {
|
||||
SNLog("[Migration Error] Unable to decode Legacy ClosedGroupControlMessage")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
return .nameChange(
|
||||
|
@ -538,7 +538,7 @@ public enum SMKLegacy {
|
|||
case "membersAdded":
|
||||
guard let members: [Data] = self.members else {
|
||||
SNLog("[Migration Error] Unable to decode Legacy ClosedGroupControlMessage")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
return .membersAdded(members: members)
|
||||
|
@ -546,14 +546,14 @@ public enum SMKLegacy {
|
|||
case "membersRemoved":
|
||||
guard let members: [Data] = self.members else {
|
||||
SNLog("[Migration Error] Unable to decode Legacy ClosedGroupControlMessage")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
return .membersRemoved(members: members)
|
||||
|
||||
case "memberLeft": return .memberLeft
|
||||
case "encryptionKeyPairRequest": return .encryptionKeyPairRequest
|
||||
default: throw GRDBStorageError.migrationFailed
|
||||
default: throw StorageError.migrationFailed
|
||||
}
|
||||
}()
|
||||
)
|
||||
|
@ -592,12 +592,12 @@ public enum SMKLegacy {
|
|||
case "mediaSaved":
|
||||
guard let timestamp: UInt64 = self.timestamp else {
|
||||
SNLog("[Migration Error] Unable to decode Legacy DataExtractionNotification")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
return .mediaSaved(timestamp: timestamp)
|
||||
|
||||
default: throw GRDBStorageError.migrationFailed
|
||||
default: throw StorageError.migrationFailed
|
||||
}
|
||||
}()
|
||||
)
|
||||
|
|
|
@ -6,6 +6,8 @@ import SessionUtilitiesKit
|
|||
|
||||
enum _001_InitialSetupMigration: Migration {
|
||||
static let identifier: String = "initialSetup"
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static let needsConfigSync: Bool = false
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
// Define the tokenizer to be used in all the FTS tables
|
||||
|
|
|
@ -10,6 +10,8 @@ import SessionSnodeKit
|
|||
/// before running the `YDBToGRDBMigration`
|
||||
enum _002_SetupStandardJobs: Migration {
|
||||
static let identifier: String = "SetupStandardJobs"
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static let needsConfigSync: Bool = false
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
// Start by adding the jobs that don't have collections (in the jobs like these
|
||||
|
|
|
@ -11,8 +11,12 @@ import SessionSnodeKit
|
|||
// ~250k messages and ~1000 threads seems to take up
|
||||
enum _003_YDBToGRDBMigration: Migration {
|
||||
static let identifier: String = "YDBToGRDBMigration"
|
||||
static let minExpectedRunDuration: TimeInterval = 20
|
||||
static let needsConfigSync: Bool = true
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
let targetIdentifier: TargetMigrations.Identifier = .messagingKit
|
||||
|
||||
// MARK: - Process Contacts, Threads & Interactions
|
||||
print("RAWR [\(Date().timeIntervalSince1970)] - SessionMessagingKit migration - Start")
|
||||
var shouldFailMigration: Bool = false
|
||||
|
@ -283,7 +287,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
}
|
||||
|
||||
// We can't properly throw within the 'enumerateKeysAndObjects' block so have to throw here
|
||||
guard !shouldFailMigration else { throw GRDBStorageError.migrationFailed }
|
||||
guard !shouldFailMigration else { throw StorageError.migrationFailed }
|
||||
|
||||
// Insert the data into GRDB
|
||||
|
||||
|
@ -411,7 +415,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
try legacyThreads.sorted(by: { lhs, rhs in lhs.uniqueId < rhs.uniqueId }).forEach { legacyThread in
|
||||
guard let threadId: String = legacyThreadIdToIdMap[legacyThread.uniqueId] else {
|
||||
SNLog("[Migration Error] Unable to migrate thread with no id mapping")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
let threadVariant: SessionThread.Variant
|
||||
|
@ -464,7 +468,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
let formationTimestamp: UInt64 = closedGroupFormation[legacyThread.uniqueId]
|
||||
else {
|
||||
SNLog("[Migration Error] Closed group missing required data")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
try ClosedGroup(
|
||||
|
@ -519,7 +523,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
if legacyThread.isOpenGroup {
|
||||
guard let openGroup: OpenGroupV2 = openGroupInfo[legacyThread.uniqueId] else {
|
||||
SNLog("[Migration Error] Open group missing required data")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
try OpenGroup(
|
||||
|
@ -673,7 +677,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
default:
|
||||
// TODO: What message types have no body?
|
||||
SNLog("[Migration Error] Unsupported interaction type")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
// Insert the data
|
||||
|
@ -727,7 +731,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
guard let interactionId: Int64 = interaction.id else {
|
||||
// TODO: Is it possible the old database has duplicates which could hit this case?
|
||||
SNLog("[Migration Error] Failed to insert interaction")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
// Store the interactionId in the lookup map to simplify job creation later
|
||||
|
@ -874,7 +878,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
guard linkPreview.imageAttachmentId == nil || attachments[linkPreview.imageAttachmentId ?? ""] != nil else {
|
||||
// TODO: Is it possible to hit this case if a quoted attachment hasn't been downloaded?
|
||||
SNLog("[Migration Error] Missing link preview attachment")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
// Setup the attachment and add it to the lookup (if it exists)
|
||||
|
@ -1268,7 +1272,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
try attachmentUploadJobs.forEach { legacyJob in
|
||||
guard let sendJob: Job = messageSendJobLegacyMap[legacyJob.messageSendJobID], let sendJobId: Int64 = sendJob.id else {
|
||||
SNLog("[Migration Error] attachmentUpload job missing associated MessageSendJob")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
let uploadJob: Job? = try Job(
|
||||
|
@ -1286,7 +1290,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
// Add the dependency to the relevant MessageSendJob
|
||||
guard let uploadJobId: Int64 = uploadJob?.id else {
|
||||
SNLog("[Migration Error] attachmentUpload job was not created")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
try JobDependencies(
|
||||
|
@ -1302,7 +1306,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
try attachmentDownloadJobs.forEach { legacyJob in
|
||||
guard let interactionId: Int64 = legacyInteractionToIdMap[legacyJob.tsMessageID] else {
|
||||
SNLog("[Migration Error] attachmentDownload job unable to find interaction")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
guard processedAttachmentIds.contains(legacyJob.attachmentID) else {
|
||||
SNLog("[Migration Error] attachmentDownload job unable to find attachment")
|
||||
|
@ -1441,7 +1445,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
guard !processedAttachmentIds.contains(legacyAttachmentId) else {
|
||||
guard isQuotedMessage else {
|
||||
SNLog("[Migration Error] Attempted to process duplicate attachment")
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
|
||||
return legacyAttachmentId
|
||||
|
|
|
@ -983,7 +983,7 @@ extension Attachment {
|
|||
|
||||
guard uploadedAttachment != nil else {
|
||||
SNLog("Couldn't update attachmentUpload job.")
|
||||
failure?(GRDBStorageError.failedToSave)
|
||||
failure?(StorageError.failedToSave)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1025,7 +1025,7 @@ extension Attachment {
|
|||
|
||||
guard updatedAttachment != nil else {
|
||||
SNLog("Couldn't update attachmentUpload job.")
|
||||
failure?(GRDBStorageError.failedToSave)
|
||||
failure?(StorageError.failedToSave)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1049,7 +1049,7 @@ extension Attachment {
|
|||
|
||||
guard uploadedAttachment != nil else {
|
||||
SNLog("Couldn't update attachmentUpload job.")
|
||||
failure?(GRDBStorageError.failedToSave)
|
||||
failure?(StorageError.failedToSave)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ public extension Profile {
|
|||
{
|
||||
guard let validProfileKey: OWSAES256Key = OWSAES256Key(data: profileKeyData) else {
|
||||
owsFailDebug("Failed to make profile key for key data")
|
||||
throw GRDBStorageError.decodingFailed
|
||||
throw StorageError.decodingFailed
|
||||
}
|
||||
|
||||
profileKey = validProfileKey
|
||||
|
|
|
@ -94,7 +94,7 @@ public extension DisappearingMessagesJob {
|
|||
|
||||
do {
|
||||
guard let interactionId: Int64 = try? (interaction.id ?? interaction.inserted(db).id) else {
|
||||
throw GRDBStorageError.objectNotFound
|
||||
throw StorageError.objectNotFound
|
||||
}
|
||||
|
||||
return updateNextRunIfNeeded(db, interactionIds: [interactionId], startedAtMs: startedAtMs)
|
||||
|
|
|
@ -211,18 +211,18 @@ extension MessageSendJob {
|
|||
|
||||
guard let messageType: String = try? container.decode(String.self, forKey: .messageType) else {
|
||||
Logger.error("Unable to decode messageSend job due to missing messageType")
|
||||
throw GRDBStorageError.decodingFailed
|
||||
throw StorageError.decodingFailed
|
||||
}
|
||||
|
||||
/// Note: This **MUST** be a `Codable.Type` rather than a `Message.Type` otherwise the decoding will result
|
||||
/// in a `Message` object being returned rather than the desired subclass
|
||||
guard let MessageType: Codable.Type = MessageSendJob.Details.supportedMessageTypes[messageType] else {
|
||||
Logger.error("Unable to decode messageSend job due to unsupported messageType")
|
||||
throw GRDBStorageError.decodingFailed
|
||||
throw StorageError.decodingFailed
|
||||
}
|
||||
guard let message: Message = try MessageType.decoded(with: container, forKey: .message) as? Message else {
|
||||
Logger.error("Unable to decode messageSend job due to message conversion issue")
|
||||
throw GRDBStorageError.decodingFailed
|
||||
throw StorageError.decodingFailed
|
||||
}
|
||||
|
||||
self = Details(
|
||||
|
@ -241,7 +241,7 @@ extension MessageSendJob {
|
|||
|
||||
guard let messageTypeString: String = maybeMessageTypeString else {
|
||||
Logger.error("Unable to encode messageSend job due to unsupported messageType")
|
||||
throw GRDBStorageError.objectNotFound
|
||||
throw StorageError.objectNotFound
|
||||
}
|
||||
|
||||
try container.encode(destination, forKey: .destination)
|
||||
|
|
|
@ -17,7 +17,7 @@ public extension Message {
|
|||
case .closedGroup: return .closedGroup(groupPublicKey: thread.id)
|
||||
case .openGroup:
|
||||
guard let openGroup: OpenGroup = try thread.openGroup.fetchOne(db) else {
|
||||
throw GRDBStorageError.objectNotFound
|
||||
throw StorageError.objectNotFound
|
||||
}
|
||||
|
||||
return .openGroupV2(room: openGroup.room, server: openGroup.server)
|
||||
|
|
|
@ -494,7 +494,7 @@ extension MessageReceiver {
|
|||
throw error
|
||||
}
|
||||
|
||||
guard let interactionId: Int64 = interaction.id else { throw GRDBStorageError.failedToSave }
|
||||
guard let interactionId: Int64 = interaction.id else { throw StorageError.failedToSave }
|
||||
|
||||
// Update and recipient and read states as needed
|
||||
try updateRecipientAndReadStates(
|
||||
|
|
|
@ -230,9 +230,7 @@ extension MessageSender {
|
|||
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000))
|
||||
).inserted(db)
|
||||
|
||||
guard let interactionId: Int64 = interaction.id else {
|
||||
throw GRDBStorageError.objectNotSaved
|
||||
}
|
||||
guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
|
||||
|
||||
// Send the update to the group
|
||||
let closedGroupControlMessage = ClosedGroupControlMessage(kind: .nameChange(name: name))
|
||||
|
@ -305,10 +303,10 @@ extension MessageSender {
|
|||
thread: SessionThread
|
||||
) throws {
|
||||
guard let disappearingMessagesConfig: DisappearingMessagesConfiguration = try thread.disappearingMessagesConfiguration.fetchOne(db) else {
|
||||
throw GRDBStorageError.objectNotFound
|
||||
throw StorageError.objectNotFound
|
||||
}
|
||||
guard let encryptionKeyPair: ClosedGroupKeyPair = try closedGroup.fetchLatestKeyPair(db) else {
|
||||
throw GRDBStorageError.objectNotFound
|
||||
throw StorageError.objectNotFound
|
||||
}
|
||||
|
||||
let groupMemberIds: [String] = allGroupMembers
|
||||
|
@ -332,9 +330,7 @@ extension MessageSender {
|
|||
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000))
|
||||
).inserted(db)
|
||||
|
||||
guard let interactionId: Int64 = interaction.id else {
|
||||
throw GRDBStorageError.objectNotSaved
|
||||
}
|
||||
guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
|
||||
|
||||
// Send the update to the group
|
||||
try MessageSender.send(
|
||||
|
@ -437,9 +433,7 @@ extension MessageSender {
|
|||
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000))
|
||||
).inserted(db)
|
||||
|
||||
guard let newInteractionId: Int64 = interaction.id else {
|
||||
throw GRDBStorageError.objectNotSaved
|
||||
}
|
||||
guard let newInteractionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
|
||||
|
||||
interactionId = newInteractionId
|
||||
}
|
||||
|
@ -505,7 +499,7 @@ extension MessageSender {
|
|||
).inserted(db)
|
||||
|
||||
guard let interactionId: Int64 = interaction.id else {
|
||||
throw GRDBStorageError.objectNotSaved
|
||||
throw StorageError.objectNotSaved
|
||||
}
|
||||
|
||||
// Send the update to the group
|
||||
|
|
|
@ -10,7 +10,7 @@ extension MessageSender {
|
|||
// MARK: - Durable
|
||||
|
||||
public static func send(_ db: Database, interaction: Interaction, with attachments: [SignalAttachment], in thread: SessionThread) throws {
|
||||
guard let interactionId: Int64 = interaction.id else { throw GRDBStorageError.objectNotSaved }
|
||||
guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
|
||||
|
||||
try prep(db, signalAttachments: attachments, for: interactionId)
|
||||
send(
|
||||
|
@ -25,7 +25,7 @@ extension MessageSender {
|
|||
public static func send(_ db: Database, interaction: Interaction, in thread: SessionThread) throws {
|
||||
// Only 'VisibleMessage' types can be sent via this method
|
||||
guard interaction.variant == .standardOutgoing else { throw MessageSenderError.invalidMessage }
|
||||
guard let interactionId: Int64 = interaction.id else { throw GRDBStorageError.objectNotSaved }
|
||||
guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
|
||||
|
||||
send(
|
||||
db,
|
||||
|
@ -64,7 +64,7 @@ extension MessageSender {
|
|||
// MARK: - Non-Durable
|
||||
|
||||
public static func sendNonDurably(_ db: Database, interaction: Interaction, with attachments: [SignalAttachment], in thread: SessionThread) throws -> Promise<Void> {
|
||||
guard let interactionId: Int64 = interaction.id else { return Promise(error: GRDBStorageError.objectNotSaved) }
|
||||
guard let interactionId: Int64 = interaction.id else { return Promise(error: StorageError.objectNotSaved) }
|
||||
|
||||
try prep(db, signalAttachments: attachments, for: interactionId)
|
||||
|
||||
|
@ -80,7 +80,7 @@ extension MessageSender {
|
|||
public static func sendNonDurably(_ db: Database, interaction: Interaction, in thread: SessionThread) throws -> Promise<Void> {
|
||||
// Only 'VisibleMessage' types can be sent via this method
|
||||
guard interaction.variant == .standardOutgoing else { throw MessageSenderError.invalidMessage }
|
||||
guard let interactionId: Int64 = interaction.id else { throw GRDBStorageError.objectNotSaved }
|
||||
guard let interactionId: Int64 = interaction.id else { throw StorageError.objectNotSaved }
|
||||
|
||||
return sendNonDurably(
|
||||
db,
|
||||
|
@ -174,9 +174,7 @@ extension MessageSender {
|
|||
// If we don't have a userKeyPair yet then there is no need to sync the configuration
|
||||
// as the user doesn't exist yet (this will get triggered on the first launch of a
|
||||
// fresh install due to the migrations getting run)
|
||||
guard Identity.userExists(db) else {
|
||||
return Promise(error: GRDBStorageError.generic)
|
||||
}
|
||||
guard Identity.userExists(db) else { return Promise(error: StorageError.generic) }
|
||||
|
||||
let destination: Message.Destination = Message.Destination.contact(
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
|
@ -188,7 +186,7 @@ extension MessageSender {
|
|||
try MessageSender
|
||||
.sendImmediate(db, message: configurationMessage, to: destination, interactionId: nil)
|
||||
.done { seal.fulfill(()) }
|
||||
.catch { _ in seal.reject(GRDBStorageError.generic) }
|
||||
.catch { _ in seal.reject(StorageError.generic) }
|
||||
.retainUntilComplete()
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -782,7 +782,7 @@ public extension SessionThreadViewModel {
|
|||
.defaulting(to: FTS5Pattern(matchingAnyTokenIn: searchTerm))
|
||||
)
|
||||
|
||||
guard let pattern: FTS5Pattern = maybePattern else { throw GRDBStorageError.invalidSearchPattern }
|
||||
guard let pattern: FTS5Pattern = maybePattern else { throw StorageError.invalidSearchPattern }
|
||||
|
||||
return pattern
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class OWSAudioSession;
|
||||
@class OWSPreferences;
|
||||
@class OWSWindowManager;
|
||||
|
||||
@protocol OWSProximityMonitoringManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* Environment is a data and data accessor class.
|
||||
* It handles application-level component wiring in order to support mocks for testing.
|
||||
* It also handles network configuration for testing/deployment server configurations.
|
||||
*
|
||||
**/
|
||||
@interface Environment : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithAudioSession:(OWSAudioSession *)audioSession
|
||||
preferences:(OWSPreferences *)preferences
|
||||
proximityMonitoringManager:(id<OWSProximityMonitoringManager>)proximityMonitoringManager
|
||||
windowManager:(OWSWindowManager *)windowManager;
|
||||
|
||||
@property (nonatomic, readonly) OWSAudioSession *audioSession;
|
||||
@property (nonatomic, readonly) id<OWSProximityMonitoringManager> proximityMonitoringManager;
|
||||
@property (nonatomic, readonly) OWSPreferences *preferences;
|
||||
@property (nonatomic, readonly) OWSWindowManager *windowManager;
|
||||
// We don't want to cover the window when we request the photo library permission
|
||||
@property (nonatomic, readwrite) BOOL isRequestingPermission;
|
||||
|
||||
@property (class, nonatomic) Environment *shared;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Should only be called by tests.
|
||||
+ (void)clearSharedForTests;
|
||||
#endif
|
||||
|
||||
@end
|
|
@ -1,62 +0,0 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "OWSWindowManager.h"
|
||||
#import <SessionMessagingKit/SessionMessagingKit-Swift.h>
|
||||
#import "OWSPreferences.h"
|
||||
|
||||
static Environment *sharedEnvironment = nil;
|
||||
|
||||
@interface Environment ()
|
||||
|
||||
@property (nonatomic) OWSAudioSession *audioSession;
|
||||
@property (nonatomic) OWSPreferences *preferences;
|
||||
@property (nonatomic) id<OWSProximityMonitoringManager> proximityMonitoringManager;
|
||||
@property (nonatomic) OWSWindowManager *windowManager;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation Environment
|
||||
|
||||
+ (Environment *)shared
|
||||
{
|
||||
return sharedEnvironment;
|
||||
}
|
||||
|
||||
+ (void)setShared:(Environment *)environment
|
||||
{
|
||||
// The main app environment should only be set once.
|
||||
//
|
||||
// App extensions may be opened multiple times in the same process,
|
||||
// so statics will persist.
|
||||
|
||||
sharedEnvironment = environment;
|
||||
}
|
||||
|
||||
+ (void)clearSharedForTests
|
||||
{
|
||||
sharedEnvironment = nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAudioSession:(OWSAudioSession *)audioSession
|
||||
preferences:(OWSPreferences *)preferences
|
||||
proximityMonitoringManager:(id<OWSProximityMonitoringManager>)proximityMonitoringManager
|
||||
windowManager:(OWSWindowManager *)windowManager
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_audioSession = audioSession;
|
||||
_preferences = preferences;
|
||||
_proximityMonitoringManager = proximityMonitoringManager;
|
||||
_windowManager = windowManager;
|
||||
_isRequestingPermission = false;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public class Environment {
|
||||
public static var shared: Environment!
|
||||
|
||||
public let primaryStorage: OWSPrimaryStorage
|
||||
public let reachabilityManager: SSKReachabilityManager
|
||||
|
||||
public let audioSession: OWSAudioSession
|
||||
public let preferences: OWSPreferences
|
||||
public let proximityMonitoringManager: OWSProximityMonitoringManager
|
||||
public let windowManager: OWSWindowManager
|
||||
public var isRequestingPermission: Bool
|
||||
|
||||
// Note: This property is configured after Environment is created.
|
||||
public let notificationsManager: Atomic<NotificationsProtocol?> = Atomic(nil)
|
||||
|
||||
public var isComplete: Bool {
|
||||
(notificationsManager.wrappedValue != nil)
|
||||
}
|
||||
|
||||
public var objectReadWriteConnection: YapDatabaseConnection
|
||||
public var sessionStoreDBConnection: YapDatabaseConnection
|
||||
public var migrationDBConnection: YapDatabaseConnection
|
||||
public var analyticsDBConnection: YapDatabaseConnection
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
public init(
|
||||
primaryStorage: OWSPrimaryStorage,
|
||||
reachabilityManager: SSKReachabilityManager,
|
||||
audioSession: OWSAudioSession,
|
||||
preferences: OWSPreferences,
|
||||
proximityMonitoringManager: OWSProximityMonitoringManager,
|
||||
windowManager: OWSWindowManager
|
||||
) {
|
||||
self.primaryStorage = primaryStorage
|
||||
self.reachabilityManager = reachabilityManager
|
||||
self.audioSession = audioSession
|
||||
self.preferences = preferences
|
||||
self.proximityMonitoringManager = proximityMonitoringManager
|
||||
self.windowManager = windowManager
|
||||
self.isRequestingPermission = false
|
||||
|
||||
self.objectReadWriteConnection = primaryStorage.newDatabaseConnection()
|
||||
self.sessionStoreDBConnection = primaryStorage.newDatabaseConnection()
|
||||
self.migrationDBConnection = primaryStorage.newDatabaseConnection()
|
||||
self.analyticsDBConnection = primaryStorage.newDatabaseConnection()
|
||||
|
||||
if Environment.shared == nil {
|
||||
Environment.shared = self
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
public static func clearSharedForTests() {
|
||||
shared = nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Objective C Support
|
||||
|
||||
@objc(SMKEnvironment)
|
||||
class SMKEnvironment: NSObject {
|
||||
@objc public static let shared: SMKEnvironment = SMKEnvironment()
|
||||
|
||||
@objc public var primaryStorage: OWSPrimaryStorage { Environment.shared.primaryStorage }
|
||||
@objc public var audioSession: OWSAudioSession { Environment.shared.audioSession }
|
||||
@objc public var windowManager: OWSWindowManager { Environment.shared.windowManager }
|
||||
|
||||
@objc public var isRequestingPermission: Bool { Environment.shared.isRequestingPermission }
|
||||
}
|
|
@ -94,7 +94,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (OWSAudioSession *)audioSession
|
||||
{
|
||||
return Environment.shared.audioSession;
|
||||
return SMKEnvironment.shared.audioSession;
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import "OWSWindowManager.h"
|
||||
#import "Environment.h"
|
||||
#import <SessionMessagingKit/SessionMessagingKit-Swift.h>
|
||||
#import <SessionUtilitiesKit/SessionUtilitiesKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
@ -153,7 +154,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
|
||||
+ (instancetype)sharedManager
|
||||
{
|
||||
return Environment.shared.windowManager;
|
||||
return SMKEnvironment.shared.windowManager;
|
||||
}
|
||||
|
||||
- (instancetype)initDefault
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
@objc
|
||||
public class SSKEnvironment: NSObject {
|
||||
@objc public let primaryStorage: OWSPrimaryStorage
|
||||
public let reachabilityManager: SSKReachabilityManager
|
||||
|
||||
// Note: This property is configured after Environment is created.
|
||||
public let notificationsManager: Atomic<NotificationsProtocol?> = Atomic(nil)
|
||||
|
||||
@objc public static var shared: SSKEnvironment!
|
||||
|
||||
public var isComplete: Bool {
|
||||
(notificationsManager.wrappedValue != nil)
|
||||
}
|
||||
|
||||
public var objectReadWriteConnection: YapDatabaseConnection
|
||||
public var sessionStoreDBConnection: YapDatabaseConnection
|
||||
public var migrationDBConnection: YapDatabaseConnection
|
||||
public var analyticsDBConnection: YapDatabaseConnection
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
@objc public init(
|
||||
primaryStorage: OWSPrimaryStorage,
|
||||
reachabilityManager: SSKReachabilityManager
|
||||
) {
|
||||
self.primaryStorage = primaryStorage
|
||||
self.reachabilityManager = reachabilityManager
|
||||
|
||||
self.objectReadWriteConnection = primaryStorage.newDatabaseConnection()
|
||||
self.sessionStoreDBConnection = primaryStorage.newDatabaseConnection()
|
||||
self.migrationDBConnection = primaryStorage.newDatabaseConnection()
|
||||
self.analyticsDBConnection = primaryStorage.newDatabaseConnection()
|
||||
|
||||
super.init()
|
||||
|
||||
if SSKEnvironment.shared == nil {
|
||||
SSKEnvironment.shared = self
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
public static func clearSharedForTests() {
|
||||
shared = nil
|
||||
}
|
||||
}
|
|
@ -115,12 +115,12 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension
|
|||
guard GRDBStorage.shared[.isReadyForAppExtensions] else { return completeSilenty() }
|
||||
|
||||
AppSetup.setupEnvironment(
|
||||
appSpecificSingletonBlock: {
|
||||
SSKEnvironment.shared.notificationsManager.mutate {
|
||||
appSpecificBlock: {
|
||||
Environment.shared.notificationsManager.mutate {
|
||||
$0 = NSENotificationPresenter()
|
||||
}
|
||||
},
|
||||
migrationCompletion: { [weak self] _, needsConfigSync in
|
||||
migrationsCompletion: { [weak self] _, needsConfigSync in
|
||||
self?.versionMigrationsDidComplete(needsConfigSync: needsConfigSync)
|
||||
completion()
|
||||
}
|
||||
|
|
|
@ -43,14 +43,12 @@ final class ShareVC : UINavigationController, ShareViewDelegate, AppModeManagerD
|
|||
}
|
||||
|
||||
AppSetup.setupEnvironment(
|
||||
appSpecificSingletonBlock: {
|
||||
SSKEnvironment.shared.notificationsManager.mutate {
|
||||
appSpecificBlock: {
|
||||
Environment.shared.notificationsManager.mutate {
|
||||
$0 = NoopNotificationsManager()
|
||||
}
|
||||
},
|
||||
migrationCompletion: { [weak self] _, needsConfigSync in
|
||||
AssertIsOnMainThread()
|
||||
|
||||
migrationsCompletion: { [weak self] _, needsConfigSync in
|
||||
// performUpdateCheck must be invoked after Environment has been initialized because
|
||||
// upgrade process may depend on Environment.
|
||||
self?.versionMigrationsDidComplete(needsConfigSync: needsConfigSync)
|
||||
|
|
|
@ -6,6 +6,8 @@ import SessionUtilitiesKit
|
|||
|
||||
enum _001_InitialSetupMigration: Migration {
|
||||
static let identifier: String = "initialSetup"
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static let needsConfigSync: Bool = false
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
try db.create(table: Snode.self) { t in
|
||||
|
|
|
@ -8,6 +8,8 @@ import SessionUtilitiesKit
|
|||
/// before running the `YDBToGRDBMigration`
|
||||
enum _002_SetupStandardJobs: Migration {
|
||||
static let identifier: String = "SetupStandardJobs"
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static let needsConfigSync: Bool = false
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
try autoreleasepool {
|
||||
|
|
|
@ -6,6 +6,8 @@ import SessionUtilitiesKit
|
|||
|
||||
enum _003_YDBToGRDBMigration: Migration {
|
||||
static let identifier: String = "YDBToGRDBMigration"
|
||||
static let minExpectedRunDuration: TimeInterval = 0.2
|
||||
static let needsConfigSync: Bool = false
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
// MARK: - OnionRequestPath, Snode Pool & Swarm
|
||||
|
|
|
@ -97,9 +97,9 @@ public extension UIView {
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
func set(_ dimension: Dimension, to otherDimension: Dimension, of view: UIView, withOffset offset: CGFloat = 0) -> NSLayoutConstraint {
|
||||
func set(_ dimension: Dimension, to otherDimension: Dimension, of view: UIView, withOffset offset: CGFloat = 0, multiplier: CGFloat = 1) -> NSLayoutConstraint {
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
let otherAnchor: NSLayoutAnchor<NSLayoutDimension> = {
|
||||
let otherAnchor: NSLayoutDimension = {
|
||||
switch otherDimension {
|
||||
case .width: return view.widthAnchor
|
||||
case .height: return view.heightAnchor
|
||||
|
@ -107,8 +107,8 @@ public extension UIView {
|
|||
}()
|
||||
let constraint: NSLayoutConstraint = {
|
||||
switch dimension {
|
||||
case .width: return widthAnchor.constraint(equalTo: otherAnchor, constant: offset)
|
||||
case .height: return heightAnchor.constraint(equalTo: otherAnchor, constant: offset)
|
||||
case .width: return widthAnchor.constraint(equalTo: otherAnchor, multiplier: multiplier, constant: offset)
|
||||
case .height: return heightAnchor.constraint(equalTo: otherAnchor, multiplier: multiplier, constant: offset)
|
||||
}
|
||||
}()
|
||||
constraint.isActive = true
|
||||
|
|
|
@ -5,25 +5,8 @@ import GRDB
|
|||
import PromiseKit
|
||||
import SignalCoreKit
|
||||
|
||||
public enum GRDBStorageError: Error { // TODO: Rename to `StorageError`
|
||||
case generic
|
||||
case migrationFailed
|
||||
case invalidKeySpec
|
||||
case decodingFailed
|
||||
|
||||
case failedToSave
|
||||
case objectNotFound
|
||||
case objectNotSaved
|
||||
|
||||
case invalidSearchPattern
|
||||
}
|
||||
|
||||
// TODO: Protocol for storage (just need to have 'read' and 'write' methods and mock 'Database'?
|
||||
|
||||
// TODO: Rename to `Storage`
|
||||
public final class GRDBStorage {
|
||||
public static var shared: GRDBStorage! // TODO: Figure out how/if we want to do this
|
||||
|
||||
private static let dbFileName: String = "Session.sqlite"
|
||||
private static let keychainService: String = "TSKeyChainService"
|
||||
private static let dbCipherKeySpecKey: String = "GRDBDatabaseCipherKeySpec"
|
||||
|
@ -40,14 +23,17 @@ public final class GRDBStorage {
|
|||
return true
|
||||
}
|
||||
|
||||
private let dbPool: DatabasePool
|
||||
private let migrator: DatabaseMigrator
|
||||
public static let shared: GRDBStorage = GRDBStorage()
|
||||
public private(set) var isValid: Bool = false
|
||||
public private(set) var hasCompletedMigrations: Bool = false
|
||||
|
||||
private var dbPool: DatabasePool?
|
||||
private var migrator: DatabaseMigrator?
|
||||
private var migrationProgressUpdater: Atomic<((String, CGFloat) -> ())>?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
public init?(
|
||||
migrations: [TargetMigrations]
|
||||
) throws {
|
||||
public init() {
|
||||
print("RAWR START \("\(GRDBStorage.sharedDatabaseDirectoryPath)/\(GRDBStorage.dbFileName)")")
|
||||
GRDBStorage.deleteDatabaseFiles() // TODO: Remove this
|
||||
try! GRDBStorage.deleteDbKeys() // TODO: Remove this
|
||||
|
@ -76,7 +62,7 @@ public final class GRDBStorage {
|
|||
// using explicit BLOB syntax, e.g.:
|
||||
//
|
||||
// x'98483C6EB40B6C31A448C22A66DED3B5E5E8D5119CAC8327B655C8B5C483648101010101010101010101010101010101'
|
||||
keySpec = try (keySpec.toHexString().data(using: .utf8) ?? { throw GRDBStorageError.invalidKeySpec }())
|
||||
keySpec = try (keySpec.toHexString().data(using: .utf8) ?? { throw StorageError.invalidKeySpec }())
|
||||
keySpec.insert(contentsOf: [120, 39], at: 0) // "x'" prefix
|
||||
keySpec.append(39) // "'" suffix
|
||||
|
||||
|
@ -90,40 +76,111 @@ public final class GRDBStorage {
|
|||
try db.execute(sql: "PRAGMA cipher_plaintext_header_size = 32")
|
||||
}
|
||||
|
||||
// Create the DatabasePool to allow us to connect to the database
|
||||
dbPool = try DatabasePool(
|
||||
path: "\(GRDBStorage.sharedDatabaseDirectoryPath)/\(GRDBStorage.dbFileName)",
|
||||
configuration: config
|
||||
)
|
||||
// Create the DatabasePool to allow us to connect to the database and mark the storage as valid
|
||||
do {
|
||||
dbPool = try DatabasePool(
|
||||
path: "\(GRDBStorage.sharedDatabaseDirectoryPath)/\(GRDBStorage.dbFileName)",
|
||||
configuration: config
|
||||
)
|
||||
isValid = true
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
// MARK: - Migrations
|
||||
|
||||
public func perform(
|
||||
migrations: [TargetMigrations],
|
||||
onProgressUpdate: ((CGFloat, TimeInterval) -> ())?,
|
||||
onComplete: @escaping (Bool, Bool) -> ()
|
||||
) {
|
||||
guard isValid, let dbPool: DatabasePool = dbPool else { return }
|
||||
|
||||
typealias MigrationInfo = (identifier: TargetMigrations.Identifier, migrations: TargetMigrations.MigrationSet)
|
||||
let sortedMigrationInfo: [MigrationInfo] = migrations
|
||||
.sorted()
|
||||
.reduce(into: [[MigrationInfo]]()) { result, next in
|
||||
next.migrations.enumerated().forEach { index, migrationSet in
|
||||
if result.count <= index {
|
||||
result.append([])
|
||||
}
|
||||
|
||||
result[index] = (result[index] + [(next.identifier, migrationSet)])
|
||||
}
|
||||
}
|
||||
.reduce(into: []) { result, next in result.append(contentsOf: next) }
|
||||
|
||||
// Setup and run any required migrations
|
||||
migrator = {
|
||||
var migrator: DatabaseMigrator = DatabaseMigrator()
|
||||
migrations
|
||||
.sorted()
|
||||
.reduce(into: [[(identifier: TargetMigrations.Identifier, migrations: TargetMigrations.MigrationSet)]]()) { result, next in
|
||||
next.migrations.enumerated().forEach { index, migrationSet in
|
||||
if result.count <= index {
|
||||
result.append([])
|
||||
}
|
||||
|
||||
result[index] = (result[index] + [(next.identifier, migrationSet)])
|
||||
}
|
||||
}
|
||||
.compactMap { $0 }
|
||||
.forEach { sortedMigrationInfo in
|
||||
sortedMigrationInfo.forEach { migrationInfo in
|
||||
migrationInfo.migrations.forEach { migration in
|
||||
migrator.registerMigration(migrationInfo.identifier, migration: migration)
|
||||
}
|
||||
}
|
||||
sortedMigrationInfo.forEach { migrationInfo in
|
||||
migrationInfo.migrations.forEach { migration in
|
||||
migrator.registerMigration(migrationInfo.identifier, migration: migration)
|
||||
}
|
||||
}
|
||||
|
||||
return migrator
|
||||
}()
|
||||
try! migrator.migrate(dbPool)
|
||||
|
||||
GRDBStorage.shared = self // TODO: Fix this
|
||||
// Determine which migrations need to be performed and gather the relevant settings needed to
|
||||
// inform the app of progress/states
|
||||
let completedMigrations: [String] = (try? dbPool.read { db in try migrator?.completedMigrations(db) })
|
||||
.defaulting(to: [])
|
||||
let unperformedMigrations: [(key: String, migration: Migration.Type)] = sortedMigrationInfo
|
||||
.reduce(into: []) { result, next in
|
||||
next.migrations.forEach { migration in
|
||||
let key: String = next.identifier.key(with: migration)
|
||||
|
||||
guard !completedMigrations.contains(key) else { return }
|
||||
|
||||
result.append((key, migration))
|
||||
}
|
||||
}
|
||||
let migrationToDurationMap: [String: TimeInterval] = unperformedMigrations
|
||||
.reduce(into: [:]) { result, next in
|
||||
result[next.key] = next.migration.minExpectedRunDuration
|
||||
}
|
||||
let unperformedMigrationDurations: [TimeInterval] = unperformedMigrations
|
||||
.map { _, migration in migration.minExpectedRunDuration }
|
||||
let totalMinExpectedDuration: TimeInterval = migrationToDurationMap.values.reduce(0, +)
|
||||
let needsConfigSync: Bool = unperformedMigrations
|
||||
.contains(where: { _, migration in migration.needsConfigSync })
|
||||
|
||||
self.migrationProgressUpdater = Atomic({ targetKey, progress in
|
||||
guard let migrationIndex: Int = unperformedMigrations.firstIndex(where: { key, _ in key == targetKey }) else {
|
||||
return
|
||||
}
|
||||
|
||||
let completedExpectedDuration: TimeInterval = (
|
||||
(migrationIndex > 0 ? unperformedMigrationDurations[0..<migrationIndex].reduce(0, +) : 0) +
|
||||
(unperformedMigrationDurations[migrationIndex] * progress)
|
||||
)
|
||||
let totalProgress: CGFloat = (completedExpectedDuration / totalMinExpectedDuration)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
onProgressUpdate?(totalProgress, totalMinExpectedDuration)
|
||||
}
|
||||
})
|
||||
|
||||
// If we have an unperformed migration then trigger the progress updater immediately
|
||||
if let firstMigrationKey: String = unperformedMigrations.first?.key {
|
||||
self.migrationProgressUpdater?.wrappedValue(firstMigrationKey, 0)
|
||||
}
|
||||
|
||||
self.migrator?.asyncMigrate(dbPool) { [weak self] _, error in
|
||||
self?.hasCompletedMigrations = true
|
||||
self?.migrationProgressUpdater = nil
|
||||
|
||||
onComplete((error == nil), needsConfigSync)
|
||||
}
|
||||
}
|
||||
|
||||
public func update(
|
||||
progress: CGFloat,
|
||||
for migration: Migration.Type,
|
||||
in target: TargetMigrations.Identifier
|
||||
) {
|
||||
GRDBStorage.shared.migrationProgressUpdater?.wrappedValue(target.key(with: migration), progress)
|
||||
}
|
||||
|
||||
// MARK: - Security
|
||||
|
@ -137,7 +194,7 @@ public final class GRDBStorage {
|
|||
var keySpec: Data = try getDatabaseCipherKeySpec()
|
||||
defer { keySpec.resetBytes(in: 0..<keySpec.count) }
|
||||
|
||||
guard keySpec.count == kSQLCipherKeySpecLength else { throw GRDBStorageError.invalidKeySpec }
|
||||
guard keySpec.count == kSQLCipherKeySpecLength else { throw StorageError.invalidKeySpec }
|
||||
|
||||
return keySpec
|
||||
}
|
||||
|
@ -151,7 +208,7 @@ public final class GRDBStorage {
|
|||
|
||||
//errSecInteractionNotAllowed
|
||||
|
||||
case (GRDBStorageError.invalidKeySpec, _):
|
||||
case (StorageError.invalidKeySpec, _):
|
||||
// For these cases it means either the keySpec or the keychain has become corrupt so in order to
|
||||
// get back to a "known good state" and behave like a new install we need to reset the storage
|
||||
// and regenerate the key
|
||||
|
@ -227,6 +284,8 @@ public final class GRDBStorage {
|
|||
// MARK: - Functions
|
||||
|
||||
@discardableResult public func write<T>(updates: (Database) throws -> T?) -> T? {
|
||||
guard isValid, let dbPool: DatabasePool = dbPool else { return nil }
|
||||
|
||||
return try? dbPool.write(updates)
|
||||
}
|
||||
|
||||
|
@ -235,6 +294,8 @@ public final class GRDBStorage {
|
|||
}
|
||||
|
||||
public func writeAsync<T>(updates: @escaping (Database) throws -> T, completion: @escaping (Database, Swift.Result<T, Error>) throws -> Void) {
|
||||
guard isValid, let dbPool: DatabasePool = dbPool else { return }
|
||||
|
||||
dbPool.asyncWrite(
|
||||
updates,
|
||||
completion: { db, result in
|
||||
|
@ -244,6 +305,8 @@ public final class GRDBStorage {
|
|||
}
|
||||
|
||||
@discardableResult public func read<T>(_ value: (Database) throws -> T?) -> T? {
|
||||
guard isValid, let dbPool: DatabasePool = dbPool else { return nil }
|
||||
|
||||
return try? dbPool.read(value)
|
||||
}
|
||||
|
||||
|
@ -262,7 +325,9 @@ public final class GRDBStorage {
|
|||
onError: @escaping (Error) -> Void,
|
||||
onChange: @escaping (Reducer.Value) -> Void
|
||||
) -> DatabaseCancellable {
|
||||
observation.start(
|
||||
guard isValid, let dbPool: DatabasePool = dbPool else { return AnyDatabaseCancellable(cancel: {}) }
|
||||
|
||||
return observation.start(
|
||||
in: dbPool,
|
||||
scheduling: scheduler,
|
||||
onError: onError,
|
||||
|
@ -271,6 +336,7 @@ public final class GRDBStorage {
|
|||
}
|
||||
|
||||
public func addObserver(_ observer: TransactionObserver?) {
|
||||
guard isValid, let dbPool: DatabasePool = dbPool else { return }
|
||||
guard let observer: TransactionObserver = observer else { return }
|
||||
|
||||
dbPool.add(transactionObserver: observer)
|
||||
|
@ -282,6 +348,8 @@ public final class GRDBStorage {
|
|||
public extension GRDBStorage {
|
||||
// FIXME: Would be good to replace these with Swift Combine
|
||||
@discardableResult func read<T>(_ value: (Database) throws -> Promise<T>) -> Promise<T> {
|
||||
guard isValid, let dbPool: DatabasePool = dbPool else { return Promise(error: StorageError.databaseInvalid) }
|
||||
|
||||
do {
|
||||
return try dbPool.read(value)
|
||||
}
|
||||
|
@ -291,6 +359,8 @@ public extension GRDBStorage {
|
|||
}
|
||||
|
||||
@discardableResult func write<T>(updates: (Database) throws -> Promise<T>) -> Promise<T> {
|
||||
guard isValid, let dbPool: DatabasePool = dbPool else { return Promise(error: StorageError.databaseInvalid) }
|
||||
|
||||
do {
|
||||
return try dbPool.write(updates)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import GRDB
|
|||
|
||||
enum _001_InitialSetupMigration: Migration {
|
||||
static let identifier: String = "initialSetup"
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static let needsConfigSync: Bool = false
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
try db.create(table: Identity.self) { t in
|
||||
|
|
|
@ -8,6 +8,8 @@ import Curve25519Kit
|
|||
/// before running the `YDBToGRDBMigration`
|
||||
enum _002_SetupStandardJobs: Migration {
|
||||
static let identifier: String = "SetupStandardJobs"
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static let needsConfigSync: Bool = false
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
try autoreleasepool {
|
||||
|
|
|
@ -5,6 +5,8 @@ import GRDB
|
|||
|
||||
enum _003_YDBToGRDBMigration: Migration {
|
||||
static let identifier: String = "YDBToGRDBMigration"
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static let needsConfigSync: Bool = false
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
// MARK: - Identity keys
|
||||
|
@ -69,7 +71,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
return
|
||||
}
|
||||
|
||||
throw GRDBStorageError.migrationFailed
|
||||
throw StorageError.migrationFailed
|
||||
}
|
||||
print("RAWR publicKey \(userX25519KeyPair.publicKey.toHexString())")
|
||||
try autoreleasepool {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum StorageError: Error {
|
||||
case generic
|
||||
case databaseInvalid
|
||||
case migrationFailed
|
||||
case invalidKeySpec
|
||||
case decodingFailed
|
||||
|
||||
case failedToSave
|
||||
case objectNotFound
|
||||
case objectNotSaved
|
||||
|
||||
case invalidSearchPattern
|
||||
}
|
|
@ -5,6 +5,8 @@ import GRDB
|
|||
|
||||
public protocol Migration {
|
||||
static var identifier: String { get }
|
||||
static var needsConfigSync: Bool { get }
|
||||
static var minExpectedRunDuration: TimeInterval { get }
|
||||
|
||||
static func migrate(_ db: Database) throws
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ public struct TargetMigrations: Comparable {
|
|||
|
||||
return (lhsIndex < rhsIndex)
|
||||
}
|
||||
|
||||
func key(with migration: Migration.Type) -> String {
|
||||
return "\(self.rawValue).\(migration.identifier)"
|
||||
}
|
||||
}
|
||||
|
||||
public typealias MigrationSet = [Migration.Type]
|
||||
|
|
|
@ -3,8 +3,6 @@ import SessionSnodeKit
|
|||
|
||||
extension OWSPrimaryStorage : OWSPrimaryStorageProtocol { }
|
||||
|
||||
var isSetup: Bool = false // TODO: Remove this
|
||||
|
||||
@objc(SNConfiguration)
|
||||
public final class Configuration : NSObject {
|
||||
|
||||
|
@ -19,21 +17,4 @@ public final class Configuration : NSObject {
|
|||
SNMessagingKit.configure(storage: Storage.shared)
|
||||
SNSnodeKit.configure()
|
||||
}
|
||||
|
||||
@objc public static func performDatabaseSetup() {
|
||||
if !isSetup {
|
||||
isSetup = true
|
||||
|
||||
// TODO: Need to store this result somewhere?
|
||||
// TODO: This function seems to get called multiple times
|
||||
//DispatchQueue.main.once
|
||||
let storage: GRDBStorage? = try? GRDBStorage(
|
||||
migrations: [
|
||||
SNUtilitiesKit.migrations(),
|
||||
SNSnodeKit.migrations(),
|
||||
SNMessagingKit.migrations()
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void (^OWSDatabaseMigrationCompletion)(BOOL success, BOOL requiresConfigurationSync);
|
||||
|
||||
// This is _NOT_ a singleton and will be instantiated each time that the SAE is used.
|
||||
@interface AppSetup : NSObject
|
||||
|
||||
+ (void)setupEnvironmentWithAppSpecificSingletonBlock:(dispatch_block_t)appSpecificSingletonBlock
|
||||
migrationCompletion:(OWSDatabaseMigrationCompletion)migrationCompletion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,93 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppSetup.h"
|
||||
#import "Environment.h"
|
||||
#import "VersionMigrations.h"
|
||||
#import <SignalUtilitiesKit/OWSDatabaseMigration.h>
|
||||
#import <SessionMessagingKit/OWSBackgroundTask.h>
|
||||
#import <SessionMessagingKit/OWSStorage.h>
|
||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation AppSetup
|
||||
|
||||
+ (void)setupEnvironmentWithAppSpecificSingletonBlock:(dispatch_block_t)appSpecificSingletonBlock
|
||||
migrationCompletion:(OWSDatabaseMigrationCompletion)migrationCompletion
|
||||
{
|
||||
OWSAssertDebug(appSpecificSingletonBlock);
|
||||
OWSAssertDebug(migrationCompletion);
|
||||
|
||||
__block OWSBackgroundTask *_Nullable backgroundTask =
|
||||
[OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// Order matters here.
|
||||
//
|
||||
// All of these "singletons" should have any dependencies used in their
|
||||
// initializers injected.
|
||||
[[OWSBackgroundTaskManager sharedManager] observeNotifications];
|
||||
|
||||
OWSPrimaryStorage *primaryStorage = [[OWSPrimaryStorage alloc] initStorage];
|
||||
[OWSPrimaryStorage protectFiles];
|
||||
|
||||
// AFNetworking (via CFNetworking) spools it's attachments to NSTemporaryDirectory().
|
||||
// If you receive a media message while the device is locked, the download will fail if the temporary directory
|
||||
// is NSFileProtectionComplete
|
||||
BOOL success = [OWSFileSystem protectFileOrFolderAtPath:NSTemporaryDirectory()
|
||||
fileProtectionType:NSFileProtectionCompleteUntilFirstUserAuthentication];
|
||||
OWSAssert(success);
|
||||
|
||||
OWSPreferences *preferences = [OWSPreferences new];
|
||||
|
||||
id<SSKReachabilityManager> reachabilityManager = [SSKReachabilityManagerImpl new];
|
||||
|
||||
OWSAudioSession *audioSession = [OWSAudioSession new];
|
||||
id<OWSProximityMonitoringManager> proximityMonitoringManager = [OWSProximityMonitoringManagerImpl new];
|
||||
OWSWindowManager *windowManager = [[OWSWindowManager alloc] initDefault];
|
||||
|
||||
[Environment setShared:[[Environment alloc] initWithAudioSession:audioSession
|
||||
preferences:preferences
|
||||
proximityMonitoringManager:proximityMonitoringManager
|
||||
windowManager:windowManager]];
|
||||
|
||||
// TODO: Add this back
|
||||
// TODO: Refactor this file to Swift
|
||||
[SSKEnvironment setShared:[[SSKEnvironment alloc] initWithPrimaryStorage:primaryStorage
|
||||
reachabilityManager:reachabilityManager]];
|
||||
|
||||
appSpecificSingletonBlock();
|
||||
|
||||
// TODO: Add this back?
|
||||
// OWSAssertDebug(SSKEnvironment.shared.isComplete);
|
||||
|
||||
[SNConfiguration performMainSetup]; // Must happen before the performUpdateCheck call below
|
||||
|
||||
// Register renamed classes.
|
||||
[NSKeyedUnarchiver setClass:[OWSDatabaseMigration class] forClassName:[OWSDatabaseMigration collection]];
|
||||
|
||||
[OWSStorage registerExtensionsWithMigrationBlock:^() {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Don't start database migrations until storage is ready.
|
||||
[VersionMigrations performUpdateCheckWithCompletion:^(BOOL successful, BOOL needsConfigSync) {
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
migrationCompletion(successful, needsConfigSync);
|
||||
|
||||
OWSAssertDebug(backgroundTask);
|
||||
backgroundTask = nil;
|
||||
}];
|
||||
});
|
||||
}];
|
||||
|
||||
// Must happen after the performUpdateCheck above to ensure all legacy database migrations have run
|
||||
[SNConfiguration performDatabaseSetup];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionMessagingKit
|
||||
import SessionUtilitiesKit
|
||||
import UIKit
|
||||
|
||||
public enum AppSetup {
|
||||
private static var hasRun: Bool = false
|
||||
|
||||
public static func setupEnvironment(
|
||||
appSpecificBlock: @escaping () -> (),
|
||||
migrationProgressChanged: ((CGFloat, TimeInterval) -> ())? = nil,
|
||||
migrationsCompletion: @escaping (Bool, Bool) -> ()
|
||||
) {
|
||||
guard !AppSetup.hasRun else { return }
|
||||
|
||||
AppSetup.hasRun = true
|
||||
|
||||
var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(labelStr: #function)
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
// Order matters here.
|
||||
//
|
||||
// All of these "singletons" should have any dependencies used in their
|
||||
// initializers injected.
|
||||
OWSBackgroundTaskManager.shared().observeNotifications()
|
||||
|
||||
let primaryStorage: OWSPrimaryStorage = OWSPrimaryStorage(storage: ())
|
||||
OWSPrimaryStorage.protectFiles()
|
||||
|
||||
// AFNetworking (via CFNetworking) spools it's attachments to NSTemporaryDirectory().
|
||||
// If you receive a media message while the device is locked, the download will fail if the temporary directory
|
||||
// is NSFileProtectionComplete
|
||||
let success: Bool = OWSFileSystem.protectFileOrFolder(
|
||||
atPath: NSTemporaryDirectory(),
|
||||
fileProtectionType: .completeUntilFirstUserAuthentication
|
||||
)
|
||||
assert(success)
|
||||
|
||||
Environment.shared = Environment(
|
||||
primaryStorage: primaryStorage,
|
||||
reachabilityManager: SSKReachabilityManagerImpl(),
|
||||
audioSession: OWSAudioSession(),
|
||||
preferences: OWSPreferences(),
|
||||
proximityMonitoringManager: OWSProximityMonitoringManagerImpl(),
|
||||
windowManager: OWSWindowManager(default: ())
|
||||
)
|
||||
appSpecificBlock()
|
||||
|
||||
/// `performMainSetup` **MUST** run before `perform(migrations:)`
|
||||
Configuration.performMainSetup()
|
||||
GRDBStorage.shared.perform(
|
||||
migrations: [
|
||||
SNUtilitiesKit.migrations(),
|
||||
SNSnodeKit.migrations(),
|
||||
SNMessagingKit.migrations()
|
||||
],
|
||||
onProgressUpdate: migrationProgressChanged,
|
||||
onComplete: { success, needsConfigSync in
|
||||
DispatchQueue.main.async {
|
||||
migrationsCompletion(success, needsConfigSync)
|
||||
|
||||
// The 'if' is only there to prevent the "variable never read" warning from showing
|
||||
if backgroundTask != nil { backgroundTask = nil }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define RECENT_CALLS_DEFAULT_KEY @"RPRecentCallsDefaultKey"
|
||||
|
||||
typedef void (^VersionMigrationCompletion)(BOOL success, BOOL requiresConfigurationSync);
|
||||
|
||||
@interface VersionMigrations : NSObject
|
||||
|
||||
+ (void)performUpdateCheckWithCompletion:(VersionMigrationCompletion)completion;
|
||||
|
||||
+ (BOOL)isVersion:(NSString *)thisVersionString
|
||||
atLeast:(NSString *)openLowerBoundVersionString
|
||||
andLessThan:(NSString *)closedUpperBoundVersionString;
|
||||
|
||||
+ (BOOL)isVersion:(NSString *)thisVersionString atLeast:(NSString *)thatVersionString;
|
||||
|
||||
+ (BOOL)isVersion:(NSString *)thisVersionString lessThan:(NSString *)thatVersionString;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,126 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VersionMigrations.h"
|
||||
#import "OWSDatabaseMigrationRunner.h"
|
||||
#import <SessionUtilitiesKit/AppContext.h>
|
||||
#import <SignalUtilitiesKit/AppVersion.h>
|
||||
#import <SessionUtilitiesKit/NSUserDefaults+OWS.h>
|
||||
#import <YapDatabase/YapDatabase.h>
|
||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define NEEDS_TO_REGISTER_PUSH_KEY @"Register For Push"
|
||||
#define NEEDS_TO_REGISTER_ATTRIBUTES @"Register Attributes"
|
||||
|
||||
@implementation VersionMigrations
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
+ (void)performUpdateCheckWithCompletion:(VersionMigrationCompletion)completion
|
||||
{
|
||||
OWSLogInfo(@"");
|
||||
|
||||
// performUpdateCheck must be invoked after Environment has been initialized because
|
||||
// upgrade process may depend on Environment.
|
||||
OWSAssertDebug(Environment.shared);
|
||||
OWSAssertDebug(completion);
|
||||
|
||||
NSString *previousVersion = AppVersion.sharedInstance.lastAppVersion;
|
||||
NSString *currentVersion = AppVersion.sharedInstance.currentAppVersion;
|
||||
|
||||
OWSLogInfo(@"Checking migrations. currentVersion: %@, lastRanVersion: %@", currentVersion, previousVersion);
|
||||
|
||||
if (!previousVersion) {
|
||||
// Note: We need to run the migrations here anyway to ensure that they don't run on subsequent launches
|
||||
// and result in unexpected data changes (eg. 'MessageRequestsMigration' auto-approves all threads
|
||||
// if this happens on the 2nd launch then any threads created during the 1st launch which haven't
|
||||
// been approved would get auto-approved, allowing the user to use contacts which haven't approved
|
||||
// comms to appear as options when creating closed groups)
|
||||
OWSLogInfo(@"No previous version found. Probably first launch since install - running migrations so they don't run on second launch.");
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[[[OWSDatabaseMigrationRunner alloc] init] runAllOutstandingWithCompletion:completion];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.1.70"] && [SUKIdentity userExists]) {
|
||||
[self clearVideoCache];
|
||||
}
|
||||
|
||||
if ([self isVersion:previousVersion atLeast:@"2.0.0" andLessThan:@"2.3.0"] && [SUKIdentity userExists]) {
|
||||
[self clearBloomFilterCache];
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[[[OWSDatabaseMigrationRunner alloc] init] runAllOutstandingWithCompletion:completion];
|
||||
});
|
||||
}
|
||||
|
||||
+ (BOOL)isVersion:(NSString *)thisVersionString
|
||||
atLeast:(NSString *)openLowerBoundVersionString
|
||||
andLessThan:(NSString *)closedUpperBoundVersionString
|
||||
{
|
||||
return [self isVersion:thisVersionString atLeast:openLowerBoundVersionString] &&
|
||||
[self isVersion:thisVersionString lessThan:closedUpperBoundVersionString];
|
||||
}
|
||||
|
||||
+ (BOOL)isVersion:(NSString *)thisVersionString atLeast:(NSString *)thatVersionString
|
||||
{
|
||||
return [thisVersionString compare:thatVersionString options:NSNumericSearch] != NSOrderedAscending;
|
||||
}
|
||||
|
||||
+ (BOOL)isVersion:(NSString *)thisVersionString lessThan:(NSString *)thatVersionString
|
||||
{
|
||||
return [thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending;
|
||||
}
|
||||
|
||||
#pragma mark Upgrading to 2.1 - Removing video cache folder
|
||||
|
||||
+ (void)clearVideoCache
|
||||
{
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
|
||||
basePath = [basePath stringByAppendingPathComponent:@"videos"];
|
||||
|
||||
NSError *error;
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:basePath]) {
|
||||
[NSFileManager.defaultManager removeItemAtPath:basePath error:&error];
|
||||
}
|
||||
|
||||
if (error) {
|
||||
OWSLogError(
|
||||
@"An error occured while removing the videos cache folder from old location: %@", error.debugDescription);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Upgrading to 2.3.0
|
||||
|
||||
// We removed bloom filter contact discovery. Clean up any local bloom filter data.
|
||||
+ (void)clearBloomFilterCache
|
||||
{
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSArray *cachesDir = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
NSString *bloomFilterPath = [[cachesDir objectAtIndex:0] stringByAppendingPathComponent:@"bloomfilter"];
|
||||
|
||||
if ([fm fileExistsAtPath:bloomFilterPath]) {
|
||||
NSError *deleteError;
|
||||
if ([fm removeItemAtPath:bloomFilterPath error:&deleteError]) {
|
||||
OWSLogInfo(@"Successfully removed bloom filter cache.");
|
||||
[LKStorage writeSyncWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
||||
[transaction removeAllObjectsInCollection:@"TSRecipient"];
|
||||
}];
|
||||
} else {
|
||||
OWSLogError(@"Failed to remove bloom filter cache with error: %@", deleteError.localizedDescription);
|
||||
}
|
||||
} else {
|
||||
OWSLogDebug(@"No bloom filter cache to remove.");
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
Loading…
Reference in New Issue