diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 483ebef34..853ff8d42 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -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 = ""; }; C374EEF325DB31D40073A857 /* VoiceMessageRecordingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecordingView.swift; sourceTree = ""; }; C379DCF3256735770002D4EB /* VisibleMessage+Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleMessage+Attachment.swift"; sourceTree = ""; }; - C37F53E8255BA9BB002AEA92 /* Environment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Environment.h; sourceTree = ""; }; - C37F5402255BA9ED002AEA92 /* Environment.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Environment.m; sourceTree = ""; }; C38D5E8C2575011E00B6A65C /* MessageSender+ClosedGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MessageSender+ClosedGroups.swift"; sourceTree = ""; }; C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SNProtoEnvelope+Conversion.swift"; sourceTree = ""; }; 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 = ""; }; FD848B9228420164000E298B /* UnicodeScalar+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UnicodeScalar+Utilities.swift"; sourceTree = ""; }; FD848B9728422F1A000E298B /* Date+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Utilities.swift"; sourceTree = ""; }; + FD848B9928442CE6000E298B /* StorageError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageError.swift; sourceTree = ""; }; + FD848B9B284435D7000E298B /* AppSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSetup.swift; sourceTree = ""; }; FD859EFF27C4691300510D0C /* MockDataGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDataGenerator.swift; sourceTree = ""; }; FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = ""; }; FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = ""; }; @@ -1676,7 +1668,7 @@ FDF0B74A28061F7A004C14C5 /* InteractionAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionAttachment.swift; sourceTree = ""; }; FDF0B74E28079E5E004C14C5 /* SendReadReceiptsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendReadReceiptsJob.swift; sourceTree = ""; }; FDF0B7502807BA56004C14C5 /* NotificationsProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsProtocol.swift; sourceTree = ""; }; - FDF0B7542807C4BB004C14C5 /* SSKEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSKEnvironment.swift; sourceTree = ""; }; + FDF0B7542807C4BB004C14C5 /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = ""; }; FDF0B7572807F368004C14C5 /* MessageReceiverError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReceiverError.swift; sourceTree = ""; }; FDF0B7592807F3A3004C14C5 /* MessageSenderError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSenderError.swift; sourceTree = ""; }; FDF0B75B2807F41D004C14C5 /* MessageSender+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MessageSender+Convenience.swift"; sourceTree = ""; }; @@ -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 */, diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index d29acf2f6..9f7cdec20 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -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, diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 97c2c0957..2b2ad8a6f 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -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() ? diff --git a/Session/Meta/AppEnvironment.swift b/Session/Meta/AppEnvironment.swift index f2fa40af4..7e1926745 100644 --- a/Session/Meta/AppEnvironment.swift +++ b/Session/Meta/AppEnvironment.swift @@ -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() diff --git a/Session/Meta/Launch Screen.storyboard b/Session/Meta/Launch Screen.storyboard index 07bbc6333..56a52cb2c 100644 --- a/Session/Meta/Launch Screen.storyboard +++ b/Session/Meta/Launch Screen.storyboard @@ -1,9 +1,10 @@ - + - + + @@ -27,7 +28,7 @@ - + @@ -41,5 +42,8 @@ + + + diff --git a/Session/Shared/LoadingViewController.swift b/Session/Shared/LoadingViewController.swift index c961caad4..8bbe92fba 100644 --- a/Session/Shared/LoadingViewController.swift +++ b/Session/Shared/LoadingViewController.swift @@ -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) } } diff --git a/Session/Shared/OWSScreenLockUI.m b/Session/Shared/OWSScreenLockUI.m index 0e8055ed2..a95a42c80 100644 --- a/Session/Shared/OWSScreenLockUI.m +++ b/Session/Shared/OWSScreenLockUI.m @@ -348,7 +348,7 @@ NS_ASSUME_NONNULL_BEGIN return ScreenLockUIStateNone; } - if (Environment.shared.isRequestingPermission) { + if (SMKEnvironment.shared.isRequestingPermission) { return ScreenLockUIStateNone; } diff --git a/SessionMessagingKit/Database/LegacyDatabase/SMKLegacy.swift b/SessionMessagingKit/Database/LegacyDatabase/SMKLegacy.swift index 06d6b7816..e06e2fade 100644 --- a/SessionMessagingKit/Database/LegacyDatabase/SMKLegacy.swift +++ b/SessionMessagingKit/Database/LegacyDatabase/SMKLegacy.swift @@ -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 } }() ) diff --git a/SessionMessagingKit/Database/Migrations/_001_InitialSetupMigration.swift b/SessionMessagingKit/Database/Migrations/_001_InitialSetupMigration.swift index 70c68de09..e25f9541e 100644 --- a/SessionMessagingKit/Database/Migrations/_001_InitialSetupMigration.swift +++ b/SessionMessagingKit/Database/Migrations/_001_InitialSetupMigration.swift @@ -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 diff --git a/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift b/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift index b0300db4b..55b0bec75 100644 --- a/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift +++ b/SessionMessagingKit/Database/Migrations/_002_SetupStandardJobs.swift @@ -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 diff --git a/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift b/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift index 6f323bb51..7cb7042e5 100644 --- a/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift +++ b/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift @@ -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 diff --git a/SessionMessagingKit/Database/Models/Attachment.swift b/SessionMessagingKit/Database/Models/Attachment.swift index acc93db4d..43700cc79 100644 --- a/SessionMessagingKit/Database/Models/Attachment.swift +++ b/SessionMessagingKit/Database/Models/Attachment.swift @@ -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 } diff --git a/SessionMessagingKit/Database/Models/Profile.swift b/SessionMessagingKit/Database/Models/Profile.swift index 4c3178c74..bd6cbf6d9 100644 --- a/SessionMessagingKit/Database/Models/Profile.swift +++ b/SessionMessagingKit/Database/Models/Profile.swift @@ -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 diff --git a/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift b/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift index fb3b5ccd1..2a42f72a5 100644 --- a/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift +++ b/SessionMessagingKit/Jobs/Types/DisappearingMessagesJob.swift @@ -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) diff --git a/SessionMessagingKit/Jobs/Types/MessageSendJob.swift b/SessionMessagingKit/Jobs/Types/MessageSendJob.swift index dde5fd1fe..51fbe5ef6 100644 --- a/SessionMessagingKit/Jobs/Types/MessageSendJob.swift +++ b/SessionMessagingKit/Jobs/Types/MessageSendJob.swift @@ -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) diff --git a/SessionMessagingKit/Messages/Message+Destination.swift b/SessionMessagingKit/Messages/Message+Destination.swift index c129e83bb..ac0522460 100644 --- a/SessionMessagingKit/Messages/Message+Destination.swift +++ b/SessionMessagingKit/Messages/Message+Destination.swift @@ -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) diff --git a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift index 3b435b178..eeebdeeba 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift @@ -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( diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift b/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift index 3215309a7..1e01e6012 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift @@ -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 diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift b/SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift index 9325c1362..df96dd2fb 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender+Convenience.swift @@ -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 { - 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 { // 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 { diff --git a/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift b/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift index 3170537d3..a49d37007 100644 --- a/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift +++ b/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift @@ -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 } diff --git a/SessionMessagingKit/Utilities/Environment.h b/SessionMessagingKit/Utilities/Environment.h deleted file mode 100644 index 16b2f8004..000000000 --- a/SessionMessagingKit/Utilities/Environment.h +++ /dev/null @@ -1,39 +0,0 @@ -#import - -@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)proximityMonitoringManager - windowManager:(OWSWindowManager *)windowManager; - -@property (nonatomic, readonly) OWSAudioSession *audioSession; -@property (nonatomic, readonly) id 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 diff --git a/SessionMessagingKit/Utilities/Environment.m b/SessionMessagingKit/Utilities/Environment.m deleted file mode 100644 index ee8f5c284..000000000 --- a/SessionMessagingKit/Utilities/Environment.m +++ /dev/null @@ -1,62 +0,0 @@ - -#import -#import "OWSWindowManager.h" -#import -#import "OWSPreferences.h" - -static Environment *sharedEnvironment = nil; - -@interface Environment () - -@property (nonatomic) OWSAudioSession *audioSession; -@property (nonatomic) OWSPreferences *preferences; -@property (nonatomic) id 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)proximityMonitoringManager - windowManager:(OWSWindowManager *)windowManager -{ - self = [super init]; - - if (!self) { - return self; - } - - _audioSession = audioSession; - _preferences = preferences; - _proximityMonitoringManager = proximityMonitoringManager; - _windowManager = windowManager; - _isRequestingPermission = false; - - return self; -} - -@end diff --git a/SessionMessagingKit/Utilities/Environment.swift b/SessionMessagingKit/Utilities/Environment.swift new file mode 100644 index 000000000..6d616b06a --- /dev/null +++ b/SessionMessagingKit/Utilities/Environment.swift @@ -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 = 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 } +} diff --git a/SessionMessagingKit/Utilities/OWSAudioPlayer.m b/SessionMessagingKit/Utilities/OWSAudioPlayer.m index f59af6416..49a272b63 100644 --- a/SessionMessagingKit/Utilities/OWSAudioPlayer.m +++ b/SessionMessagingKit/Utilities/OWSAudioPlayer.m @@ -94,7 +94,7 @@ NS_ASSUME_NONNULL_BEGIN - (OWSAudioSession *)audioSession { - return Environment.shared.audioSession; + return SMKEnvironment.shared.audioSession; } #pragma mark diff --git a/SessionMessagingKit/Utilities/OWSWindowManager.m b/SessionMessagingKit/Utilities/OWSWindowManager.m index 9083d9fb4..c1fa33d4f 100644 --- a/SessionMessagingKit/Utilities/OWSWindowManager.m +++ b/SessionMessagingKit/Utilities/OWSWindowManager.m @@ -4,6 +4,7 @@ #import "OWSWindowManager.h" #import "Environment.h" +#import #import 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 diff --git a/SessionMessagingKit/Utilities/SSKEnvironment.swift b/SessionMessagingKit/Utilities/SSKEnvironment.swift deleted file mode 100644 index 9338ccab1..000000000 --- a/SessionMessagingKit/Utilities/SSKEnvironment.swift +++ /dev/null @@ -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 = 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 - } -} diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index 6c3c60b47..c0df8e76d 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -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() } diff --git a/SessionShareExtension/ShareVC.swift b/SessionShareExtension/ShareVC.swift index d8b30de32..a300e2bbe 100644 --- a/SessionShareExtension/ShareVC.swift +++ b/SessionShareExtension/ShareVC.swift @@ -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) diff --git a/SessionSnodeKit/Database/Migrations/_001_InitialSetupMigration.swift b/SessionSnodeKit/Database/Migrations/_001_InitialSetupMigration.swift index 7ee5082d7..d8b29ae3c 100644 --- a/SessionSnodeKit/Database/Migrations/_001_InitialSetupMigration.swift +++ b/SessionSnodeKit/Database/Migrations/_001_InitialSetupMigration.swift @@ -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 diff --git a/SessionSnodeKit/Database/Migrations/_002_SetupStandardJobs.swift b/SessionSnodeKit/Database/Migrations/_002_SetupStandardJobs.swift index 81fb80437..145cc544b 100644 --- a/SessionSnodeKit/Database/Migrations/_002_SetupStandardJobs.swift +++ b/SessionSnodeKit/Database/Migrations/_002_SetupStandardJobs.swift @@ -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 { diff --git a/SessionSnodeKit/Database/Migrations/_003_YDBToGRDBMigration.swift b/SessionSnodeKit/Database/Migrations/_003_YDBToGRDBMigration.swift index a29fde245..0896fa6a5 100644 --- a/SessionSnodeKit/Database/Migrations/_003_YDBToGRDBMigration.swift +++ b/SessionSnodeKit/Database/Migrations/_003_YDBToGRDBMigration.swift @@ -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 diff --git a/SessionUIKit/Utilities/UIView+Constraints.swift b/SessionUIKit/Utilities/UIView+Constraints.swift index 61e6e4f36..f9b237c64 100644 --- a/SessionUIKit/Utilities/UIView+Constraints.swift +++ b/SessionUIKit/Utilities/UIView+Constraints.swift @@ -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 = { + 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 diff --git a/SessionUtilitiesKit/Database/GRDBStorage.swift b/SessionUtilitiesKit/Database/GRDBStorage.swift index 30bfdddeb..305cdedf4 100644 --- a/SessionUtilitiesKit/Database/GRDBStorage.swift +++ b/SessionUtilitiesKit/Database/GRDBStorage.swift @@ -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..(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(updates: @escaping (Database) throws -> T, completion: @escaping (Database, Swift.Result) 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(_ 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(_ value: (Database) throws -> Promise) -> Promise { + 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(updates: (Database) throws -> Promise) -> Promise { + guard isValid, let dbPool: DatabasePool = dbPool else { return Promise(error: StorageError.databaseInvalid) } + do { return try dbPool.write(updates) } diff --git a/SessionUtilitiesKit/Database/Migrations/_001_InitialSetupMigration.swift b/SessionUtilitiesKit/Database/Migrations/_001_InitialSetupMigration.swift index ec4369845..fd1392956 100644 --- a/SessionUtilitiesKit/Database/Migrations/_001_InitialSetupMigration.swift +++ b/SessionUtilitiesKit/Database/Migrations/_001_InitialSetupMigration.swift @@ -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 diff --git a/SessionUtilitiesKit/Database/Migrations/_002_SetupStandardJobs.swift b/SessionUtilitiesKit/Database/Migrations/_002_SetupStandardJobs.swift index 473c9ff9a..dd8e6b800 100644 --- a/SessionUtilitiesKit/Database/Migrations/_002_SetupStandardJobs.swift +++ b/SessionUtilitiesKit/Database/Migrations/_002_SetupStandardJobs.swift @@ -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 { diff --git a/SessionUtilitiesKit/Database/Migrations/_003_YDBToGRDBMigration.swift b/SessionUtilitiesKit/Database/Migrations/_003_YDBToGRDBMigration.swift index 3f8cc5b32..a5b04d43e 100644 --- a/SessionUtilitiesKit/Database/Migrations/_003_YDBToGRDBMigration.swift +++ b/SessionUtilitiesKit/Database/Migrations/_003_YDBToGRDBMigration.swift @@ -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 { diff --git a/SessionUtilitiesKit/Database/StorageError.swift b/SessionUtilitiesKit/Database/StorageError.swift new file mode 100644 index 000000000..04ad00a98 --- /dev/null +++ b/SessionUtilitiesKit/Database/StorageError.swift @@ -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 +} diff --git a/SessionUtilitiesKit/Database/Types/Migration.swift b/SessionUtilitiesKit/Database/Types/Migration.swift index 493e00d06..69d27e754 100644 --- a/SessionUtilitiesKit/Database/Types/Migration.swift +++ b/SessionUtilitiesKit/Database/Types/Migration.swift @@ -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 } diff --git a/SessionUtilitiesKit/Database/Types/TargetMigrations.swift b/SessionUtilitiesKit/Database/Types/TargetMigrations.swift index 4718d5714..de793d071 100644 --- a/SessionUtilitiesKit/Database/Types/TargetMigrations.swift +++ b/SessionUtilitiesKit/Database/Types/TargetMigrations.swift @@ -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] diff --git a/SignalUtilitiesKit/Configuration.swift b/SignalUtilitiesKit/Configuration.swift index f8382dacc..000a6ef43 100644 --- a/SignalUtilitiesKit/Configuration.swift +++ b/SignalUtilitiesKit/Configuration.swift @@ -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() - ] - ) - } - } } diff --git a/SignalUtilitiesKit/Utilities/AppSetup.h b/SignalUtilitiesKit/Utilities/AppSetup.h deleted file mode 100644 index aa4f587c7..000000000 --- a/SignalUtilitiesKit/Utilities/AppSetup.h +++ /dev/null @@ -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 diff --git a/SignalUtilitiesKit/Utilities/AppSetup.m b/SignalUtilitiesKit/Utilities/AppSetup.m deleted file mode 100644 index 8c2e33da3..000000000 --- a/SignalUtilitiesKit/Utilities/AppSetup.m +++ /dev/null @@ -1,93 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "AppSetup.h" -#import "Environment.h" -#import "VersionMigrations.h" -#import -#import -#import -#import - -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 reachabilityManager = [SSKReachabilityManagerImpl new]; - - OWSAudioSession *audioSession = [OWSAudioSession new]; - id 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 diff --git a/SignalUtilitiesKit/Utilities/AppSetup.swift b/SignalUtilitiesKit/Utilities/AppSetup.swift new file mode 100644 index 000000000..021f2a3c6 --- /dev/null +++ b/SignalUtilitiesKit/Utilities/AppSetup.swift @@ -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 } + } + } + ) + } + } +} diff --git a/SignalUtilitiesKit/Utilities/VersionMigrations.h b/SignalUtilitiesKit/Utilities/VersionMigrations.h deleted file mode 100644 index 932366703..000000000 --- a/SignalUtilitiesKit/Utilities/VersionMigrations.h +++ /dev/null @@ -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 diff --git a/SignalUtilitiesKit/Utilities/VersionMigrations.m b/SignalUtilitiesKit/Utilities/VersionMigrations.m deleted file mode 100644 index f7d7c0a26..000000000 --- a/SignalUtilitiesKit/Utilities/VersionMigrations.m +++ /dev/null @@ -1,126 +0,0 @@ -// -// Copyright (c) 2019 Open Whisper Systems. All rights reserved. -// - -#import "VersionMigrations.h" -#import "OWSDatabaseMigrationRunner.h" -#import -#import -#import -#import -#import - -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