Merge branch 'dev' into onboarding

pull/891/head
Ryan ZHAO 10 months ago
commit 0b7ff7b623

@ -227,7 +227,7 @@ local sim_delete_cmd = 'if [ -f build/artifacts/sim_uuid ]; then rm -f /Users/$U
name: 'Build',
commands: [
'mkdir build',
'NSUnbufferedIO=YES set -o pipefail && xcodebuild archive -workspace Session.xcworkspace -scheme Session -derivedDataPath ./build/derivedData -parallelizeTargets -configuration "App Store Release" -sdk iphonesimulator -archivePath ./build/Session_sim.xcarchive -destination "generic/platform=iOS Simulator" | xcbeautify --is-ci',
'NSUnbufferedIO=YES set -o pipefail && xcodebuild archive -workspace Session.xcworkspace -scheme Session -derivedDataPath ./build/derivedData -parallelizeTargets -configuration "App_Store_Release" -sdk iphonesimulator -archivePath ./build/Session_sim.xcarchive -destination "generic/platform=iOS Simulator" | xcbeautify --is-ci',
],
depends_on: [
'Install CocoaPods',

@ -1 +1 @@
Subproject commit 714230c0c8867ac8662603fd6debb5b4c4369ef1
Subproject commit fdfd9dbb698e4ce6e6e08dd1b85eec428ab2077e

@ -130,16 +130,12 @@
7B81682A28B6F1420069F315 /* ReactionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682928B6F1420069F315 /* ReactionResponse.swift */; };
7B81682C28B72F480069F315 /* PendingChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682B28B72F480069F315 /* PendingChange.swift */; };
7B81FB5A2AB01B17002FB267 /* LoadingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81FB582AB01AA8002FB267 /* LoadingIndicatorView.swift */; };
7B87EF422A8D9840002A0E8F /* DisplayNameScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B87EF412A8D9840002A0E8F /* DisplayNameScreen.swift */; };
7B87EF442A8DA720002A0E8F /* SessionTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B87EF432A8DA720002A0E8F /* SessionTextField.swift */; };
7B87EF462A8DDA8E002A0E8F /* PNModeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B87EF452A8DDA8E002A0E8F /* PNModeScreen.swift */; };
7B87EF482A8DFBBF002A0E8F /* RecoveryPasswordScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B87EF472A8DFBBF002A0E8F /* RecoveryPasswordScreen.swift */; };
7B87EF4A2A92DFB4002A0E8F /* LoadAccountScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B87EF492A92DFB4002A0E8F /* LoadAccountScreen.swift */; };
7B87EF4C2A933355002A0E8F /* LoadingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B87EF4B2A933355002A0E8F /* LoadingScreen.swift */; };
7B8914772A7CAAE200A4C627 /* SessionHostingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8914762A7CAAE200A4C627 /* SessionHostingViewController.swift */; };
7B8C44C528B49DDA00FBE25F /* NewConversationVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8C44C428B49DDA00FBE25F /* NewConversationVC.swift */; };
7B8D5FC428332600008324D9 /* VisibleMessage+Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8D5FC328332600008324D9 /* VisibleMessage+Reaction.swift */; };
7B93D07127CF194000811CB6 /* MessageRequestResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B93D06F27CF194000811CB6 /* MessageRequestResponse.swift */; };
7B93D07727CF1A8A00811CB6 /* MockDataGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B93D07527CF1A8900811CB6 /* MockDataGenerator.swift */; };
7B9C1C7D2AF206CD003FEAE9 /* AttributedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9C1C7C2AF206CD003FEAE9 /* AttributedText.swift */; };
7B9F71C928470667006DFE7B /* ReactionListSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9F71C828470667006DFE7B /* ReactionListSheet.swift */; };
7B9F71D02852EEE2006DFE7B /* Emoji+Category.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9F71CB2852EEE2006DFE7B /* Emoji+Category.swift */; };
7B9F71D12852EEE2006DFE7B /* EmojiWithSkinTones+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9F71CC2852EEE2006DFE7B /* EmojiWithSkinTones+String.swift */; };
@ -162,7 +158,9 @@
7BAF54D027ACCEEC003D12F8 /* EmptySearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54CD27ACCEEC003D12F8 /* EmptySearchResultCell.swift */; };
7BAF54D327ACCF01003D12F8 /* ShareAppExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D127ACCF01003D12F8 /* ShareAppExtensionContext.swift */; };
7BAF54D427ACCF01003D12F8 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */; };
7BAFA1192A39669400B76CB9 /* BezierPathView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAFA1182A39669400B76CB9 /* BezierPathView.swift */; };
7BAFA75A2AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAFA7592AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift */; };
7BB92B3F28C825FD0082762F /* NewConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */; };
7BBBDC44286EAD2D00747E59 /* TappableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */; };
7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */; };
7BC01A3E241F40AB00BC7C55 /* NotificationServiceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */; };
@ -173,26 +171,14 @@
7BD687D12A5D0D1200D8E455 /* MessageInfoScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD687D02A5D0D1200D8E455 /* MessageInfoScreen.swift */; };
7BD976972A776C76001B466F /* SessionCarouselView+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE2701D2A64C11500CEB71A /* SessionCarouselView+SwiftUI.swift */; };
7BDCFC08242186E700641C39 /* NotificationServiceExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */; };
7BDE2A982A8B122900AE4393 /* LandingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDE2A972A8B122900AE4393 /* LandingScreen.swift */; };
7BDE2A9A2A8C59CF00AE4393 /* AttributedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDE2A992A8C59CF00AE4393 /* AttributedText.swift */; };
7BF570D32A9C1F9300DB013E /* Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BF570D22A9C1F9300DB013E /* Toast.swift */; };
7BF8D1FB2A70AF57005F1D6E /* SwiftUI+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BF8D1FA2A70AF57005F1D6E /* SwiftUI+Theme.swift */; };
7BFA8AE32831D0D4001876F3 /* ContextMenuVC+EmojiReactsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFA8AE22831D0D4001876F3 /* ContextMenuVC+EmojiReactsView.swift */; };
7BFD1A8A2745C4F000FB91B9 /* Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A892745C4F000FB91B9 /* Permissions.swift */; };
7BFD1A8C2747150E00FB91B9 /* TurnServerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A8B2747150E00FB91B9 /* TurnServerInfo.swift */; };
7BFD1A972747689000FB91B9 /* Session-Turn-Server in Resources */ = {isa = PBXBuildFile; fileRef = 7BFD1A962747689000FB91B9 /* Session-Turn-Server */; };
88C614F4223F9BE0CB94E4E5 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 35C5EF2AACD4A39D6E0E2C49 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit.framework */; };
941061402BBE45EB0056C084 /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9410613F2BBE45EB0056C084 /* ActivityView.swift */; };
9422EE2B2B8C3A97004C740D /* String+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9422EE2A2B8C3A97004C740D /* String+Utilities.swift */; };
942C9CA22B67769000B5153A /* SessionSearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 942C9CA12B67769000B5153A /* SessionSearchBar.swift */; };
943C6D762B705B7D004ACE64 /* CompatibleScrollingVStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943C6D752B705B7D004ACE64 /* CompatibleScrollingVStack.swift */; };
943C6D822B75E061004ACE64 /* Message+DisappearingMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943C6D812B75E061004ACE64 /* Message+DisappearingMessages.swift */; };
946B34472B5DF0B7004CB4A3 /* QRCodeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946B34462B5DF0B7004CB4A3 /* QRCodeScreen.swift */; };
946B34492B5E04BB004CB4A3 /* CustomTopTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946B34482B5E04BB004CB4A3 /* CustomTopTabBar.swift */; };
946B344B2B5E08F3004CB4A3 /* ScanQRCodeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946B344A2B5E08F3004CB4A3 /* ScanQRCodeScreen.swift */; };
946B344D2B5F67B4004CB4A3 /* StartConversationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946B344C2B5F67B4004CB4A3 /* StartConversationScreen.swift */; };
946B344F2B61D80B004CB4A3 /* InviteAFriendScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946B344E2B61D80B004CB4A3 /* InviteAFriendScreen.swift */; };
946B34512B61D818004CB4A3 /* NewMessageScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946B34502B61D818004CB4A3 /* NewMessageScreen.swift */; };
94B3DC172AF8592200C88531 /* QuoteView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94B3DC162AF8592200C88531 /* QuoteView_SwiftUI.swift */; };
A11CD70D17FA230600A2D1B1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */; };
A163E8AB16F3F6AA0094D68B /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A163E8AA16F3F6A90094D68B /* Security.framework */; };
@ -214,6 +200,11 @@
B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D2825C7A4B400488AB4 /* InputView.swift */; };
B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3225C7A8C600488AB4 /* InputViewButton.swift */; };
B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8269D3C25C7B34D00488AB4 /* InputTextView.swift */; };
B82B40882399EB0E00A248E7 /* LandingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B40872399EB0E00A248E7 /* LandingVC.swift */; };
B82B408A2399EC0600A248E7 /* FakeChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B40892399EC0600A248E7 /* FakeChatView.swift */; };
B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408B239A068800A248E7 /* RegisterVC.swift */; };
B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */; };
B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B408F239DD75000A248E7 /* RestoreVC.swift */; };
B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B835246D25C38ABF0089A44F /* ConversationVC.swift */; };
B835247925C38D880089A44F /* MessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B835247825C38D880089A44F /* MessageCell.swift */; };
B835249B25C3AB650089A44F /* VisibleMessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B835249A25C3AB650089A44F /* VisibleMessageCell.swift */; };
@ -223,6 +214,7 @@
B849789625D4A2F500D0D0B3 /* LinkPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B849789525D4A2F500D0D0B3 /* LinkPreviewView.swift */; };
B84A89BC25DE328A0040017D /* ProfilePictureVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84A89BB25DE328A0040017D /* ProfilePictureVC.swift */; };
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */; };
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357C223A1BD1200AAF6CD /* SeedVC.swift */; };
B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */; };
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8569AE225CBB19A00DBA3DB /* DocumentView.swift */; };
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
@ -241,6 +233,7 @@
B8856DE6256F15F2001CE70E /* String+SSK.swift in Sources */ = {isa = PBXBuildFile; fileRef = C33FDB3F255A580C00E217F9 /* String+SSK.swift */; };
B8856E09256F1676001CE70E /* UIDevice+featureSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38EF237255B6D65007E1867 /* UIDevice+featureSupport.swift */; };
B8856E1A256F1700001CE70E /* OWSMath.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FDB14255A580800E217F9 /* OWSMath.h */; settings = {ATTRIBUTES = (Public, ); }; };
B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A62398B23E00211ABE /* QRCodeVC.swift */; };
B886B4A92398BA1500211ABE /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A82398BA1500211ABE /* QRCode.swift */; };
B88FA7F2260C3EB10049422F /* OpenGroupSuggestionGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88FA7F1260C3EB10049422F /* OpenGroupSuggestionGrid.swift */; };
B88FA7FB26114EA70049422F /* Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88FA7FA26114EA70049422F /* Hex.swift */; };
@ -255,9 +248,11 @@
B8BF43BA26CC95FB007828D1 /* WebRTC+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BF43B926CC95FB007828D1 /* WebRTC+Utilities.swift */; };
B8C2B2C82563685C00551B4D /* CircleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8C2B2C72563685C00551B4D /* CircleView.swift */; };
B8CCF6352396005F0091D419 /* SpaceMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8CCF6342396005F0091D419 /* SpaceMono-Regular.ttf */; };
B8CCF63723961D6D0091D419 /* NewDMVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63623961D6D0091D419 /* NewDMVC.swift */; };
B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8CCF63E23975CFB0091D419 /* JoinOpenGroupVC.swift */; };
B8D07405265C683300F77E07 /* ElegantIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8D07404265C683300F77E07 /* ElegantIcons.ttf */; };
B8D07406265C683A00F77E07 /* ElegantIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B8D07404265C683300F77E07 /* ElegantIcons.ttf */; };
B8D0A25025E3678700C1835E /* LinkDeviceVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D0A24F25E3678700C1835E /* LinkDeviceVC.swift */; };
B8D0A25925E367AC00C1835E /* Notification+MessageReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D0A25825E367AC00C1835E /* Notification+MessageReceiver.swift */; };
B8D0A26925E4A2C200C1835E /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8D0A26825E4A2C200C1835E /* Onboarding.swift */; };
B8D64FBB25BA78310029CFC0 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; };
@ -321,6 +316,7 @@
C331FFE82558FB0000070591 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C3CF8824D8EED300E1CCE7 /* TextView.swift */; };
C331FFE92558FB0000070591 /* Separator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B82394911B00BA5194 /* Separator.swift */; };
C331FFF32558FF0300070591 /* PathStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B879D44A247E1D9200DB3608 /* PathStatusView.swift */; };
C331FFF42558FF0300070591 /* PNOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C353F8F8244809150011121A /* PNOptionView.swift */; };
C331FFFE2558FF3B00070591 /* FullConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AA238F669C00BA5194 /* FullConversationCell.swift */; };
C33FD9AF255A548A00E217F9 /* SignalUtilitiesKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C33FD9AD255A548A00E217F9 /* SignalUtilitiesKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33FD9B3255A548A00E217F9 /* SignalUtilitiesKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C33FD9AB255A548A00E217F9 /* SignalUtilitiesKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -344,6 +340,7 @@
C352A35B2557824E00338F3E /* AttachmentUploadJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352A35A2557824E00338F3E /* AttachmentUploadJob.swift */; };
C352A36D2557858E00338F3E /* NSTimer+Proxying.m in Sources */ = {isa = PBXBuildFile; fileRef = C352A36C2557858D00338F3E /* NSTimer+Proxying.m */; };
C352A3772557864000338F3E /* NSTimer+Proxying.h in Headers */ = {isa = PBXBuildFile; fileRef = C352A3762557859C00338F3E /* NSTimer+Proxying.h */; settings = {ATTRIBUTES = (Public, ); }; };
C3548F0624456447009433A8 /* PNModeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3548F0524456447009433A8 /* PNModeVC.swift */; };
C3548F0824456AB6009433A8 /* UIView+Wrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */; };
C354E75A23FE2A7600CE22E3 /* BaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C354E75923FE2A7600CE22E3 /* BaseVC.swift */; };
C35D0DB525AE5F1200B6BF49 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C35D0DB425AE5F1200B6BF49 /* UIEdgeInsets.swift */; };
@ -643,9 +640,6 @@
FD52090528B4915F006098F6 /* PrivacySettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD52090428B4915F006098F6 /* PrivacySettingsViewModel.swift */; };
FD52090928B59411006098F6 /* ScreenLockUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD52090828B59411006098F6 /* ScreenLockUI.swift */; };
FD52090B28B59BB4006098F6 /* ScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD52090A28B59BB4006098F6 /* ScreenLockViewController.swift */; };
FD5312362C1A5BCE0013DA11 /* SSNMockedExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5312352C1A5BCE0013DA11 /* SSNMockedExtensions.swift */; };
FD5312372C1A5BE80013DA11 /* SSNMockedExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5312352C1A5BCE0013DA11 /* SSNMockedExtensions.swift */; };
FD5312382C1A5BEA0013DA11 /* SSNMockedExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5312352C1A5BCE0013DA11 /* SSNMockedExtensions.swift */; };
FD559DF52A7368CB00C7C62A /* DispatchQueue+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD559DF42A7368CB00C7C62A /* DispatchQueue+Utilities.swift */; };
FD5931A72A8DA5DA0040147D /* SQLInterpolation+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5931A62A8DA5DA0040147D /* SQLInterpolation+Utilities.swift */; };
FD5931AB2A8DCB0A0040147D /* ScopeAdapter+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5931AA2A8DCB0A0040147D /* ScopeAdapter+Utilities.swift */; };
@ -876,6 +870,8 @@
FDD250722837234B00198BDA /* MediaGalleryNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */; };
FDD82C3F2A205D0A00425F05 /* ProcessResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD82C3E2A205D0A00425F05 /* ProcessResult.swift */; };
FDDC08F229A300E800BF9681 /* LibSessionTypeConversionUtilitiesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDDC08F129A300E800BF9681 /* LibSessionTypeConversionUtilitiesSpec.swift */; };
FDDD554C2C1FC812006CBF03 /* CheckForAppUpdatesJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDDD554B2C1FC812006CBF03 /* CheckForAppUpdatesJob.swift */; };
FDDD554E2C1FCB77006CBF03 /* _019_ScheduleAppUpdateCheckJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDDD554D2C1FCB77006CBF03 /* _019_ScheduleAppUpdateCheckJob.swift */; };
FDDF074429C3E3D000E5E8B5 /* FetchRequest+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDDF074329C3E3D000E5E8B5 /* FetchRequest+Utilities.swift */; };
FDDF074A29DAB36900E5E8B5 /* JobRunnerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDDF074929DAB36900E5E8B5 /* JobRunnerSpec.swift */; };
FDE125232A837E4E002DA685 /* MainAppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE125222A837E4E002DA685 /* MainAppContext.swift */; };
@ -1311,16 +1307,12 @@
7B81682928B6F1420069F315 /* ReactionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionResponse.swift; sourceTree = "<group>"; };
7B81682B28B72F480069F315 /* PendingChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PendingChange.swift; sourceTree = "<group>"; };
7B81FB582AB01AA8002FB267 /* LoadingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingIndicatorView.swift; sourceTree = "<group>"; };
7B87EF412A8D9840002A0E8F /* DisplayNameScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameScreen.swift; sourceTree = "<group>"; };
7B87EF432A8DA720002A0E8F /* SessionTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionTextField.swift; sourceTree = "<group>"; };
7B87EF452A8DDA8E002A0E8F /* PNModeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNModeScreen.swift; sourceTree = "<group>"; };
7B87EF472A8DFBBF002A0E8F /* RecoveryPasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPasswordScreen.swift; sourceTree = "<group>"; };
7B87EF492A92DFB4002A0E8F /* LoadAccountScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadAccountScreen.swift; sourceTree = "<group>"; };
7B87EF4B2A933355002A0E8F /* LoadingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingScreen.swift; sourceTree = "<group>"; };
7B8914762A7CAAE200A4C627 /* SessionHostingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionHostingViewController.swift; sourceTree = "<group>"; };
7B8C44C428B49DDA00FBE25F /* NewConversationVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationVC.swift; sourceTree = "<group>"; };
7B8D5FC328332600008324D9 /* VisibleMessage+Reaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleMessage+Reaction.swift"; sourceTree = "<group>"; };
7B93D06F27CF194000811CB6 /* MessageRequestResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageRequestResponse.swift; sourceTree = "<group>"; };
7B93D07527CF1A8900811CB6 /* MockDataGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDataGenerator.swift; sourceTree = "<group>"; };
7B9C1C7C2AF206CD003FEAE9 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = "<group>"; };
7B9F71C828470667006DFE7B /* ReactionListSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionListSheet.swift; sourceTree = "<group>"; };
7B9F71CB2852EEE2006DFE7B /* Emoji+Category.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Emoji+Category.swift"; sourceTree = "<group>"; };
7B9F71CC2852EEE2006DFE7B /* EmojiWithSkinTones+String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EmojiWithSkinTones+String.swift"; sourceTree = "<group>"; };
@ -1344,7 +1336,9 @@
7BAF54D127ACCF01003D12F8 /* ShareAppExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareAppExtensionContext.swift; sourceTree = "<group>"; };
7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
7BAFA1182A39669400B76CB9 /* BezierPathView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BezierPathView.swift; sourceTree = "<group>"; };
7BAFA7592AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewView_SwiftUI.swift; sourceTree = "<group>"; };
7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationViewModel.swift; sourceTree = "<group>"; };
7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TappableLabel.swift; sourceTree = "<group>"; };
7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTitleViewController.swift; sourceTree = "<group>"; };
7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1357,10 +1351,7 @@
7BD687D22A5D283200D8E455 /* build_libSession_util.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_libSession_util.sh; sourceTree = "<group>"; };
7BDCFC0424206E7300641C39 /* SessionNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SessionNotificationServiceExtension.entitlements; sourceTree = "<group>"; };
7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtensionContext.swift; sourceTree = "<group>"; };
7BDE2A972A8B122900AE4393 /* LandingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandingScreen.swift; sourceTree = "<group>"; };
7BDE2A992A8C59CF00AE4393 /* AttributedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedText.swift; sourceTree = "<group>"; };
7BE2701D2A64C11500CEB71A /* SessionCarouselView+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCarouselView+SwiftUI.swift"; sourceTree = "<group>"; };
7BF570D22A9C1F9300DB013E /* Toast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toast.swift; sourceTree = "<group>"; };
7BF8D1FA2A70AF57005F1D6E /* SwiftUI+Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SwiftUI+Theme.swift"; sourceTree = "<group>"; };
7BFA8AE22831D0D4001876F3 /* ContextMenuVC+EmojiReactsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContextMenuVC+EmojiReactsView.swift"; sourceTree = "<group>"; };
7BFD1A892745C4F000FB91B9 /* Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Permissions.swift; sourceTree = "<group>"; };
@ -1371,17 +1362,8 @@
81B86106CE32C944E6A3A8F2 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionShareExtension.app_store_release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionShareExtension.app_store_release.xcconfig"; path = "Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionShareExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionShareExtension.app_store_release.xcconfig"; sourceTree = "<group>"; };
89EE44A6174C653D2F4DB573 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SignalUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SignalUtilitiesKit.debug.xcconfig"; path = "Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SignalUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SignalUtilitiesKit.debug.xcconfig"; sourceTree = "<group>"; };
91271A534D1D4AB949B1319E /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.app store release.xcconfig"; path = "Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.app store release.xcconfig"; sourceTree = "<group>"; };
9410613F2BBE45EB0056C084 /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = "<group>"; };
9422EE2A2B8C3A97004C740D /* String+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Utilities.swift"; sourceTree = "<group>"; };
942C9CA12B67769000B5153A /* SessionSearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionSearchBar.swift; sourceTree = "<group>"; };
943C6D752B705B7D004ACE64 /* CompatibleScrollingVStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompatibleScrollingVStack.swift; sourceTree = "<group>"; };
943C6D812B75E061004ACE64 /* Message+DisappearingMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+DisappearingMessages.swift"; sourceTree = "<group>"; };
946B34462B5DF0B7004CB4A3 /* QRCodeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScreen.swift; sourceTree = "<group>"; };
946B34482B5E04BB004CB4A3 /* CustomTopTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTopTabBar.swift; sourceTree = "<group>"; };
946B344A2B5E08F3004CB4A3 /* ScanQRCodeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQRCodeScreen.swift; sourceTree = "<group>"; };
946B344C2B5F67B4004CB4A3 /* StartConversationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartConversationScreen.swift; sourceTree = "<group>"; };
946B344E2B61D80B004CB4A3 /* InviteAFriendScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteAFriendScreen.swift; sourceTree = "<group>"; };
946B34502B61D818004CB4A3 /* NewMessageScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewMessageScreen.swift; sourceTree = "<group>"; };
94B3DC162AF8592200C88531 /* QuoteView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuoteView_SwiftUI.swift; sourceTree = "<group>"; };
987AE169AEA3246E4C0897B1 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
998762BFD08C2415FE71186C /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; path = "Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; sourceTree = "<group>"; };
@ -1411,6 +1393,11 @@
B8269D2825C7A4B400488AB4 /* InputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputView.swift; sourceTree = "<group>"; };
B8269D3225C7A8C600488AB4 /* InputViewButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputViewButton.swift; sourceTree = "<group>"; };
B8269D3C25C7B34D00488AB4 /* InputTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputTextView.swift; sourceTree = "<group>"; };
B82B40872399EB0E00A248E7 /* LandingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LandingVC.swift; sourceTree = "<group>"; };
B82B40892399EC0600A248E7 /* FakeChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeChatView.swift; sourceTree = "<group>"; };
B82B408B239A068800A248E7 /* RegisterVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterVC.swift; sourceTree = "<group>"; };
B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayNameVC.swift; sourceTree = "<group>"; };
B82B408F239DD75000A248E7 /* RestoreVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreVC.swift; sourceTree = "<group>"; };
B835246D25C38ABF0089A44F /* ConversationVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationVC.swift; sourceTree = "<group>"; };
B835247825C38D880089A44F /* MessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageCell.swift; sourceTree = "<group>"; };
B835249A25C3AB650089A44F /* VisibleMessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleMessageCell.swift; sourceTree = "<group>"; };
@ -1420,6 +1407,7 @@
B849789525D4A2F500D0D0B3 /* LinkPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewView.swift; sourceTree = "<group>"; };
B84A89BB25DE328A0040017D /* ProfilePictureVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePictureVC.swift; sourceTree = "<group>"; };
B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedReminderView.swift; sourceTree = "<group>"; };
B85357C223A1BD1200AAF6CD /* SeedVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedVC.swift; sourceTree = "<group>"; };
B8544E3023D16CA500299F14 /* DeviceUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceUtilities.swift; sourceTree = "<group>"; };
B8569AC225CB5D2900DBA3DB /* ConversationVC+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConversationVC+Interaction.swift"; sourceTree = "<group>"; };
B8569AE225CBB19A00DBA3DB /* DocumentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentView.swift; sourceTree = "<group>"; };
@ -1432,6 +1420,7 @@
B879D44A247E1D9200DB3608 /* PathStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathStatusView.swift; sourceTree = "<group>"; };
B87EF18026377A1D00124B3C /* Features.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Features.swift; sourceTree = "<group>"; };
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraints.swift"; sourceTree = "<group>"; };
B886B4A62398B23E00211ABE /* QRCodeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeVC.swift; sourceTree = "<group>"; };
B886B4A82398BA1500211ABE /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = "<group>"; };
B88FA7B726045D100049422F /* OpenGroupAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupAPI.swift; sourceTree = "<group>"; };
B88FA7F1260C3EB10049422F /* OpenGroupSuggestionGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupSuggestionGrid.swift; sourceTree = "<group>"; };
@ -1454,9 +1443,11 @@
B8BF43B926CC95FB007828D1 /* WebRTC+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebRTC+Utilities.swift"; sourceTree = "<group>"; };
B8C2B2C72563685C00551B4D /* CircleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleView.swift; sourceTree = "<group>"; };
B8CCF6342396005F0091D419 /* SpaceMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SpaceMono-Regular.ttf"; sourceTree = "<group>"; };
B8CCF63623961D6D0091D419 /* NewDMVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDMVC.swift; sourceTree = "<group>"; };
B8CCF638239721E20091D419 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = "<group>"; };
B8CCF63E23975CFB0091D419 /* JoinOpenGroupVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinOpenGroupVC.swift; sourceTree = "<group>"; };
B8D07404265C683300F77E07 /* ElegantIcons.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = ElegantIcons.ttf; sourceTree = "<group>"; };
B8D0A24F25E3678700C1835E /* LinkDeviceVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkDeviceVC.swift; sourceTree = "<group>"; };
B8D0A25825E367AC00C1835E /* Notification+MessageReceiver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Notification+MessageReceiver.swift"; path = "SessionMessagingKit/Sending & Receiving/Notification+MessageReceiver.swift"; sourceTree = SOURCE_ROOT; };
B8D0A26825E4A2C200C1835E /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = "<group>"; };
B8D84EA225DF745A005A043E /* LinkPreviewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewState.swift; sourceTree = "<group>"; };
@ -1548,6 +1539,8 @@
C352A35A2557824E00338F3E /* AttachmentUploadJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentUploadJob.swift; sourceTree = "<group>"; };
C352A36C2557858D00338F3E /* NSTimer+Proxying.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSTimer+Proxying.m"; sourceTree = "<group>"; };
C352A3762557859C00338F3E /* NSTimer+Proxying.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSTimer+Proxying.h"; sourceTree = "<group>"; };
C353F8F8244809150011121A /* PNOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNOptionView.swift; sourceTree = "<group>"; };
C3548F0524456447009433A8 /* PNModeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNModeVC.swift; sourceTree = "<group>"; };
C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Wrapping.swift"; sourceTree = "<group>"; };
C354E75923FE2A7600CE22E3 /* BaseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseVC.swift; sourceTree = "<group>"; };
C35D0DB425AE5F1200B6BF49 /* UIEdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIEdgeInsets.swift; sourceTree = "<group>"; };
@ -1828,7 +1821,6 @@
FD52090628B49738006098F6 /* ConfirmationModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationModal.swift; sourceTree = "<group>"; };
FD52090828B59411006098F6 /* ScreenLockUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenLockUI.swift; sourceTree = "<group>"; };
FD52090A28B59BB4006098F6 /* ScreenLockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenLockViewController.swift; sourceTree = "<group>"; };
FD5312352C1A5BCE0013DA11 /* SSNMockedExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSNMockedExtensions.swift; sourceTree = "<group>"; };
FD559DF42A7368CB00C7C62A /* DispatchQueue+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Utilities.swift"; sourceTree = "<group>"; };
FD5931A62A8DA5DA0040147D /* SQLInterpolation+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SQLInterpolation+Utilities.swift"; sourceTree = "<group>"; };
FD5931AA2A8DCB0A0040147D /* ScopeAdapter+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ScopeAdapter+Utilities.swift"; sourceTree = "<group>"; };
@ -2075,6 +2067,8 @@
FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryNavigationController.swift; sourceTree = "<group>"; };
FDD82C3E2A205D0A00425F05 /* ProcessResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessResult.swift; sourceTree = "<group>"; };
FDDC08F129A300E800BF9681 /* LibSessionTypeConversionUtilitiesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibSessionTypeConversionUtilitiesSpec.swift; sourceTree = "<group>"; };
FDDD554B2C1FC812006CBF03 /* CheckForAppUpdatesJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckForAppUpdatesJob.swift; sourceTree = "<group>"; };
FDDD554D2C1FCB77006CBF03 /* _019_ScheduleAppUpdateCheckJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _019_ScheduleAppUpdateCheckJob.swift; sourceTree = "<group>"; };
FDDF074329C3E3D000E5E8B5 /* FetchRequest+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FetchRequest+Utilities.swift"; sourceTree = "<group>"; };
FDDF074929DAB36900E5E8B5 /* JobRunnerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobRunnerSpec.swift; sourceTree = "<group>"; };
FDE125222A837E4E002DA685 /* MainAppContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainAppContext.swift; sourceTree = "<group>"; };
@ -2513,9 +2507,9 @@
7B8C44C328B49DA900FBE25F /* New Conversation */ = {
isa = PBXGroup;
children = (
946B344C2B5F67B4004CB4A3 /* StartConversationScreen.swift */,
946B344E2B61D80B004CB4A3 /* InviteAFriendScreen.swift */,
946B34502B61D818004CB4A3 /* NewMessageScreen.swift */,
B8CCF63623961D6D0091D419 /* NewDMVC.swift */,
7B8C44C428B49DDA00FBE25F /* NewConversationVC.swift */,
7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */,
);
path = "New Conversation";
sourceTree = "<group>";
@ -2596,19 +2590,6 @@
path = TurnServers;
sourceTree = "<group>";
};
942C9CA02B67762400B5153A /* SwiftUI */ = {
isa = PBXGroup;
children = (
7BDE2A992A8C59CF00AE4393 /* AttributedText.swift */,
7B87EF432A8DA720002A0E8F /* SessionTextField.swift */,
7BF570D22A9C1F9300DB013E /* Toast.swift */,
942C9CA12B67769000B5153A /* SessionSearchBar.swift */,
943C6D752B705B7D004ACE64 /* CompatibleScrollingVStack.swift */,
9410613F2BBE45EB0056C084 /* ActivityView.swift */,
);
path = SwiftUI;
sourceTree = "<group>";
};
B8041A7325C8F758003C2166 /* Content Views */ = {
isa = PBXGroup;
children = (
@ -2832,6 +2813,7 @@
4CA46F4B219CCC630038ABDE /* CaptionView.swift */,
34F308A01ECB469700BB7697 /* OWSBezierPathView.h */,
34F308A11ECB469700BB7697 /* OWSBezierPathView.m */,
7BAFA1182A39669400B76CB9 /* BezierPathView.swift */,
C354E75923FE2A7600CE22E3 /* BaseVC.swift */,
B8BB82AA238F669C00BA5194 /* FullConversationCell.swift */,
4542DF53208D40AC007B4E76 /* LoadingViewController.swift */,
@ -2847,7 +2829,6 @@
7B2561C329874851005C086C /* SessionCarouselView+Info.swift */,
7B3A3933298882D6002FE4AC /* SessionCarouselViewDelegate.swift */,
7B8914762A7CAAE200A4C627 /* SessionHostingViewController.swift */,
946B344A2B5E08F3004CB4A3 /* ScanQRCodeScreen.swift */,
7B81FB582AB01AA8002FB267 /* LoadingIndicatorView.swift */,
);
path = Shared;
@ -3091,7 +3072,6 @@
C331FFCC2558FAF300070591 /* Components */ = {
isa = PBXGroup;
children = (
942C9CA02B67762400B5153A /* SwiftUI */,
B8B5BCEB2394D869003823C9 /* SessionButton.swift */,
FD52090228B4680F006098F6 /* RadioButton.swift */,
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
@ -3108,6 +3088,7 @@
C38EF2A4255B6D93007E1867 /* ProfilePictureView.swift */,
FD71165A28E6DDBC00B47552 /* StyledNavigationController.swift */,
FD0B77AF29B69A65009169BA /* TopBannerController.swift */,
7B9C1C7C2AF206CD003FEAE9 /* AttributedText.swift */,
);
path = Components;
sourceTree = "<group>";
@ -3146,13 +3127,17 @@
C360968D25AD16B4008B62B2 /* Onboarding */ = {
isa = PBXGroup;
children = (
B82B408D239DC00D00A248E7 /* DisplayNameVC.swift */,
B82B40892399EC0600A248E7 /* FakeChatView.swift */,
B82B40872399EB0E00A248E7 /* LandingVC.swift */,
B8D0A24F25E3678700C1835E /* LinkDeviceVC.swift */,
C3548F0524456447009433A8 /* PNModeVC.swift */,
C353F8F8244809150011121A /* PNOptionView.swift */,
B82B408B239A068800A248E7 /* RegisterVC.swift */,
B82B408F239DD75000A248E7 /* RestoreVC.swift */,
B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */,
B85357C223A1BD1200AAF6CD /* SeedVC.swift */,
B8D0A26825E4A2C200C1835E /* Onboarding.swift */,
7BDE2A972A8B122900AE4393 /* LandingScreen.swift */,
7B87EF412A8D9840002A0E8F /* DisplayNameScreen.swift */,
7B87EF452A8DDA8E002A0E8F /* PNModeScreen.swift */,
7B87EF492A92DFB4002A0E8F /* LoadAccountScreen.swift */,
7B87EF4B2A933355002A0E8F /* LoadingScreen.swift */,
);
path = Onboarding;
sourceTree = "<group>";
@ -3172,10 +3157,9 @@
C360969125AD1765008B62B2 /* Settings */ = {
isa = PBXGroup;
children = (
7B87EF472A8DFBBF002A0E8F /* RecoveryPasswordScreen.swift */,
946B34462B5DF0B7004CB4A3 /* QRCodeScreen.swift */,
FD37E9CD28A1E682003AE748 /* Views */,
FD71162D28E168C700B47552 /* SettingsViewModel.swift */,
B886B4A62398B23E00211ABE /* QRCodeVC.swift */,
FD52090428B4915F006098F6 /* PrivacySettingsViewModel.swift */,
FD37EA0428AA00C1003AE748 /* NotificationSettingsViewModel.swift */,
FD37EA1828AC5CCA003AE748 /* NotificationSoundViewModel.swift */,
@ -3775,6 +3759,7 @@
FDFE75B02ABD2D2400655640 /* _016_MakeBrokenProfileTimestampsNullable.swift */,
FD428B222B4B9969006D0888 /* _017_RebuildFTSIfNeeded_2_4_5.swift */,
7B5233C5290636D700F8F375 /* _018_DisappearingMessagesConfiguration.swift */,
FDDD554D2C1FCB77006CBF03 /* _019_ScheduleAppUpdateCheckJob.swift */,
);
path = Migrations;
sourceTree = "<group>";
@ -3992,14 +3977,6 @@
path = "Shared Models";
sourceTree = "<group>";
};
FD5312342C1A5BAC0013DA11 /* _TestUtilities */ = {
isa = PBXGroup;
children = (
FD5312352C1A5BCE0013DA11 /* SSNMockedExtensions.swift */,
);
path = _TestUtilities;
sourceTree = "<group>";
};
FD7115F528C8150600B47552 /* Combine */ = {
isa = PBXGroup;
children = (
@ -4074,7 +4051,6 @@
FD37EA0A28AB12E2003AE748 /* SessionCell.swift */,
FD71164728E2CE8700B47552 /* SessionCell+AccessoryView.swift */,
FD71164528E2CC1300B47552 /* SessionHighlightingBackgroundLabel.swift */,
946B34482B5E04BB004CB4A3 /* CustomTopTabBar.swift */,
7B71A98E2925E2A600E54854 /* SessionFooterView.swift */,
);
path = Views;
@ -4136,7 +4112,6 @@
FD7C37AF2BBB8B1E009DEEA7 /* SessionSnodeKitTests */ = {
isa = PBXGroup;
children = (
FD5312342C1A5BAC0013DA11 /* _TestUtilities */,
FD7C37BC2BBB8BB1009DEEA7 /* Models */,
);
path = SessionSnodeKitTests;
@ -4484,6 +4459,7 @@
FD2B4AFE2946C93200AB4848 /* ConfigurationSyncJob.swift */,
7B7E5B512A4D024C00A8208E /* ExpirationUpdateJob.swift */,
7B7AD41E2A5512CA00469FB1 /* GetExpirationJob.swift */,
FDDD554B2C1FC812006CBF03 /* CheckForAppUpdatesJob.swift */,
);
path = Types;
sourceTree = "<group>";
@ -5867,9 +5843,7 @@
7BBBDC44286EAD2D00747E59 /* TappableLabel.swift in Sources */,
FD09B7E328865FDA00ED0B66 /* HighlightMentionBackgroundView.swift in Sources */,
C331FFB82558FA8D00070591 /* DeviceUtilities.swift in Sources */,
941061402BBE45EB0056C084 /* ActivityView.swift in Sources */,
C331FFE72558FB0000070591 /* TextField.swift in Sources */,
942C9CA22B67769000B5153A /* SessionSearchBar.swift in Sources */,
FD71165B28E6DDBC00B47552 /* StyledNavigationController.swift in Sources */,
C331FFE32558FB0000070591 /* TabBar.swift in Sources */,
FD37E9D528A1FCE8003AE748 /* Theme+OceanLight.swift in Sources */,
@ -5880,13 +5854,10 @@
FD37EA0128A60473003AE748 /* UIKit+Theme.swift in Sources */,
FD37E9CF28A1EB1B003AE748 /* Theme.swift in Sources */,
C331FFB92558FA8D00070591 /* UIView+Constraints.swift in Sources */,
7BDE2A9A2A8C59CF00AE4393 /* AttributedText.swift in Sources */,
7B87EF442A8DA720002A0E8F /* SessionTextField.swift in Sources */,
7BF570D32A9C1F9300DB013E /* Toast.swift in Sources */,
7B9C1C7D2AF206CD003FEAE9 /* AttributedText.swift in Sources */,
FD0B77B029B69A65009169BA /* TopBannerController.swift in Sources */,
FD37E9F628A5F106003AE748 /* Configuration.swift in Sources */,
7BF8D1FB2A70AF57005F1D6E /* SwiftUI+Theme.swift in Sources */,
943C6D762B705B7D004ACE64 /* CompatibleScrollingVStack.swift in Sources */,
FDBB25E72988BBBE00F1508E /* UIContextualAction+Theming.swift in Sources */,
C331FFE02558FB0000070591 /* SearchBar.swift in Sources */,
FD16AB5B2A1DD7CA0083D849 /* PlaceholderIcon.swift in Sources */,
@ -6212,6 +6183,7 @@
7B81682A28B6F1420069F315 /* ReactionResponse.swift in Sources */,
7B7E5B522A4D024C00A8208E /* ExpirationUpdateJob.swift in Sources */,
FD09799727FFA84A00936362 /* RecipientState.swift in Sources */,
FDDD554E2C1FCB77006CBF03 /* _019_ScheduleAppUpdateCheckJob.swift in Sources */,
FDA8EB00280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift in Sources */,
FD09798927FD1C5A00936362 /* OpenGroup.swift in Sources */,
FD848B9628422A2A000E298B /* MessageViewModel.swift in Sources */,
@ -6314,6 +6286,7 @@
C32C599E256DB02B003C73A2 /* TypingIndicators.swift in Sources */,
FD716E682850318E00C96BF4 /* CallMode.swift in Sources */,
FD09799527FE7B8E00936362 /* Interaction.swift in Sources */,
FDDD554C2C1FC812006CBF03 /* CheckForAppUpdatesJob.swift in Sources */,
FD37EA0D28AB2A45003AE748 /* _005_FixDeletedMessageReadState.swift in Sources */,
7BAA7B6628D2DE4700AE1489 /* _009_OpenGroupPermission.swift in Sources */,
FDC4380927B31D4E00C60D73 /* OpenGroupAPIError.swift in Sources */,
@ -6388,6 +6361,7 @@
FD7115EB28C5D78E00B47552 /* ThreadSettingsViewModel.swift in Sources */,
B8041AA725C90927003C2166 /* TypingIndicatorCell.swift in Sources */,
7B4EF25A2934743000CB351D /* SessionTableViewTitleView.swift in Sources */,
B8CCF63723961D6D0091D419 /* NewDMVC.swift in Sources */,
FDFDE12A282D056B0098B17F /* MediaZoomAnimationController.swift in Sources */,
4C1885D2218F8E1C00B67051 /* PhotoGridViewCell.swift in Sources */,
34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */,
@ -6412,6 +6386,8 @@
C328255225CA64470062D0A7 /* ContextMenuVC+ActionView.swift in Sources */,
450DF2091E0DD2C6003D14BE /* UserNotificationsAdaptee.swift in Sources */,
C3548F0824456AB6009433A8 /* UIView+Wrapping.swift in Sources */,
B82B408A2399EC0600A248E7 /* FakeChatView.swift in Sources */,
B82B40882399EB0E00A248E7 /* LandingVC.swift in Sources */,
45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */,
B83524A525C3BA4B0089A44F /* InfoMessageCell.swift in Sources */,
7B9F71D82853100A006DFE7B /* EmojiWithSkinTones.swift in Sources */,
@ -6433,12 +6409,14 @@
FD09C5E828264937000CE219 /* MediaDetailViewController.swift in Sources */,
3496955E219B605E00DCFE74 /* PhotoLibrary.swift in Sources */,
7BA37AFD2AEF7C3D002438F8 /* VoiceMessageView_SwiftUI.swift in Sources */,
7B8C44C528B49DDA00FBE25F /* NewConversationVC.swift in Sources */,
7B1B52E028580D51006069F2 /* EmojiSkinTonePicker.swift in Sources */,
B849789625D4A2F500D0D0B3 /* LinkPreviewView.swift in Sources */,
FD71164428E2CB8A00B47552 /* SessionCell+Accessory.swift in Sources */,
7B1B52DF28580D51006069F2 /* EmojiPickerCollectionView.swift in Sources */,
FD71165228E410BE00B47552 /* SessionTableSection.swift in Sources */,
C3D0972B2510499C00F6E3E4 /* BackgroundPoller.swift in Sources */,
C3548F0624456447009433A8 /* PNModeVC.swift in Sources */,
FD71164828E2CE8700B47552 /* SessionCell+AccessoryView.swift in Sources */,
B80A579F23DFF1F300876683 /* NewClosedGroupVC.swift in Sources */,
7BA68909272A27BE00EFC32F /* SessionCall.swift in Sources */,
@ -6447,11 +6425,11 @@
7BA37AF92AEB365C002438F8 /* DocumentView_SwiftUI.swift in Sources */,
FDB7400D28EBEC240094D718 /* DateHeaderCell.swift in Sources */,
B8D0A26925E4A2C200C1835E /* Onboarding.swift in Sources */,
946B344B2B5E08F3004CB4A3 /* ScanQRCodeScreen.swift in Sources */,
34D1F0521F7E8EA30066283D /* GiphyDownloader.swift in Sources */,
4CC613362227A00400E21A3A /* ConversationSearch.swift in Sources */,
FD87DCFE28B7582C00AF0F98 /* BlockedContactsViewModel.swift in Sources */,
FD37E9DD28A384EB003AE748 /* PrimaryColorSelectionView.swift in Sources */,
B82B408C239A068800A248E7 /* RegisterVC.swift in Sources */,
FDC498B72AC15F7D00EDD897 /* AppNotificationCategory.swift in Sources */,
FDFDE126282D05380098B17F /* MediaInteractiveDismiss.swift in Sources */,
34BECE301F7ABCF800D7438D /* GifPickerLayout.swift in Sources */,
@ -6464,9 +6442,9 @@
FD71162228D983ED00B47552 /* QRCodeScanningViewController.swift in Sources */,
B8D84EA325DF745A005A043E /* LinkPreviewState.swift in Sources */,
45C0DC1E1E69011F00E04C47 /* UIStoryboard+OWS.swift in Sources */,
B82B408E239DC00D00A248E7 /* DisplayNameVC.swift in Sources */,
B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */,
7B7037432834B81F000DCF35 /* ReactionContainerView.swift in Sources */,
7B87EF422A8D9840002A0E8F /* DisplayNameScreen.swift in Sources */,
7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */,
FD71163F28E2C82C00B47552 /* SessionHeaderView.swift in Sources */,
7B8914772A7CAAE200A4C627 /* SessionHostingViewController.swift in Sources */,
@ -6483,8 +6461,6 @@
7BAFA75A2AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift in Sources */,
FD71163E28E2C82900B47552 /* SessionCell.swift in Sources */,
4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */,
946B34472B5DF0B7004CB4A3 /* QRCodeScreen.swift in Sources */,
946B34512B61D818004CB4A3 /* NewMessageScreen.swift in Sources */,
C328254925CA60E60062D0A7 /* ContextMenuVC+Action.swift in Sources */,
FD71164628E2CC1300B47552 /* SessionHighlightingBackgroundLabel.swift in Sources */,
4542DF54208D40AC007B4E76 /* LoadingViewController.swift in Sources */,
@ -6495,12 +6471,13 @@
34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */,
7BAF54CF27ACCEEC003D12F8 /* GlobalSearchViewController.swift in Sources */,
FD37EA1728AC5605003AE748 /* NotificationContentViewModel.swift in Sources */,
7B87EF4C2A933355002A0E8F /* LoadingScreen.swift in Sources */,
B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */,
C331FFF42558FF0300070591 /* PNOptionView.swift in Sources */,
94B3DC172AF8592200C88531 /* QuoteView_SwiftUI.swift in Sources */,
7BB92B3F28C825FD0082762F /* NewConversationViewModel.swift in Sources */,
4C4AE6A1224AF35700D4AF6F /* SendMediaNavigationController.swift in Sources */,
B82149C125D605C6009C0F2A /* InfoBanner.swift in Sources */,
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */,
946B344D2B5F67B4004CB4A3 /* StartConversationScreen.swift in Sources */,
7B9F71D02852EEE2006DFE7B /* Emoji+Category.swift in Sources */,
7BAADFCC27B0EF23007BCF92 /* CallVideoView.swift in Sources */,
B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */,
@ -6508,6 +6485,7 @@
FD848B9828422F1A000E298B /* Date+Utilities.swift in Sources */,
FD37E9DB28A244E9003AE748 /* ThemePreviewView.swift in Sources */,
7B3A3934298882D6002FE4AC /* SessionCarouselViewDelegate.swift in Sources */,
B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */,
45B5360E206DD8BB00D61655 /* UIResponder+OWS.swift in Sources */,
7B9F71C928470667006DFE7B /* ReactionListSheet.swift in Sources */,
FD12A8412AD63BEA00EEBA0D /* NavigatableState.swift in Sources */,
@ -6524,7 +6502,6 @@
7BAADFCE27B215FE007BCF92 /* UIView+Draggable.swift in Sources */,
45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */,
FDF222072818CECF000A4995 /* ConversationViewModel.swift in Sources */,
946B344F2B61D80B004CB4A3 /* InviteAFriendScreen.swift in Sources */,
4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */,
7BD976972A776C76001B466F /* SessionCarouselView+SwiftUI.swift in Sources */,
B8041A9525C8FA1D003C2166 /* MediaLoaderView.swift in Sources */,
@ -6540,20 +6517,18 @@
7B9F71D32852EEE2006DFE7B /* Emoji.swift in Sources */,
FDC498BB2AC1606C00EDD897 /* AppNotificationUserInfoKey.swift in Sources */,
C328250F25CA06020062D0A7 /* VoiceMessageView.swift in Sources */,
B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */,
3488F9362191CC4000E524CC /* MediaView.swift in Sources */,
B8569AC325CB5D2900DBA3DB /* ConversationVC+Interaction.swift in Sources */,
7B87EF462A8DDA8E002A0E8F /* PNModeScreen.swift in Sources */,
3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */,
C31D1DE32521718E005D4DA8 /* UserSelectionVC.swift in Sources */,
34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */,
C31A6C5C247F2CF3001123EF /* CGRect+Utilities.swift in Sources */,
7BDE2A982A8B122900AE4393 /* LandingScreen.swift in Sources */,
FD4B200E283492210034334B /* InsetLockableTableView.swift in Sources */,
B8269D3325C7A8C600488AB4 /* InputViewButton.swift in Sources */,
FD12A8472AD63C3400EEBA0D /* PagedObservationSource.swift in Sources */,
B8269D3D25C7B34D00488AB4 /* InputTextView.swift in Sources */,
7B0EFDF0275084AA00FFAAE7 /* CallMessageCell.swift in Sources */,
7B87EF4A2A92DFB4002A0E8F /* LoadAccountScreen.swift in Sources */,
C3AAFFF225AE99710089E6DD /* AppDelegate.swift in Sources */,
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */,
@ -6561,7 +6536,6 @@
FD39352C28F382920084DADA /* VersionFooterView.swift in Sources */,
FD12A83F2AD63BDF00EEBA0D /* Navigatable.swift in Sources */,
7B9F71D22852EEE2006DFE7B /* Emoji+SkinTones.swift in Sources */,
946B34492B5E04BB004CB4A3 /* CustomTopTabBar.swift in Sources */,
7B7CB18E270D066F0079FF93 /* IncomingCallBanner.swift in Sources */,
B8569AE325CBB19A00DBA3DB /* DocumentView.swift in Sources */,
FDF848F529413EEC007DCAE5 /* SessionCell+Styling.swift in Sources */,
@ -6570,13 +6544,14 @@
7B5802992AAEF1B50050EEB1 /* OpenGroupInvitationView_SwiftUI.swift in Sources */,
FD716E6C28505E1C00C96BF4 /* MessageRequestsViewModel.swift in Sources */,
C35E8AAE2485E51D00ACB629 /* IP2Country.swift in Sources */,
7B87EF482A8DFBBF002A0E8F /* RecoveryPasswordScreen.swift in Sources */,
B835249B25C3AB650089A44F /* VisibleMessageCell.swift in Sources */,
B8D0A25025E3678700C1835E /* LinkDeviceVC.swift in Sources */,
B894D0752339EDCF00B4D94D /* NukeDataModal.swift in Sources */,
7B5233C42900E90F00F8F375 /* SessionLabelCarouselView.swift in Sources */,
7B93D07727CF1A8A00811CB6 /* MockDataGenerator.swift in Sources */,
7B1B52D828580C6D006069F2 /* EmojiPickerSheet.swift in Sources */,
FD368A6A29DE9E30000DBF1E /* UIContextualAction+Utilities.swift in Sources */,
7BAFA1192A39669400B76CB9 /* BezierPathView.swift in Sources */,
7B4C75CD26BB92060000AC89 /* DeletedMessageView.swift in Sources */,
FDD250722837234B00198BDA /* MediaGalleryNavigationController.swift in Sources */,
FDD2506E283711D600198BDA /* DifferenceKit+Utilities.swift in Sources */,
@ -6592,7 +6567,6 @@
B8EB20F02640F7F000773E52 /* OpenGroupInvitationView.swift in Sources */,
C328254025CA55880062D0A7 /* ContextMenuVC.swift in Sources */,
7BD687D12A5D0D1200D8E455 /* MessageInfoScreen.swift in Sources */,
7BD687D12A5D0D1200D8E455 /* MessageInfoScreen.swift in Sources */,
B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */,
FD71162E28E168C700B47552 /* SettingsViewModel.swift in Sources */,
);
@ -6606,7 +6580,6 @@
FD2AAAF028ED57B500A49611 /* SynchronousStorage.swift in Sources */,
FD23CE292A6775650000B97C /* MockCrypto.swift in Sources */,
FD19363F2ACA66DE004BCF0F /* DatabaseSpec.swift in Sources */,
FD5312382C1A5BEA0013DA11 /* SSNMockedExtensions.swift in Sources */,
FD23CE332A67C4D90000B97C /* MockNetwork.swift in Sources */,
FD71161528D00D6700B47552 /* ThreadDisappearingMessagesViewModelSpec.swift in Sources */,
FD23CE2D2A678E1E0000B97C /* MockCaches.swift in Sources */,
@ -6639,7 +6612,6 @@
FD7C37C82BBB8C11009DEEA7 /* NimbleExtensions.swift in Sources */,
FD7C37C52BBB8C01009DEEA7 /* MockJobRunner.swift in Sources */,
FD7C37CB2BBB8D36009DEEA7 /* MockUserDefaults.swift in Sources */,
FD5312362C1A5BCE0013DA11 /* SSNMockedExtensions.swift in Sources */,
FD7C37C12BBB8BEA009DEEA7 /* MockCrypto.swift in Sources */,
FD7C37C72BBB8C0E009DEEA7 /* TestExtensions.swift in Sources */,
FD5E93D82C12E3B50038C25A /* FileUploadResponseSpec.swift in Sources */,
@ -6686,7 +6658,6 @@
buildActionMask = 2147483647;
files = (
FDDC08F229A300E800BF9681 /* LibSessionTypeConversionUtilitiesSpec.swift in Sources */,
FD5312372C1A5BE80013DA11 /* SSNMockedExtensions.swift in Sources */,
FDC2909427D710B4005DAE71 /* SOGSEndpointSpec.swift in Sources */,
FD96F3A529DBC3DC00401309 /* MessageSendJobSpec.swift in Sources */,
FDC2909127D709CA005DAE71 /* SOGSMessageSpec.swift in Sources */,
@ -8071,7 +8042,7 @@
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 453;
CURRENT_PROJECT_VERSION = 452;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -8108,7 +8079,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 2.7.0;
MARKETING_VERSION = 2.6.2;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = (
"-fobjc-arc-exceptions",
@ -8149,7 +8120,7 @@
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CURRENT_PROJECT_VERSION = 453;
CURRENT_PROJECT_VERSION = 452;
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
@ -8181,7 +8152,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 2.7.0;
MARKETING_VERSION = 2.6.2;
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = (
"-DNS_BLOCK_ASSERTIONS=1",
@ -8212,7 +8183,6 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 445;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -8250,7 +8220,6 @@
"$(SRCROOT)",
);
LLVM_LTO = NO;
MARKETING_VERSION = 2.7.0;
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
@ -8284,7 +8253,6 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 445;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -8322,7 +8290,6 @@
"$(SRCROOT)",
);
LLVM_LTO = NO;
MARKETING_VERSION = 2.7.0;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
PRODUCT_NAME = Session;

@ -298,7 +298,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
guard BackgroundPoller.isValid else { return }
Log.info("Background poll failed due to manual timeout")
Log.info("Background poll failed due to manual timeout.")
BackgroundPoller.isValid = false
if Singleton.hasAppContext && Singleton.appContext.isInBackground {
@ -321,7 +321,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// 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 Singleton.hasAppContext && Singleton.appContext.isInBackground else { return }
guard Singleton.hasAppContext && Singleton.appContext.isInBackground else {
BackgroundPoller.isValid = false
return
}
BackgroundPoller.poll { result in
guard BackgroundPoller.isValid else { return }

@ -15,46 +15,50 @@ public final class BackgroundPoller {
completionHandler: @escaping (UIBackgroundFetchResult) -> Void,
using dependencies: Dependencies = Dependencies()
) {
let (groupIds, servers): (Set<String>, Set<String>) = Storage.shared.read { db in
(
try ClosedGroup
.select(.threadId)
.joining(
required: ClosedGroup.members
.filter(GroupMember.Columns.profileId == getUserHexEncodedPublicKey(db))
)
.asRequest(of: String.self)
.fetchSet(db),
/// The default room promise creates an OpenGroup with an empty `roomToken` value, we
/// don't want to start a poller for this as the user hasn't actually joined a room
///
/// We also want to exclude any rooms which have failed to poll too many times in a row from
/// the background poll as they are likely to fail again
try OpenGroup
.select(.server)
.filter(
OpenGroup.Columns.roomToken != "" &&
OpenGroup.Columns.isActive &&
OpenGroup.Columns.pollFailureCount < OpenGroupAPI.Poller.maxRoomFailureCountForBackgroundPoll
)
.distinct()
.asRequest(of: String.self)
.fetchSet(db)
)
}
.defaulting(to: ([], []))
Log.info("[BackgroundPoller] Fetching 1 User, \(groupIds.count) \("group", number: groupIds.count), \(servers.count) \("communit", number: servers.count, singular: "y", plural: "ies").")
Publishers
.MergeMany(
[pollForMessages(using: dependencies)]
.appending(contentsOf: pollForClosedGroupMessages(using: dependencies))
.appending(
contentsOf: Storage.shared
.read { db in
/// The default room promise creates an OpenGroup with an empty `roomToken` value, we
/// don't want to start a poller for this as the user hasn't actually joined a room
///
/// We also want to exclude any rooms which have failed to poll too many times in a row from
/// the background poll as they are likely to fail again
try OpenGroup
.select(.server)
.filter(
OpenGroup.Columns.roomToken != "" &&
OpenGroup.Columns.isActive &&
OpenGroup.Columns.pollFailureCount < OpenGroupAPI.Poller.maxRoomFailureCountForBackgroundPoll
)
.distinct()
.asRequest(of: String.self)
.fetchSet(db)
}
.defaulting(to: [])
.map { server -> AnyPublisher<Void, Error> in
let poller: OpenGroupAPI.Poller = OpenGroupAPI.Poller(for: server)
poller.stop()
return poller.poll(
calledFromBackgroundPoller: true,
isBackgroundPollerValid: { BackgroundPoller.isValid },
isPostCapabilitiesRetry: false,
using: dependencies
)
}
)
.appending(contentsOf: pollForClosedGroupMessages(groupIds: groupIds, using: dependencies))
.appending(contentsOf: pollForCommunityMessages(servers: servers, using: dependencies))
)
.subscribe(on: DispatchQueue.global(qos: .background), using: dependencies)
.receive(on: DispatchQueue.main, using: dependencies)
.collect()
.handleEvents(
receiveOutput: { _ in
Log.info("[BackgroundPoller] Finished polling.")
}
)
.sinkUntilComplete(
receiveCompletion: { result in
// If we have already invalidated the timer then do nothing (we essentially timed out)
@ -63,7 +67,7 @@ public final class BackgroundPoller {
switch result {
case .finished: completionHandler(.newData)
case .failure(let error):
SNLog("Background poll failed due to error: \(error)")
Log.error("[BackgroundPoller] Failed due to error: \(error).")
completionHandler(.failed)
}
}
@ -83,39 +87,55 @@ public final class BackgroundPoller {
drainBehaviour: .alwaysRandom,
using: dependencies
)
.handleEvents(
receiveOutput: { _, _, validMessageCount, _ in
Log.info("[BackgroundPoller] Received \(validMessageCount) valid \("message", number: validMessageCount).")
}
)
.map { _ in () }
.eraseToAnyPublisher()
}
private static func pollForClosedGroupMessages(
groupIds: Set<String>,
using dependencies: Dependencies
) -> [AnyPublisher<Void, Error>] {
// Fetch all closed groups (excluding any don't contain the current user as a
// GroupMemeber as the user is no longer a member of those)
return Storage.shared
.read { db in
try ClosedGroup
.select(.threadId)
.joining(
required: ClosedGroup.members
.filter(GroupMember.Columns.profileId == getUserHexEncodedPublicKey(db))
)
.asRequest(of: String.self)
.fetchAll(db)
}
.defaulting(to: [])
.map { groupPublicKey in
return ClosedGroupPoller()
.poll(
namespaces: ClosedGroupPoller.namespaces,
for: groupPublicKey,
calledFromBackgroundPoller: true,
isBackgroundPollValid: { BackgroundPoller.isValid },
drainBehaviour: .alwaysRandom,
using: dependencies
)
.map { _ in () }
.eraseToAnyPublisher()
}
return groupIds.map { groupPublicKey in
return ClosedGroupPoller()
.poll(
namespaces: ClosedGroupPoller.namespaces,
for: groupPublicKey,
calledFromBackgroundPoller: true,
isBackgroundPollValid: { BackgroundPoller.isValid },
drainBehaviour: .alwaysRandom,
using: dependencies
)
.handleEvents(
receiveOutput: { _, _, validMessageCount, _ in
Log.info("[BackgroundPoller] Received \(validMessageCount) valid \("message", number: validMessageCount) for group: \(groupPublicKey).")
}
)
.map { _ in () }
.eraseToAnyPublisher()
}
}
private static func pollForCommunityMessages(
servers: Set<String>,
using dependencies: Dependencies
) -> [AnyPublisher<Void, Error>] {
return servers.map { server -> AnyPublisher<Void, Error> in
let poller: OpenGroupAPI.Poller = OpenGroupAPI.Poller(for: server)
poller.stop()
return poller.poll(
calledFromBackgroundPoller: true,
isBackgroundPollerValid: { BackgroundPoller.isValid },
isPostCapabilitiesRetry: false,
using: dependencies
)
}
}
}

@ -35,7 +35,8 @@ public enum SNMessagingKit: MigratableTarget { // Just to make the external API
_015_BlockCommunityMessageRequests.self,
_016_MakeBrokenProfileTimestampsNullable.self,
_017_RebuildFTSIfNeeded_2_4_5.self,
_018_DisappearingMessagesConfiguration.self
_018_DisappearingMessagesConfiguration.self,
_019_ScheduleAppUpdateCheckJob.self
]
]
)
@ -60,5 +61,6 @@ public enum SNMessagingKit: MigratableTarget { // Just to make the external API
JobRunner.setExecutor(ConfigMessageReceiveJob.self, for: .configMessageReceive)
JobRunner.setExecutor(ExpirationUpdateJob.self, for: .expirationUpdate)
JobRunner.setExecutor(GetExpirationJob.self, for: .getExpiration)
JobRunner.setExecutor(CheckForAppUpdatesJob.self, for: .checkForAppUpdates)
}
}

@ -0,0 +1,25 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import SessionUtilitiesKit
enum _019_ScheduleAppUpdateCheckJob: Migration {
static let target: TargetMigrations.Identifier = .messagingKit
static let identifier: String = "ScheduleAppUpdateCheckJob" // stringlint:disable
static let needsConfigSync: Bool = false
static let minExpectedRunDuration: TimeInterval = 0.1
static var requirements: [MigrationRequirement] = [.libSessionStateLoaded]
static let fetchedTables: [(TableRecord & FetchableRecord).Type] = []
static let createdOrAlteredTables: [(TableRecord & FetchableRecord).Type] = []
static let droppedTables: [(TableRecord & FetchableRecord).Type] = []
static func migrate(_ db: GRDB.Database) throws {
_ = try Job(
variant: .checkForAppUpdates,
behaviour: .recurring
).migrationSafeInserted(db)
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
}
}

@ -376,6 +376,10 @@ extension DisappearingMessagesConfiguration {
TimeInterval(60),
at: 0
)
result.insert(
TimeInterval(30),
at: 0
)
result.insert(
TimeInterval(10),
at: 0
@ -391,6 +395,10 @@ extension DisappearingMessagesConfiguration {
]
.map { TimeInterval($0) }
#if targetEnvironment(simulator)
result.insert(
TimeInterval(30),
at: 0
)
result.insert(
TimeInterval(10),
at: 0

@ -0,0 +1,56 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Combine
import SessionUtilitiesKit
import SessionSnodeKit
public enum CheckForAppUpdatesJob: JobExecutor {
private static let updateCheckFrequency: TimeInterval = (4 * 60 * 60) // Max every 4 hours
public static var maxFailureCount: Int = -1
public static var requiresThreadId: Bool = false
public static let requiresInteractionId: Bool = false
public static func run(
_ job: Job,
queue: DispatchQueue,
success: @escaping (Job, Bool, Dependencies) -> (),
failure: @escaping (Job, Error?, Bool, Dependencies) -> (),
deferred: @escaping (Job, Dependencies) -> (),
using dependencies: Dependencies
) {
dependencies.storage
.readPublisher(using: dependencies) { db -> [UInt8]? in Identity.fetchUserEd25519KeyPair(db)?.secretKey }
.subscribe(on: queue)
.receive(on: queue)
.tryFlatMap { maybeEd25519SecretKey in
guard let ed25519SecretKey: [UInt8] = maybeEd25519SecretKey else { throw StorageError.objectNotFound }
return LibSession.checkClientVersion(
ed25519SecretKey: ed25519SecretKey,
using: dependencies
)
}
.sinkUntilComplete(
receiveCompletion: { _ in
var updatedJob: Job = job.with(
failureCount: 0,
nextRunTimestamp: (dependencies.dateNow.timeIntervalSince1970 + updateCheckFrequency)
)
dependencies.storage.write(using: dependencies) { db in
try updatedJob.save(db)
}
success(updatedJob, false, dependencies)
},
receiveValue: { _, versionInfo in
switch versionInfo.prerelease {
case .none: Log.info("[CheckForAppUpdatesJob] Latest version: \(versionInfo.version)")
case .some(let prerelease):
Log.info("[CheckForAppUpdatesJob] Latest version: \(versionInfo.version), pre-release version: \(prerelease.version)")
}
}
)
}
}

@ -49,7 +49,7 @@ public final class ClosedGroupPoller: Poller {
// MARK: - Abstract Methods
override func pollerName(for publicKey: String) -> String {
return "closed group with public key: \(publicKey)"
return "Closed group poller with public key: \(publicKey)"
}
override func nextPollDelay(for publicKey: String, using dependencies: Dependencies) -> TimeInterval {
@ -80,8 +80,7 @@ public final class ClosedGroupPoller: Poller {
return nextPollInterval
}
override func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> Bool {
SNLog("Polling failed for closed group with public key: \(publicKey) due to error: \(error).")
return true
override func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> PollerErrorResponse {
return .continuePolling
}
}

@ -63,7 +63,7 @@ public final class CurrentUserPoller: Poller {
return min(maxRetryInterval, nextDelay)
}
override func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> Bool {
override func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> PollerErrorResponse {
if UserDefaults.sharedLokiProject?[.isMainAppActive] != true {
// Do nothing when an error gets throws right after returning from the background (happens frequently)
}
@ -71,13 +71,13 @@ public final class CurrentUserPoller: Poller {
let drainBehaviour: Atomic<SwarmDrainBehaviour> = drainBehaviour.wrappedValue[publicKey],
case .limitedReuse(_, .some(let targetSnode), _, _, _) = drainBehaviour.wrappedValue
{
SNLog("Main Poller polling \(targetSnode) failed with error: \(period: "\(error)"); switching to next snode.")
drainBehaviour.mutate { $0 = $0.clearTargetSnode() }
return .continuePollingInfo("Switching from \(targetSnode) to next snode.")
}
else {
SNLog("Polling failed due to having no target service node.")
return .continuePollingInfo("Had no target snode.")
}
return true
return .continuePolling
}
}

@ -17,6 +17,12 @@ public class Poller {
hadValidHashUpdate: Bool
)
internal enum PollerErrorResponse {
case stopPolling
case continuePolling
case continuePollingInfo(String)
}
private var cancellables: Atomic<[String: AnyCancellable]> = Atomic([:])
internal var isPolling: Atomic<[String: Bool]> = Atomic([:])
internal var pollCount: Atomic<[String: Int]> = Atomic([:])
@ -72,7 +78,7 @@ public class Poller {
}
/// Perform and logic which should occur when the poll errors, will stop polling if `false` is returned
internal func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> Bool {
internal func handlePollError(_ error: Error, for publicKey: String, using dependencies: Dependencies) -> PollerErrorResponse {
preconditionFailure("abstract class - override in subclass")
}
@ -125,18 +131,24 @@ public class Poller {
.sink(
receiveCompletion: { _ in }, // Never called
receiveValue: { result in
// If the polling has been cancelled then don't continue
guard self?.isPolling.wrappedValue[swarmPublicKey] == true else { return }
let endTime: TimeInterval = dependencies.dateNow.timeIntervalSince1970
// Log information about the poll
switch result {
case .failure(let error):
// Determine if the error should stop us from polling anymore
guard self?.handlePollError(error, for: swarmPublicKey, using: dependencies) == true else {
return
switch self?.handlePollError(error, for: swarmPublicKey, using: dependencies) {
case .stopPolling: return
case .continuePollingInfo(let info):
Log.error("\(pollerName) failed to process any messages due to error: \(error). \(info)")
case .continuePolling, .none:
Log.error("\(pollerName) failed to process any messages due to error: \(error).")
}
Log.error("\(pollerName) failed to process any messages due to error: \(error)")
case .success(let response):
let duration: TimeUnit = .seconds(endTime - lastPollStart)

@ -8,6 +8,7 @@ import SessionMessagingKit
enum NotificationError: Error, CustomStringConvertible {
case processing(PushNotificationAPI.ProcessResult)
case messageProcessing
case ignorableMessage
case messageHandling(MessageReceiverError)
case other(Error)
@ -15,6 +16,7 @@ enum NotificationError: Error, CustomStringConvertible {
switch self {
case .processing(let result): return "Failed to process notification (\(result)) (NotificationError.processing)."
case .messageProcessing: return "Failed to process message (NotificationError.messageProcessing)."
case .ignorableMessage: return "Ignorable message (NotificationError.ignorableMessage)."
case .messageHandling(let error): return "Failed to handle message (\(error)) (NotificationError.messageHandling)."
case .other(let error): return "Unknown error occurred: \(error) (NotificationError.other)."
}

@ -15,7 +15,6 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
private var didPerformSetup = false
private var contentHandler: ((UNNotificationContent) -> Void)?
private var request: UNNotificationRequest?
private var openGroupPollCancellable: AnyCancellable?
private var hasCompleted: Atomic<Bool> = Atomic(false)
public static let isFromRemoteKey = "remote" // stringlint:disable
@ -27,19 +26,22 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// MARK: Did receive a remote push notification request
override public func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
Log.info("didReceive called.")
self.contentHandler = contentHandler
self.request = request
guard let notificationContent = request.content.mutableCopy() as? UNMutableNotificationContent else {
return self.completeSilenty()
}
// Abort if the main app is running
guard !(UserDefaults.sharedLokiProject?[.isMainAppActive]).defaulting(to: false) else {
Log.info("didReceive called while main app running.")
return self.completeSilenty(isMainAppAndActive: true)
}
guard let notificationContent = request.content.mutableCopy() as? UNMutableNotificationContent else {
Log.info("didReceive called with no content.")
return self.completeSilenty()
}
Log.info("didReceive called.")
/// Create the context if we don't have it (needed before _any_ interaction with the database)
if !Singleton.hasAppContext {
Singleton.setup(appContext: NotificationServiceExtensionContext())
@ -50,23 +52,10 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// Perform main setup
Storage.resumeDatabaseAccess()
LibSession.resumeNetworkAccess()
DispatchQueue.main.sync { self.setUpIfNecessary() { } }
// Handle the push notification
Singleton.appReadiness.runNowOrWhenAppDidBecomeReady {
let openGroupPollingPublishers: [AnyPublisher<Void, Error>] = self.pollForOpenGroups()
defer {
self.openGroupPollCancellable = Publishers
.MergeMany(openGroupPollingPublishers)
.subscribe(on: DispatchQueue.global(qos: .background))
.subscribe(on: DispatchQueue.main)
.sink(
receiveCompletion: { [weak self] _ in self?.completeSilenty() },
receiveValue: { _ in }
)
}
let (maybeData, metadata, result) = PushNotificationAPI.processNotification(
notificationContent: notificationContent
)
@ -84,16 +73,17 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// Just log if the notification was too long (a ~2k message should be able to fit so
// these will most commonly be call or config messages)
case .successTooLong:
return Log.info("Received too long notification for namespace: \(metadata.namespace).")
Log.info("Received too long notification for namespace: \(metadata.namespace).")
return self.completeSilenty()
case .legacyForceSilent, .failureNoContent: return
case .legacyForceSilent, .failureNoContent: return self.completeSilenty()
}
}
// HACK: It is important to use write synchronously here to avoid a race condition
// where the completeSilenty() is called before the local notification request
// is added to notification center
Storage.shared.write { db in
Storage.shared.write { [weak self] db in
do {
guard let processedMessage: ProcessedMessage = try Message.processRawReceivedMessageAsNotification(db, data: data, metadata: metadata) else {
throw NotificationError.messageProcessing
@ -119,7 +109,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
/// extension, for all other message types we want to just use the standard `MessageReceiver.handle` call
case .standard(let threadId, let threadVariant, _, let messageInfo) where messageInfo.message is CallMessage:
guard let callMessage = messageInfo.message as? CallMessage else {
return self.completeSilenty()
throw NotificationError.ignorableMessage
}
// Throw if the message is outdated and shouldn't be processed
@ -138,7 +128,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
)
guard case .preOffer = callMessage.kind else {
return self.completeSilenty()
throw NotificationError.ignorableMessage
}
switch (db[.areCallsEnabled], isCallOngoing) {
@ -179,7 +169,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
case (true, false):
try MessageReceiver.insertCallInfoMessage(db, for: callMessage)
self.handleSuccessForIncomingCall(db, for: callMessage)
return self?.handleSuccessForIncomingCall(db, for: callMessage)
}
// Perform any required post-handling logic
@ -200,6 +190,11 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
associatedWithProto: proto
)
}
db.afterNextTransaction(
onCommit: { _ in self?.completeSilenty() },
onRollback: { _ in self?.completeSilenty() }
)
}
catch {
// If an error occurred we want to rollback the transaction (by throwing) and then handle
@ -207,16 +202,16 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
let handleError = {
switch error {
case MessageReceiverError.invalidGroupPublicKey, MessageReceiverError.noGroupKeyPair,
MessageReceiverError.outdatedMessage:
self.completeSilenty()
MessageReceiverError.outdatedMessage, NotificationError.ignorableMessage:
self?.completeSilenty()
case NotificationError.messageProcessing:
self.handleFailure(for: notificationContent, error: .messageProcessing)
self?.handleFailure(for: notificationContent, error: .messageProcessing)
case let msgError as MessageReceiverError:
self.handleFailure(for: notificationContent, error: .messageHandling(msgError))
self?.handleFailure(for: notificationContent, error: .messageHandling(msgError))
default: self.handleFailure(for: notificationContent, error: .other(error))
default: self?.handleFailure(for: notificationContent, error: .other(error))
}
}
@ -330,12 +325,11 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
Log.warn("Execution time expired.")
openGroupPollCancellable?.cancel()
completeSilenty()
}
private func completeSilenty() {
// Ensure we on'y run this once
private func completeSilenty(isMainAppAndActive: Bool = false) {
// Ensure we only run this once
guard
hasCompleted.mutate({ hasCompleted in
let wasCompleted: Bool = hasCompleted
@ -349,9 +343,11 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
.read { db in try Interaction.fetchUnreadCount(db) }
.map { NSNumber(value: $0) }
.defaulting(to: NSNumber(value: 0))
Log.info("Complete silently.")
LibSession.suspendNetworkAccess()
Storage.suspendDatabaseAccess()
if !isMainAppAndActive {
Storage.suspendDatabaseAccess()
}
Log.flush()
self.contentHandler!(silentContent)
@ -361,23 +357,32 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
if #available(iOSApplicationExtension 14.5, *), Preferences.isCallKitSupported {
guard let caller: String = callMessage.sender, let timestamp = callMessage.sentTimestamp else { return }
let payload: JSON = [
"uuid": callMessage.uuid, // stringlint:disable
"caller": caller, // stringlint:disable
"timestamp": timestamp // stringlint:disable
]
CXProvider.reportNewIncomingVoIPPushPayload(payload) { error in
if let error = error {
self.handleFailureForVoIP(db, for: callMessage)
Log.error("Failed to notify main app of call message: \(error).")
}
else {
Log.info("Successfully notified main app of call message.")
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = Date()
self.completeSilenty()
let reportCall: () -> () = { [weak self] in
let payload: JSON = [
"uuid": callMessage.uuid, // stringlint:disable
"caller": caller, // stringlint:disable
"timestamp": timestamp // stringlint:disable
]
CXProvider.reportNewIncomingVoIPPushPayload(payload) { error in
if let error = error {
Log.error("Failed to notify main app of call message: \(error).")
Storage.shared.read { db in
self?.handleFailureForVoIP(db, for: callMessage)
}
}
else {
Log.info("Successfully notified main app of call message.")
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = Date()
self?.completeSilenty()
}
}
}
db.afterNextTransaction(
onCommit: { _ in reportCall() },
onRollback: { _ in reportCall() }
)
}
else {
self.handleFailureForVoIP(db, for: callMessage)
@ -412,12 +417,15 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
}
semaphore.wait()
Log.info("Add remote notification request.")
Log.flush()
db.afterNextTransaction(
onCommit: { [weak self] _ in self?.completeSilenty() },
onRollback: { [weak self] _ in self?.completeSilenty() }
)
}
private func handleFailure(for content: UNMutableNotificationContent, error: NotificationError) {
Log.error("Show generic failure message due to error: \(error).")
LibSession.suspendNetworkAccess()
Storage.suspendDatabaseAccess()
Log.flush()
@ -427,36 +435,4 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
content.userInfo = userInfo
contentHandler!(content)
}
// MARK: - Poll for open groups
private func pollForOpenGroups() -> [AnyPublisher<Void, Error>] {
return Storage.shared
.read { db in
// The default room promise creates an OpenGroup with an empty `roomToken` value,
// we don't want to start a poller for this as the user hasn't actually joined a room
try OpenGroup
.select(.server)
.filter(OpenGroup.Columns.roomToken != "")
.filter(OpenGroup.Columns.isActive)
.distinct()
.asRequest(of: String.self)
.fetchSet(db)
}
.defaulting(to: [])
.map { server -> AnyPublisher<Void, Error> in
OpenGroupAPI.Poller(for: server)
.poll(calledFromBackgroundPoller: true, isPostCapabilitiesRetry: false)
.timeout(
.seconds(20),
scheduler: DispatchQueue.global(qos: .default),
customError: { NotificationServiceError.timeout }
)
.eraseToAnyPublisher()
}
}
private enum NotificationServiceError: Error {
case timeout
}
}

@ -204,7 +204,7 @@ public extension LibSession {
switch result {
case .failure(let error): throw error
case .success(let nodes):
guard nodes.count > count else { throw SnodeAPIError.unableToRetrieveSwarm }
guard nodes.count >= count else { throw SnodeAPIError.unableToRetrieveSwarm }
return nodes
}
@ -358,6 +358,7 @@ public extension LibSession {
}
static func checkClientVersion(
ed25519SecretKey: [UInt8],
using dependencies: Dependencies = Dependencies()
) -> AnyPublisher<(ResponseInfoType, AppVersionResponse), Error> {
typealias Output = (success: Bool, timeout: Bool, statusCode: Int, data: Data?)
@ -366,9 +367,12 @@ public extension LibSession {
.tryFlatMap { network in
return CallbackWrapper<Output>
.create { wrapper in
var cEd25519SecretKey: [UInt8] = Array(ed25519SecretKey)
network_get_client_version(
network,
CLIENT_PLATFORM_IOS,
&cEd25519SecretKey,
Int64(floor(Network.fileDownloadTimeout * 1000)),
{ success, timeout, statusCode, dataPtr, dataLen, ctx in
let data: Data? = dataPtr.map { Data(bytes: $0, count: dataLen) }
@ -422,7 +426,7 @@ public extension LibSession {
return nil
}
guard network_init(&network, cCachePath, Features.useTestnet, true, &error) else {
guard network_init(&network, cCachePath, Features.useTestnet, !Singleton.appContext.isMainApp, true, &error) else {
Log.error("[LibQuic] Unable to create network object: \(String(cString: error))")
return nil
}
@ -550,6 +554,7 @@ public extension LibSession {
throw SnodeAPIError.nodeNotFound(String(responseString.suffix(64)))
case (504, _): throw NetworkError.gatewayTimeout
case (_, .none): throw NetworkError.unknown
case (_, .some(let responseString)): throw NetworkError.requestFailed(error: responseString, rawData: data)
}

@ -2,10 +2,96 @@
import Foundation
public struct AppVersionResponse: Codable {
public class AppVersionResponse: AppVersionInfo {
enum CodingKeys: String, CodingKey {
case prerelease
}
public let prerelease: AppVersionInfo?
public init(
version: String,
updated: TimeInterval?,
name: String?,
notes: String?,
assets: [Asset]?,
prerelease: AppVersionInfo?
) {
self.prerelease = prerelease
super.init(
version: version,
updated: updated,
name: name,
notes: notes,
assets: assets
)
}
required init(from decoder: Decoder) throws {
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
self.prerelease = try? container.decode(AppVersionInfo?.self, forKey: .prerelease)
try super.init(from: decoder)
}
public override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(prerelease, forKey: .prerelease)
}
}
// MARK: - AppVersionInfo
public class AppVersionInfo: Codable {
enum CodingKeys: String, CodingKey {
case version = "result"
case updated
case name
case notes
case assets
}
public struct Asset: Codable {
enum CodingKeys: String, CodingKey {
case name
case url
}
public let name: String
public let url: String
}
public let version: String
public let updated: TimeInterval?
public let name: String?
public let notes: String?
public let assets: [Asset]?
public init(
version: String,
updated: TimeInterval?,
name: String?,
notes: String?,
assets: [Asset]?
) {
self.version = version
self.updated = updated
self.name = name
self.notes = notes
self.assets = assets
}
public func encode(to encoder: Encoder) throws {
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
try container.encode(version, forKey: .version)
try container.encodeIfPresent(updated, forKey: .updated)
try container.encodeIfPresent(name, forKey: .name)
try container.encodeIfPresent(notes, forKey: .notes)
try container.encodeIfPresent(assets, forKey: .assets)
}
}

@ -128,6 +128,9 @@ public struct Job: Codable, Equatable, Hashable, Identifiable, FetchableRecord,
/// This is a job that runs once whenever a message is marked as read because of syncing from user config and
/// needs to get expiration from network
case getExpiration
/// This is a job that runs at most once every 24 hours in order to check if there is a new update available on GitHub
case checkForAppUpdates = 3011
}
public enum Behaviour: Int, Codable, DatabaseValueConvertible, CaseIterable {

@ -37,8 +37,7 @@ public enum Log {
public static func appResumedExecution() {
guard logger.wrappedValue != nil else { return }
Log.empty()
Log.empty()
logger.wrappedValue?.loadExtensionLogsAndResumeLogging()
}
public static func logFilePath() -> String? {
@ -46,7 +45,51 @@ public enum Log {
let logger: Logger = logger.wrappedValue
else { return nil }
return logger.fileLogger.logFileManager.sortedLogFilePaths.first
let logFiles: [String] = logger.fileLogger.logFileManager.sortedLogFilePaths
guard !logFiles.isEmpty else { return nil }
// If the latest log file is too short (ie. less that ~100kb) then we want to create a temporary file
// which contains the previous log file logs plus the logs from the newest file so we don't miss info
// that might be relevant for debugging
guard
logFiles.count > 1,
let attributes: [FileAttributeKey: Any] = try? FileManager.default.attributesOfItem(atPath: logFiles[0]),
let fileSize: UInt64 = attributes[.size] as? UInt64,
fileSize < (100 * 1024)
else { return logFiles[0] }
// The file is too small so lets create a temp file to share instead
let tempDirectory: String = NSTemporaryDirectory()
let tempFilePath: String = URL(fileURLWithPath: tempDirectory)
.appendingPathComponent(URL(fileURLWithPath: logFiles[1]).lastPathComponent)
.path
do {
try FileManager.default.copyItem(
atPath: logFiles[1],
toPath: tempFilePath
)
guard let fileHandle: FileHandle = FileHandle(forWritingAtPath: tempFilePath) else {
throw StorageError.objectNotFound
}
// Ensure we close the file handle
defer { fileHandle.closeFile() }
// Move to the end of the file to insert the logs
if #available(iOS 13.4, *) { try fileHandle.seekToEnd() }
else { fileHandle.seekToEndOfFile() }
// Append the data from the newest log to the temp file
let newestLogData: Data = try Data(contentsOf: URL(fileURLWithPath: logFiles[0]))
if #available(iOS 13.4, *) { try fileHandle.write(contentsOf: newestLogData) }
else { fileHandle.write(newestLogData) }
}
catch { return logFiles[0] }
return tempFilePath
}
public static func flush() {
@ -129,7 +172,7 @@ public enum Log {
) {
guard
let logger: Logger = logger.wrappedValue,
logger.startupCompleted.wrappedValue
!logger.isSuspended.wrappedValue
else { return pendingStartupLogs.mutate { $0.append((level, message, withPrefixes, silenceForTests)) } }
logger.log(level, message, withPrefixes: withPrefixes, silenceForTests: silenceForTests)
@ -143,7 +186,7 @@ public class Logger {
private let primaryPrefix: String
private let forceNSLog: Bool
fileprivate let fileLogger: DDFileLogger
fileprivate let startupCompleted: Atomic<Bool> = Atomic(false)
fileprivate let isSuspended: Atomic<Bool> = Atomic(true)
fileprivate var retrievePendingStartupLogs: (() -> [Log.LogInfo])?
public init(
@ -178,19 +221,22 @@ public class Logger {
// Now that we are setup we should load the extension logs which will then
// complete the startup process when completed
self.loadExtensionLogs()
self.loadExtensionLogsAndResumeLogging()
}
// MARK: - Functions
private func loadExtensionLogs() {
fileprivate func loadExtensionLogsAndResumeLogging() {
// Pause logging while we load the extension logs (want to avoid interleaving them where possible)
isSuspended.mutate { $0 = true }
// The extensions write their logs to the app shared directory but the main app writes
// to a local directory (so they can be exported via XCode) - the below code reads any
// logs from the shared directly and attempts to add them to the main app logs to make
// debugging user issues in extensions easier
DispatchQueue.global(qos: .utility).async { [weak self] in
guard let currentLogFileInfo: DDLogFileInfo = self?.fileLogger.currentLogFileInfo else {
self?.completeStartup(error: "Unable to retrieve current log file.")
self?.completeResumeLogging(error: "Unable to retrieve current log file.")
return
}
@ -257,21 +303,27 @@ public class Logger {
}
}
catch {
self?.completeStartup(error: "Unable to write extension logs to current log file")
self?.completeResumeLogging(error: "Unable to write extension logs to current log file")
return
}
self?.completeStartup()
self?.completeResumeLogging()
}
}
}
private func completeStartup(error: String? = nil) {
let pendingLogs: [Log.LogInfo] = startupCompleted.mutate { startupCompleted in
startupCompleted = true
private func completeResumeLogging(error: String? = nil) {
let pendingLogs: [Log.LogInfo] = isSuspended.mutate { isSuspended in
isSuspended = false
return (retrievePendingStartupLogs?() ?? [])
}
// If we had an error loading the extension logs then actually log it
if let error: String = error {
Log.empty()
log(.error, error, withPrefixes: true, silenceForTests: false)
}
// After creating a new logger we want to log two empty lines to make it easier to read
Log.empty()
Log.empty()

@ -82,8 +82,8 @@ public extension String.StringInterpolation {
appendInterpolation(value == 1 ? "" : "s") // stringlint:disable
}
public mutating func appendInterpolation(period value: String) {
appendInterpolation(value.hasSuffix(".") ? "" : ".") // stringlint:disable
mutating func appendInterpolation(_ value: String, number: Int, singular: String = "", plural: String = "s") {
appendInterpolation("\(value)\(number == 1 ? singular : plural)") // stringlint:disable
}
mutating func appendInterpolation(_ value: TimeUnit, unit: TimeUnit.Unit, resolution: Int = 2) {

@ -156,7 +156,7 @@ public final class JobRunner: JobRunnerType {
case (.failed(let lhsError, let lhsPermanent), .failed(let rhsError, let rhsPermanent)):
return (
// Not a perfect solution but should be good enough
"\(lhsError ?? JobRunnerError.generic)" == "\(rhsError ?? JobRunnerError.generic)" &&
"\(lhsError ?? JobRunnerError.unknown)" == "\(rhsError ?? JobRunnerError.unknown)" &&
lhsPermanent == rhsPermanent
)
@ -299,7 +299,8 @@ public final class JobRunner: JobRunnerType {
jobVariants: [
jobVariants.remove(.expirationUpdate),
jobVariants.remove(.getExpiration),
jobVariants.remove(.disappearingMessages)
jobVariants.remove(.disappearingMessages),
jobVariants.remove(.checkForAppUpdates) // Don't want this to block other jobs
].compactMap { $0 }
),
@ -1632,7 +1633,7 @@ public final class JobQueue: Hashable {
// immediately (in this case we don't trigger any job callbacks because the
// job isn't actually done, it's going to try again immediately)
if self.type == .blocking && job.shouldBlock {
SNLog("[JobRunner] \(queueContext) \(job.variant) job failed; retrying immediately")
SNLog("[JobRunner] \(queueContext) \(job.variant) job failed due to error: \(error ?? JobRunnerError.unknown); retrying immediately")
// If it was a possible deferral loop then we don't actually want to
// retry the job (even if it's a blocking one, this gives a small chance
@ -1664,7 +1665,7 @@ public final class JobQueue: Hashable {
let maxFailureCount: Int = (executorMap.wrappedValue[job.variant]?.maxFailureCount ?? 0)
let nextRunTimestamp: TimeInterval = (dependencies.dateNow.timeIntervalSince1970 + JobRunner.getRetryInterval(for: job))
var dependantJobIds: [Int64] = []
var failureText: String = "failed"
var failureText: String = "failed due to error: \(error ?? JobRunnerError.unknown)"
dependencies.storage.write(using: dependencies) { db in
/// Retrieve a list of dependant jobs so we can clear them from the queue
@ -1683,8 +1684,8 @@ public final class JobQueue: Hashable {
)
else {
failureText = (maxFailureCount >= 0 && updatedFailureCount > maxFailureCount ?
"failed permanently; too many retries" :
"failed permanently"
"failed permanently due to error: \(error ?? JobRunnerError.unknown); too many retries" :
"failed permanently due to error: \(error ?? JobRunnerError.unknown)"
)
// If the job permanently failed or we have performed all of our retry attempts
@ -1696,7 +1697,7 @@ public final class JobQueue: Hashable {
return
}
failureText = "failed; scheduling retry (failure count is \(updatedFailureCount))"
failureText = "failed due to error: \(error ?? JobRunnerError.unknown); scheduling retry (failure count is \(updatedFailureCount))"
try job
.with(

@ -3,8 +3,6 @@
import Foundation
public enum JobRunnerError: Error {
case generic
case executorMissing
case jobIdMissing
case requiredThreadIdMissing
@ -14,4 +12,6 @@ public enum JobRunnerError: Error {
case missingDependencies
case possibleDeferralLoop
case unknown
}

@ -15,6 +15,7 @@ public enum NetworkError: Error, Equatable, CustomStringConvertible {
case internalServerError
case badGateway
case serviceUnavailable
case gatewayTimeout
case badRequest(error: String, rawData: Data?)
case requestFailed(error: String, rawData: Data?)
case timeout
@ -33,6 +34,7 @@ public enum NetworkError: Error, Equatable, CustomStringConvertible {
case .internalServerError: return "Internal server error (NetworkError.internalServerError)."
case .badGateway: return "Bad gateway (NetworkError.badGateway)."
case .serviceUnavailable: return "Service unavailable (NetworkError.serviceUnavailable)."
case .gatewayTimeout: return "Gateway timeout (NetworkError.gatewayTimeout)."
case .badRequest(let error, _), .requestFailed(let error, _): return error
case .timeout: return "The request timed out (NetworkError.timeout)."
case .suspended: return "Network requests are suspended (NetworkError.suspended)."

Loading…
Cancel
Save