Refactored AppContext and AppRediness to Swift

pull/946/head
Morgan Pretty 4 months ago
parent f532496ee4
commit cbcdb9b37f

@ -208,4 +208,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: dd814a5a92577bb2a94dac6a1cc482f193721cdf
COCOAPODS: 1.12.1
COCOAPODS: 1.14.3

@ -273,8 +273,6 @@
C32C5A24256DB7DB003C73A2 /* SNUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB6B255A580F00E217F9 /* SNUserDefaults.swift */; };
C32C5A48256DB8F0003C73A2 /* BuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */; };
C32C5A88256DBCF9003C73A2 /* MessageReceiver+ClosedGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = C32C5A87256DBCF9003C73A2 /* MessageReceiver+ClosedGroups.swift */; };
C32C5C3D256DCBAF003C73A2 /* AppReadiness.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB75255A581000E217F9 /* AppReadiness.m */; };
C32C5C46256DCBB2003C73A2 /* AppReadiness.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB01255A580700E217F9 /* AppReadiness.h */; settings = {ATTRIBUTES = (Public, ); }; };
C32C5D83256DD5B6003C73A2 /* SSKKeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBBC255A581600E217F9 /* SSKKeychainStorage.swift */; };
C32C5DBF256DD743003C73A2 /* ClosedGroupPoller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB34255A580B00E217F9 /* ClosedGroupPoller.swift */; };
C32C5DC9256DD935003C73A2 /* ProxiedContentDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDAF2255A580500E217F9 /* ProxiedContentDownloader.swift */; };
@ -420,8 +418,6 @@
C3D9E35E25675F640040E4F3 /* OWSFileSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDA8E255A57FD00E217F9 /* OWSFileSystem.m */; };
C3D9E379256760340040E4F3 /* MIMETypeUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3D9E38A256760390040E4F3 /* OWSFileSystem.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDBAB255A581500E217F9 /* OWSFileSystem.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3D9E39B256763C20040E4F3 /* AppContext.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB85255A581100E217F9 /* AppContext.m */; };
C3D9E3A4256763DE0040E4F3 /* AppContext.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB8A255A581200E217F9 /* AppContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3D9E4C02567767F0040E4F3 /* DataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBB6255A581600E217F9 /* DataSource.m */; };
C3D9E4D12567777D0040E4F3 /* OWSMediaUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB22255A580900E217F9 /* OWSMediaUtils.swift */; };
C3D9E4DA256778410040E4F3 /* UIImage+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB81255A581100E217F9 /* UIImage+OWS.m */; };
@ -623,6 +619,11 @@
FD3C906D27E43C4B00CD579F /* MessageSenderEncryptionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C906C27E43C4B00CD579F /* MessageSenderEncryptionSpec.swift */; };
FD3C907127E445E500CD579F /* MessageReceiverDecryptionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C907027E445E500CD579F /* MessageReceiverDecryptionSpec.swift */; };
FD3E0C84283B5835002A425C /* SessionThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3E0C83283B5835002A425C /* SessionThreadViewModel.swift */; };
FD428B192B4B576F006D0888 /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD428B182B4B576F006D0888 /* AppContext.swift */; };
FD428B1B2B4B6098006D0888 /* Notifications+Lifecycle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD428B1A2B4B6098006D0888 /* Notifications+Lifecycle.swift */; };
FD428B1D2B4B6FDC006D0888 /* UIApplicationState+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD428B1C2B4B6FDC006D0888 /* UIApplicationState+Utilities.swift */; };
FD428B1F2B4B758B006D0888 /* AppReadiness.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD428B1E2B4B758B006D0888 /* AppReadiness.swift */; };
FD428B212B4B75EA006D0888 /* Singleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD428B202B4B75EA006D0888 /* Singleton.swift */; };
FD42F9A8285064B800A0C77D /* PushNotificationAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDBDE255A581900E217F9 /* PushNotificationAPI.swift */; };
FD4324302999F0BC008A0213 /* ValidatableResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD43242F2999F0BC008A0213 /* ValidatableResponse.swift */; };
FD432432299C6933008A0213 /* _011_AddPendingReadReceipts.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD432431299C6933008A0213 /* _011_AddPendingReadReceipts.swift */; };
@ -1422,7 +1423,6 @@
C33FDAF2255A580500E217F9 /* ProxiedContentDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProxiedContentDownloader.swift; sourceTree = "<group>"; };
C33FDAFC255A580600E217F9 /* MIMETypeUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIMETypeUtil.h; sourceTree = "<group>"; };
C33FDAFD255A580600E217F9 /* LRUCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LRUCache.swift; sourceTree = "<group>"; };
C33FDB01255A580700E217F9 /* AppReadiness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppReadiness.h; sourceTree = "<group>"; };
C33FDB14255A580800E217F9 /* OWSMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMath.h; sourceTree = "<group>"; };
C33FDB1C255A580900E217F9 /* UIImage+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+OWS.h"; sourceTree = "<group>"; };
C33FDB22255A580900E217F9 /* OWSMediaUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSMediaUtils.swift; sourceTree = "<group>"; };
@ -1439,12 +1439,9 @@
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>"; };
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>"; };
C33FDB8A255A581200E217F9 /* AppContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppContext.h; sourceTree = "<group>"; };
C33FDB8F255A581200E217F9 /* ParamParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParamParser.swift; sourceTree = "<group>"; };
C33FDBA8255A581500E217F9 /* LinkPreviewDraft.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkPreviewDraft.swift; sourceTree = "<group>"; };
C33FDBAB255A581500E217F9 /* OWSFileSystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSFileSystem.h; sourceTree = "<group>"; };
@ -1740,6 +1737,11 @@
FD3C907027E445E500CD579F /* MessageReceiverDecryptionSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReceiverDecryptionSpec.swift; sourceTree = "<group>"; };
FD3C907427E83AC200CD579F /* OpenGroupServerIdLookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupServerIdLookup.swift; sourceTree = "<group>"; };
FD3E0C83283B5835002A425C /* SessionThreadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionThreadViewModel.swift; sourceTree = "<group>"; };
FD428B182B4B576F006D0888 /* AppContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContext.swift; sourceTree = "<group>"; };
FD428B1A2B4B6098006D0888 /* Notifications+Lifecycle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notifications+Lifecycle.swift"; sourceTree = "<group>"; };
FD428B1C2B4B6FDC006D0888 /* UIApplicationState+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplicationState+Utilities.swift"; sourceTree = "<group>"; };
FD428B1E2B4B758B006D0888 /* AppReadiness.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReadiness.swift; sourceTree = "<group>"; };
FD428B202B4B75EA006D0888 /* Singleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Singleton.swift; sourceTree = "<group>"; };
FD43242F2999F0BC008A0213 /* ValidatableResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatableResponse.swift; sourceTree = "<group>"; };
FD432431299C6933008A0213 /* _011_AddPendingReadReceipts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _011_AddPendingReadReceipts.swift; sourceTree = "<group>"; };
FD432433299C6985008A0213 /* PendingReadReceipt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PendingReadReceipt.swift; sourceTree = "<group>"; };
@ -2629,11 +2631,10 @@
B8A582B0258C66C900AFD84C /* General */ = {
isa = PBXGroup;
children = (
FD428B182B4B576F006D0888 /* AppContext.swift */,
7BD477A727EC39F5004E2822 /* Atomic.swift */,
FDC438CC27BC641200C60D73 /* Set+Utilities.swift */,
7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */,
C33FDB8A255A581200E217F9 /* AppContext.h */,
C33FDB85255A581100E217F9 /* AppContext.m */,
C3C2A5D12553860800C340D1 /* Array+Utilities.swift */,
FDC4383D27B4708600C60D73 /* Atomic.swift */,
C33FDAA8255A57FF00E217F9 /* BuildConfiguration.swift */,
@ -2672,6 +2673,8 @@
FD5D201D27B0D87C00FEA984 /* SessionId.swift */,
7B7CB191271508AD0079FF93 /* CallRingTonePlayer.swift */,
7B0EFDED274F598600FFAAE7 /* TimestampUtils.swift */,
FD428B1A2B4B6098006D0888 /* Notifications+Lifecycle.swift */,
FD428B202B4B75EA006D0888 /* Singleton.swift */,
);
path = General;
sourceTree = "<group>";
@ -3250,8 +3253,7 @@
C3BBE0B32554F0D30050F1E3 /* Utilities */ = {
isa = PBXGroup;
children = (
C33FDB01255A580700E217F9 /* AppReadiness.h */,
C33FDB75255A581000E217F9 /* AppReadiness.m */,
FD428B1E2B4B758B006D0888 /* AppReadiness.swift */,
FD23CE232A675C440000B97C /* Crypto+SessionMessagingKit.swift */,
FD859EF127BF6BA200510D0C /* Data+Utilities.swift */,
C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */,
@ -3599,6 +3601,7 @@
C33FDB38255A580B00E217F9 /* OWSBackgroundTask.h */,
C33FDC1B255A581F00E217F9 /* OWSBackgroundTask.m */,
FD29598C2A43BC0B00888A17 /* Version.swift */,
FD428B1C2B4B6FDC006D0888 /* UIApplicationState+Utilities.swift */,
);
path = Utilities;
sourceTree = "<group>";
@ -4538,7 +4541,6 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
C3D9E3A4256763DE0040E4F3 /* AppContext.h in Headers */,
C3D9E38A256760390040E4F3 /* OWSFileSystem.h in Headers */,
C3D9E379256760340040E4F3 /* MIMETypeUtil.h in Headers */,
C3D9E50E25677A510040E4F3 /* DataSource.h in Headers */,
@ -4559,7 +4561,6 @@
buildActionMask = 2147483647;
files = (
C3C2A6F425539DE700C340D1 /* SessionMessagingKit.h in Headers */,
C32C5C46256DCBB2003C73A2 /* AppReadiness.h in Headers */,
B8856D72256F1421001CE70E /* OWSWindowManager.h in Headers */,
B8856CF7256F105E001CE70E /* OWSAudioPlayer.h in Headers */,
);
@ -5800,10 +5801,10 @@
FDE658A129418C7900A33BC1 /* CryptoKit+Utilities.swift in Sources */,
FDFD645927F26C6800808CA1 /* Array+Utilities.swift in Sources */,
7B1D74B027C365960030B423 /* Timer+MainThread.swift in Sources */,
FD428B192B4B576F006D0888 /* AppContext.swift in Sources */,
C32C5D83256DD5B6003C73A2 /* SSKKeychainStorage.swift in Sources */,
FD559DF52A7368CB00C7C62A /* DispatchQueue+Utilities.swift in Sources */,
FDF8488329405A12007DCAE5 /* BatchResponse.swift in Sources */,
C3D9E39B256763C20040E4F3 /* AppContext.m in Sources */,
FDFF9FDF2A787F57005E0628 /* JSONEncoder+Utilities.swift in Sources */,
C3BBE0A82554D4DE0050F1E3 /* JSON.swift in Sources */,
FD17D7C127F5200100122BE0 /* TypedTableDefinition.swift in Sources */,
@ -5865,6 +5866,7 @@
FD848B9A28442CE6000E298B /* StorageError.swift in Sources */,
FD17D7B827F51ECA00122BE0 /* Migration.swift in Sources */,
FD7728982849E8110018502F /* UITableView+ReusableView.swift in Sources */,
FD428B1B2B4B6098006D0888 /* Notifications+Lifecycle.swift in Sources */,
7B0EFDEE274F598600FFAAE7 /* TimestampUtils.swift in Sources */,
FD52090028AF6153006098F6 /* OWSBackgroundTask.m in Sources */,
C32C5DDB256DD9FF003C73A2 /* ContentProxy.swift in Sources */,
@ -5876,8 +5878,10 @@
FDF8488629405A61007DCAE5 /* Request.swift in Sources */,
FD23CE302A67B8820000B97C /* Caches.swift in Sources */,
FD17D7A127F40D2500122BE0 /* Storage.swift in Sources */,
FD428B1D2B4B6FDC006D0888 /* UIApplicationState+Utilities.swift in Sources */,
FD1A94FB2900D1C2000D73D3 /* PersistableRecord+Utilities.swift in Sources */,
FD5D201E27B0D87C00FEA984 /* SessionId.swift in Sources */,
FD428B212B4B75EA006D0888 /* Singleton.swift in Sources */,
C32C5A24256DB7DB003C73A2 /* SNUserDefaults.swift in Sources */,
FD8ECF922938552800C0D1BB /* Threading.swift in Sources */,
B8856D7B256F14F4001CE70E /* UIView+OWS.m in Sources */,
@ -6014,6 +6018,7 @@
FDF8488829405A9A007DCAE5 /* SOGSBatchRequest.swift in Sources */,
FD245C662850665900B966DD /* OpenGroupAPI.swift in Sources */,
FD245C5B2850660500B966DD /* ReadReceipt.swift in Sources */,
FD428B1F2B4B758B006D0888 /* AppReadiness.swift in Sources */,
B8F5F60325EDE16F003BF8D4 /* DataExtractionNotification.swift in Sources */,
C3A71D1E25589AC30043A11F /* WebSocketProto.swift in Sources */,
C3C2A7852553AAF300C340D1 /* SessionProtos.pb.swift in Sources */,
@ -6086,7 +6091,6 @@
FD245C642850664F00B966DD /* Threading.swift in Sources */,
FD848B8D283E0B26000E298B /* MessageInputTypes.swift in Sources */,
FDFE75B12ABD2D2400655640 /* _016_MakeBrokenProfileTimestampsNullable.swift in Sources */,
C32C5C3D256DCBAF003C73A2 /* AppReadiness.m in Sources */,
FD09799B27FFC82D00936362 /* Quote.swift in Sources */,
FD245C6D285066A400B966DD /* NotifyPushServerJob.swift in Sources */,
C3C2A74425539EB700C340D1 /* Message.swift in Sources */,

@ -394,7 +394,8 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
self.hasEnded = true
DispatchQueue.main.async {
if let currentBanner = IncomingCallBanner.current { currentBanner.dismiss() }
if let callVC = CurrentAppContext().frontmostViewController() as? CallVC { callVC.handleEndCallMessage() }
guard Singleton.hasAppContext else { return }
if let callVC = Singleton.appContext.frontmostViewController as? CallVC { callVC.handleEndCallMessage() }
if let miniCallView = MiniCallView.current { miniCallView.dismiss() }
AppEnvironment.shared.callManager.reportCurrentCallEnded(reason: .remoteEnded)
}

@ -20,11 +20,14 @@ extension SessionCallManager {
public func answerCallAction() -> Bool {
guard let call: SessionCall = (self.currentCall as? SessionCall) else { return false }
if let _ = CurrentAppContext().frontmostViewController() as? CallVC {
if Singleton.hasAppContext, Singleton.appContext.frontmostViewController is CallVC {
call.answerSessionCall()
}
else {
guard let presentingVC = CurrentAppContext().frontmostViewController() else { return false } // FIXME: Handle more gracefully
guard
Singleton.hasAppContext,
let presentingVC = Singleton.appContext.frontmostViewController
else { return false } // FIXME: Handle more gracefully
let callVC = CallVC(for: call)
if let conversationVC = presentingVC as? ConversationVC {

@ -27,7 +27,7 @@ extension SessionCallManager: CXProviderDelegate {
guard let call: SessionCall = (self.currentCall as? SessionCall) else { return action.fail() }
if CurrentAppContext().isMainAppAndActive {
if Singleton.hasAppContext && Singleton.appContext.isMainAppAndActive {
if answerCallAction() {
action.fulfill()
}

@ -143,7 +143,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
UserDefaults.sharedLokiProject?[.isCallOngoing] = false
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = nil
if CurrentAppContext().isInBackground() {
if Singleton.hasAppContext && Singleton.appContext.isInBackground {
(UIApplication.shared.delegate as? AppDelegate)?.stopPollers()
DDLog.flushLog()
}
@ -190,7 +190,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
}
public static func suspendDatabaseIfCallEndedInBackground() {
if CurrentAppContext().isInBackground() {
if Singleton.hasAppContext && Singleton.appContext.isInBackground {
// Stop all jobs except for message sending and when completed suspend the database
JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend) {
Storage.suspendDatabaseAccess()
@ -213,9 +213,9 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
}
DispatchQueue.main.async {
guard CurrentAppContext().isMainAppAndActive else { return }
guard Singleton.hasAppContext && Singleton.appContext.isMainAppAndActive else { return }
guard let presentingVC = CurrentAppContext().frontmostViewController() else {
guard let presentingVC = Singleton.appContext.frontmostViewController else {
preconditionFailure() // FIXME: Handle more gracefully
}
@ -235,6 +235,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
}
public func handleAnswerMessage(_ message: CallMessage) {
guard Singleton.hasAppContext else { return }
guard Thread.isMainThread else {
DispatchQueue.main.async {
self.handleAnswerMessage(message)
@ -242,10 +243,11 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
return
}
(CurrentAppContext().frontmostViewController() as? CallVC)?.handleAnswerMessage(message)
(Singleton.appContext.frontmostViewController as? CallVC)?.handleAnswerMessage(message)
}
public func dismissAllCallUI() {
guard Singleton.hasAppContext else { return }
guard Thread.isMainThread else {
DispatchQueue.main.async {
self.dismissAllCallUI()
@ -254,7 +256,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
}
IncomingCallBanner.current?.dismiss()
(CurrentAppContext().frontmostViewController() as? CallVC)?.handleEndCallMessage()
(Singleton.appContext.frontmostViewController as? CallVC)?.handleEndCallMessage()
MiniCallView.current?.dismiss()
}
}

@ -518,8 +518,10 @@ final class CallVC: UIViewController, VideoPreviewDelegate {
}
private func addFloatingVideoView() {
guard Singleton.hasAppContext else { return }
let safeAreaInsets = UIApplication.shared.keyWindow?.safeAreaInsets
CurrentAppContext().mainWindow?.addSubview(floatingViewContainer)
Singleton.appContext.mainWindow?.addSubview(floatingViewContainer)
floatingViewContainer.autoPinEdge(toSuperviewEdge: .right, withInset: Values.smallSpacing)
let topMargin = (safeAreaInsets?.top ?? 0) + Values.veryLargeSpacing
floatingViewContainer.autoPinEdge(toSuperviewEdge: .top, withInset: topMargin)

@ -196,7 +196,10 @@ final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate {
public func showCallVC(answer: Bool) {
dismiss()
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } // FIXME: Handle more gracefully
guard
Singleton.hasAppContext,
let presentingVC = Singleton.appContext.frontmostViewController
else { preconditionFailure() } // FIXME: Handle more gracefully
let callVC = CallVC(for: self.call)
if let conversationVC = presentingVC as? ConversationVC {
@ -213,8 +216,9 @@ final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate {
}
public func show() {
guard Singleton.hasAppContext, let window: UIWindow = Singleton.appContext.mainWindow else { return }
self.alpha = 0.0
let window = CurrentAppContext().mainWindow!
window.addSubview(self)
let topMargin = window.safeAreaInsets.top - Values.smallSpacing

@ -153,13 +153,18 @@ final class MiniCallView: UIView, RTCVideoViewDelegate {
@objc private func handleTap(_ gestureRecognizer: UITapGestureRecognizer) {
dismiss()
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() } // FIXME: Handle more gracefully
guard
Singleton.hasAppContext,
let presentingVC = Singleton.appContext.frontmostViewController else { preconditionFailure() } // FIXME: Handle more gracefully
presentingVC.present(callVC, animated: true, completion: nil)
}
public func show() {
self.alpha = 0.0
guard let window: UIWindow = CurrentAppContext().mainWindow else { return }
guard
Singleton.hasAppContext,
let window: UIWindow = Singleton.appContext.mainWindow
else { return }
window.addSubview(self)
left = self.autoPinEdge(toSuperviewEdge: .left)

@ -2287,7 +2287,7 @@ extension ConversationVC:
self.viewModel.stopAudio()
// Create URL
let directory: String = OWSTemporaryDirectory()
let directory: String = Singleton.appContext.temporaryDirectory
let fileName: String = "\(SnodeAPI.currentOffsetTimestampMs()).m4a"
let url: URL = URL(fileURLWithPath: directory).appendingPathComponent(fileName)

@ -62,7 +62,7 @@ final class VoiceMessageRecordingView: UIView {
private lazy var chevronImageView: UIImageView = {
let result: UIImageView = UIImageView(
image: (CurrentAppContext().isRTL ?
image: (Singleton.hasAppContext && Singleton.appContext.isRTL ?
UIImage(named: "small_chevron_left")?.withHorizontallyFlippedOrientation() :
UIImage(named: "small_chevron_left")
)?
@ -276,9 +276,9 @@ final class VoiceMessageRecordingView: UIView {
// MARK: - Interaction
func handleLongPressMoved(to location: CGPoint) {
if (!CurrentAppContext().isRTL && location.x < bounds.center.x) || (CurrentAppContext().isRTL && location.x > bounds.center.x) {
if (Singleton.hasAppContext && (!Singleton.appContext.isRTL && location.x < bounds.center.x) || (Singleton.appContext.isRTL && location.x > bounds.center.x)) {
let translationX = location.x - bounds.center.x
let sign: CGFloat = (CurrentAppContext().isRTL ? 1 : -1)
let sign: CGFloat = (Singleton.appContext.isRTL ? 1 : -1)
let chevronDamping: CGFloat = 4
let labelDamping: CGFloat = 3
let chevronX = (chevronDamping * (sqrt(abs(translationX)) / sqrt(chevronDamping))) * sign

@ -44,7 +44,7 @@ import SessionUtilitiesKit
NotificationCenter.default.addObserver(self,
selector: #selector(didBecomeActive),
name: NSNotification.Name.OWSApplicationDidBecomeActive,
name: .sessionDidBecomeActive,
object: nil)
}

@ -111,7 +111,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
// Flip horizontally for RTL languages
result.transform = CGAffineTransform.identity
.scaledBy(
x: (CurrentAppContext().isRTL ? -1 : 1),
x: (Singleton.hasAppContext && Singleton.appContext.isRTL ? -1 : 1),
y: 1
)
@ -795,8 +795,8 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
// Only allow swipes to the left; allowing swipes to the right gets in the way of
// the default iOS swipe to go back gesture
guard
(CurrentAppContext().isRTL && v.x > 0) ||
(!CurrentAppContext().isRTL && v.x < 0)
(Singleton.hasAppContext && Singleton.appContext.isRTL && v.x > 0) ||
(!Singleton.hasAppContext || !Singleton.appContext.isRTL && v.x < 0)
else { return false }
return abs(v.x) > abs(v.y) // It has to be more horizontal than vertical
@ -935,8 +935,8 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
.translation(in: self)
.x
.clamp(
(CurrentAppContext().isRTL ? 0 : -CGFloat.greatestFiniteMagnitude),
(CurrentAppContext().isRTL ? CGFloat.greatestFiniteMagnitude : 0)
(Singleton.hasAppContext && Singleton.appContext.isRTL ? 0 : -CGFloat.greatestFiniteMagnitude),
(Singleton.hasAppContext && Singleton.appContext.isRTL ? CGFloat.greatestFiniteMagnitude : 0)
)
switch gestureRecognizer.state {
@ -945,7 +945,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
case .changed:
// The idea here is to asymptotically approach a maximum drag distance
let damping: CGFloat = 20
let sign: CGFloat = (CurrentAppContext().isRTL ? 1 : -1)
let sign: CGFloat = (Singleton.hasAppContext && Singleton.appContext.isRTL ? 1 : -1)
let x = (damping * (sqrt(abs(translationX)) / sqrt(damping))) * sign
viewsToMoveForReply.forEach { $0.transform = CGAffineTransform(translationX: x, y: 0) }
@ -1205,7 +1205,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
// we only highlight those cases)
normalizedBody
.ranges(
of: (CurrentAppContext().isRTL ?
of: (Singleton.hasAppContext && Singleton.appContext.isRTL ?
"(\(part.lowercased()))(^|[^a-zA-Z0-9])" :
"(^|[^a-zA-Z0-9])(\(part.lowercased()))"
),

@ -13,7 +13,7 @@ extension Emoji {
static func warmAvailableCache() {
owsAssertDebug(!Thread.isMainThread)
guard CurrentAppContext().isMainAppAndActive else { return }
guard Singleton.hasAppContext && Singleton.appContext.isMainAppAndActive else { return }
var availableCache = [Emoji: Bool]()
var uncachedEmoji = [Emoji]()

@ -223,7 +223,7 @@ final class HomeVC: BaseVC, SessionUtilRespondingViewController, UITableViewData
// Note: This is a hack to ensure `isRTL` is initially gets run on the main thread so the value
// is cached (it gets called on background threads and if it hasn't cached the value then it can
// cause odd performance issues since it accesses UIKit)
_ = CurrentAppContext().isRTL
if Singleton.hasAppContext { _ = Singleton.appContext.isRTL }
// Preparation
SessionApp.homeViewController.mutate { $0 = self }

@ -123,7 +123,7 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
NotificationCenter.default.addObserver(
self,
selector: #selector(didBecomeActive),
name: .OWSApplicationDidBecomeActive,
name: .sessionDidBecomeActive,
object: nil
)

@ -637,7 +637,7 @@ private class DoneButton: UIView {
private lazy var chevron: UIView = {
let image: UIImage = {
guard CurrentAppContext().isRTL else { return #imageLiteral(resourceName: "small_chevron_right") }
guard Singleton.hasAppContext && Singleton.appContext.isRTL else { return #imageLiteral(resourceName: "small_chevron_right") }
return #imageLiteral(resourceName: "small_chevron_left")
}()

@ -32,7 +32,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
startTime = CACurrentMediaTime()
// These should be the first things we do (the startup process can fail without them)
SetCurrentAppContext(MainAppContext())
Singleton.setup(appContext: MainAppContext())
verifyDBKeysAvailableBeforeBackgroundLaunch()
Cryptography.seedRandom()
@ -97,7 +97,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
guard !SNUtilitiesKit.isRunningTests else { return true }
self.window = mainWindow
CurrentAppContext().mainWindow = mainWindow
Singleton.appContext.setMainWindow(mainWindow)
// Show LoadingViewController until the async database view registrations are complete.
mainWindow.rootViewController = self.loadingViewController
@ -147,7 +147,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
SNLog("Checking for pending migrations")
let initialLaunchFailed: Bool = self.initialLaunchFailed
AppReadiness.invalidate()
Singleton.appReadiness.invalidate()
// If the user went to the background too quickly then the database can be suspended before
// properly starting up, in this case an alert will be shown but we can recover from it so
@ -220,7 +220,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
ensureRootViewController(calledFrom: .didBecomeActive)
AppReadiness.runNowOrWhenAppDidBecomeReady { [weak self] in
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady { [weak self] in
self?.handleActivation()
/// Clear all notifications whenever we become active once the app is ready
@ -235,7 +235,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
// On every activation, clear old temp directories.
ClearOldTemporaryDirectories()
guard Singleton.hasAppContext else { return }
Singleton.appContext.clearOldTemporaryDirectories()
}
func applicationWillResignActive(_ application: UIApplication) {
@ -275,7 +277,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
BackgroundPoller.isValid = false
if CurrentAppContext().isInBackground() {
if Singleton.hasAppContext && Singleton.appContext.isInBackground {
Storage.suspendDatabaseAccess()
}
@ -289,19 +291,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// incorrectly set this 'isValid' flag to true after it should have timed out)
BackgroundPoller.isValid = true
AppReadiness.runNowOrWhenAppDidBecomeReady {
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady {
// If the 'AppReadiness' process takes too long then it's possible for the user to open
// the app after this closure is registered but before it's actually triggered - this can
// result in the `BackgroundPoller` incorrectly getting called in the foreground, this check
// is here to prevent that
guard CurrentAppContext().isInBackground() else { return }
guard Singleton.hasAppContext && Singleton.appContext.isInBackground else { return }
BackgroundPoller.poll { result in
guard BackgroundPoller.isValid else { return }
BackgroundPoller.isValid = false
if CurrentAppContext().isInBackground() {
if Singleton.hasAppContext && Singleton.appContext.isInBackground {
Storage.suspendDatabaseAccess()
}
@ -343,7 +345,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
/// **Note:** This this does much more than set a flag - it will also run all deferred blocks (including the JobRunner
/// `appDidBecomeActive` method hence why it **must** also come after calling
/// `JobRunner.appDidFinishLaunching()`)
AppReadiness.setAppIsReady()
Singleton.appReadiness.setAppReady()
/// Remove the sleep blocking once the startup is done (needs to run on the main thread and sleeping while
/// doing the startup could suspend the database causing errors/crashes
@ -504,7 +506,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
private func enableBackgroundRefreshIfNecessary() {
AppReadiness.runNowOrWhenAppDidBecomeReady {
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady {
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
}
}
@ -523,7 +525,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
startPollersIfNeeded()
if CurrentAppContext().isMainApp {
if Singleton.hasAppContext && Singleton.appContext.isMainApp {
handleAppActivatedWithOngoingCallIfNeeded()
}
}
@ -538,7 +540,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
guard
Storage.shared.isValid &&
(
AppReadiness.isAppReady() ||
Singleton.appReadiness.isAppReady ||
lifecycleMethod == .finishLaunching ||
lifecycleMethod == .enterForeground(initialLaunchFailed: true)
) &&
@ -654,10 +656,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
private func clearAllNotificationsAndRestoreBadgeCount() {
AppReadiness.runNowOrWhenAppDidBecomeReady {
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady {
AppEnvironment.shared.notificationPresenter.clearAllNotifications()
guard CurrentAppContext().isMainApp else { return }
guard Singleton.hasAppContext && Singleton.appContext.isMainApp else { return }
/// On application startup the `Storage.read` can be slightly slow while GRDB spins up it's database
/// read pools (up to a few seconds), since this read is blocking we want to dispatch it to run async to ensure
@ -675,7 +677,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
AppReadiness.runNowOrWhenAppDidBecomeReady {
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady {
guard Identity.userCompletedRequiredOnboarding() else { return }
SessionApp.homeViewController.wrappedValue?.createNewConversation()
@ -694,7 +696,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
return
}
AppReadiness.runNowOrWhenAppDidBecomeReady {
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady {
// We need to respect the in-app notification sound preference. This method, which is called
// for modern UNUserNotification users, could be a place to do that, but since we'd still
// need to handle this behavior for legacy UINotification users anyway, we "allow" all
@ -708,7 +710,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
/// the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from
/// application:didFinishLaunchingWithOptions:.
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
AppReadiness.runNowOrWhenAppDidBecomeReady {
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady {
AppEnvironment.shared.userNotificationActionHandler.handleNotificationResponse(response, completionHandler: completionHandler)
}
}
@ -737,7 +739,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
guard let callerId: String = notification.userInfo?[Notification.Key.senderId.rawValue] as? String else {
return
}
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() }
guard
Singleton.hasAppContext,
let presentingVC = Singleton.appContext.frontmostViewController
else { preconditionFailure() }
let callMissedTipsModal: CallMissedTipsModal = CallMissedTipsModal(
caller: Profile.displayName(id: callerId)
@ -821,15 +826,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func handleAppActivatedWithOngoingCallIfNeeded() {
guard
let call: SessionCall = (AppEnvironment.shared.callManager.currentCall as? SessionCall),
MiniCallView.current == nil
MiniCallView.current == nil,
Singleton.hasAppContext
else { return }
if let callVC = CurrentAppContext().frontmostViewController() as? CallVC, callVC.call.uuid == call.uuid {
if let callVC = Singleton.appContext.frontmostViewController as? CallVC, callVC.call.uuid == call.uuid {
return
}
// FIXME: Handle more gracefully
guard let presentingVC = CurrentAppContext().frontmostViewController() else { preconditionFailure() }
guard let presentingVC = Singleton.appContext.frontmostViewController else { preconditionFailure() }
let callVC: CallVC = CallVC(for: call)

@ -4,14 +4,14 @@ import UIKit
import SignalCoreKit
import SessionUtilitiesKit
final class MainAppContext: NSObject, AppContext {
final class MainAppContext: AppContext {
var _temporaryDirectory: String?
var reportedApplicationState: UIApplication.State
let appLaunchTime = Date()
let isMainApp: Bool = true
var isMainAppAndActive: Bool { UIApplication.shared.applicationState == .active }
var isShareExtension: Bool = false
var appActiveBlocks: [AppActiveBlock] = []
var frontmostViewController: UIViewController? { UIApplication.shared.frontmostViewControllerIgnoringAlerts }
var mainWindow: UIWindow?
var wasWokenUpByPushNotification: Bool = false
@ -35,11 +35,9 @@ final class MainAppContext: NSObject, AppContext {
// MARK: - Initialization
override init() {
init() {
self.reportedApplicationState = .inactive
super.init()
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationWillEnterForeground(notification:)),
@ -85,7 +83,7 @@ final class MainAppContext: NSObject, AppContext {
OWSLogger.info("")
NotificationCenter.default.post(
name: .OWSApplicationWillEnterForeground,
name: .sessionWillEnterForeground,
object: nil
)
}
@ -99,7 +97,7 @@ final class MainAppContext: NSObject, AppContext {
DDLog.flushLog()
NotificationCenter.default.post(
name: .OWSApplicationDidEnterBackground,
name: .sessionDidEnterBackground,
object: nil
)
}
@ -113,7 +111,7 @@ final class MainAppContext: NSObject, AppContext {
DDLog.flushLog()
NotificationCenter.default.post(
name: .OWSApplicationWillResignActive,
name: .sessionWillResignActive,
object: nil
)
}
@ -126,11 +124,9 @@ final class MainAppContext: NSObject, AppContext {
OWSLogger.info("")
NotificationCenter.default.post(
name: .OWSApplicationDidBecomeActive,
name: .sessionDidBecomeActive,
object: nil
)
self.runAppActiveBlocks()
}
@objc private func applicationWillTerminate(notification: NSNotification) {
@ -142,6 +138,10 @@ final class MainAppContext: NSObject, AppContext {
// MARK: - AppContext Functions
func setMainWindow(_ mainWindow: UIWindow) {
self.mainWindow = mainWindow
}
func setStatusBarHidden(_ isHidden: Bool, animated isAnimated: Bool) {
UIApplication.shared.setStatusBarHidden(isHidden, with: (isAnimated ? .slide : .none))
}
@ -154,7 +154,7 @@ final class MainAppContext: NSObject, AppContext {
return (reportedApplicationState == .background)
}
func beginBackgroundTask(expirationHandler: @escaping BackgroundTaskExpirationHandler) -> UIBackgroundTaskIdentifier {
func beginBackgroundTask(expirationHandler: @escaping () -> ()) -> UIBackgroundTaskIdentifier {
return UIApplication.shared.beginBackgroundTask(expirationHandler: expirationHandler)
}
@ -179,70 +179,51 @@ final class MainAppContext: NSObject, AppContext {
UIApplication.shared.isIdleTimerDisabled = shouldBeBlocking
}
func frontmostViewController() -> UIViewController? {
UIApplication.shared.frontmostViewControllerIgnoringAlerts
}
func setNetworkActivityIndicatorVisible(_ value: Bool) {
UIApplication.shared.isNetworkActivityIndicatorVisible = value
}
// MARK: -
func runNowOr(whenMainAppIsActive block: @escaping AppActiveBlock) {
Threading.dispatchMainThreadSafe { [weak self] in
if self?.isMainAppAndActive == true {
// App active blocks typically will be used to safely access the
// shared data container, so use a background task to protect this
// work.
var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: #function)
block()
if backgroundTask != nil { backgroundTask = nil }
return
}
func clearOldTemporaryDirectories() {
// We use the lowest priority queue for this, and wait N seconds
// to avoid interfering with app startup.
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + .seconds(3)) { [weak self] in
guard
self?.isAppForegroundAndActive == true, // Abort if app not active
let thresholdDate: Date = self?.appLaunchTime
else { return }
// Ignore the "current" temp directory.
let currentTempDirName: String = URL(fileURLWithPath: Singleton.appContext.temporaryDirectory).lastPathComponent
let dirPath = NSTemporaryDirectory()
self?.appActiveBlocks.append(block)
guard let fileNames: [String] = try? FileManager.default.contentsOfDirectory(atPath: dirPath) else { return }
fileNames.forEach { fileName in
guard fileName != currentTempDirName else { return }
// Delete files with either:
//
// a) "ows_temp" name prefix.
// b) modified time before app launch time.
let filePath: String = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName).absoluteString
if !fileName.hasPrefix("ows_temp") {
// It's fine if we can't get the attributes (the file may have been deleted since we found it),
// also don't delete files which were created in the last N minutes
guard
let attributes: [FileAttributeKey: Any] = try? FileManager.default.attributesOfItem(atPath: filePath),
let modificationDate: Date = attributes[.modificationDate] as? Date,
modificationDate.timeIntervalSince1970 <= thresholdDate.timeIntervalSince1970
else { return }
}
if (!OWSFileSystem.deleteFile(filePath)) {
// This can happen if the app launches before the phone is unlocked.
// Clean up will occur when app becomes active.
}
}
}
}
func runAppActiveBlocks() {
// App active blocks typically will be used to safely access the
// shared data container, so use a background task to protect this
// work.
var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: #function)
let appActiveBlocks: [AppActiveBlock] = self.appActiveBlocks
self.appActiveBlocks.removeAll()
appActiveBlocks.forEach { $0() }
if backgroundTask != nil { backgroundTask = nil }
}
func appDocumentDirectoryPath() -> String {
let targetPath: String? = FileManager.default
.urls(
for: .documentDirectory,
in: .userDomainMask
)
.last?
.path
owsAssertDebug(targetPath != nil)
return (targetPath ?? "")
}
func appSharedDataDirectoryPath() -> String {
let targetPath: String? = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.applicationGroup)?
.path
owsAssertDebug(targetPath != nil)
return (targetPath ?? "")
}
func appUserDefaults() -> UserDefaults {
owsAssertDebug(UserDefaults.sharedLokiProject != nil)
return (UserDefaults.sharedLokiProject ?? UserDefaults.standard)
}
}

@ -273,7 +273,7 @@ public enum PushRegistrationError: Error {
// NOTE: This function MUST report an incoming call.
public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
SNLog("[Calls] Receive new voip notification.")
owsAssertDebug(CurrentAppContext().isMainApp)
owsAssertDebug(Singleton.hasAppContext && Singleton.appContext.isMainApp)
owsAssertDebug(type == .voIP)
let payload = payload.dictionaryPayload

@ -275,7 +275,7 @@ public class UserNotificationActionHandler: NSObject {
func handleNotificationResponse( _ response: UNNotificationResponse) -> AnyPublisher<Void, Error> {
AssertIsOnMainThread()
assert(AppReadiness.isAppReady())
assert(Singleton.appReadiness.isAppReady)
let userInfo: [AnyHashable: Any] = response.notification.request.content.userInfo
let applicationState: UIApplication.State = UIApplication.shared.applicationState

@ -94,7 +94,7 @@ final class AppearanceViewController: BaseVC {
trailing: Values.largeSpacing
)
if CurrentAppContext().isRTL {
if Singleton.hasAppContext && Singleton.appContext.isRTL {
result.transform = CGAffineTransform.identity.scaledBy(x: -1, y: 1)
}

@ -204,7 +204,7 @@ public class BlockedContactsViewModel: SessionTableViewModel, NavigatableStateHo
lastName
)
]
.reversed(if: CurrentAppContext().isRTL)
.reversed(if: Singleton.hasAppContext && Singleton.appContext.isRTL)
.joined(separator: " ")
}
@ -223,7 +223,7 @@ public class BlockedContactsViewModel: SessionTableViewModel, NavigatableStateHo
(contactNames.count - numNamesToShow)
)
]
.reversed(if: CurrentAppContext().isRTL)
.reversed(if: Singleton.hasAppContext && Singleton.appContext.isRTL)
.joined(separator: " ")
}()
let confirmationModal: ConfirmationModal = ConfirmationModal(

@ -186,7 +186,8 @@ class HelpViewModel: SessionTableViewModel, NavigatableStateHolder, ObservableTa
guard
let latestLogFilePath: String = logFilePaths.first,
let viewController: UIViewController = CurrentAppContext().frontmostViewController()
Singleton.hasAppContext,
let viewController: UIViewController = Singleton.appContext.frontmostViewController
else { return }
let showShareSheet: () -> () = {

@ -665,7 +665,7 @@ public final class FullConversationCell: UITableViewCell, SwipeActionOptimisticC
// with the term so we use the regex below to ensure we only highlight those cases)
normalizedSnippet
.ranges(
of: (CurrentAppContext().isRTL ?
of: (Singleton.hasAppContext && Singleton.appContext.isRTL ?
"(\(part.lowercased()))(^|[^a-zA-Z0-9])" :
"(^|[^a-zA-Z0-9])(\(part.lowercased()))"
),

@ -120,25 +120,25 @@ class ScreenLockUI {
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationDidBecomeActive),
name: .OWSApplicationDidBecomeActive,
name: .sessionDidBecomeActive,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationWillResignActive),
name: .OWSApplicationWillResignActive,
name: .sessionWillResignActive,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationWillEnterForeground),
name: .OWSApplicationWillEnterForeground,
name: .sessionWillEnterForeground,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationDidEnterBackground),
name: .OWSApplicationDidEnterBackground,
name: .sessionDidEnterBackground,
object: nil
)
NotificationCenter.default.addObserver(
@ -166,7 +166,7 @@ class ScreenLockUI {
//
// It's not safe to access OWSScreenLock.isScreenLockEnabled
// until the app is ready.
AppReadiness.runNowOrWhenAppWillBecomeReady { [weak self] in
Singleton.appReadiness.runNowOrWhenAppWillBecomeReady { [weak self] in
self?.isScreenLockLocked = Storage.shared[.isScreenLockEnabled]
self?.ensureUI()
}
@ -175,7 +175,7 @@ class ScreenLockUI {
// MARK: - Functions
private func tryToActivateScreenLockBasedOnCountdown() {
guard AppReadiness.isAppReady() else {
guard Singleton.appReadiness.isAppReady else {
// It's not safe to access OWSScreenLock.isScreenLockEnabled
// until the app is ready.
//
@ -203,8 +203,8 @@ class ScreenLockUI {
/// * The blocking window has the correct state.
/// * That we show the "iOS auth UI to unlock" if necessary.
private func ensureUI() {
guard AppReadiness.isAppReady() else {
AppReadiness.runNowOrWhenAppWillBecomeReady { [weak self] in
guard Singleton.appReadiness.isAppReady else {
Singleton.appReadiness.runNowOrWhenAppWillBecomeReady { [weak self] in
self?.ensureUI()
}
return
@ -362,7 +362,7 @@ class ScreenLockUI {
@objc private func clockDidChange() {
Logger.info("clock did change")
guard AppReadiness.isAppReady() else {
guard Singleton.appReadiness.isAppReady else {
// It's not safe to access OWSScreenLock.isScreenLockEnabled
// until the app is ready.
//

@ -18,7 +18,10 @@ public enum Permissions {
return true
case .denied, .restricted:
guard let presentingViewController: UIViewController = (presentingViewController ?? CurrentAppContext().frontmostViewController()) else { return false }
guard
Singleton.hasAppContext,
let presentingViewController: UIViewController = (presentingViewController ?? Singleton.appContext.frontmostViewController)
else { return false }
let confirmationModal: ConfirmationModal = ConfirmationModal(
info: ConfirmationModal.Info(
@ -57,7 +60,10 @@ public enum Permissions {
switch AVAudioSession.sharedInstance().recordPermission {
case .granted: break
case .denied:
guard let presentingViewController: UIViewController = (presentingViewController ?? CurrentAppContext().frontmostViewController()) else { return }
guard
Singleton.hasAppContext,
let presentingViewController: UIViewController = (presentingViewController ?? Singleton.appContext.frontmostViewController)
else { return }
onNotGranted?()
let confirmationModal: ConfirmationModal = ConfirmationModal(
@ -129,7 +135,10 @@ public enum Permissions {
switch authorizationStatus {
case .authorized, .limited: onAuthorized()
case .denied, .restricted:
guard let presentingViewController: UIViewController = (presentingViewController ?? CurrentAppContext().frontmostViewController()) else { return }
guard
Singleton.hasAppContext,
let presentingViewController: UIViewController = (presentingViewController ?? Singleton.appContext.frontmostViewController)
else { return }
let confirmationModal: ConfirmationModal = ConfirmationModal(
info: ConfirmationModal.Info(

@ -16,7 +16,10 @@ import SignalCoreKit
}
internal func findFrontmostViewController(ignoringAlerts: Bool) -> UIViewController? {
guard let window: UIWindow = CurrentAppContext().mainWindow else { return nil }
guard
Singleton.hasAppContext,
let window: UIWindow = Singleton.appContext.mainWindow
else { return nil }
guard let viewController: UIViewController = window.rootViewController else {
owsFailDebug("Missing root view controller.")

@ -1477,14 +1477,14 @@ enum _003_YDBToGRDBMigration: Migration {
)
db[.areLinkPreviewsEnabled] = (legacyPreferences[SMKLegacy.preferencesKeyAreLinkPreviewsEnabled] as? Bool == true)
db[.areCallsEnabled] = (legacyPreferences[SMKLegacy.preferencesKeyAreCallsEnabled] as? Bool == true)
db[.hasHiddenMessageRequests] = CurrentAppContext().appUserDefaults()
db[.hasHiddenMessageRequests] = Singleton.appContext.appUserDefaults
.bool(forKey: SMKLegacy.userDefaultsHasHiddenMessageRequests)
// Note: The 'hasViewedSeed' was originally stored on standard user defaults
db[.hasViewedSeed] = UserDefaults.standard.bool(forKey: SMKLegacy.userDefaultsHasViewedSeedKey)
db[.hasSavedThread] = (legacyPreferences[SMKLegacy.preferencesKeyHasSavedThreadKey] as? Bool == true)
db[.hasSentAMessage] = (legacyPreferences[SMKLegacy.preferencesKeyHasSentAMessageKey] as? Bool == true)
db[.isReadyForAppExtensions] = CurrentAppContext().appUserDefaults().bool(forKey: SMKLegacy.preferencesKeyIsReadyForAppExtensions)
db[.isReadyForAppExtensions] = Singleton.appContext.appUserDefaults.bool(forKey: SMKLegacy.preferencesKeyIsReadyForAppExtensions)
// We want this setting to be on by default
db[.trimOpenGroupMessagesOlderThanSixMonths] = true

@ -955,7 +955,7 @@ public extension Interaction {
!attachmentDescription.isEmpty,
!body.isEmpty
{
if CurrentAppContext().isRTL {
if Singleton.hasAppContext && Singleton.appContext.isRTL {
return "\(body): \(attachmentDescription)"
}

@ -83,7 +83,7 @@ public enum AttachmentDownloadJob: JobExecutor {
}
let temporaryFileUrl: URL = URL(
fileURLWithPath: OWSTemporaryDirectoryAccessibleAfterFirstAuth() + UUID().uuidString
fileURLWithPath: Singleton.appContext.temporaryDirectoryAccessibleAfterFirstAuth + UUID().uuidString
)
Just(attachment.downloadUrl)

@ -3,6 +3,5 @@
FOUNDATION_EXPORT double SessionMessagingKitVersionNumber;
FOUNDATION_EXPORT const unsigned char SessionMessagingKitVersionString[];
#import <SessionMessagingKit/AppReadiness.h>
#import <SessionMessagingKit/OWSAudioPlayer.h>
#import <SessionMessagingKit/OWSWindowManager.h>

@ -900,7 +900,7 @@ public class SignalAttachment: Equatable, Hashable {
}
private class var videoTempPath: URL {
let videoDir = URL(fileURLWithPath: OWSTemporaryDirectory()).appendingPathComponent("video")
let videoDir = URL(fileURLWithPath: Singleton.appContext.temporaryDirectory).appendingPathComponent("video")
OWSFileSystem.ensureDirectoryExists(videoDir.path)
return videoDir
}

@ -53,7 +53,8 @@ extension MessageReceiver {
// It is enough just ignoring the pre offers, other call messages
// for this call would be dropped because of no Session call instance
guard
CurrentAppContext().isMainApp,
Singleton.hasAppContext,
Singleton.appContext.isMainApp,
let sender: String = message.sender,
(try? Contact
.filter(id: sender)

@ -456,9 +456,9 @@ public enum PushNotificationAPI {
// after device restart until device is unlocked for the first time. If the app receives a push
// notification, we won't be able to access the keychain to process that notification, so we should
// just terminate by throwing an uncaught exception
if CurrentAppContext().isMainApp || CurrentAppContext().isInBackground() {
let appState: UIApplication.State = CurrentAppContext().reportedApplicationState
SNLog("CipherKeySpec inaccessible. New install or no unlock since device restart?, ApplicationState: \(NSStringForUIApplicationState(appState))")
if Singleton.hasAppContext && (Singleton.appContext.isMainApp || Singleton.appContext.isInBackground) {
let appState: UIApplication.State = Singleton.appContext.reportedApplicationState
SNLog("CipherKeySpec inaccessible. New install or no unlock since device restart?, ApplicationState: \(appState.name)")
throw StorageError.keySpecInaccessible
}

@ -257,7 +257,8 @@ internal extension SessionUtil {
// we just deleted then return to the home screen
DispatchQueue.main.async {
guard
let rootViewController: UIViewController = CurrentAppContext().mainWindow?.rootViewController,
Singleton.hasAppContext,
let rootViewController: UIViewController = Singleton.appContext.mainWindow?.rootViewController,
let topBannerController: TopBannerController = (rootViewController as? TopBannerController),
!topBannerController.children.isEmpty,
let navController: UINavigationController = topBannerController.children[0] as? UINavigationController

@ -1,39 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^AppReadyBlock)(void);
@interface AppReadiness : NSObject
- (instancetype)init NS_UNAVAILABLE;
// This method can be called on any thread.
+ (BOOL)isAppReady;
// These methods should only be called on the main thread.
+ (void)invalidate;
+ (void)setAppIsReady;
// If the app is ready, the block is called immediately;
// otherwise it is called when the app becomes ready.
//
// This method should only be called on the main thread.
// The block will always be called on the main thread.
//
// * The "will become ready" blocks are called before the "did become ready" blocks.
// * The "will become ready" blocks should be used for internal setup of components
// so that they are ready to interact with other components of the system.
// * The "did become ready" blocks should be used for any work that should be done
// on app launch, especially work that uses other components.
// * We should usually use "did become ready" blocks since they are safer.
+ (void)runNowOrWhenAppWillBecomeReady:(AppReadyBlock)block NS_SWIFT_NAME(runNowOrWhenAppWillBecomeReady(_:));
+ (void)runNowOrWhenAppDidBecomeReady:(AppReadyBlock)block NS_SWIFT_NAME(runNowOrWhenAppDidBecomeReady(_:));
@end
NS_ASSUME_NONNULL_END

@ -1,149 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "AppReadiness.h"
#import "AppContext.h"
#import <SignalCoreKit/SignalCoreKit.h>
#import <SignalCoreKit/Threading.h>
#import <SessionUtilitiesKit/SessionUtilitiesKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface AppReadiness ()
@property (atomic) BOOL isAppReady;
@property (nonatomic) NSMutableArray<AppReadyBlock> *appWillBecomeReadyBlocks;
@property (nonatomic) NSMutableArray<AppReadyBlock> *appDidBecomeReadyBlocks;
@end
#pragma mark -
@implementation AppReadiness
+ (instancetype)sharedManager
{
static AppReadiness *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] initDefault];
});
return sharedMyManager;
}
- (instancetype)initDefault
{
self = [super init];
if (!self) {
return self;
}
self.appWillBecomeReadyBlocks = [NSMutableArray new];
self.appDidBecomeReadyBlocks = [NSMutableArray new];
return self;
}
+ (BOOL)isAppReady
{
return [self.sharedManager isAppReady];
}
+ (void)runNowOrWhenAppWillBecomeReady:(AppReadyBlock)block
{
if ([NSThread isMainThread]) {
[self.sharedManager runNowOrWhenAppWillBecomeReady:block];
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self.sharedManager runNowOrWhenAppWillBecomeReady:block];
});
}
}
- (void)runNowOrWhenAppWillBecomeReady:(AppReadyBlock)block
{
if ([SNUtilitiesKitConfiguration isRunningTests]) {
// We don't need to do any "on app ready" work in the tests.
return;
}
if (self.isAppReady) {
block();
return;
}
[self.appWillBecomeReadyBlocks addObject:block];
}
+ (void)runNowOrWhenAppDidBecomeReady:(AppReadyBlock)block
{
if ([NSThread isMainThread]) {
[self.sharedManager runNowOrWhenAppDidBecomeReady:block];
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
[self.sharedManager runNowOrWhenAppDidBecomeReady:block];
});
}
}
- (void)runNowOrWhenAppDidBecomeReady:(AppReadyBlock)block
{
if ([SNUtilitiesKitConfiguration isRunningTests]) {
// We don't need to do any "on app ready" work in the tests.
return;
}
if (self.isAppReady) {
block();
return;
}
[self.appDidBecomeReadyBlocks addObject:block];
}
+ (void)invalidate
{
[self.sharedManager invalidate];
}
- (void)invalidate
{
self.isAppReady = NO;
}
+ (void)setAppIsReady
{
[self.sharedManager setAppIsReady];
}
- (void)setAppIsReady
{
self.isAppReady = YES;
[self runAppReadyBlocks];
}
- (void)runAppReadyBlocks
{
NSArray<AppReadyBlock> *appWillBecomeReadyBlocks = [self.appWillBecomeReadyBlocks copy];
[self.appWillBecomeReadyBlocks removeAllObjects];
NSArray<AppReadyBlock> *appDidBecomeReadyBlocks = [self.appDidBecomeReadyBlocks copy];
[self.appDidBecomeReadyBlocks removeAllObjects];
// We invoke the _will become_ blocks before the _did become_ blocks.
for (AppReadyBlock block in appWillBecomeReadyBlocks) {
block();
}
for (AppReadyBlock block in appDidBecomeReadyBlocks) {
block();
}
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,73 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import SessionUtilitiesKit
// MARK: - Singleton
public extension Singleton {
// FIXME: This will be reworked to be part of dependencies in the Groups Rebuild branch
fileprivate static var _appReadiness: Atomic<AppReadiness> = Atomic(AppReadiness())
static var appReadiness: AppReadiness { _appReadiness.wrappedValue }
}
// MARK: - AppReadiness
public class AppReadiness {
public private(set) var isAppReady: Bool = false
private var appWillBecomeReadyBlocks: Atomic<[() -> ()]> = Atomic([])
private var appDidBecomeReadyBlocks: Atomic<[() -> ()]> = Atomic([])
public func setAppReady() {
guard Thread.isMainThread else {
DispatchQueue.main.async { [weak self] in self?.setAppReady() }
return
}
// Update the flag
isAppReady = true
// Trigure the closures
let willBecomeReadyClosures: [() -> ()] = appWillBecomeReadyBlocks.wrappedValue
let didBecomeReadyClosures: [() -> ()] = appDidBecomeReadyBlocks.wrappedValue
appWillBecomeReadyBlocks.mutate { $0 = [] }
appDidBecomeReadyBlocks.mutate { $0 = [] }
willBecomeReadyClosures.forEach { $0() }
didBecomeReadyClosures.forEach { $0() }
}
public func invalidate() {
isAppReady = false
}
public func runNowOrWhenAppWillBecomeReady(closure: @escaping () -> ()) {
// We don't need to do any "on app ready" work in the tests.
guard !SNUtilitiesKitConfiguration.isRunningTests else { return }
guard !isAppReady else {
guard Thread.isMainThread else {
DispatchQueue.main.async { [weak self] in self?.runNowOrWhenAppWillBecomeReady(closure: closure) }
return
}
return closure()
}
appWillBecomeReadyBlocks.mutate { $0.append(closure) }
}
public func runNowOrWhenAppDidBecomeReady(closure: @escaping () -> ()) {
// We don't need to do any "on app ready" work in the tests.
guard !SNUtilitiesKitConfiguration.isRunningTests else { return }
guard !isAppReady else {
guard Thread.isMainThread else {
DispatchQueue.main.async { [weak self] in self?.runNowOrWhenAppDidBecomeReady(closure: closure) }
return
}
return closure()
}
appDidBecomeReadyBlocks.mutate { $0.append(closure) }
}
}

@ -36,7 +36,7 @@ public class DeviceSleepManager: NSObject {
NotificationCenter.default.addObserver(
self,
selector: #selector(didEnterBackground),
name: NSNotification.Name.OWSApplicationDidEnterBackground,
name: .sessionDidEnterBackground,
object: nil
)
}
@ -73,6 +73,8 @@ public class DeviceSleepManager: NSObject {
}
let shouldBlock = blocks.count > 0
CurrentAppContext().ensureSleepBlocking(shouldBlock, blockingObjects: blocks)
guard Singleton.hasAppContext else { return }
Singleton.appContext.ensureSleepBlocking(shouldBlock, blockingObjects: blocks)
}
}

@ -75,7 +75,7 @@ NS_ASSUME_NONNULL_BEGIN
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:OWSApplicationDidEnterBackgroundNotification
name:NSNotification.sessionDidEnterBackground
object:nil];
return self;

@ -102,7 +102,7 @@ const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActive:)
name:OWSApplicationWillResignActiveNotification
name:NSNotification.sessionWillResignActive
object:nil];
[self ensureWindowState];

@ -17,7 +17,7 @@ public class OWSProximityMonitoringManagerImpl: NSObject, OWSProximityMonitoring
public override init() {
super.init()
AppReadiness.runNowOrWhenAppWillBecomeReady {
Singleton.appReadiness.runNowOrWhenAppWillBecomeReady {
self.setup()
}
}

@ -39,8 +39,8 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
}
/// Create the context if we don't have it (needed before _any_ interaction with the database)
if !HasAppContext() {
SetCurrentAppContext(NotificationServiceExtensionContext())
if !Singleton.hasAppContext {
Singleton.setup(appContext: NotificationServiceExtensionContext())
}
let isCallOngoing: Bool = (UserDefaults.sharedLokiProject?[.isCallOngoing])
@ -52,7 +52,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
DispatchQueue.main.sync { self.setUpIfNecessary() { } }
// Handle the push notification
AppReadiness.runNowOrWhenAppDidBecomeReady {
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady {
let openGroupPollingPublishers: [AnyPublisher<Void, Error>] = self.pollForOpenGroups()
defer {
self.openGroupPollCancellable = Publishers
@ -278,7 +278,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
AssertIsOnMainThread()
// Only mark the app as ready once.
guard !AppReadiness.isAppReady() else { return }
guard !Singleton.appReadiness.isAppReady else { return }
// App isn't ready until storage is ready AND all version migrations are complete.
guard Storage.shared.isValid && migrationsCompleted else {
@ -290,7 +290,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
SignalUtilitiesKit.Configuration.performMainSetup()
// Note that this does much more than set a flag; it will also run all deferred blocks.
AppReadiness.setAppIsReady()
Singleton.appReadiness.setAppReady()
}
// MARK: Handle completion

@ -6,12 +6,13 @@ import Foundation
import SignalUtilitiesKit
import SessionUtilitiesKit
final class NotificationServiceExtensionContext : NSObject, AppContext {
let appLaunchTime = Date()
let isMainApp = false
let isMainAppAndActive = false
var isShareExtension: Bool = false
final class NotificationServiceExtensionContext: AppContext {
var _temporaryDirectory: String?
let appLaunchTime: Date = Date()
let reportedApplicationState: UIApplication.State = .background
let isRTL: Bool = false
var openSystemSettingsAction: UIAlertAction?
var wasWokenUpByPushNotification = true
@ -25,52 +26,14 @@ final class NotificationServiceExtensionContext : NSObject, AppContext {
return .init(timeIntervalSince1970: buildTimestamp)
}()
override init() { super.init() }
func canPresentNotifications() -> Bool { true }
func isAppForegroundAndActive() -> Bool { false }
func isInBackground() -> Bool { true }
func mainApplicationStateOnLaunch() -> UIApplication.State { .inactive }
func appDocumentDirectoryPath() -> String {
guard let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
preconditionFailure("Couldn't get document directory.")
}
return documentDirectoryURL.path
}
func appSharedDataDirectoryPath() -> String {
guard let groupContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.applicationGroup) else {
preconditionFailure("Couldn't get shared data directory.")
}
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
let interfaceOrientation = UIInterfaceOrientation.unknown
let isRTL = false
let reportedApplicationState = UIApplication.State.background
let statusBarHeight = CGFloat.zero
var mainWindow: UIWindow?
func beginBackgroundTask(expirationHandler: @escaping BackgroundTaskExpirationHandler) -> UIBackgroundTaskIdentifier { .invalid }
func beginBackgroundTask(expirationHandler: @escaping BackgroundTaskExpirationHandler) -> UInt { 0 }
func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier) { }
func endBackgroundTask(_ backgroundTaskIdentifier: UInt) { }
func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any]) { }
func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjectsDescription: String) { }
func frontmostViewController() -> UIViewController? { nil }
func runNowOr(whenMainAppIsActive block: @escaping AppActiveBlock) { }
func setNetworkActivityIndicatorVisible(_ value: Bool) { }
func setStatusBarHidden(_ isHidden: Bool, animated isAnimated: Bool) { }
}

@ -7,7 +7,5 @@
// Separate iOS Frameworks from other imports.
#import <SessionUtilitiesKit/UIView+OWS.h>
#import <SessionUtilitiesKit/AppContext.h>
#import <SessionMessagingKit/AppReadiness.h>
#import <SignalUtilitiesKit/AppVersion.h>
#import <SessionUtilitiesKit/OWSMath.h>

@ -7,14 +7,13 @@ import SessionMessagingKit
import SignalCoreKit
/// This is _NOT_ a singleton and will be instantiated each time that the SAE is used.
final class ShareAppExtensionContext: NSObject, AppContext {
final class ShareAppExtensionContext: AppContext {
var _temporaryDirectory: String?
var rootViewController: UIViewController
var reportedApplicationState: UIApplication.State
let appLaunchTime = Date()
let isMainApp = false
let isMainAppAndActive = false
var isShareExtension: Bool = true
let appLaunchTime: Date = Date()
let isShareExtension: Bool = true
var mainWindow: UIWindow?
var wasWokenUpByPushNotification: Bool = false
@ -41,8 +40,6 @@ final class ShareAppExtensionContext: NSObject, AppContext {
self.rootViewController = rootViewController
self.reportedApplicationState = .active
super.init()
NotificationCenter.default.addObserver(
self,
selector: #selector(extensionHostDidBecomeActive(notification:)),
@ -82,7 +79,7 @@ final class ShareAppExtensionContext: NSObject, AppContext {
self.reportedApplicationState = .active
NotificationCenter.default.post(
name: .OWSApplicationDidBecomeActive,
name: .sessionDidBecomeActive,
object: nil
)
}
@ -96,7 +93,7 @@ final class ShareAppExtensionContext: NSObject, AppContext {
DDLog.flushLog()
NotificationCenter.default.post(
name: .OWSApplicationWillResignActive,
name: .sessionWillResignActive,
object: nil
)
}
@ -110,7 +107,7 @@ final class ShareAppExtensionContext: NSObject, AppContext {
self.reportedApplicationState = .background
NotificationCenter.default.post(
name: .OWSApplicationDidEnterBackground,
name: .sessionDidEnterBackground,
object: nil
)
}
@ -123,74 +120,22 @@ final class ShareAppExtensionContext: NSObject, AppContext {
self.reportedApplicationState = .inactive
NotificationCenter.default.post(
name: .OWSApplicationWillEnterForeground,
name: .sessionWillEnterForeground,
object: nil
)
}
// MARK: - AppContext Functions
func isAppForegroundAndActive() -> Bool {
return (reportedApplicationState == .active)
}
func isInBackground() -> Bool {
return (reportedApplicationState == .background)
}
func frontmostViewController() -> UIViewController? {
return rootViewController.findFrontmostViewController(ignoringAlerts: true)
}
func appDocumentDirectoryPath() -> String {
let targetPath: String? = FileManager.default
.urls(
for: .documentDirectory,
in: .userDomainMask
)
.last?
.path
owsAssertDebug(targetPath != nil)
return (targetPath ?? "")
}
func appSharedDataDirectoryPath() -> String {
let targetPath: String? = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.applicationGroup)?
.path
owsAssertDebug(targetPath != nil)
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")
}
func beginBackgroundTask(expirationHandler: @escaping BackgroundTaskExpirationHandler) -> UIBackgroundTaskIdentifier {
return .invalid
}
func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier) {
owsAssertDebug(backgroundTaskIdentifier == .invalid)
}
func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any]) {
OWSLogger.debug("Ignoring request to block sleep.")
}
func setNetworkActivityIndicatorVisible(_ value: Bool) {
owsFailDebug("")
}
func runNowOr(whenMainAppIsActive block: @escaping AppActiveBlock) {
owsFailDebug("cannot run main app active blocks in share extension.")
}
}

@ -6,6 +6,7 @@ import CoreServices
import SignalUtilitiesKit
import SessionUIKit
import SessionUtilitiesKit
import SessionMessagingKit
import SignalCoreKit
final class ShareNavController: UINavigationController, ShareViewDelegate {
@ -31,9 +32,8 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
// This should be the first thing we do (Note: If you leave the share context and return to it
// the context will already exist, trying to override it results in the share context crashing
// so ensure it doesn't exist first)
if !HasAppContext() {
let appContext = ShareAppExtensionContext(rootViewController: self)
SetCurrentAppContext(appContext)
if !Singleton.hasAppContext {
Singleton.setup(appContext: ShareAppExtensionContext(rootViewController: self))
}
Logger.info("")
@ -79,7 +79,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
NotificationCenter.default.addObserver(
self,
selector: #selector(applicationDidEnterBackground),
name: .OWSApplicationDidEnterBackground,
name: .sessionDidEnterBackground,
object: nil
)
@ -124,7 +124,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
showLockScreenOrMainContent()
return
}
guard !AppReadiness.isAppReady() else {
guard !Singleton.appReadiness.isAppReady else {
// Only mark the app as ready once.
showLockScreenOrMainContent()
return
@ -136,7 +136,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
// Note that this does much more than set a flag;
// it will also run all deferred blocks.
AppReadiness.setAppIsReady()
Singleton.appReadiness.setAppReady()
// We don't need to use messageFetcherJob in the SAE.
// We don't need to use SyncPushTokensJob in the SAE.
@ -154,7 +154,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
AppReadiness.runNowOrWhenAppDidBecomeReady { [weak self] in
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady { [weak self] in
AssertIsOnMainThread()
self?.showLockScreenOrMainContent()
}
@ -180,7 +180,7 @@ final class ShareNavController: UINavigationController, ShareViewDelegate {
// Share extensions reside in a process that may be reused between usages.
// That isn't safe; the codebase is full of statics (e.g. singletons) which
// we can't easily clean up.
ExitShareExtension()
exit(0)
}
// MARK: - Updating

@ -2,14 +2,6 @@
NS_ASSUME_NONNULL_BEGIN
// Use instead of NSTemporaryDirectory()
// prefer the more restrictice OWSTemporaryDirectory,
// unless the temp data may need to be accessed while the device is locked.
NSString *OWSTemporaryDirectory(void);
NSString *OWSTemporaryDirectoryAccessibleAfterFirstAuth(void);
void ClearOldTemporaryDirectories(void);
void ClearOldTemporaryDirectoriesSync(void);
@interface OWSFileSystem : NSObject
- (instancetype)init NS_UNAVAILABLE;
@ -33,6 +25,7 @@ void ClearOldTemporaryDirectoriesSync(void);
// Returns NO IFF the directory does not exist and could not be created.
+ (BOOL)ensureDirectoryExists:(NSString *)dirPath;
+ (BOOL)ensureDirectoryExists:(NSString *)dirPath fileProtectionType:(NSFileProtectionType)fileProtectionType;
+ (BOOL)ensureFileExists:(NSString *)filePath;

@ -1,7 +1,7 @@
// stringlint:disable
#import "OWSFileSystem.h"
#import "AppContext.h"
#import <SessionUtilitiesKit/SessionUtilitiesKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@ -67,12 +67,12 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)appDocumentDirectoryPath
{
return CurrentAppContext().appDocumentDirectoryPath;
return [OWSCurrentAppContext appDocumentDirectoryPath];
}
+ (NSString *)appSharedDataDirectoryPath
{
return CurrentAppContext().appSharedDataDirectoryPath;
return [OWSCurrentAppContext appSharedDataDirectoryPath];
}
+ (NSString *)cachesDirectoryPath
@ -231,7 +231,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)temporaryFilePathWithFileExtension:(NSString *_Nullable)fileExtension
{
NSString *temporaryDirectory = OWSTemporaryDirectory();
NSString *temporaryDirectory = [OWSCurrentAppContext temporaryDirectory];
NSString *tempFileName = NSUUID.UUID.UUIDString;
if (fileExtension.length > 0) {
tempFileName = [[tempFileName stringByAppendingString:@"."] stringByAppendingString:fileExtension];
@ -270,87 +270,4 @@ NS_ASSUME_NONNULL_BEGIN
@end
#pragma mark -
NSString *OWSTemporaryDirectory(void)
{
static NSString *dirPath;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *dirName = [NSString stringWithFormat:@"ows_temp_%@", NSUUID.UUID.UUIDString];
dirPath = [NSTemporaryDirectory() stringByAppendingPathComponent:dirName];
[OWSFileSystem ensureDirectoryExists:dirPath fileProtectionType:NSFileProtectionComplete];
});
return dirPath;
}
NSString *OWSTemporaryDirectoryAccessibleAfterFirstAuth(void)
{
NSString *dirPath = NSTemporaryDirectory();
[OWSFileSystem ensureDirectoryExists:dirPath
fileProtectionType:NSFileProtectionCompleteUntilFirstUserAuthentication];
return dirPath;
}
void ClearOldTemporaryDirectoriesSync(void)
{
// Ignore the "current" temp directory.
NSString *currentTempDirName = OWSTemporaryDirectory().lastPathComponent;
NSDate *thresholdDate = CurrentAppContext().appLaunchTime;
NSString *dirPath = NSTemporaryDirectory();
NSError *error;
NSArray<NSString *> *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dirPath error:&error];
if (error) {
return;
}
for (NSString *fileName in fileNames) {
if (!CurrentAppContext().isAppForegroundAndActive) {
// Abort if app not active.
return;
}
if ([fileName isEqualToString:currentTempDirName]) {
continue;
}
NSString *filePath = [dirPath stringByAppendingPathComponent:fileName];
// Delete files with either:
//
// a) "ows_temp" name prefix.
// b) modified time before app launch time.
if (![fileName hasPrefix:@"ows_temp"]) {
NSError *e;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&e];
if (!attributes || e) {
// This is fine; the file may have been deleted since we found it.
continue;
}
// Don't delete files which were created in the last N minutes.
NSDate *creationDate = attributes.fileModificationDate;
if (creationDate.timeIntervalSince1970 > thresholdDate.timeIntervalSince1970) {
continue;
}
}
if (![OWSFileSystem deleteFile:filePath]) {
// This can happen if the app launches before the phone is unlocked.
// Clean up will occur when app becomes active.
}
}
}
// NOTE: We need to call this method on launch _and_ every time the app becomes active,
// since file protection may prevent it from succeeding in the background.
void ClearOldTemporaryDirectories(void)
{
// We use the lowest priority queue for this, and wait N seconds
// to avoid interfering with app startup.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.f * NSEC_PER_SEC)),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
^{
ClearOldTemporaryDirectoriesSync();
});
}
NS_ASSUME_NONNULL_END

@ -381,9 +381,9 @@ open class Storage {
// after device restart until device is unlocked for the first time. If the app receives a push
// notification, we won't be able to access the keychain to process that notification, so we should
// just terminate by throwing an uncaught exception
if CurrentAppContext().isMainApp || CurrentAppContext().isInBackground() {
let appState: UIApplication.State = CurrentAppContext().reportedApplicationState
SNLog("CipherKeySpec inaccessible. New install or no unlock since device restart?, ApplicationState: \(NSStringForUIApplicationState(appState))")
if Singleton.hasAppContext && (Singleton.appContext.isMainApp || Singleton.appContext.isInBackground) {
let appState: UIApplication.State = Singleton.appContext.reportedApplicationState
SNLog("CipherKeySpec inaccessible. New install or no unlock since device restart?, ApplicationState: \(appState.name)")
// In this case we should have already detected the situation earlier and exited
// gracefully (in the app delegate) using isDatabasePasswordAccessible, but we

@ -1,111 +0,0 @@
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
// These are fired whenever the corresponding "main app" or "app extension"
// notification is fired.
//
// 1. This saves you the work of observing both.
// 2. This allows us to ensure that any critical work (e.g. re-opening
// databases) has been done before app re-enters foreground, etc.
extern NSString *const OWSApplicationDidEnterBackgroundNotification;
extern NSString *const OWSApplicationWillEnterForegroundNotification;
extern NSString *const OWSApplicationWillResignActiveNotification;
extern NSString *const OWSApplicationDidBecomeActiveNotification;
typedef void (^BackgroundTaskExpirationHandler)(void);
typedef void (^AppActiveBlock)(void);
NSString *NSStringForUIApplicationState(UIApplicationState value);
@class OWSAES256Key;
@protocol SSKKeychainStorage;
@protocol AppContext <NSObject>
@property (nonatomic, readonly) BOOL isMainApp;
@property (nonatomic, readonly) BOOL isMainAppAndActive;
@property (nonatomic, readonly) BOOL isShareExtension;
/// Whether the app was woken up by a silent push notification. This is important for determining whether attachments should be downloaded or not.
@property (nonatomic) BOOL wasWokenUpByPushNotification;
// Whether the user is using a right-to-left language like Arabic.
@property (nonatomic, readonly) BOOL isRTL;
@property (atomic, nullable) UIWindow *mainWindow;
// Unlike UIApplication.applicationState, this is thread-safe.
// It contains the "last known" application state.
//
// Because it is updated in response to "will/did-style" events, it is
// conservative and skews toward less-active and not-foreground:
//
// * It doesn't report "is active" until the app is active
// and reports "inactive" as soon as it _will become_ inactive.
// * It doesn't report "is foreground (but inactive)" until the app is
// foreground & inactive and reports "background" as soon as it _will
// enter_ background.
//
// This conservatism is useful, since we want to err on the side of
// caution when, for example, we do work that should only be done
// when the app is foreground and active.
@property (atomic, readonly) UIApplicationState reportedApplicationState;
// A convenience accessor for reportedApplicationState.
//
// This method is thread-safe.
- (BOOL)isInBackground;
// A convenience accessor for reportedApplicationState.
//
// This method is thread-safe.
- (BOOL)isAppForegroundAndActive;
// Should start a background task if isMainApp is YES.
// Should just return UIBackgroundTaskInvalid if isMainApp is NO.
- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:
(BackgroundTaskExpirationHandler)expirationHandler;
// Should be a NOOP if isMainApp is NO.
- (void)endBackgroundTask:(UIBackgroundTaskIdentifier)backgroundTaskIdentifier;
// Should be a NOOP if isMainApp is NO.
- (void)ensureSleepBlocking:(BOOL)shouldBeBlocking blockingObjects:(NSArray<id> *)blockingObjects;
- (void)setStatusBarHidden:(BOOL)isHidden animated:(BOOL)isAnimated;
@property (nonatomic, readonly) CGFloat statusBarHeight;
// Returns the VC that should be used to present alerts, modals, etc.
- (nullable UIViewController *)frontmostViewController;
// Returns nil if isMainApp is NO
@property (nullable, nonatomic, readonly) UIAlertAction *openSystemSettingsAction;
// Should be a NOOP if isMainApp is NO.
- (void)setNetworkActivityIndicatorVisible:(BOOL)value;
- (void)runNowOrWhenMainAppIsActive:(AppActiveBlock)block;
@property (atomic, readonly) NSDate *appLaunchTime;
- (NSString *)appDocumentDirectoryPath;
- (NSString *)appSharedDataDirectoryPath;
- (NSUserDefaults *)appUserDefaults;
@end
id<AppContext> CurrentAppContext(void);
BOOL HasAppContext(void);
void SetCurrentAppContext(id<AppContext> appContext);
void ExitShareExtension(void);
#ifdef DEBUG
void ClearCurrentAppContextForTests(void);
#endif
NS_ASSUME_NONNULL_END

@ -1,57 +0,0 @@
// stringlint:disable
#import "AppContext.h"
NS_ASSUME_NONNULL_BEGIN
NSString *const OWSApplicationDidEnterBackgroundNotification = @"OWSApplicationDidEnterBackgroundNotification";
NSString *const OWSApplicationWillEnterForegroundNotification = @"OWSApplicationWillEnterForegroundNotification";
NSString *const OWSApplicationWillResignActiveNotification = @"OWSApplicationWillResignActiveNotification";
NSString *const OWSApplicationDidBecomeActiveNotification = @"OWSApplicationDidBecomeActiveNotification";
NSString *NSStringForUIApplicationState(UIApplicationState value)
{
switch (value) {
case UIApplicationStateActive:
return @"UIApplicationStateActive";
case UIApplicationStateInactive:
return @"UIApplicationStateInactive";
case UIApplicationStateBackground:
return @"UIApplicationStateBackground";
}
}
static id<AppContext> currentAppContext = nil;
id<AppContext> CurrentAppContext(void)
{
return currentAppContext;
}
BOOL HasAppContext(void)
{
return (currentAppContext != nil);
}
void SetCurrentAppContext(id<AppContext> appContext)
{
// The main app context should only be set once.
//
// App extensions may be opened multiple times in the same process,
// so statics will persist.
currentAppContext = appContext;
}
#ifdef DEBUG
void ClearCurrentAppContextForTests()
{
currentAppContext = nil;
}
#endif
void ExitShareExtension(void)
{
exit(0);
}
NS_ASSUME_NONNULL_END

@ -0,0 +1,125 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import SignalCoreKit
// MARK: - Singleton
public extension Singleton {
// FIXME: This will be reworked to be part of dependencies in the Groups Rebuild branch
fileprivate static var _appContext: Atomic<AppContext?> = Atomic(nil)
static var appContext: AppContext { _appContext.wrappedValue! }
static var hasAppContext: Bool { _appContext.wrappedValue != nil }
static func setup(appContext: AppContext) { _appContext.mutate { $0 = appContext } }
}
// MARK: - AppContext
public protocol AppContext: AnyObject {
var _temporaryDirectory: String? { get set }
var isMainApp: Bool { get }
var isMainAppAndActive: Bool { get }
var isShareExtension: Bool { get }
var reportedApplicationState: UIApplication.State { get }
var mainWindow: UIWindow? { get }
var isRTL: Bool { get }
var frontmostViewController: UIViewController? { get }
func setMainWindow(_ mainWindow: UIWindow)
func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any])
func beginBackgroundTask(expirationHandler: @escaping () -> ()) -> UIBackgroundTaskIdentifier
func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier)
/// **Note:** We need to call this method on launch _and_ every time the app becomes active,
/// since file protection may prevent it from succeeding in the background.
func clearOldTemporaryDirectories()
}
// MARK: - Defaults
public extension AppContext {
var isMainApp: Bool { false }
var isMainAppAndActive: Bool { false }
var isShareExtension: Bool { false }
var mainWindow: UIWindow? { nil }
var frontmostViewController: UIViewController? { nil }
var isInBackground: Bool { reportedApplicationState == .background }
var isAppForegroundAndActive: Bool { reportedApplicationState == .active }
// MARK: - Paths
var appUserDefaults: UserDefaults {
return (UserDefaults.sharedLokiProject ?? UserDefaults.standard)
}
var temporaryDirectory: String {
if let dir: String = _temporaryDirectory { return dir }
let dirName: String = "ows_temp_\(UUID().uuidString)"
let dirPath: String = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(dirName)
.absoluteString
_temporaryDirectory = dirPath
return dirPath
}
var temporaryDirectoryAccessibleAfterFirstAuth: String {
let dirPath: String = NSTemporaryDirectory()
OWSFileSystem.ensureDirectoryExists(dirPath, fileProtectionType: .completeUntilFirstUserAuthentication)
return dirPath;
}
var appDocumentDirectoryPath: String {
let targetPath: String? = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.last?
.path
owsAssertDebug(targetPath != nil)
return (targetPath ?? "")
}
// MARK: - Functions
func setMainWindow(_ mainWindow: UIWindow) {}
func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any]) {}
func beginBackgroundTask(expirationHandler: @escaping () -> ()) -> UIBackgroundTaskIdentifier { return .invalid }
func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier) {}
func clearOldTemporaryDirectories() {}
}
// MARK: - Objective C Support
// FIXME: Remove this once the OWSFileSystem has been refactored to Swift
@objc public class OWSCurrentAppContext: NSObject {
@objc public static var isRTL: Bool { Singleton.appContext.isRTL }
@objc public static var isMainApp: Bool { Singleton.appContext.isMainApp }
@objc public static var isMainAppAndActive: Bool { Singleton.appContext.isMainAppAndActive }
@objc public static var isAppForegroundAndActive: Bool { Singleton.appContext.isAppForegroundAndActive }
@objc public static var temporaryDirectory: String { Singleton.appContext.temporaryDirectory }
@objc public static var appUserDefaults: UserDefaults { Singleton.appContext.appUserDefaults }
@objc public static var appDocumentDirectoryPath: String { Singleton.appContext.appDocumentDirectoryPath }
// FIXME: This will be reworked to be part of dependencies in the Groups Rebuild branch
@objc static var appSharedDataDirectoryPath: String {
let targetPath: String? = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: UserDefaults.applicationGroup)?
.path
owsAssertDebug(targetPath != nil)
return (targetPath ?? "")
}
@objc static func beginBackgroundTask(expirationHandler: @escaping () -> ()) -> UIBackgroundTaskIdentifier {
return Singleton.appContext.beginBackgroundTask { expirationHandler() }
}
@objc static func endBackgroundTask(_ backgroundTaskIdentifier: UIBackgroundTaskIdentifier) {
Singleton.appContext.endBackgroundTask(backgroundTaskIdentifier)
}
}

@ -19,7 +19,7 @@ public class LRUCache<KeyType: Hashable & Equatable, ValueType> {
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(didEnterBackground),
name: NSNotification.Name.OWSApplicationDidEnterBackground,
name: .sessionDidEnterBackground,
object: nil)
}

@ -3,7 +3,7 @@
//
#import "NSUserDefaults+OWS.h"
#import "AppContext.h"
#import <SessionUtilitiesKit/SessionUtilitiesKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSUserDefaults *)appUserDefaults
{
return CurrentAppContext().appUserDefaults;
return [OWSCurrentAppContext appUserDefaults];
}
+ (void)migrateToSharedUserDefaults

@ -0,0 +1,17 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import Foundation
public extension Notification.Name {
static let sessionDidEnterBackground = Notification.Name("sessionDidEnterBackground")
static let sessionWillEnterForeground = Notification.Name("sessionWillEnterForeground")
static let sessionWillResignActive = Notification.Name("sessionWillResignActive")
static let sessionDidBecomeActive = Notification.Name("sessionDidBecomeActive")
}
@objc public extension NSNotification {
@objc static let sessionDidEnterBackground = Notification.Name.sessionDidEnterBackground.rawValue as NSString
@objc static let sessionWillEnterForeground = Notification.Name.sessionWillEnterForeground.rawValue as NSString
@objc static let sessionWillResignActive = Notification.Name.sessionWillResignActive.rawValue as NSString
@objc static let sessionDidBecomeActive = Notification.Name.sessionDidBecomeActive.rawValue as NSString
}

@ -0,0 +1,6 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import Foundation
// FIXME: This will be reworked to be part of dependencies in the Groups Rebuild branch
public enum Singleton {}

@ -6,7 +6,7 @@
#import "OWSMath.h"
#import <PureLayout/PureLayout.h>
#import <SessionUtilitiesKit/AppContext.h>
#import <SessionUtilitiesKit/SessionUtilitiesKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@ -390,7 +390,7 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
- (NSTextAlignment)textAlignmentUnnatural
{
return (CurrentAppContext().isRTL ? NSTextAlignmentLeft : NSTextAlignmentRight);
return ([OWSCurrentAppContext isRTL] ? NSTextAlignmentLeft : NSTextAlignmentRight);
}
- (void)setHLayoutMargins:(CGFloat)value

@ -191,8 +191,8 @@ public final class JobRunner: JobRunnerType {
self.allowToExecuteJobs = (
isTestingJobRunner || (
HasAppContext() &&
CurrentAppContext().isMainApp &&
Singleton.hasAppContext &&
Singleton.appContext.isMainApp &&
!SNUtilitiesKit.isRunningTests
)
)

@ -3,7 +3,6 @@
FOUNDATION_EXPORT double SessionUtilitiesKitVersionNumber;
FOUNDATION_EXPORT const unsigned char SessionUtilitiesKitVersionString[];
#import <SessionUtilitiesKit/AppContext.h>
#import <SessionUtilitiesKit/DataSource.h>
#import <SessionUtilitiesKit/MIMETypeUtil.h>
#import <SessionUtilitiesKit/NSData+Image.h>

@ -631,7 +631,7 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
removeAssetRequestFromQueue(assetRequest: assetRequest)
return
}
guard CurrentAppContext().isMainAppAndActive || CurrentAppContext().isShareExtension else {
guard Singleton.hasAppContext && (Singleton.appContext.isMainAppAndActive || Singleton.appContext.isShareExtension) else {
// If app is not active, fail the asset request.
assetRequest.state = .failed
assetRequestDidFail(assetRequest: assetRequest)
@ -900,7 +900,7 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
// We write assets to the temporary directory so that iOS can clean them up.
// We try to eagerly clean up these assets when they are no longer in use.
let tempDirPath = OWSTemporaryDirectory()
let tempDirPath = Singleton.appContext.temporaryDirectory
let dirPath = (tempDirPath as NSString).appendingPathComponent(downloadFolderName)
do {
let fileManager = FileManager.default

@ -3,7 +3,6 @@
//
#import "OWSBackgroundTask.h"
#import "AppContext.h"
#import <SessionUtilitiesKit/SessionUtilitiesKit.h>
#import <SessionUtilitiesKit/SessionUtilitiesKit-Swift.h>
@ -64,7 +63,7 @@ typedef NSNumber *OWSTaskId;
self.backgroundTaskId = UIBackgroundTaskInvalid;
self.expirationMap = [NSMutableDictionary new];
self.idCounter = 0;
self.isAppActive = CurrentAppContext().isMainAppAndActive;
self.isAppActive = [OWSCurrentAppContext isMainAppAndActive];
return self;
}
@ -76,16 +75,16 @@ typedef NSNumber *OWSTaskId;
- (void)observeNotifications
{
if (!CurrentAppContext().isMainApp) {
if (![OWSCurrentAppContext isMainApp]) {
return;
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidBecomeActive:)
name:OWSApplicationDidBecomeActiveNotification
name:NSNotification.sessionDidBecomeActive
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillResignActive:)
name:OWSApplicationWillResignActiveNotification
name:NSNotification.sessionWillResignActive
object:nil];
}
@ -164,7 +163,7 @@ typedef NSNumber *OWSTaskId;
// Begins or end a background task if necessary.
- (BOOL)ensureBackgroundTaskState
{
if (!CurrentAppContext().isMainApp) {
if (![OWSCurrentAppContext isMainApp]) {
// We can't create background tasks in the SAE, but pretend that we succeeded.
return YES;
}
@ -187,7 +186,7 @@ typedef NSNumber *OWSTaskId;
// Need to end background task.
UIBackgroundTaskIdentifier backgroundTaskId = self.backgroundTaskId;
self.backgroundTaskId = UIBackgroundTaskInvalid;
[CurrentAppContext() endBackgroundTask:backgroundTaskId];
[OWSCurrentAppContext endBackgroundTask:backgroundTaskId];
return YES;
}
}
@ -198,7 +197,7 @@ typedef NSNumber *OWSTaskId;
{
@synchronized(self)
{
self.backgroundTaskId = [CurrentAppContext() beginBackgroundTaskWithExpirationHandler:^{
self.backgroundTaskId = [OWSCurrentAppContext beginBackgroundTaskWithExpirationHandler:^{
// Supposedly [UIApplication beginBackgroundTaskWithExpirationHandler]'s handler
// will always be called on the main thread, but in practice we've observed
// otherwise.
@ -243,7 +242,7 @@ typedef NSNumber *OWSTaskId;
}
if (backgroundTaskId != UIBackgroundTaskInvalid) {
// Apparently we need to "end" even expired background tasks.
[CurrentAppContext() endBackgroundTask:backgroundTaskId];
[OWSCurrentAppContext endBackgroundTask:backgroundTaskId];
}
}];
}

@ -0,0 +1,14 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import UIKit.UIApplication
public extension UIApplication.State {
var name: String {
switch self {
case .active: return "Active"
case .background: return "Background"
case .inactive: return "Inactive"
@unknown default: return "Unknown"
}
}
}

@ -155,7 +155,7 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC
NotificationCenter.default.addObserver(
self,
selector: #selector(didBecomeActive),
name: .OWSApplicationDidBecomeActive,
name: .sessionDidBecomeActive,
object: nil
)
}

@ -4,7 +4,6 @@
#import "OWSViewController.h"
#import "UIView+OWS.h"
#import "AppContext.h"
#import <PureLayout/PureLayout.h>
#import <SessionUIKit/SessionUIKit.h>
#import <SignalCoreKit/OWSAsserts.h>
@ -214,7 +213,7 @@ UIInterfaceOrientationMask DefaultUIInterfaceOrientationMask(void)
};
if (self.shouldAnimateBottomLayout && CurrentAppContext().isAppForegroundAndActive) {
if (self.shouldAnimateBottomLayout && [OWSCurrentAppContext isAppForegroundAndActive]) {
updateLayout();
} else {
// UIKit by default animates all changes in response to keyboard events.

@ -7,10 +7,12 @@ import SessionUtilitiesKit
public extension UIEdgeInsets {
init(top: CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat) {
self.init(top: top,
left: CurrentAppContext().isRTL ? trailing : leading,
bottom: bottom,
right: CurrentAppContext().isRTL ? leading : trailing)
self.init(
top: top,
left: (Singleton.hasAppContext && Singleton.appContext.isRTL ? trailing : leading),
bottom: bottom,
right: (Singleton.hasAppContext && Singleton.appContext.isRTL ? leading : trailing)
)
}
}

@ -67,8 +67,7 @@ public extension UIViewController {
static func createOWSBackButton(target: Any?, selector: Selector) -> UIBarButtonItem {
let backButton: UIButton = UIButton(type: .custom)
let isRTL: Bool = CurrentAppContext().isRTL
let isRTL: Bool = (Singleton.hasAppContext && Singleton.appContext.isRTL)
// Nudge closer to the left edge to match default back button item.
let extraLeftPadding: CGFloat = (isRTL ? 0 : -8)

Loading…
Cancel
Save