Fixed build errors and cleaned up Dependencies interface further
Fixed a build error Cleaned up the Dependencies interface for UserDefaults and Storage settings Refactored the AppVersion class to be in Swift
This commit is contained in:
parent
7b04a4b888
commit
3abeeffd3d
|
@ -280,7 +280,7 @@
|
|||
C328255225CA64470062D0A7 /* ContextMenuVC+ActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C328255125CA64470062D0A7 /* ContextMenuVC+ActionView.swift */; };
|
||||
C32C598A256D0664003C73A2 /* SNProtoEnvelope+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */; };
|
||||
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */; };
|
||||
C32C5A24256DB7DB003C73A2 /* SNUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */; };
|
||||
C32C5A24256DB7DB003C73A2 /* UserDefaultsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* UserDefaultsInfo.swift */; };
|
||||
C32C5A48256DB8F0003C73A2 /* BuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */; };
|
||||
C32C5A88256DBCF9003C73A2 /* MessageReceiver+LegacyClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32C5A87256DBCF9003C73A2 /* MessageReceiver+LegacyClosedGroups.swift */; };
|
||||
C32C5C3D256DCBAF003C73A2 /* AppReadiness.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB75255A581000E217F9 /* AppReadiness.m */; };
|
||||
|
@ -291,8 +291,6 @@
|
|||
C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAFD255A580600E217F9 /* LRUCache.swift */; };
|
||||
C32C5DDB256DD9FF003C73A2 /* ContentProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB68255A580F00E217F9 /* ContentProxy.swift */; };
|
||||
C32C5E0C256DDAFA003C73A2 /* NSRegularExpression+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */; };
|
||||
C32C600F256E07F5003C73A2 /* NSUserDefaults+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */; };
|
||||
C32C6018256E07F9003C73A2 /* NSUserDefaults+OWS.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33100282559000A00070591 /* UIView+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33100272559000A00070591 /* UIView+Utilities.swift */; };
|
||||
C331FF1F2558F9D300070591 /* SessionUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C331FF1D2558F9D300070591 /* SessionUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C331FF222558F9D300070591 /* SessionUIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C331FF1B2558F9D300070591 /* SessionUIKit.framework */; };
|
||||
|
@ -316,11 +314,9 @@
|
|||
C33FD9C4255A54EF00E217F9 /* SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A59F255385C100C340D1 /* SessionSnodeKit.framework */; };
|
||||
C33FD9C5255A54EF00E217F9 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
|
||||
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */; };
|
||||
C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8B255A57FD00E217F9 /* AppVersion.m */; };
|
||||
C33FDC58255A582000E217F9 /* ReverseDispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */; };
|
||||
C33FDC98255A582000E217F9 /* SwiftSingletons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDADE255A580400E217F9 /* SwiftSingletons.swift */; };
|
||||
C33FDD03255A582000E217F9 /* WeakTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB49255A580C00E217F9 /* WeakTimer.swift */; };
|
||||
C33FDD06255A582000E217F9 /* AppVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB4C255A580D00E217F9 /* AppVersion.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
C33FDD23255A582000E217F9 /* FeatureFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB69255A580F00E217F9 /* FeatureFlags.swift */; };
|
||||
C33FDD3A255A582000E217F9 /* Notification+Loki.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB80255A581100E217F9 /* Notification+Loki.swift */; };
|
||||
C33FDD49255A582000E217F9 /* ParamParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB8F255A581200E217F9 /* ParamParser.swift */; };
|
||||
|
@ -495,6 +491,7 @@
|
|||
FD09C5EC282B8F18000CE219 /* AttachmentError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD09C5EB282B8F17000CE219 /* AttachmentError.swift */; };
|
||||
FD0B77B029B69A65009169BA /* TopBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0B77AF29B69A65009169BA /* TopBannerController.swift */; };
|
||||
FD0B77B229B82B7A009169BA /* ArrayUtilitiesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0B77B129B82B7A009169BA /* ArrayUtilitiesSpec.swift */; };
|
||||
FD0E353C2AB9880B006A81F7 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0E353A2AB98773006A81F7 /* AppVersion.swift */; };
|
||||
FD16AB5B2A1DD7CA0083D849 /* PlaceholderIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A3255B6D93007E1867 /* PlaceholderIcon.swift */; };
|
||||
FD16AB5F2A1DD98F0083D849 /* ProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */; };
|
||||
FD16AB612A1DD9B60083D849 /* ProfilePictureView+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD16AB602A1DD9B60083D849 /* ProfilePictureView+Convenience.swift */; };
|
||||
|
@ -1502,7 +1499,6 @@
|
|||
C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityManager.swift; sourceTree = "<group>"; };
|
||||
C33FDA7A255A57FB00E217F9 /* NSRegularExpression+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+SSK.swift"; sourceTree = "<group>"; };
|
||||
C33FDA87255A57FC00E217F9 /* TypingIndicators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypingIndicators.swift; sourceTree = "<group>"; };
|
||||
C33FDA8B255A57FD00E217F9 /* AppVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppVersion.m; sourceTree = "<group>"; };
|
||||
C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSFileSystem.m; sourceTree = "<group>"; };
|
||||
C33FDA9E255A57FF00E217F9 /* ReverseDispatchQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReverseDispatchQueue.swift; sourceTree = "<group>"; };
|
||||
C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildConfiguration.swift; sourceTree = "<group>"; };
|
||||
|
@ -1523,14 +1519,11 @@
|
|||
C33FDB3F255A580C00E217F9 /* String+SSK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+SSK.swift"; sourceTree = "<group>"; };
|
||||
C33FDB41255A580C00E217F9 /* MIMETypeUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIMETypeUtil.m; sourceTree = "<group>"; };
|
||||
C33FDB49255A580C00E217F9 /* WeakTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakTimer.swift; sourceTree = "<group>"; };
|
||||
C33FDB4C255A580D00E217F9 /* AppVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppVersion.h; sourceTree = "<group>"; };
|
||||
C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+OWS.h"; sourceTree = "<group>"; };
|
||||
C33FDB54255A580D00E217F9 /* DataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSource.h; sourceTree = "<group>"; };
|
||||
C33FDB68255A580F00E217F9 /* ContentProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentProxy.swift; sourceTree = "<group>"; };
|
||||
C33FDB69255A580F00E217F9 /* FeatureFlags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureFlags.swift; sourceTree = "<group>"; };
|
||||
C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SNUserDefaults.swift; sourceTree = "<group>"; };
|
||||
C33FDB6B255A580F00E217F9 /* UserDefaultsInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsInfo.swift; sourceTree = "<group>"; };
|
||||
C33FDB75255A581000E217F9 /* AppReadiness.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppReadiness.m; sourceTree = "<group>"; };
|
||||
C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+OWS.m"; sourceTree = "<group>"; };
|
||||
C33FDB80255A581100E217F9 /* Notification+Loki.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Notification+Loki.swift"; sourceTree = "<group>"; };
|
||||
C33FDB81255A581100E217F9 /* UIImage+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+OWS.m"; sourceTree = "<group>"; };
|
||||
C33FDB85255A581100E217F9 /* AppContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppContext.m; sourceTree = "<group>"; };
|
||||
|
@ -1737,6 +1730,7 @@
|
|||
FD09C5EB282B8F17000CE219 /* AttachmentError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentError.swift; sourceTree = "<group>"; };
|
||||
FD0B77AF29B69A65009169BA /* TopBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBannerController.swift; sourceTree = "<group>"; };
|
||||
FD0B77B129B82B7A009169BA /* ArrayUtilitiesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayUtilitiesSpec.swift; sourceTree = "<group>"; };
|
||||
FD0E353A2AB98773006A81F7 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = "<group>"; };
|
||||
FD16AB602A1DD9B60083D849 /* ProfilePictureView+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfilePictureView+Convenience.swift"; sourceTree = "<group>"; };
|
||||
FD17D79527F3E04600122BE0 /* _001_InitialSetupMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _001_InitialSetupMigration.swift; sourceTree = "<group>"; };
|
||||
FD17D79827F40AB800122BE0 /* _003_YDBToGRDBMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _003_YDBToGRDBMigration.swift; sourceTree = "<group>"; };
|
||||
|
@ -2776,12 +2770,9 @@
|
|||
C352A3762557859C00338F3E /* NSTimer+Proxying.h */,
|
||||
C352A36C2557858D00338F3E /* NSTimer+Proxying.m */,
|
||||
7B1D74AF27C365960030B423 /* Timer+MainThread.swift */,
|
||||
C33FDB51255A580D00E217F9 /* NSUserDefaults+OWS.h */,
|
||||
C33FDB77255A581000E217F9 /* NSUserDefaults+OWS.m */,
|
||||
C33FDB14255A580800E217F9 /* OWSMath.h */,
|
||||
FD705A91278D051200F16121 /* ReusableView.swift */,
|
||||
FD17D7AF27F4225C00122BE0 /* Set+Utilities.swift */,
|
||||
C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */,
|
||||
FD77289D284EF1C50018502F /* Sodium+Utilities.swift */,
|
||||
C33FDB3F255A580C00E217F9 /* String+SSK.swift */,
|
||||
C3C2AC2D2553CBEB00C340D1 /* String+Trimming.swift */,
|
||||
|
@ -3543,8 +3534,6 @@
|
|||
C38EF307255B6DBE007E1867 /* UIGestureRecognizer+OWS.swift */,
|
||||
C33FDBD3255A581800E217F9 /* OWSSignalAddress.swift */,
|
||||
C33FDA6F255A57FA00E217F9 /* ReachabilityManager.swift */,
|
||||
C33FDB4C255A580D00E217F9 /* AppVersion.h */,
|
||||
C33FDA8B255A57FD00E217F9 /* AppVersion.m */,
|
||||
C38EF3E4255B6DF4007E1867 /* CommonStrings.swift */,
|
||||
C38EF304255B6DBE007E1867 /* ImageCache.swift */,
|
||||
C38EF2F2255B6DBC007E1867 /* Searcher.swift */,
|
||||
|
@ -3714,6 +3703,7 @@
|
|||
FD09796527F6B0A800936362 /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FD0E353A2AB98773006A81F7 /* AppVersion.swift */,
|
||||
FDFBB74A2A1EFF4900CA7350 /* Bencode.swift */,
|
||||
FD97B23F2A3FEB050027DD57 /* ARC4RandomNumberGenerator.swift */,
|
||||
FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */,
|
||||
|
@ -4531,6 +4521,7 @@
|
|||
FD23CE2F2A67B8820000B97C /* CacheInfo.swift */,
|
||||
FDC6D75F2862B3F600B04575 /* Dependencies.swift */,
|
||||
FDF01FAC2A9ECC4200CAF969 /* SingletonInfo.swift */,
|
||||
C33FDB6B255A580F00E217F9 /* UserDefaultsInfo.swift */,
|
||||
);
|
||||
path = "Dependency Injection";
|
||||
sourceTree = "<group>";
|
||||
|
@ -4687,7 +4678,6 @@
|
|||
files = (
|
||||
C38EF35E255B6DCC007E1867 /* OWSViewController.h in Headers */,
|
||||
C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */,
|
||||
C33FDD06255A582000E217F9 /* AppVersion.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -4713,7 +4703,6 @@
|
|||
C352A3772557864000338F3E /* NSTimer+Proxying.h in Headers */,
|
||||
FD30036A2A3ADEC100B5A5FB /* CExceptionHelper.h in Headers */,
|
||||
C3C2A67D255388CC00C340D1 /* SessionUtilitiesKit.h in Headers */,
|
||||
C32C6018256E07F9003C73A2 /* NSUserDefaults+OWS.h in Headers */,
|
||||
B8856D8D256F1502001CE70E /* UIView+OWS.h in Headers */,
|
||||
FD52090128AF61BA006098F6 /* OWSBackgroundTask.h in Headers */,
|
||||
);
|
||||
|
@ -5828,7 +5817,6 @@
|
|||
C33FDC29255A581F00E217F9 /* ReachabilityManager.swift in Sources */,
|
||||
C38EF407255B6DF7007E1867 /* Toast.swift in Sources */,
|
||||
C38EF38C255B6DD2007E1867 /* ApprovalRailCellView.swift in Sources */,
|
||||
C33FDC45255A581F00E217F9 /* AppVersion.m in Sources */,
|
||||
C38EF3C7255B6DE7007E1867 /* ImageEditorCanvasView.swift in Sources */,
|
||||
C38EF400255B6DF7007E1867 /* GalleryRailView.swift in Sources */,
|
||||
C38EF32E255B6DBF007E1867 /* ImageCache.swift in Sources */,
|
||||
|
@ -5973,6 +5961,7 @@
|
|||
FD7115FE28C8202D00B47552 /* ReplaySubject.swift in Sources */,
|
||||
C32C5DC9256DD935003C73A2 /* ProxiedContentDownloader.swift in Sources */,
|
||||
C3D9E4C02567767F0040E4F3 /* DataSource.m in Sources */,
|
||||
FD0E353C2AB9880B006A81F7 /* AppVersion.swift in Sources */,
|
||||
C32C5DD2256DD9E5003C73A2 /* LRUCache.swift in Sources */,
|
||||
FD71160228C8255900B47552 /* UIControl+Combine.swift in Sources */,
|
||||
FDFBB74B2A1EFF4900CA7350 /* Bencode.swift in Sources */,
|
||||
|
@ -5988,7 +5977,6 @@
|
|||
FD30036E2A3AE26000B5A5FB /* CExceptionHelper.mm in Sources */,
|
||||
FD9AECA72AAAF5B0009B3406 /* Crypto+SessionUtilitiesKit.swift in Sources */,
|
||||
C3D9E4DA256778410040E4F3 /* UIImage+OWS.m in Sources */,
|
||||
C32C600F256E07F5003C73A2 /* NSUserDefaults+OWS.m in Sources */,
|
||||
FDE658A329418E2F00A33BC1 /* KeyPair.swift in Sources */,
|
||||
FD5931AB2A8DCB0A0040147D /* ScopeAdapter+Utilities.swift in Sources */,
|
||||
FD37E9FF28A5F2CD003AE748 /* Configuration.swift in Sources */,
|
||||
|
@ -6029,7 +6017,7 @@
|
|||
FD17D7A127F40D2500122BE0 /* Storage.swift in Sources */,
|
||||
FD1A94FB2900D1C2000D73D3 /* PersistableRecord+Utilities.swift in Sources */,
|
||||
FD5D201E27B0D87C00FEA984 /* SessionId.swift in Sources */,
|
||||
C32C5A24256DB7DB003C73A2 /* SNUserDefaults.swift in Sources */,
|
||||
C32C5A24256DB7DB003C73A2 /* UserDefaultsInfo.swift in Sources */,
|
||||
FD8ECF922938552800C0D1BB /* Threading.swift in Sources */,
|
||||
B8856D7B256F14F4001CE70E /* UIView+OWS.m in Sources */,
|
||||
FDF22211281B5E0B000A4995 /* TableRecord+Utilities.swift in Sources */,
|
||||
|
|
|
@ -91,10 +91,13 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
|||
)
|
||||
}
|
||||
|
||||
public func reportOutgoingCall(_ call: SessionCall) {
|
||||
public func reportOutgoingCall(
|
||||
_ call: SessionCall,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
AssertIsOnMainThread()
|
||||
UserDefaults.sharedLokiProject?[.isCallOngoing] = true
|
||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = Date()
|
||||
dependencies[defaults: .appGroup, key: .isCallOngoing] = true
|
||||
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = Date()
|
||||
|
||||
call.stateDidChange = {
|
||||
if call.hasStartedConnecting {
|
||||
|
@ -107,7 +110,12 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
public func reportIncomingCall(_ call: SessionCall, callerName: String, completion: @escaping (Error?) -> Void) {
|
||||
public func reportIncomingCall(
|
||||
_ call: SessionCall,
|
||||
callerName: String,
|
||||
using dependencies: Dependencies = Dependencies(),
|
||||
completion: @escaping (Error?) -> Void
|
||||
) {
|
||||
let provider = provider ?? Self.sharedProvider(useSystemCallLog: false)
|
||||
// Construct a CXCallUpdate describing the incoming call, including the caller.
|
||||
let update = CXCallUpdate()
|
||||
|
@ -124,8 +132,8 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
|||
completion(error)
|
||||
return
|
||||
}
|
||||
UserDefaults.sharedLokiProject?[.isCallOngoing] = true
|
||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = Date()
|
||||
dependencies[defaults: .appGroup, key: .isCallOngoing] = true
|
||||
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = Date()
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
|
@ -143,8 +151,8 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
|||
|
||||
func handleCallEnded() {
|
||||
WebRTCSession.current = nil
|
||||
UserDefaults.sharedLokiProject?[.isCallOngoing] = false
|
||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = nil
|
||||
dependencies[defaults: .appGroup, key: .isCallOngoing] = false
|
||||
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = nil
|
||||
|
||||
if CurrentAppContext().isInBackground() {
|
||||
(UIApplication.shared.delegate as? AppDelegate)?.stopPollers()
|
||||
|
|
|
@ -77,7 +77,7 @@ extension ConversationVC:
|
|||
@objc func startCall(_ sender: Any?) {
|
||||
guard SessionCall.isEnabled else { return }
|
||||
guard viewModel.threadData.threadIsBlocked == false else { return }
|
||||
guard Dependencies()[singleton: .storage][.areCallsEnabled] else {
|
||||
guard Dependencies()[singleton: .storage, key: .areCallsEnabled] else {
|
||||
let confirmationModal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "modal_call_permission_request_title".localized(),
|
||||
|
@ -240,7 +240,7 @@ extension ConversationVC:
|
|||
// MARK: - ExpandingAttachmentsButtonDelegate
|
||||
|
||||
func handleGIFButtonTapped() {
|
||||
guard Dependencies()[singleton: .storage][.isGiphyEnabled] else {
|
||||
guard Dependencies()[singleton: .storage, key: .isGiphyEnabled] else {
|
||||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
info: ConfirmationModal.Info(
|
||||
title: "GIPHY_PERMISSION_TITLE".localized(),
|
||||
|
@ -624,7 +624,7 @@ extension ConversationVC:
|
|||
}
|
||||
|
||||
func handleMessageSent() {
|
||||
if Dependencies()[singleton: .storage][.playNotificationSoundInForeground] {
|
||||
if Dependencies()[singleton: .storage, key: .playNotificationSoundInForeground] {
|
||||
let soundID = Preferences.Sound.systemSoundId(for: .messageSent, quiet: true)
|
||||
AudioServicesPlaySystemSound(soundID)
|
||||
}
|
||||
|
|
|
@ -1044,7 +1044,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
|
|||
.firstIndex(where: { $0.id == interactionId }),
|
||||
currentIndex < (messageSection.elements.count - 1),
|
||||
messageSection.elements[currentIndex + 1].cellType == .audio,
|
||||
dependencies[singleton: .storage][.shouldAutoPlayConsecutiveAudioMessages] == true
|
||||
dependencies[singleton: .storage, key: .shouldAutoPlayConsecutiveAudioMessages]
|
||||
else { return }
|
||||
|
||||
let nextItem: MessageViewModel = messageSection.elements[currentIndex + 1]
|
||||
|
|
|
@ -291,15 +291,15 @@ final class InputView: UIView, InputViewButtonDelegate, InputTextViewDelegate, M
|
|||
// Suggest that the user enable link previews if they haven't already and we haven't
|
||||
// told them about link previews yet
|
||||
let text = inputTextView.text!
|
||||
let areLinkPreviewsEnabled: Bool = dependencies[singleton: .storage][.areLinkPreviewsEnabled]
|
||||
let areLinkPreviewsEnabled: Bool = dependencies[singleton: .storage, key: .areLinkPreviewsEnabled]
|
||||
|
||||
if
|
||||
!LinkPreview.allPreviewUrls(forMessageBodyText: text).isEmpty &&
|
||||
!areLinkPreviewsEnabled &&
|
||||
!UserDefaults.standard[.hasSeenLinkPreviewSuggestion]
|
||||
!dependencies[defaults: .standard, key: .hasSeenLinkPreviewSuggestion]
|
||||
{
|
||||
delegate?.showLinkPreviewSuggestionModal()
|
||||
UserDefaults.standard[.hasSeenLinkPreviewSuggestion] = true
|
||||
dependencies[defaults: .standard, key: .hasSeenLinkPreviewSuggestion] = true
|
||||
return
|
||||
}
|
||||
// Check that link previews are enabled
|
||||
|
|
|
@ -141,7 +141,7 @@ final class CallMessageCell: MessageCell {
|
|||
|
||||
let shouldShowInfoIcon: Bool = (
|
||||
messageInfo.state == .permissionDenied &&
|
||||
!Dependencies()[singleton: .storage][.areCallsEnabled]
|
||||
!Dependencies()[singleton: .storage, key: .areCallsEnabled]
|
||||
)
|
||||
infoImageViewWidthConstraint.constant = (shouldShowInfoIcon ? CallMessageCell.iconSize : 0)
|
||||
infoImageViewHeightConstraint.constant = (shouldShowInfoIcon ? CallMessageCell.iconSize : 0)
|
||||
|
@ -177,7 +177,7 @@ final class CallMessageCell: MessageCell {
|
|||
else { return }
|
||||
|
||||
// Should only be tappable if the info icon is visible
|
||||
guard messageInfo.state == .permissionDenied && !Dependencies()[singleton: .storage][.areCallsEnabled] else { return }
|
||||
guard messageInfo.state == .permissionDenied && !Dependencies()[singleton: .storage, key: .areCallsEnabled] else { return }
|
||||
|
||||
self.delegate?.handleItemTapped(cellViewModel, gestureRecognizer: gestureRecognizer)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
// Called via the OS so create a default 'Dependencies' instance
|
||||
let dependencies: Dependencies = Dependencies()
|
||||
Cryptography.seedRandom()
|
||||
AppVersion.sharedInstance()
|
||||
AppVersion.configure(using: dependencies)
|
||||
AppEnvironment.shared.pushRegistrationManager.createVoipRegistryIfNecessary()
|
||||
|
||||
// Prevent the device from sleeping during database view async registration
|
||||
|
@ -99,8 +99,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
)
|
||||
|
||||
if Environment.shared?.callManager.wrappedValue?.currentCall == nil {
|
||||
UserDefaults.sharedLokiProject?[.isCallOngoing] = false
|
||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = nil
|
||||
dependencies[defaults: .appGroup, key: .isCallOngoing] = false
|
||||
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = nil
|
||||
}
|
||||
|
||||
// No point continuing if we are running tests
|
||||
|
@ -127,7 +127,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(showMissedCallTipsIfNeeded(_:)),
|
||||
selector: #selector(showMissedCallTipsIfNeededNotification(_:)),
|
||||
name: .missedCall,
|
||||
object: nil
|
||||
)
|
||||
|
@ -236,7 +236,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
|
||||
// Called via the OS so create a default 'Dependencies' instance
|
||||
let dependencies: Dependencies = Dependencies()
|
||||
UserDefaults.sharedLokiProject?[.isMainAppActive] = true
|
||||
dependencies[defaults: .appGroup, key: .isMainAppActive] = true
|
||||
|
||||
ensureRootViewController(calledFrom: .didBecomeActive, using: dependencies)
|
||||
|
||||
|
@ -259,9 +259,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
clearAllNotificationsAndRestoreBadgeCount()
|
||||
// Called via the OS so create a default 'Dependencies' instance
|
||||
let dependencies: Dependencies = Dependencies()
|
||||
|
||||
UserDefaults.sharedLokiProject?[.isMainAppActive] = false
|
||||
clearAllNotificationsAndRestoreBadgeCount(using: dependencies)
|
||||
|
||||
dependencies[defaults: .appGroup, key: .isMainAppActive] = false
|
||||
|
||||
DDLog.flushLog()
|
||||
}
|
||||
|
@ -374,7 +377,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
DeviceSleepManager.sharedInstance.removeBlock(blockObject: self)
|
||||
|
||||
/// App launch hasn't really completed until the main screen is loaded so wait until then to register it
|
||||
AppVersion.sharedInstance().mainAppLaunchDidComplete()
|
||||
AppVersion.shared.mainAppLaunchDidComplete(using: dependencies)
|
||||
|
||||
/// App won't be ready for extensions and no need to enqueue a config sync unless we successfully completed startup
|
||||
dependencies[singleton: .storage].writeAsync { db in
|
||||
|
@ -387,13 +390,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
db[.isReadyForAppExtensions] = true
|
||||
|
||||
if Identity.userCompletedRequiredOnboarding(db) {
|
||||
let appVersion: AppVersion = AppVersion.sharedInstance()
|
||||
|
||||
// If the device needs to sync config or the user updated to a new version
|
||||
if
|
||||
needsConfigSync || (
|
||||
(appVersion.lastAppVersion?.count ?? 0) > 0 &&
|
||||
appVersion.lastAppVersion != appVersion.currentAppVersion
|
||||
(AppVersion.shared.lastAppVersion?.count ?? 0) > 0 &&
|
||||
AppVersion.shared.lastAppVersion != AppVersion.shared.currentAppVersion
|
||||
)
|
||||
{
|
||||
ConfigurationSyncJob.enqueue(db, publicKey: getUserHexEncodedPublicKey(db))
|
||||
|
@ -539,7 +540,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
/// called again - this can result in odd behaviours so hold off on running this logic until it's properly called again
|
||||
guard
|
||||
Identity.userExists(using: dependencies) &&
|
||||
UserDefaults.sharedLokiProject?[.isMainAppActive] == true
|
||||
dependencies[defaults: .appGroup, key: .isMainAppActive] == true
|
||||
else { return }
|
||||
|
||||
enableBackgroundRefreshIfNecessary()
|
||||
|
@ -589,7 +590,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
let presentedViewController: UIViewController? = self?.window?.rootViewController?.presentedViewController
|
||||
let targetRootViewController: UIViewController = TopBannerController(
|
||||
child: StyledNavigationController(rootViewController: rootViewController),
|
||||
cachedWarning: UserDefaults.sharedLokiProject?[.topBannerWarningToShow]
|
||||
cachedWarning: dependencies[defaults: .appGroup, key: .topBannerWarningToShow]
|
||||
.map { rawValue in TopBannerController.Warning(rawValue: rawValue) }
|
||||
)
|
||||
|
||||
|
@ -687,7 +688,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
#endif
|
||||
}
|
||||
|
||||
private func clearAllNotificationsAndRestoreBadgeCount(using dependencies: Dependencies = Dependencies()) {
|
||||
private func clearAllNotificationsAndRestoreBadgeCount(using dependencies: Dependencies) {
|
||||
AppReadiness.runNowOrWhenAppDidBecomeReady {
|
||||
AppEnvironment.shared.notificationPresenter.clearAllNotifications()
|
||||
|
||||
|
@ -760,11 +761,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
handleActivation()
|
||||
}
|
||||
|
||||
@objc public func showMissedCallTipsIfNeeded(_ notification: Notification) {
|
||||
guard !UserDefaults.standard[.hasSeenCallMissedTips] else { return }
|
||||
@objc public func showMissedCallTipsIfNeededNotification(_ notification: Notification) {
|
||||
showMissedCallTipsIfNeeded(notification)
|
||||
}
|
||||
|
||||
private func showMissedCallTipsIfNeeded(
|
||||
_ notification: Notification,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
guard !dependencies[defaults: .standard, key: .hasSeenCallMissedTips] else { return }
|
||||
guard Thread.isMainThread else {
|
||||
DispatchQueue.main.async {
|
||||
self.showMissedCallTipsIfNeeded(notification)
|
||||
self.showMissedCallTipsIfNeeded(notification, using: dependencies)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -778,7 +786,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
)
|
||||
presentingVC.present(callMissedTipsModal, animated: true, completion: nil)
|
||||
|
||||
UserDefaults.standard[.hasSeenCallMissedTips] = true
|
||||
dependencies[defaults: .standard, key: .hasSeenCallMissedTips] = true
|
||||
}
|
||||
|
||||
// MARK: - Polling
|
||||
|
|
|
@ -239,10 +239,4 @@ final class MainAppContext: NSObject, AppContext {
|
|||
|
||||
return (targetPath ?? "")
|
||||
}
|
||||
|
||||
func appUserDefaults() -> UserDefaults {
|
||||
owsAssertDebug(UserDefaults.sharedLokiProject != nil)
|
||||
|
||||
return (UserDefaults.sharedLokiProject ?? UserDefaults.standard)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -512,7 +512,7 @@ public class NotificationPresenter: NotificationsProtocol {
|
|||
|
||||
private func checkIfShouldPlaySound(applicationState: UIApplication.State) -> Bool {
|
||||
guard applicationState == .active else { return true }
|
||||
guard Dependencies()[singleton: .storage][.playNotificationSoundInForeground] else { return false }
|
||||
guard Dependencies()[singleton: .storage, key: .playNotificationSoundInForeground] else { return false }
|
||||
|
||||
let nowMs: UInt64 = UInt64(floor(Date().timeIntervalSince1970 * 1000))
|
||||
let recentThreshold = nowMs - UInt64(kAudioNotificationsThrottleInterval * Double(kSecondInMs))
|
||||
|
|
|
@ -23,7 +23,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
// Don't run when inactive or not in main app or if the user doesn't exist yet
|
||||
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
|
||||
guard dependencies[defaults: .appGroup, key: .isMainAppActive] else {
|
||||
return deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
||||
}
|
||||
guard Identity.userCompletedRequiredOnboarding() else {
|
||||
|
@ -32,12 +32,12 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
}
|
||||
|
||||
// Determine if the device has 'Fast Mode' (APNS) enabled
|
||||
let isUsingFullAPNs: Bool = UserDefaults.standard[.isUsingFullAPNs]
|
||||
let isUsingFullAPNs: Bool = dependencies[defaults: .standard, key: .isUsingFullAPNs]
|
||||
|
||||
// If the job is running and 'Fast Mode' is disabled then we should try to unregister the existing
|
||||
// token
|
||||
guard isUsingFullAPNs else {
|
||||
Just(dependencies[singleton: .storage][.lastRecordedPushToken])
|
||||
Just(dependencies[singleton: .storage, key: .lastRecordedPushToken])
|
||||
.setFailureType(to: Error.self)
|
||||
.flatMap { lastRecordedPushToken -> AnyPublisher<Void, Error> in
|
||||
// Tell the device to unregister for remote notifications (essentially try to invalidate
|
||||
|
@ -92,7 +92,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
/// • We want to force an update
|
||||
let timeSinceLastSubscription: TimeInterval = dependencies.dateNow
|
||||
.timeIntervalSince(
|
||||
dependencies.standardUserDefaults[.lastPushNotificationSync]
|
||||
dependencies[defaults: .standard, key: .lastPushNotificationSync]
|
||||
.defaulting(to: Date.distantPast)
|
||||
)
|
||||
let uploadOnlyIfStale: Bool? = {
|
||||
|
@ -106,7 +106,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
|
||||
guard
|
||||
timeSinceLastSubscription >= SyncPushTokensJob.maxFrequency ||
|
||||
dependencies.storage[.lastRecordedPushToken] != pushToken ||
|
||||
dependencies[singleton: .storage, key: .lastRecordedPushToken] != pushToken ||
|
||||
uploadOnlyIfStale == false
|
||||
else {
|
||||
SNLog("[SyncPushTokensJob] OS subscription completed, skipping server subscription due to frequency")
|
||||
|
@ -131,7 +131,7 @@ public enum SyncPushTokensJob: JobExecutor {
|
|||
case .finished:
|
||||
Logger.warn("Recording push tokens locally. pushToken: \(redact(pushToken)), voipToken: \(redact(voipToken))")
|
||||
SNLog("[SyncPushTokensJob] Completed")
|
||||
dependencies[singleton: .standardUserDefaults][.lastPushNotificationSync] = dependencies.dateNow
|
||||
dependencies[defaults: .standard, key: .lastPushNotificationSync] = dependencies.dateNow
|
||||
|
||||
dependencies[singleton: .storage].write(using: dependencies) { db in
|
||||
db[.lastRecordedPushToken] = pushToken
|
||||
|
|
|
@ -102,7 +102,7 @@ enum Onboarding {
|
|||
profileNameRetrievalIdentifier.mutate { $0 = nil }
|
||||
profileNameRetrievalPublisher.mutate { $0 = nil }
|
||||
|
||||
dependencies[singleton: .standardUserDefaults][.hasSyncedInitialConfiguration] = false
|
||||
dependencies[defaults: .standard, key: .hasSyncedInitialConfiguration] = false
|
||||
}
|
||||
|
||||
func preregister(
|
||||
|
@ -164,7 +164,7 @@ enum Onboarding {
|
|||
// home screen a configuration sync is triggered (yes, the logic is a
|
||||
// bit weird). This is needed so that if the user registers and
|
||||
// immediately links a device, there'll be a configuration in their swarm.
|
||||
dependencies[singleton: .standardUserDefaults][.hasSyncedInitialConfiguration] = (self == .register)
|
||||
dependencies[defaults: .standard, key: .hasSyncedInitialConfiguration] = (self == .register)
|
||||
|
||||
// Only continue if this isn't a new account
|
||||
guard self != .register else { return }
|
||||
|
|
|
@ -85,7 +85,7 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
|
|||
let registerButton = SessionButton(style: .filled, size: .large)
|
||||
registerButton.accessibilityLabel = "Continue with settings"
|
||||
registerButton.setTitle("continue_2".localized(), for: .normal)
|
||||
registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside)
|
||||
registerButton.addTarget(self, action: #selector(registerTapped), for: UIControl.Event.touchUpInside)
|
||||
|
||||
// Set up register button container
|
||||
let registerButtonContainer = UIView(wrapping: registerButton, withInsets: UIEdgeInsets(top: 0, leading: Values.massiveSpacing, bottom: 0, trailing: Values.massiveSpacing), shouldAdaptForIPadWithWidth: Values.iPadButtonWidth)
|
||||
|
@ -128,7 +128,9 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
|
|||
optionViews.filter { $0 != optionView }.forEach { $0.isSelected = false }
|
||||
}
|
||||
|
||||
@objc private func register() {
|
||||
@objc private func registerTapped() { register() }
|
||||
|
||||
private func register(using dependencies: Dependencies = Dependencies()) {
|
||||
guard selectedOptionView != nil else {
|
||||
let modal: ConfirmationModal = ConfirmationModal(
|
||||
targetView: self.view,
|
||||
|
@ -141,7 +143,7 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
|
|||
self.present(modal, animated: true)
|
||||
return
|
||||
}
|
||||
UserDefaults.standard[.isUsingFullAPNs] = (selectedOptionView == apnsOptionView)
|
||||
dependencies[defaults: .standard, key: .isUsingFullAPNs] = (selectedOptionView == apnsOptionView)
|
||||
|
||||
// If we are registering then we can just continue on
|
||||
guard flow != .register else {
|
||||
|
@ -155,14 +157,13 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
|
|||
|
||||
// Check if we already have a profile name (ie. profile retrieval completed while waiting on
|
||||
// this screen)
|
||||
let existingProfileName: String? = Dependencies()[singleton: .storage]
|
||||
.read { db in
|
||||
try Profile
|
||||
.filter(id: getUserHexEncodedPublicKey(db))
|
||||
.select(.name)
|
||||
.asRequest(of: String.self)
|
||||
.fetchOne(db)
|
||||
}
|
||||
let existingProfileName: String? = dependencies[singleton: .storage].read { db in
|
||||
try Profile
|
||||
.filter(id: getUserHexEncodedPublicKey(db))
|
||||
.select(.name)
|
||||
.asRequest(of: String.self)
|
||||
.fetchOne(db)
|
||||
}
|
||||
|
||||
guard existingProfileName?.isEmpty != false else {
|
||||
// If we have one then we can go straight to the home screen
|
||||
|
|
|
@ -86,9 +86,9 @@ class NotificationSettingsViewModel: SessionTableViewModel<NoNav, NotificationSe
|
|||
.handleEvents(didFail: { SNLog("[NotificationSettingsViewModel] Observation failed with error: \($0)") })
|
||||
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
|
||||
.manualRefreshFrom(forcedRefresh)
|
||||
.map { dbState -> State in
|
||||
.map { [dependencies] dbState -> State in
|
||||
State(
|
||||
isUsingFullAPNs: UserDefaults.standard[.isUsingFullAPNs],
|
||||
isUsingFullAPNs: dependencies[defaults: .standard, key: .isUsingFullAPNs],
|
||||
notificationSound: dbState.notificationSound,
|
||||
playNotificationSoundInForeground: dbState.playNotificationSoundInForeground,
|
||||
previewType: dbState.previewType
|
||||
|
|
|
@ -262,8 +262,8 @@ final class NukeDataModal: Modal {
|
|||
|
||||
private func deleteAllLocalData(using dependencies: Dependencies = Dependencies()) {
|
||||
// Unregister push notifications if needed
|
||||
let isUsingFullAPNs: Bool = UserDefaults.standard[.isUsingFullAPNs]
|
||||
let maybeDeviceToken: String? = UserDefaults.standard[.deviceToken]
|
||||
let isUsingFullAPNs: Bool = dependencies[defaults: .standard, key: .isUsingFullAPNs]
|
||||
let maybeDeviceToken: String? = dependencies[defaults: .standard, key: .deviceToken]
|
||||
|
||||
if isUsingFullAPNs, let deviceToken: String = maybeDeviceToken {
|
||||
PushNotificationAPI
|
||||
|
@ -298,11 +298,11 @@ final class NukeDataModal: Modal {
|
|||
|
||||
// Call through to the SessionApp's "resetAppData" which will wipe out logs, database and
|
||||
// profile storage
|
||||
let wasUnlinked: Bool = UserDefaults.standard[.wasUnlinked]
|
||||
let wasUnlinked: Bool = dependencies[defaults: .standard, key: .wasUnlinked]
|
||||
|
||||
SessionApp.resetAppData {
|
||||
// Resetting the data clears the old user defaults. We need to restore the unlink default.
|
||||
UserDefaults.standard[.wasUnlinked] = wasUnlinked
|
||||
dependencies[defaults: .standard, key: .wasUnlinked] = wasUnlinked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ class ScreenLockUI {
|
|||
// until the app is ready.
|
||||
AppReadiness.runNowOrWhenAppWillBecomeReady { [weak self] in
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
self?.isScreenLockLocked.mutate { $0 = Dependencies()[singleton: .storage][.isScreenLockEnabled] }
|
||||
self?.isScreenLockLocked.mutate { $0 = Dependencies()[singleton: .storage, key: .isScreenLockEnabled] }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self?.ensureUI()
|
||||
|
@ -189,7 +189,7 @@ class ScreenLockUI {
|
|||
Logger.verbose("tryToActivateScreenLockUponBecomingActive NO 0")
|
||||
return
|
||||
}
|
||||
guard Dependencies()[singleton: .storage][.isScreenLockEnabled] else {
|
||||
guard Dependencies()[singleton: .storage, key: .isScreenLockEnabled] else {
|
||||
// Screen lock is not enabled.
|
||||
Logger.verbose("tryToActivateScreenLockUponBecomingActive NO 1")
|
||||
return;
|
||||
|
@ -378,7 +378,7 @@ class ScreenLockUI {
|
|||
}
|
||||
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
self.isScreenLockLocked.mutate { $0 = Dependencies()[singleton: .storage][.isScreenLockEnabled] }
|
||||
self.isScreenLockLocked.mutate { $0 = Dependencies()[singleton: .storage, key: .isScreenLockEnabled] }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
// NOTE: this notifications fires _before_ applicationDidBecomeActive,
|
||||
|
|
|
@ -58,7 +58,7 @@ public struct ConfigDump: Codable, Equatable, Hashable, FetchableRecord, Persist
|
|||
|
||||
// MARK: - Convenience
|
||||
|
||||
public extension ConfigDump.Variant, CustomStringConvertible {
|
||||
public extension ConfigDump.Variant {
|
||||
static let userVariants: Set<ConfigDump.Variant> = [
|
||||
.userProfile, .contacts, .convoInfoVolatile, .userGroups
|
||||
]
|
||||
|
@ -132,7 +132,9 @@ public extension ConfigDump.Variant, CustomStringConvertible {
|
|||
default: return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ConfigDump.Variant: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .userProfile: return "userProfile"
|
||||
|
|
|
@ -216,7 +216,7 @@ public extension LinkPreview {
|
|||
selectedRange: NSRange? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> String? {
|
||||
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else { return nil }
|
||||
guard dependencies[singleton: .storage, key: .areLinkPreviewsEnabled] else { return nil }
|
||||
guard let body: String = body else { return nil }
|
||||
|
||||
if let cachedUrl = previewUrlCache.wrappedValue.object(forKey: body as NSString) as String? {
|
||||
|
@ -297,7 +297,7 @@ public extension LinkPreview {
|
|||
|
||||
// Exit early if link previews are not enabled in order to avoid
|
||||
// tainting the cache.
|
||||
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else { return }
|
||||
guard dependencies[singleton: .storage, key: .areLinkPreviewsEnabled] else { return }
|
||||
|
||||
serialQueue.sync {
|
||||
linkPreviewDraftCache = linkPreviewDraft
|
||||
|
@ -308,7 +308,7 @@ public extension LinkPreview {
|
|||
previewUrl: String?,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<LinkPreviewDraft, Error> {
|
||||
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else {
|
||||
guard dependencies[singleton: .storage, key: .areLinkPreviewsEnabled] else {
|
||||
return Fail(error: LinkPreviewError.featureDisabled)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
|
|
@ -49,9 +49,12 @@ public enum DisappearingMessagesJob: JobExecutor {
|
|||
// MARK: - Convenience
|
||||
|
||||
public extension DisappearingMessagesJob {
|
||||
@discardableResult static func updateNextRunIfNeeded(_ db: Database) -> Job? {
|
||||
@discardableResult static func updateNextRunIfNeeded(
|
||||
_ db: Database,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Job? {
|
||||
// Don't run when inactive or not in main app
|
||||
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else { return nil }
|
||||
guard dependencies[defaults: .appGroup, key: .isMainAppActive] else { return nil }
|
||||
|
||||
// If there is another expiring message then update the job to run 1 second after it's meant to expire
|
||||
let nextExpirationTimestampMs: Double? = try? Interaction
|
||||
|
|
|
@ -40,7 +40,7 @@ public enum GarbageCollectionJob: JobExecutor {
|
|||
/// app at about the same time every day will trigger the garbage collection) - since this runs when the app becomes active we
|
||||
/// want to prevent it running to frequently (the app becomes active if a system alert, the notification center or the control panel
|
||||
/// are shown)
|
||||
let lastGarbageCollection: Date = dependencies[singleton: .standardUserDefaults][.lastGarbageCollection]
|
||||
let lastGarbageCollection: Date = dependencies[defaults: .standard, key: .lastGarbageCollection]
|
||||
.defaulting(to: Date.distantPast)
|
||||
let finalTypesToCollect: Set<Types> = {
|
||||
guard
|
||||
|
@ -461,7 +461,7 @@ public enum GarbageCollectionJob: JobExecutor {
|
|||
// If we did a full collection then update the 'lastGarbageCollection' date to
|
||||
// prevent a full collection from running again in the next 23 hours
|
||||
if job.behaviour == .recurringOnActive && dependencies.dateNow.timeIntervalSince(lastGarbageCollection) > (23 * 60 * 60) {
|
||||
dependencies[singleton: .standardUserDefaults][.lastGarbageCollection] = dependencies.dateNow
|
||||
dependencies[defaults: .standard, key: .lastGarbageCollection] = dependencies.dateNow
|
||||
}
|
||||
|
||||
success(job, false, dependencies)
|
||||
|
|
|
@ -18,7 +18,7 @@ public enum RetrieveDefaultOpenGroupRoomsJob: JobExecutor {
|
|||
using dependencies: Dependencies
|
||||
) {
|
||||
// Don't run when inactive or not in main app
|
||||
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
|
||||
guard dependencies[defaults: .appGroup, key: .isMainAppActive] else {
|
||||
deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ public enum UpdateProfilePictureJob: JobExecutor {
|
|||
using dependencies: Dependencies
|
||||
) {
|
||||
// Don't run when inactive or not in main app
|
||||
guard (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
|
||||
guard dependencies[defaults: .appGroup, key: .isMainAppActive] else {
|
||||
return deferred(job, dependencies) // Don't need to do anything if it's not the main app
|
||||
}
|
||||
|
||||
// Only re-upload the profile picture if enough time has passed since the last upload
|
||||
guard
|
||||
let lastProfilePictureUpload: Date = dependencies[singleton: .standardUserDefaults][.lastProfilePictureUpload],
|
||||
let lastProfilePictureUpload: Date = dependencies[defaults: .standard, key: .lastProfilePictureUpload],
|
||||
dependencies.dateNow.timeIntervalSince(lastProfilePictureUpload) > (14 * 24 * 60 * 60)
|
||||
else {
|
||||
// Reset the `nextRunTimestamp` value just in case the last run failed so we don't get stuck
|
||||
|
|
|
@ -1117,7 +1117,7 @@ public final class OpenGroupManager {
|
|||
// don't double up on fetch requests by storing the existing request as a promise if
|
||||
// there is one.
|
||||
let threadId: String = OpenGroup.idFor(roomToken: roomToken, server: server)
|
||||
let lastOpenGroupImageUpdate: Date? = dependencies[singleton: .standardUserDefaults][.lastOpenGroupImageUpdate]
|
||||
let lastOpenGroupImageUpdate: Date? = dependencies[defaults: .standard, key: .lastOpenGroupImageUpdate]
|
||||
let now: Date = dependencies.dateNow
|
||||
let timeSinceLastUpdate: TimeInterval = (lastOpenGroupImageUpdate.map { now.timeIntervalSince($0) } ?? .greatestFiniteMagnitude)
|
||||
let updateInterval: TimeInterval = (7 * 24 * 60 * 60)
|
||||
|
@ -1198,7 +1198,7 @@ public final class OpenGroupManager {
|
|||
.filter(id: threadId)
|
||||
.updateAll(db, OpenGroup.Columns.imageData.set(to: imageData))
|
||||
}
|
||||
dependencies[singleton: .standardUserDefaults][.lastOpenGroupImageUpdate] = now
|
||||
dependencies[defaults: .standard, key: .lastOpenGroupImageUpdate] = now
|
||||
}
|
||||
|
||||
resolver(Result.success(imageData))
|
||||
|
@ -1244,7 +1244,7 @@ public extension OpenGroupManager {
|
|||
return storedTimeSinceLastOpen
|
||||
}
|
||||
|
||||
guard let lastOpen: Date = dependencies[singleton: .standardUserDefaults][.lastOpen] else {
|
||||
guard let lastOpen: Date = dependencies[defaults: .standard, key: .lastOpen] else {
|
||||
_timeSinceLastOpen = .greatestFiniteMagnitude
|
||||
return .greatestFiniteMagnitude
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ extension MessageReceiver {
|
|||
guard threadVariant == .contact else { return }
|
||||
|
||||
switch message.kind {
|
||||
case .preOffer: try MessageReceiver.handleNewCallMessage(db, message: message)
|
||||
case .preOffer: try MessageReceiver.handleNewCallMessage(db, message: message, using: dependencies)
|
||||
case .offer: MessageReceiver.handleOfferCallMessage(db, message: message)
|
||||
case .answer: MessageReceiver.handleAnswerCallMessage(db, message: message, using: dependencies)
|
||||
case .provisionalAnswer: break // TODO: Implement
|
||||
|
@ -44,12 +44,16 @@ extension MessageReceiver {
|
|||
|
||||
// MARK: - Specific Handling
|
||||
|
||||
private static func handleNewCallMessage(_ db: Database, message: CallMessage) throws {
|
||||
private static func handleNewCallMessage(
|
||||
_ db: Database,
|
||||
message: CallMessage,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
SNLog("[Calls] Received pre-offer message.")
|
||||
|
||||
// Determine whether the app is active based on the prefs rather than the UIApplication state to avoid
|
||||
// requiring main-thread execution
|
||||
let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false)
|
||||
let isMainAppActive: Bool = dependencies[defaults: .appGroup, key: .isMainAppActive]
|
||||
|
||||
// It is enough just ignoring the pre offers, other call messages
|
||||
// for this call would be dropped because of no Session call instance
|
||||
|
|
|
@ -21,7 +21,7 @@ extension MessageReceiver {
|
|||
// Note: `message.sentTimestamp` is in ms (convert to TimeInterval before converting to
|
||||
// seconds to maintain the accuracy)
|
||||
let messageSentTimestamp: TimeInterval = (TimeInterval(message.sentTimestamp ?? 0) / 1000)
|
||||
let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false)
|
||||
let isMainAppActive: Bool = dependencies[defaults: .appGroup, key: .isMainAppActive]
|
||||
let expiresInSeconds: TimeInterval? = proto.hasExpirationTimer ? TimeInterval(proto.expirationTimer) : nil
|
||||
|
||||
// Update profile if needed (want to do this regardless of whether the message exists or
|
||||
|
@ -400,7 +400,7 @@ extension MessageReceiver {
|
|||
case .react:
|
||||
// Determine whether the app is active based on the prefs rather than the UIApplication state to avoid
|
||||
// requiring main-thread execution
|
||||
let isMainAppActive: Bool = (UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false)
|
||||
let isMainAppActive: Bool = dependencies[defaults: .appGroup, key: .isMainAppActive]
|
||||
let timestampMs: Int64 = Int64(messageSentTimestamp * 1000)
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let reaction: Reaction = try Reaction(
|
||||
|
|
|
@ -303,7 +303,7 @@ public final class MessageSender {
|
|||
guard
|
||||
let job: Job = job,
|
||||
shouldNotify &&
|
||||
UserDefaults.sharedLokiProject?[.isMainAppActive] != true
|
||||
!dependencies[defaults: .appGroup, key: .isMainAppActive]
|
||||
else { return }
|
||||
|
||||
NotifyPushServerJob.run(
|
||||
|
|
|
@ -30,12 +30,6 @@ extension PushNotificationAPI {
|
|||
/// The message's swarm expiry timestamp (unix epoch milliseconds)
|
||||
public let expirationTimestampMs: Int64
|
||||
|
||||
/// The swarm timestamp when the message was created (unix epoch milliseconds)
|
||||
public let createdTimestampMs: Int64
|
||||
|
||||
/// The message's swarm expiry timestamp (unix epoch milliseconds)
|
||||
public let expirationTimestampMs: Int64
|
||||
|
||||
/// The length of the message data. This is always included, even if the message content
|
||||
/// itself was too large to fit into the push notification.
|
||||
public let dataLength: Int
|
||||
|
|
|
@ -30,9 +30,9 @@ public enum PushNotificationAPI {
|
|||
HTTP.PreparedRequest<PushNotificationAPI.LegacyPushServerResponse>?
|
||||
)
|
||||
let hexEncodedToken: String = token.toHexString()
|
||||
let oldToken: String? = dependencies[singleton: .standardUserDefaults][.deviceToken]
|
||||
let lastUploadTime: Double = dependencies[singleton: .standardUserDefaults][.lastDeviceTokenUpload]
|
||||
let now: TimeInterval = Date().timeIntervalSince1970
|
||||
let oldToken: String? = dependencies[defaults: .standard, key: .deviceToken]
|
||||
let lastUploadTime: Double = dependencies[defaults: .standard, key: .lastDeviceTokenUpload]
|
||||
let now: TimeInterval = dependencies.dateNow.timeIntervalSince1970
|
||||
|
||||
guard isForcedUpdate || hexEncodedToken != oldToken || now - lastUploadTime > tokenExpirationInterval else {
|
||||
SNLog("Device token hasn't changed or expired; no need to re-upload.")
|
||||
|
@ -54,9 +54,9 @@ public enum PushNotificationAPI {
|
|||
receiveOutput: { _, response in
|
||||
guard response.success == true else { return }
|
||||
|
||||
dependencies[singleton: .standardUserDefaults][.deviceToken] = hexEncodedToken
|
||||
dependencies[singleton: .standardUserDefaults][.lastDeviceTokenUpload] = now
|
||||
dependencies[singleton: .standardUserDefaults][.isUsingFullAPNs] = true
|
||||
dependencies[defaults: .standard, key: .deviceToken] = hexEncodedToken
|
||||
dependencies[defaults: .standard, key: .lastDeviceTokenUpload] = now
|
||||
dependencies[defaults: .standard, key: .isUsingFullAPNs] = true
|
||||
}
|
||||
)
|
||||
let preparedLegacyGroupRequest = try PushNotificationAPI
|
||||
|
@ -131,7 +131,7 @@ public enum PushNotificationAPI {
|
|||
receiveOutput: { _, response in
|
||||
guard response.success == true else { return }
|
||||
|
||||
dependencies[singleton: .standardUserDefaults][.deviceToken] = nil
|
||||
dependencies[defaults: .standard, key: .deviceToken] = nil
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -175,8 +175,8 @@ public enum PushNotificationAPI {
|
|||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> HTTP.PreparedRequest<SubscribeResponse> {
|
||||
guard
|
||||
dependencies[singleton: .standardUserDefaults][.isUsingFullAPNs],
|
||||
let token: String = dependencies[singleton: .standardUserDefaults][.deviceToken]
|
||||
dependencies[defaults: .standard, key: .isUsingFullAPNs],
|
||||
let token: String = dependencies[defaults: .standard, key: .deviceToken]
|
||||
else { throw HTTPError.invalidRequest }
|
||||
|
||||
guard let notificationsEncryptionKey: Data = try? getOrGenerateEncryptionKey(using: dependencies) else {
|
||||
|
@ -312,13 +312,13 @@ public enum PushNotificationAPI {
|
|||
legacyGroupIds: Set<String>,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> HTTP.PreparedRequest<LegacyPushServerResponse>? {
|
||||
let isUsingFullAPNs = dependencies[singleton: .standardUserDefaults][.isUsingFullAPNs]
|
||||
let isUsingFullAPNs = dependencies[defaults: .standard, key: .isUsingFullAPNs]
|
||||
|
||||
// Only continue if PNs are enabled and we have a device token
|
||||
guard
|
||||
!legacyGroupIds.isEmpty,
|
||||
(forced || isUsingFullAPNs),
|
||||
let deviceToken: String = (token ?? dependencies[singleton: .standardUserDefaults][.deviceToken])
|
||||
let deviceToken: String = (token ?? dependencies[defaults: .standard, key: .deviceToken])
|
||||
else { return nil }
|
||||
|
||||
return try PushNotificationAPI
|
||||
|
|
|
@ -61,7 +61,7 @@ public final class CurrentUserPoller: Poller {
|
|||
}
|
||||
|
||||
override func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> Bool {
|
||||
if UserDefaults.sharedLokiProject?[.isMainAppActive] != true {
|
||||
if !dependencies[defaults: .appGroup, key: .isMainAppActive] {
|
||||
// Do nothing when an error gets throws right after returning from the background (happens frequently)
|
||||
}
|
||||
else if
|
||||
|
|
|
@ -170,7 +170,7 @@ extension OpenGroupAPI {
|
|||
dependencies.mutate(cache: .openGroupManager) { cache in
|
||||
cache.hasPerformedInitialPoll[server] = true
|
||||
cache.timeSinceLastPoll[server] = dependencies.dateNow.timeIntervalSince1970
|
||||
dependencies[singleton: .standardUserDefaults][.lastOpen] = dependencies.dateNow
|
||||
dependencies[defaults: .standard, key: .lastOpen] = dependencies.dateNow
|
||||
}
|
||||
|
||||
SNLog("Open group polling finished for \(server).")
|
||||
|
|
|
@ -37,7 +37,7 @@ public class TypingIndicators {
|
|||
//
|
||||
// We also don't want to show/send typing indicators for message requests
|
||||
guard
|
||||
dependencies[singleton: .storage][.typingIndicatorsEnabled] &&
|
||||
dependencies[singleton: .storage, key: .typingIndicatorsEnabled] &&
|
||||
!threadIsBlocked &&
|
||||
!threadIsMessageRequest
|
||||
else { return nil }
|
||||
|
|
|
@ -374,7 +374,8 @@ public struct ProfileManager {
|
|||
try success?(db)
|
||||
}
|
||||
},
|
||||
failure: failure
|
||||
failure: failure,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -383,7 +384,8 @@ public struct ProfileManager {
|
|||
queue: DispatchQueue,
|
||||
imageData: Data,
|
||||
success: @escaping ((downloadUrl: String, fileName: String, profileKey: Data)) -> (),
|
||||
failure: ((ProfileManagerError) -> ())? = nil
|
||||
failure: ((ProfileManagerError) -> ())? = nil,
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
queue.async {
|
||||
// If the profile avatar was updated or removed then encrypt with a new profile key
|
||||
|
@ -504,7 +506,7 @@ public struct ProfileManager {
|
|||
|
||||
// Update the cached avatar image value
|
||||
profileAvatarCache.mutate { $0[fileName] = avatarImageData }
|
||||
UserDefaults.standard[.lastProfilePictureUpload] = Date()
|
||||
dependencies[defaults: .standard, key: .lastProfilePictureUpload] = dependencies.dateNow
|
||||
|
||||
SNLog("Successfully uploaded avatar image.")
|
||||
success((downloadUrl, fileName, newProfileKey))
|
||||
|
|
|
@ -63,7 +63,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
dependencies[singleton: .storage] = mockStorage
|
||||
dependencies[singleton: .network] = mockNetwork
|
||||
dependencies[singleton: .crypto] = mockCrypto
|
||||
dependencies[singleton: .standardUserDefaults] = mockUserDefaults
|
||||
dependencies[defaults: .standard] = mockUserDefaults
|
||||
dependencies[cache: .openGroupManager] = mockOGMCache
|
||||
|
||||
testInteraction1 = Interaction(
|
||||
|
@ -252,7 +252,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
it("defaults the time since last open to greatestFiniteMagnitude") {
|
||||
mockUserDefaults
|
||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
||||
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||
}
|
||||
.thenReturn(nil)
|
||||
|
||||
|
@ -264,7 +264,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
it("returns the time since the last open") {
|
||||
mockUserDefaults
|
||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
||||
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||
}
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567880))
|
||||
dependencies.dateNow = Date(timeIntervalSince1970: 1234567890)
|
||||
|
@ -277,7 +277,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
it("caches the time since the last open") {
|
||||
mockUserDefaults
|
||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
||||
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||
}
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567770))
|
||||
dependencies.dateNow = Date(timeIntervalSince1970: 1234567780)
|
||||
|
@ -287,7 +287,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
|
||||
mockUserDefaults
|
||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
||||
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||
}
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||
|
||||
|
@ -327,7 +327,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
|
||||
mockUserDefaults
|
||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
||||
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||
}
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||
}
|
||||
|
@ -783,7 +783,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
|
||||
mockUserDefaults
|
||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
||||
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||
}
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||
}
|
||||
|
@ -928,7 +928,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
|
||||
mockUserDefaults
|
||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
||||
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||
}
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||
}
|
||||
|
@ -1209,7 +1209,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
|
||||
mockUserDefaults
|
||||
.when { (defaults: inout any UserDefaultsType) -> Any? in
|
||||
defaults.object(forKey: SNUserDefaults.Date.lastOpen.rawValue)
|
||||
defaults.object(forKey: UserDefaultsInfo.DateKey.lastOpen.rawValue)
|
||||
}
|
||||
.thenReturn(nil)
|
||||
}
|
||||
|
@ -3245,7 +3245,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
.to(call(matchingParameters: .all) {
|
||||
$0.set(
|
||||
testDate,
|
||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
||||
forKey: UserDefaultsInfo.DateKey.lastOpenGroupImageUpdate.rawValue
|
||||
)
|
||||
})
|
||||
expect(
|
||||
|
@ -3354,7 +3354,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
.toNot(call(matchingParameters: .all) {
|
||||
$0.set(
|
||||
dependencies.dateNow,
|
||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
||||
forKey: UserDefaultsInfo.DateKey.lastOpenGroupImageUpdate.rawValue
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -3436,7 +3436,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
.to(call(matchingParameters: .all) {
|
||||
$0.set(
|
||||
dependencies.dateNow,
|
||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
||||
forKey: UserDefaultsInfo.DateKey.lastOpenGroupImageUpdate.rawValue
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
|||
}
|
||||
|
||||
// Abort if the main app is running
|
||||
guard !(UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
|
||||
guard !dependencies[defaults: .appGroup, key: .isMainAppActive] else {
|
||||
return self.completeSilenty(using: dependencies)
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,8 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
|||
SetCurrentAppContext(NotificationServiceExtensionContext())
|
||||
}
|
||||
|
||||
let isCallOngoing: Bool = (UserDefaults.sharedLokiProject?[.isCallOngoing])
|
||||
.defaulting(to: false)
|
||||
let lastCallPreOffer: Date? = UserDefaults.sharedLokiProject?[.lastCallPreOffer]
|
||||
let isCallOngoing: Bool = dependencies[defaults: .appGroup, key: .isCallOngoing]
|
||||
let lastCallPreOffer: Date? = dependencies[defaults: .appGroup, key: .lastCallPreOffer]
|
||||
|
||||
// Perform main setup
|
||||
Storage.resumeDatabaseAccess(using: dependencies)
|
||||
|
@ -240,8 +239,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
|||
NSLog("[NotificationServiceExtension] Performing setup")
|
||||
didPerformSetup = true
|
||||
|
||||
_ = AppVersion.sharedInstance()
|
||||
|
||||
AppVersion.configure(using: dependencies)
|
||||
Cryptography.seedRandom()
|
||||
|
||||
AppSetup.setupEnvironment(
|
||||
|
@ -264,7 +262,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
|||
// this path should never occur. However, the service does have our push token
|
||||
// so it is possible that could change in the future. If it does, do nothing
|
||||
// and don't disturb the user. Messages will be processed when they open the app.
|
||||
guard dependencies[singleton: .storage][.isReadyForAppExtensions] else {
|
||||
guard dependencies[singleton: .storage, key: .isReadyForAppExtensions] else {
|
||||
NSLog("[NotificationServiceExtension] Not ready for extensions")
|
||||
self?.completeSilenty(using: dependencies)
|
||||
return
|
||||
|
@ -368,7 +366,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
|||
}
|
||||
else {
|
||||
NSLog("[NotificationServiceExtension] Successfully notified main app of call message.")
|
||||
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = Date()
|
||||
dependencies[defaults: .appGroup, key: .lastCallPreOffer] = Date()
|
||||
self.completeSilenty(using: dependencies)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,13 +46,6 @@ final class NotificationServiceExtensionContext : NSObject, AppContext {
|
|||
return groupContainerURL.path
|
||||
}
|
||||
|
||||
func appUserDefaults() -> UserDefaults {
|
||||
guard let userDefaults = UserDefaults.sharedLokiProject else {
|
||||
preconditionFailure("Couldn't set up shared user defaults.")
|
||||
}
|
||||
return userDefaults
|
||||
}
|
||||
|
||||
// MARK: - Currently Unused
|
||||
|
||||
let frame = CGRect.zero
|
||||
|
|
|
@ -9,5 +9,4 @@
|
|||
#import <SessionUtilitiesKit/UIView+OWS.h>
|
||||
#import <SessionUtilitiesKit/AppContext.h>
|
||||
#import <SessionMessagingKit/AppReadiness.h>
|
||||
#import <SignalUtilitiesKit/AppVersion.h>
|
||||
#import <SessionUtilitiesKit/OWSMath.h>
|
||||
|
|
|
@ -164,12 +164,6 @@ final class ShareAppExtensionContext: NSObject, AppContext {
|
|||
return (targetPath ?? "")
|
||||
}
|
||||
|
||||
func appUserDefaults() -> UserDefaults {
|
||||
owsAssertDebug(UserDefaults.sharedLokiProject != nil)
|
||||
|
||||
return (UserDefaults.sharedLokiProject ?? UserDefaults.standard)
|
||||
}
|
||||
|
||||
func setStatusBarHidden(_ isHidden: Bool, animated isAnimated: Bool) {
|
||||
OWSLogger.info("Ignoring request to show/hide status bar since we're in an app extension")
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
|||
override func loadView() {
|
||||
super.loadView()
|
||||
|
||||
// Called via the OS so create a default 'Dependencies' instance
|
||||
let dependencies: Dependencies = Dependencies()
|
||||
|
||||
view.themeBackgroundColor = .backgroundPrimary
|
||||
|
||||
// This should be the first thing we do (Note: If you leave the share context and return to it
|
||||
|
@ -38,7 +41,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
|||
|
||||
Logger.info("")
|
||||
|
||||
_ = AppVersion.sharedInstance()
|
||||
AppVersion.configure(using: dependencies)
|
||||
|
||||
Cryptography.seedRandom()
|
||||
|
||||
|
@ -51,9 +54,6 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
// Called via the OS so create a default 'Dependencies' instance
|
||||
let dependencies: Dependencies = Dependencies()
|
||||
|
||||
AppSetup.setupEnvironment(
|
||||
appSpecificBlock: {
|
||||
Environment.shared?.notificationsManager.mutate {
|
||||
|
@ -158,7 +158,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
|||
// We don't need to use SyncPushTokensJob in the SAE.
|
||||
// We don't need to use DeviceSleepManager in the SAE.
|
||||
|
||||
AppVersion.sharedInstance().saeLaunchDidComplete()
|
||||
AppVersion.shared.saeLaunchDidComplete(using: dependencies)
|
||||
|
||||
showLockScreenOrMainContent(using: dependencies)
|
||||
|
||||
|
@ -188,7 +188,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
|||
// Called via the OS so create a default 'Dependencies' instance
|
||||
let dependencies: Dependencies = Dependencies()
|
||||
|
||||
if dependencies[singleton: .storage][.isScreenLockEnabled] {
|
||||
if dependencies[singleton: .storage, key: .isScreenLockEnabled] {
|
||||
self.dismiss(animated: false) { [weak self] in
|
||||
AssertIsOnMainThread()
|
||||
self?.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
||||
|
@ -210,7 +210,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
|
|||
private func showLockScreenOrMainContent(
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
if dependencies[singleton: .storage][.isScreenLockEnabled] {
|
||||
if dependencies[singleton: .storage, key: .isScreenLockEnabled] {
|
||||
showLockScreen()
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -26,8 +26,8 @@ public final class SnodeAPI {
|
|||
|
||||
// MARK: - Hardfork version
|
||||
|
||||
public static var hardfork = UserDefaults.standard[.hardfork]
|
||||
public static var softfork = UserDefaults.standard[.softfork]
|
||||
public static var hardfork: Int = Dependencies()[defaults: .standard, key: .hardfork]
|
||||
public static var softfork: Int = Dependencies()[defaults: .standard, key: .softfork]
|
||||
|
||||
// MARK: - Settings
|
||||
|
||||
|
@ -158,7 +158,7 @@ public final class SnodeAPI {
|
|||
loadSnodePoolIfNeeded(using: dependencies)
|
||||
|
||||
let now: Date = Date()
|
||||
let hasSnodePoolExpired: Bool = dependencies[singleton: .storage][.lastSnodePoolRefreshDate]
|
||||
let hasSnodePoolExpired: Bool = dependencies[singleton: .storage, key: .lastSnodePoolRefreshDate]
|
||||
.map { now.timeIntervalSince($0) > 2 * 60 * 60 }
|
||||
.defaulting(to: true)
|
||||
let snodePool: Set<Snode> = SnodeAPI.snodePool.wrappedValue
|
||||
|
@ -1199,14 +1199,14 @@ public final class SnodeAPI {
|
|||
|
||||
if snodeResponse.hardFork[1] > softfork {
|
||||
softfork = snodeResponse.hardFork[1]
|
||||
UserDefaults.standard[.softfork] = softfork
|
||||
dependencies[defaults: .standard, key: .softfork] = softfork
|
||||
}
|
||||
|
||||
if snodeResponse.hardFork[0] > hardfork {
|
||||
hardfork = snodeResponse.hardFork[0]
|
||||
UserDefaults.standard[.hardfork] = hardfork
|
||||
dependencies[defaults: .standard, key: .hardfork] = hardfork
|
||||
softfork = snodeResponse.hardFork[1]
|
||||
UserDefaults.standard[.softfork] = softfork
|
||||
dependencies[defaults: .standard, key: .softfork] = softfork
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -64,7 +64,7 @@ public class TopBannerController: UIViewController {
|
|||
)
|
||||
result.contentMode = .center
|
||||
result.themeTintColor = .black
|
||||
result.addTarget(self, action: #selector(dismissBanner), for: .touchUpInside)
|
||||
result.addTarget(self, action: #selector(dismissBannerTapped), for: .touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
@ -136,9 +136,11 @@ public class TopBannerController: UIViewController {
|
|||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc private func dismissBanner() {
|
||||
@objc private func dismissBannerTapped() { dismissBanner() }
|
||||
|
||||
private func dismissBanner(using dependencies: Dependencies = Dependencies()) {
|
||||
// Remove the cached warning
|
||||
UserDefaults.sharedLokiProject?[.topBannerWarningToShow] = nil
|
||||
dependencies[defaults: .appGroup, key: .topBannerWarningToShow] = nil
|
||||
|
||||
UIView.animate(
|
||||
withDuration: 0.3,
|
||||
|
@ -155,10 +157,14 @@ public class TopBannerController: UIViewController {
|
|||
|
||||
// MARK: - Functions
|
||||
|
||||
public static func show(warning: Warning, inWindowFor view: UIView? = nil) {
|
||||
public static func show(
|
||||
warning: Warning,
|
||||
inWindowFor view: UIView? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
guard Thread.isMainThread else {
|
||||
DispatchQueue.main.async {
|
||||
TopBannerController.show(warning: warning, inWindowFor: view)
|
||||
TopBannerController.show(warning: warning, inWindowFor: view, using: dependencies)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -169,7 +175,7 @@ public class TopBannerController: UIViewController {
|
|||
}
|
||||
|
||||
// Cache the banner to show (so we can show it on re-launch)
|
||||
UserDefaults.sharedLokiProject?[.topBannerWarningToShow] = warning.rawValue
|
||||
dependencies[defaults: .appGroup, key: .topBannerWarningToShow] = warning.rawValue
|
||||
|
||||
UIView.performWithoutAnimation {
|
||||
instance.bannerLabel.text = warning.text
|
||||
|
|
|
@ -36,7 +36,7 @@ public enum ThemeManager {
|
|||
internal static var dependencies: Dependencies = Dependencies()
|
||||
|
||||
public static var currentTheme: Theme = {
|
||||
(_initialTheme ?? dependencies[singleton: .storage][.theme].defaulting(to: Theme.classicDark))
|
||||
(_initialTheme ?? dependencies[singleton: .storage, key: .theme].defaulting(to: Theme.classicDark))
|
||||
}() {
|
||||
didSet {
|
||||
// Only update if it was changed
|
||||
|
@ -60,7 +60,7 @@ public enum ThemeManager {
|
|||
}
|
||||
|
||||
public static var primaryColor: Theme.PrimaryColor = {
|
||||
(_initialPrimaryColor ?? dependencies[singleton: .storage][.themePrimaryColor].defaulting(to: Theme.PrimaryColor.green))
|
||||
(_initialPrimaryColor ?? dependencies[singleton: .storage, key: .themePrimaryColor].defaulting(to: Theme.PrimaryColor.green))
|
||||
}() {
|
||||
didSet {
|
||||
// Only update if it was changed
|
||||
|
@ -75,7 +75,7 @@ public enum ThemeManager {
|
|||
}
|
||||
|
||||
public static var matchSystemNightModeSetting: Bool = {
|
||||
(_initialMatchSystemNightModeSetting ?? dependencies[singleton: .storage][.themeMatchSystemDayNightCycle])
|
||||
(_initialMatchSystemNightModeSetting ?? dependencies[singleton: .storage, key: .themeMatchSystemDayNightCycle])
|
||||
}() {
|
||||
didSet {
|
||||
// Only update if it was changed
|
||||
|
@ -134,8 +134,8 @@ public enum ThemeManager {
|
|||
}
|
||||
|
||||
public static func applySavedTheme(using dependencies: Dependencies) {
|
||||
ThemeManager.primaryColor = dependencies[singleton: .storage][.themePrimaryColor].defaulting(to: Theme.PrimaryColor.green)
|
||||
ThemeManager.currentTheme = dependencies[singleton: .storage][.theme].defaulting(to: Theme.classicDark)
|
||||
ThemeManager.primaryColor = dependencies[singleton: .storage, key: .themePrimaryColor].defaulting(to: Theme.PrimaryColor.green)
|
||||
ThemeManager.currentTheme = dependencies[singleton: .storage, key: .theme].defaulting(to: Theme.classicDark)
|
||||
}
|
||||
|
||||
public static func applyNavigationStyling() {
|
||||
|
|
|
@ -150,31 +150,7 @@ public protocol EnumStringSetting: RawRepresentable where RawValue == String {}
|
|||
|
||||
// MARK: - GRDB Interactions
|
||||
|
||||
public extension Storage {
|
||||
subscript(key: Setting.BoolKey) -> Bool {
|
||||
// Default to false if it doesn't exist
|
||||
return (read { db in db[key] } ?? false)
|
||||
}
|
||||
|
||||
subscript(key: Setting.DoubleKey) -> Double? { return read { db in db[key] } }
|
||||
subscript(key: Setting.IntKey) -> Int? { return read { db in db[key] } }
|
||||
subscript(key: Setting.StringKey) -> String? { return read { db in db[key] } }
|
||||
subscript(key: Setting.DateKey) -> Date? { return read { db in db[key] } }
|
||||
|
||||
subscript<T: EnumIntSetting>(key: Setting.EnumKey) -> T? { return read { db in db[key] } }
|
||||
subscript<T: EnumStringSetting>(key: Setting.EnumKey) -> T? { return read { db in db[key] } }
|
||||
}
|
||||
|
||||
public extension Database {
|
||||
@discardableResult func unsafeSet<T: Numeric>(key: String, value: T?) -> Setting? {
|
||||
guard let value: T = value else {
|
||||
_ = try? Setting.filter(id: key).deleteAll(self)
|
||||
return nil
|
||||
}
|
||||
|
||||
return try? Setting(key: key, value: value)?.saved(self)
|
||||
}
|
||||
|
||||
private subscript(key: String) -> Setting? {
|
||||
get { try? Setting.filter(id: key).fetchOne(self) }
|
||||
set {
|
||||
|
|
|
@ -8,6 +8,7 @@ public class Dependencies {
|
|||
|
||||
private static var singletonInstances: Atomic<[Int: Any]> = Atomic([:])
|
||||
private static var cacheInstances: Atomic<[Int: MutableCacheType]> = Atomic([:])
|
||||
private static var userDefaultsInstances: Atomic<[Int: (any UserDefaultsType)]> = Atomic([:])
|
||||
|
||||
// MARK: - Subscript Access
|
||||
|
||||
|
@ -19,6 +20,9 @@ public class Dependencies {
|
|||
getValueSettingIfNull(cache: cache, &Dependencies.cacheInstances)
|
||||
}
|
||||
|
||||
public subscript<U: UserDefaultsType>(defaults defaults: UserDefaultsInfo.Config<U>) -> U {
|
||||
getValueSettingIfNull(defaults: defaults, &Dependencies.userDefaultsInstances)
|
||||
}
|
||||
|
||||
// MARK: - Timing and Async Handling
|
||||
|
||||
|
@ -82,4 +86,80 @@ public class Dependencies {
|
|||
|
||||
return cache.immutableInstance(value)
|
||||
}
|
||||
|
||||
@discardableResult private func getValueSettingIfNull<U: UserDefaultsType>(
|
||||
defaults: UserDefaultsInfo.Config<U>,
|
||||
_ store: inout Atomic<[Int: UserDefaultsType]>
|
||||
) -> U {
|
||||
guard let value: U = (store.wrappedValue[defaults.key] as? U) else {
|
||||
let value: U = defaults.createInstance(self)
|
||||
store.mutate { $0[defaults.key] = value }
|
||||
return value
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Storage Setting Convenience
|
||||
|
||||
public extension Dependencies {
|
||||
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.BoolKey) -> Bool {
|
||||
return self[singleton: singleton]
|
||||
.read { db in db[key] }
|
||||
.defaulting(to: false) // Default to false if it doesn't exist
|
||||
}
|
||||
|
||||
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.DoubleKey) -> Double? {
|
||||
return self[singleton: singleton].read { db in db[key] }
|
||||
}
|
||||
|
||||
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.IntKey) -> Int? {
|
||||
return self[singleton: singleton].read { db in db[key] }
|
||||
}
|
||||
|
||||
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.StringKey) -> String? {
|
||||
return self[singleton: singleton].read { db in db[key] }
|
||||
}
|
||||
|
||||
subscript(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.DateKey) -> Date? {
|
||||
return self[singleton: singleton].read { db in db[key] }
|
||||
}
|
||||
|
||||
subscript<T: EnumIntSetting>(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.EnumKey) -> T? {
|
||||
return self[singleton: singleton].read { db in db[key] }
|
||||
}
|
||||
|
||||
subscript<T: EnumStringSetting>(singleton singleton: SingletonInfo.Config<Storage>, key key: Setting.EnumKey) -> T? {
|
||||
return self[singleton: singleton].read { db in db[key] }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserDefaults Convenience
|
||||
|
||||
public extension Dependencies {
|
||||
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.BoolKey) -> Bool {
|
||||
get { return self[defaults: defaults].bool(forKey: key.rawValue) }
|
||||
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||
}
|
||||
|
||||
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.DateKey) -> Date? {
|
||||
get { return self[defaults: defaults].object(forKey: key.rawValue) as? Date }
|
||||
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||
}
|
||||
|
||||
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.DoubleKey) -> Double {
|
||||
get { return self[defaults: defaults].double(forKey: key.rawValue) }
|
||||
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||
}
|
||||
|
||||
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.IntKey) -> Int {
|
||||
get { return self[defaults: defaults].integer(forKey: key.rawValue) }
|
||||
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||
}
|
||||
|
||||
subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>, key key: UserDefaultsInfo.StringKey) -> String? {
|
||||
get { return self[defaults: defaults].string(forKey: key.rawValue) }
|
||||
set { self[defaults: defaults].set(newValue, forKey: key.rawValue) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
import Foundation
|
||||
|
||||
// MARK: - UserDefaultsStorage
|
||||
|
||||
public class UserDefaultsStorage {}
|
||||
|
||||
public extension UserDefaultsStorage {
|
||||
static var standard: UserDefaultsInfo.Config = UserDefaultsInfo.create(identifier: "standard") { _ in
|
||||
UserDefaults.standard
|
||||
}
|
||||
static var appGroup: UserDefaultsInfo.Config = UserDefaultsInfo.create(identifier: UserDefaults.applicationGroup) { _ in
|
||||
UserDefaults(suiteName: UserDefaults.applicationGroup)!
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserDefaultsInfo
|
||||
|
||||
public enum UserDefaultsInfo {
|
||||
public class Config<U: UserDefaultsType>: UserDefaultsStorage {
|
||||
public let key: Int
|
||||
public let createInstance: (Dependencies) -> U
|
||||
|
||||
fileprivate init(
|
||||
identifier: String,
|
||||
createInstance: @escaping (Dependencies) -> U
|
||||
) {
|
||||
self.key = identifier.hashValue
|
||||
self.createInstance = createInstance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension UserDefaultsInfo {
|
||||
static func create<U: UserDefaultsType>(
|
||||
identifier: String,
|
||||
createInstance: @escaping (Dependencies) -> U
|
||||
) -> UserDefaultsInfo.Config<U> {
|
||||
return UserDefaultsInfo.Config(
|
||||
identifier: identifier,
|
||||
createInstance: createInstance
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UserDefaultsType
|
||||
|
||||
public protocol UserDefaultsType: AnyObject {
|
||||
func object(forKey defaultName: String) -> Any?
|
||||
func string(forKey defaultName: String) -> String?
|
||||
func array(forKey defaultName: String) -> [Any]?
|
||||
func dictionary(forKey defaultName: String) -> [String : Any]?
|
||||
func data(forKey defaultName: String) -> Data?
|
||||
func stringArray(forKey defaultName: String) -> [String]?
|
||||
func integer(forKey defaultName: String) -> Int
|
||||
func float(forKey defaultName: String) -> Float
|
||||
func double(forKey defaultName: String) -> Double
|
||||
func bool(forKey defaultName: String) -> Bool
|
||||
func url(forKey defaultName: String) -> URL?
|
||||
|
||||
func set(_ value: Any?, forKey defaultName: String)
|
||||
func set(_ value: Int, forKey defaultName: String)
|
||||
func set(_ value: Float, forKey defaultName: String)
|
||||
func set(_ value: Double, forKey defaultName: String)
|
||||
func set(_ value: Bool, forKey defaultName: String)
|
||||
func set(_ url: URL?, forKey defaultName: String)
|
||||
}
|
||||
|
||||
extension UserDefaults: UserDefaultsType {}
|
||||
|
||||
public extension UserDefaults {
|
||||
static let applicationGroup: String = "group.com.loki-project.loki-messenger"
|
||||
|
||||
static func removeAll() {
|
||||
UserDefaultsStorage.standard.createInstance(Dependencies()).removeAll()
|
||||
UserDefaultsStorage.appGroup.createInstance(Dependencies()).removeAll()
|
||||
}
|
||||
|
||||
private func removeAll() {
|
||||
let data: [String: Any] = self.dictionaryRepresentation()
|
||||
data.forEach { key, _ in self.removeObject(forKey: key) }
|
||||
self.synchronize() // Shouldn't be needed but better safe than sorry
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public extension UserDefaultsInfo {
|
||||
struct BoolKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||
public let rawValue: String
|
||||
|
||||
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||
public init(stringLiteral value: String) { self.init(value) }
|
||||
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||
}
|
||||
|
||||
struct DateKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||
public let rawValue: String
|
||||
|
||||
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||
public init(stringLiteral value: String) { self.init(value) }
|
||||
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||
}
|
||||
|
||||
struct DoubleKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||
public let rawValue: String
|
||||
|
||||
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||
public init(stringLiteral value: String) { self.init(value) }
|
||||
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||
}
|
||||
|
||||
struct IntKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||
public let rawValue: String
|
||||
|
||||
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||
public init(stringLiteral value: String) { self.init(value) }
|
||||
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||
}
|
||||
|
||||
struct StringKey: RawRepresentable, ExpressibleByStringLiteral, Hashable {
|
||||
public let rawValue: String
|
||||
|
||||
public init(_ rawValue: String) { self.rawValue = rawValue }
|
||||
public init?(rawValue: String) { self.rawValue = rawValue }
|
||||
public init(stringLiteral value: String) { self.init(value) }
|
||||
public init(unicodeScalarLiteral value: String) { self.init(value) }
|
||||
public init(extendedGraphemeClusterLiteral value: String) { self.init(value) }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserDefault Values
|
||||
|
||||
public extension UserDefaultsInfo.BoolKey {
|
||||
/// Indicates whether the user has synced an initial config message from this device
|
||||
static let hasSyncedInitialConfiguration: UserDefaultsInfo.BoolKey = "hasSyncedConfiguration"
|
||||
|
||||
/// Indicates whether the user has seen the suggestion to enable link previews
|
||||
static let hasSeenLinkPreviewSuggestion: UserDefaultsInfo.BoolKey = "hasSeenLinkPreviewSuggestion"
|
||||
|
||||
/// Indicates whether the user has seen the IP exposure warning when enabling calls
|
||||
///
|
||||
/// **Note:** This is currently not in use (it was decided that it's better to warn the user every time they enable calls instead
|
||||
/// of just the first time)
|
||||
static let hasSeenCallIPExposureWarning: UserDefaultsInfo.BoolKey = "hasSeenCallIPExposureWarning"
|
||||
|
||||
/// Indicates whether the user has seen the missed call tips modal
|
||||
static let hasSeenCallMissedTips: UserDefaultsInfo.BoolKey = "hasSeenCallMissedTips"
|
||||
|
||||
/// Indicates whether the user is registered for APNS (ie. "Fast Mode" notifications)
|
||||
static let isUsingFullAPNs: UserDefaultsInfo.BoolKey = "isUsingFullAPNs"
|
||||
|
||||
/// Indicates whether the device was unlinked from an account
|
||||
///
|
||||
/// **Note:** This doesn't seem to be properly used (we basically just maintain the existing value)
|
||||
static let wasUnlinked: UserDefaultsInfo.BoolKey = "wasUnlinked"
|
||||
|
||||
/// Indicates whether the main app is active, this is set to `true` while the app is in the foreground and `false` when
|
||||
/// the app is in the background
|
||||
static let isMainAppActive: UserDefaultsInfo.BoolKey = "isMainAppActive"
|
||||
|
||||
/// Indicates whether there is an ongoing call
|
||||
static let isCallOngoing: UserDefaultsInfo.BoolKey = "isCallOngoing"
|
||||
}
|
||||
|
||||
public extension UserDefaultsInfo.DateKey {
|
||||
/// The date/time when the users profile picture was last uploaded to the server (used to rate-limit re-uploading)
|
||||
static let lastProfilePictureUpload: UserDefaultsInfo.DateKey = "lastProfilePictureUpload"
|
||||
|
||||
/// The date/time when the device last updated the default open group room images (used to rate-limit re-downloading)
|
||||
static let lastOpenGroupImageUpdate: UserDefaultsInfo.DateKey = "lastOpenGroupImageUpdate"
|
||||
|
||||
/// The date/time when any open group last had a successful poll (used as a fallback date/time if the open group hasn't been polled
|
||||
/// this session)
|
||||
static let lastOpen: UserDefaultsInfo.DateKey = "lastOpen"
|
||||
|
||||
/// The date/time when the last garbage collection was performed (used to rate-limit garbage collection)
|
||||
static let lastGarbageCollection: UserDefaultsInfo.DateKey = "lastGarbageCollection"
|
||||
|
||||
/// The date/time when we last subscribed for push notifications (used to rate-limit calling our subscription endpoint)
|
||||
static let lastPushNotificationSync: UserDefaultsInfo.DateKey = "lastPushNotificationSync"
|
||||
|
||||
/// The date/time when we received a call pre-offer (used to suppress call notifications which are too old)
|
||||
static let lastCallPreOffer: UserDefaultsInfo.DateKey = "lastCallPreOffer"
|
||||
}
|
||||
|
||||
public extension UserDefaultsInfo.DoubleKey {
|
||||
/// The timestamp when we last uploaded the users push token (used to rate-limit calling our subscription endpoint)
|
||||
///
|
||||
/// **Note:** Looks like this replicates the `lastPushNotificationSync` behaviour
|
||||
static let lastDeviceTokenUpload: UserDefaultsInfo.DoubleKey = "lastDeviceTokenUploadTime"
|
||||
}
|
||||
|
||||
public extension UserDefaultsInfo.IntKey {
|
||||
/// The latest hardfork value returned when interacting with a service node
|
||||
static let hardfork: UserDefaultsInfo.IntKey = "hardfork"
|
||||
|
||||
/// The latest softfork value returned when interacting with a service node
|
||||
static let softfork: UserDefaultsInfo.IntKey = "softfork"
|
||||
}
|
||||
|
||||
public extension UserDefaultsInfo.StringKey {
|
||||
/// The most recently subscribed APNS token
|
||||
static let deviceToken: UserDefaultsInfo.StringKey = "deviceToken"
|
||||
|
||||
/// The warning to show at the top of the app
|
||||
static let topBannerWarningToShow: UserDefaultsInfo.StringKey = "topBannerWarningToShow"
|
||||
}
|
|
@ -94,8 +94,6 @@ NSString *NSStringForUIApplicationState(UIApplicationState value);
|
|||
|
||||
- (NSString *)appSharedDataDirectoryPath;
|
||||
|
||||
- (NSUserDefaults *)appUserDefaults;
|
||||
|
||||
@end
|
||||
|
||||
id<AppContext> CurrentAppContext(void);
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSUserDefaults (OWS)
|
||||
|
||||
+ (NSUserDefaults *)appUserDefaults;
|
||||
|
||||
+ (void)migrateToSharedUserDefaults;
|
||||
|
||||
+ (void)removeAll;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,45 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSUserDefaults+OWS.h"
|
||||
#import "AppContext.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation NSUserDefaults (OWS)
|
||||
|
||||
+ (NSUserDefaults *)appUserDefaults
|
||||
{
|
||||
return CurrentAppContext().appUserDefaults;
|
||||
}
|
||||
|
||||
+ (void)migrateToSharedUserDefaults
|
||||
{
|
||||
NSUserDefaults *appUserDefaults = self.appUserDefaults;
|
||||
|
||||
NSDictionary<NSString *, id> *dictionary = [NSUserDefaults standardUserDefaults].dictionaryRepresentation;
|
||||
for (NSString *key in dictionary) {
|
||||
id value = dictionary[key];
|
||||
[appUserDefaults setObject:value forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)removeAll
|
||||
{
|
||||
[NSUserDefaults.standardUserDefaults removeAll];
|
||||
[self.appUserDefaults removeAll];
|
||||
}
|
||||
|
||||
- (void)removeAll
|
||||
{
|
||||
NSDictionary<NSString *, id> *dictionary = self.dictionaryRepresentation;
|
||||
for (NSString *key in dictionary) {
|
||||
[self removeObjectForKey:key];
|
||||
}
|
||||
[self synchronize];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,106 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
// MARK: - Singleton
|
||||
public extension Singleton {
|
||||
static let standardUserDefaults: SingletonInfo.Config<UserDefaultsType> = SingletonInfo.create { _ in
|
||||
UserDefaults.standard
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserDefaultsType
|
||||
|
||||
public protocol UserDefaultsType: AnyObject {
|
||||
func object(forKey defaultName: String) -> Any?
|
||||
func string(forKey defaultName: String) -> String?
|
||||
func array(forKey defaultName: String) -> [Any]?
|
||||
func dictionary(forKey defaultName: String) -> [String : Any]?
|
||||
func data(forKey defaultName: String) -> Data?
|
||||
func stringArray(forKey defaultName: String) -> [String]?
|
||||
func integer(forKey defaultName: String) -> Int
|
||||
func float(forKey defaultName: String) -> Float
|
||||
func double(forKey defaultName: String) -> Double
|
||||
func bool(forKey defaultName: String) -> Bool
|
||||
func url(forKey defaultName: String) -> URL?
|
||||
|
||||
func set(_ value: Any?, forKey defaultName: String)
|
||||
func set(_ value: Int, forKey defaultName: String)
|
||||
func set(_ value: Float, forKey defaultName: String)
|
||||
func set(_ value: Double, forKey defaultName: String)
|
||||
func set(_ value: Bool, forKey defaultName: String)
|
||||
func set(_ url: URL?, forKey defaultName: String)
|
||||
}
|
||||
|
||||
extension UserDefaults: UserDefaultsType {}
|
||||
|
||||
public enum SNUserDefaults {
|
||||
|
||||
public enum Bool: Swift.String {
|
||||
case hasSyncedInitialConfiguration = "hasSyncedConfiguration"
|
||||
case hasSeenLinkPreviewSuggestion
|
||||
case hasSeenCallIPExposureWarning
|
||||
case hasSeenCallMissedTips
|
||||
case isUsingFullAPNs
|
||||
case wasUnlinked
|
||||
case isMainAppActive
|
||||
case isCallOngoing
|
||||
}
|
||||
|
||||
public enum Date: Swift.String {
|
||||
case lastProfilePictureUpload
|
||||
case lastOpenGroupImageUpdate
|
||||
case lastOpen
|
||||
case lastGarbageCollection
|
||||
case lastPushNotificationSync
|
||||
case lastCallPreOffer
|
||||
}
|
||||
|
||||
public enum Double: Swift.String {
|
||||
case lastDeviceTokenUpload = "lastDeviceTokenUploadTime"
|
||||
}
|
||||
|
||||
public enum Int: Swift.String {
|
||||
case appMode
|
||||
case hardfork
|
||||
case softfork
|
||||
}
|
||||
|
||||
public enum String : Swift.String {
|
||||
case deviceToken
|
||||
case topBannerWarningToShow
|
||||
}
|
||||
}
|
||||
|
||||
public extension UserDefaults {
|
||||
static let applicationGroup: String = "group.com.loki-project.loki-messenger"
|
||||
|
||||
@objc static var sharedLokiProject: UserDefaults? {
|
||||
UserDefaults(suiteName: UserDefaults.applicationGroup)
|
||||
}
|
||||
}
|
||||
|
||||
public extension UserDefaultsType {
|
||||
subscript(bool: SNUserDefaults.Bool) -> Bool {
|
||||
get { return self.bool(forKey: bool.rawValue) }
|
||||
set { set(newValue, forKey: bool.rawValue) }
|
||||
}
|
||||
|
||||
subscript(date: SNUserDefaults.Date) -> Date? {
|
||||
get { return self.object(forKey: date.rawValue) as? Date }
|
||||
set { set(newValue, forKey: date.rawValue) }
|
||||
}
|
||||
|
||||
subscript(double: SNUserDefaults.Double) -> Double {
|
||||
get { return self.double(forKey: double.rawValue) }
|
||||
set { set(newValue, forKey: double.rawValue) }
|
||||
}
|
||||
|
||||
subscript(int: SNUserDefaults.Int) -> Int {
|
||||
get { return self.integer(forKey: int.rawValue) }
|
||||
set { set(newValue, forKey: int.rawValue) }
|
||||
}
|
||||
|
||||
subscript(string: SNUserDefaults.String) -> String? {
|
||||
get { return self.string(forKey: string.rawValue) }
|
||||
set { set(newValue, forKey: string.rawValue) }
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ FOUNDATION_EXPORT const unsigned char SessionUtilitiesKitVersionString[];
|
|||
#import <SessionUtilitiesKit/MIMETypeUtil.h>
|
||||
#import <SessionUtilitiesKit/NSData+Image.h>
|
||||
#import <SessionUtilitiesKit/NSTimer+Proxying.h>
|
||||
#import <SessionUtilitiesKit/NSUserDefaults+OWS.h>
|
||||
#import <SessionUtilitiesKit/OWSFileSystem.h>
|
||||
#import <SessionUtilitiesKit/OWSMath.h>
|
||||
#import <SessionUtilitiesKit/UIImage+OWS.h>
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
public class AppVersion {
|
||||
private static var _shared: AppVersion?
|
||||
public static var shared: AppVersion {
|
||||
let result: AppVersion = (_shared ?? AppVersion())
|
||||
_shared = result
|
||||
return result
|
||||
}
|
||||
|
||||
public var isFirstLaunch: Bool { self.firstAppVersion != nil }
|
||||
|
||||
public let isValid: Bool
|
||||
public let currentAppVersion: String
|
||||
|
||||
/// The version of the app when it was first launched (`nil` if the app has never been launched before)
|
||||
public var firstAppVersion: String?
|
||||
|
||||
/// The version of the app the last time it was launched (`nil` if the app has never been launched before)
|
||||
public var lastAppVersion: String?
|
||||
|
||||
public var lastCompletedLaunchAppVersion: String?
|
||||
public var lastCompletedLaunchMainAppVersion: String?
|
||||
public var lastCompletedLaunchSAEAppVersion: String?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
private init(using dependencies: Dependencies = Dependencies()) {
|
||||
guard let currentAppVersion: String = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
|
||||
self.isValid = false
|
||||
self.currentAppVersion = ""
|
||||
self.firstAppVersion = nil
|
||||
self.lastAppVersion = nil
|
||||
self.lastCompletedLaunchAppVersion = nil
|
||||
self.lastCompletedLaunchMainAppVersion = nil
|
||||
self.lastCompletedLaunchSAEAppVersion = nil
|
||||
return
|
||||
}
|
||||
|
||||
let oldFirstAppVersion: String? = dependencies[defaults: .appGroup, key: .firstAppVersion]
|
||||
|
||||
self.isValid = true
|
||||
self.currentAppVersion = currentAppVersion
|
||||
self.firstAppVersion = dependencies[defaults: .appGroup, key: .firstAppVersion]
|
||||
.defaulting(to: currentAppVersion)
|
||||
self.lastAppVersion = dependencies[defaults: .appGroup, key: .lastAppVersion]
|
||||
self.lastCompletedLaunchAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchAppVersion]
|
||||
self.lastCompletedLaunchMainAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchMainAppVersion]
|
||||
self.lastCompletedLaunchSAEAppVersion = dependencies[defaults: .appGroup, key: .lastCompletedLaunchSAEAppVersion]
|
||||
|
||||
// Ensure the value for the "first launched version".
|
||||
if oldFirstAppVersion == nil {
|
||||
dependencies[defaults: .appGroup, key: .firstAppVersion] = currentAppVersion
|
||||
}
|
||||
|
||||
// Update the value for the "most recently launched version".
|
||||
dependencies[defaults: .appGroup, key: .lastAppVersion] = currentAppVersion
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
public static func configure(using dependencies: Dependencies) {
|
||||
_shared = AppVersion(using: dependencies)
|
||||
}
|
||||
|
||||
private func appLaunchDidComplete(using dependencies: Dependencies) {
|
||||
lastCompletedLaunchAppVersion = currentAppVersion
|
||||
|
||||
// Update the value for the "most recently launch-completed version".
|
||||
dependencies[defaults: .appGroup, key: .lastCompletedLaunchAppVersion] = currentAppVersion
|
||||
}
|
||||
|
||||
public func mainAppLaunchDidComplete(using dependencies: Dependencies) {
|
||||
lastCompletedLaunchMainAppVersion = currentAppVersion
|
||||
|
||||
dependencies[defaults: .appGroup, key: .lastCompletedLaunchMainAppVersion] = currentAppVersion
|
||||
appLaunchDidComplete(using: dependencies)
|
||||
}
|
||||
|
||||
public func saeLaunchDidComplete(using dependencies: Dependencies) {
|
||||
lastCompletedLaunchSAEAppVersion = currentAppVersion
|
||||
dependencies[defaults: .appGroup, key: .lastCompletedLaunchSAEAppVersion] = currentAppVersion
|
||||
appLaunchDidComplete(using: dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UserDefaults Keys
|
||||
|
||||
private extension UserDefaultsInfo.StringKey {
|
||||
/// The version of the app when it was first launched
|
||||
static let firstAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_FirstAppVersion"
|
||||
|
||||
/// The version of the app when it was last launched
|
||||
static let lastAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_LastVersion"
|
||||
|
||||
static let lastCompletedLaunchAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion"
|
||||
static let lastCompletedLaunchMainAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp"
|
||||
static let lastCompletedLaunchSAEAppVersion: UserDefaultsInfo.StringKey = "kNSUserDefaults_LastCompletedLaunchAppVersion_SAE"
|
||||
}
|
|
@ -3,5 +3,4 @@
|
|||
FOUNDATION_EXPORT double SignalUtilitiesKitVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char SignalUtilitiesKitVersionString[];
|
||||
|
||||
#import <SignalUtilitiesKit/AppVersion.h>
|
||||
#import <SignalUtilitiesKit/OWSViewController.h>
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AppVersion : NSObject
|
||||
|
||||
// The properties are updated immediately after launch.
|
||||
@property (atomic, readonly) NSString *firstAppVersion;
|
||||
@property (atomic, nullable, readonly) NSString *lastAppVersion;
|
||||
@property (atomic, readonly) NSString *currentAppVersion;
|
||||
|
||||
// There properties aren't updated until appLaunchDidComplete is called.
|
||||
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchAppVersion;
|
||||
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchMainAppVersion;
|
||||
@property (atomic, nullable, readonly) NSString *lastCompletedLaunchSAEAppVersion;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (void)mainAppLaunchDidComplete;
|
||||
- (void)saeLaunchDidComplete;
|
||||
|
||||
- (BOOL)isFirstLaunch;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,133 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppVersion.h"
|
||||
#import "NSUserDefaults+OWS.h"
|
||||
#import <SignalCoreKit/OWSAsserts.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const kNSUserDefaults_FirstAppVersion = @"kNSUserDefaults_FirstAppVersion";
|
||||
NSString *const kNSUserDefaults_LastAppVersion = @"kNSUserDefaults_LastVersion";
|
||||
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion = @"kNSUserDefaults_LastCompletedLaunchAppVersion";
|
||||
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp
|
||||
= @"kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp";
|
||||
NSString *const kNSUserDefaults_LastCompletedLaunchAppVersion_SAE
|
||||
= @"kNSUserDefaults_LastCompletedLaunchAppVersion_SAE";
|
||||
|
||||
@interface AppVersion ()
|
||||
|
||||
@property (atomic) NSString *firstAppVersion;
|
||||
@property (atomic, nullable) NSString *lastAppVersion;
|
||||
@property (atomic) NSString *currentAppVersion;
|
||||
|
||||
@property (atomic, nullable) NSString *lastCompletedLaunchAppVersion;
|
||||
@property (atomic, nullable) NSString *lastCompletedLaunchMainAppVersion;
|
||||
@property (atomic, nullable) NSString *lastCompletedLaunchSAEAppVersion;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation AppVersion
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
static AppVersion *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [AppVersion new];
|
||||
[instance configure];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (void)configure {
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
self.currentAppVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||
|
||||
// The version of the app when it was first launched.
|
||||
// nil if the app has never been launched before.
|
||||
self.firstAppVersion = [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_FirstAppVersion];
|
||||
// The version of the app the last time it was launched.
|
||||
// nil if the app has never been launched before.
|
||||
self.lastAppVersion = [[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastAppVersion];
|
||||
self.lastCompletedLaunchAppVersion =
|
||||
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
|
||||
self.lastCompletedLaunchMainAppVersion =
|
||||
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp];
|
||||
self.lastCompletedLaunchSAEAppVersion =
|
||||
[[NSUserDefaults appUserDefaults] objectForKey:kNSUserDefaults_LastCompletedLaunchAppVersion_SAE];
|
||||
|
||||
// Ensure the value for the "first launched version".
|
||||
if (!self.firstAppVersion) {
|
||||
self.firstAppVersion = self.currentAppVersion;
|
||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion forKey:kNSUserDefaults_FirstAppVersion];
|
||||
}
|
||||
|
||||
// Update the value for the "most recently launched version".
|
||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion forKey:kNSUserDefaults_LastAppVersion];
|
||||
[[NSUserDefaults appUserDefaults] synchronize];
|
||||
|
||||
// The long version string looks like an IPv4 address.
|
||||
// To prevent the log scrubber from scrubbing it,
|
||||
// we replace . with _.
|
||||
NSString *longVersionString = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]
|
||||
stringByReplacingOccurrencesOfString:@"."
|
||||
withString:@"_"];
|
||||
|
||||
OWSLogInfo(@"firstAppVersion: %@", self.firstAppVersion);
|
||||
OWSLogInfo(@"lastAppVersion: %@", self.lastAppVersion);
|
||||
OWSLogInfo(@"currentAppVersion: %@ (%@)", self.currentAppVersion, longVersionString);
|
||||
|
||||
OWSLogInfo(@"lastCompletedLaunchAppVersion: %@", self.lastCompletedLaunchAppVersion);
|
||||
OWSLogInfo(@"lastCompletedLaunchMainAppVersion: %@", self.lastCompletedLaunchMainAppVersion);
|
||||
OWSLogInfo(@"lastCompletedLaunchSAEAppVersion: %@", self.lastCompletedLaunchSAEAppVersion);
|
||||
}
|
||||
|
||||
- (void)appLaunchDidComplete
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"appLaunchDidComplete");
|
||||
|
||||
self.lastCompletedLaunchAppVersion = self.currentAppVersion;
|
||||
|
||||
// Update the value for the "most recently launch-completed version".
|
||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
|
||||
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion];
|
||||
[[NSUserDefaults appUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
- (void)mainAppLaunchDidComplete
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
self.lastCompletedLaunchMainAppVersion = self.currentAppVersion;
|
||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
|
||||
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion_MainApp];
|
||||
|
||||
[self appLaunchDidComplete];
|
||||
}
|
||||
|
||||
- (void)saeLaunchDidComplete
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
self.lastCompletedLaunchSAEAppVersion = self.currentAppVersion;
|
||||
[[NSUserDefaults appUserDefaults] setObject:self.currentAppVersion
|
||||
forKey:kNSUserDefaults_LastCompletedLaunchAppVersion_SAE];
|
||||
|
||||
[self appLaunchDidComplete];
|
||||
}
|
||||
|
||||
- (BOOL)isFirstLaunch
|
||||
{
|
||||
return self.firstAppVersion != nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -7,6 +7,7 @@ import Foundation
|
|||
public class TestDependencies: Dependencies {
|
||||
private var singletonInstances: [Int: Any] = [:]
|
||||
private var cacheInstances: [Int: MutableCacheType] = [:]
|
||||
private var defaultsInstances: [Int: (any UserDefaultsType)] = [:]
|
||||
|
||||
// MARK: - Subscript Access
|
||||
|
||||
|
@ -28,6 +29,15 @@ public class TestDependencies: Dependencies {
|
|||
set { cacheInstances[cache.key] = newValue.map { cache.mutableInstance($0) } }
|
||||
}
|
||||
|
||||
override public subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>) -> U {
|
||||
return getValueSettingIfNull(defaults: defaults, &defaultsInstances)
|
||||
}
|
||||
|
||||
public subscript<U>(defaults defaults: UserDefaultsInfo.Config<U>) -> U? {
|
||||
get { return (defaultsInstances[defaults.key] as? U) }
|
||||
set { defaultsInstances[defaults.key] = newValue }
|
||||
}
|
||||
|
||||
// MARK: - Timing and Async Handling
|
||||
|
||||
private var _dateNow: Atomic<Date?>
|
||||
|
@ -120,4 +130,17 @@ public class TestDependencies: Dependencies {
|
|||
|
||||
return cache.immutableInstance(value)
|
||||
}
|
||||
|
||||
@discardableResult private func getValueSettingIfNull<U>(
|
||||
defaults: UserDefaultsInfo.Config<U>,
|
||||
_ store: inout [Int: (any UserDefaultsType)]
|
||||
) -> U {
|
||||
guard let value: U = (store[defaults.key] as? U) else {
|
||||
let value: U = defaults.createInstance(self)
|
||||
store[defaults.key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue