mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			131 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			131 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import Foundation
 | |
| import Combine
 | |
| import GRDB
 | |
| import SessionSnodeKit
 | |
| import SessionMessagingKit
 | |
| import SessionUtilitiesKit
 | |
| 
 | |
| public final class BackgroundPoller {
 | |
|     private static var publishers: [AnyPublisher<Void, Error>] = []
 | |
|     public static var isValid: Bool = false
 | |
| 
 | |
|     public static func poll(
 | |
|         completionHandler: @escaping (UIBackgroundFetchResult) -> Void,
 | |
|         using dependencies: Dependencies = Dependencies()
 | |
|     ) {
 | |
|         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
 | |
|                                 )
 | |
|                             }
 | |
|                     )
 | |
|             )
 | |
|             .subscribe(on: DispatchQueue.global(qos: .background), using: dependencies)
 | |
|             .receive(on: DispatchQueue.main, using: dependencies)
 | |
|             .collect()
 | |
|             .sinkUntilComplete(
 | |
|                 receiveCompletion: { result in
 | |
|                     // If we have already invalidated the timer then do nothing (we essentially timed out)
 | |
|                     guard BackgroundPoller.isValid else { return }
 | |
|                     
 | |
|                     switch result {
 | |
|                         case .finished: completionHandler(.newData)
 | |
|                         case .failure(let error):
 | |
|                             SNLog("Background poll failed due to error: \(error)")
 | |
|                             completionHandler(.failed)
 | |
|                     }
 | |
|                 }
 | |
|             )
 | |
|     }
 | |
|     
 | |
|     private static func pollForMessages(
 | |
|         using dependencies: Dependencies
 | |
|     ) -> AnyPublisher<Void, Error> {
 | |
|         let userPublicKey: String = getUserHexEncodedPublicKey()
 | |
| 
 | |
|         return SnodeAPI.getSwarm(for: userPublicKey)
 | |
|             .tryFlatMapWithRandomSnode { snode -> AnyPublisher<[Message], Error> in
 | |
|                 CurrentUserPoller.poll(
 | |
|                     namespaces: CurrentUserPoller.namespaces,
 | |
|                     from: snode,
 | |
|                     for: userPublicKey,
 | |
|                     calledFromBackgroundPoller: true,
 | |
|                     isBackgroundPollValid: { BackgroundPoller.isValid },
 | |
|                     using: dependencies
 | |
|                 )
 | |
|             }
 | |
|             .map { _ in () }
 | |
|             .eraseToAnyPublisher()
 | |
|     }
 | |
|     
 | |
|     private static func pollForClosedGroupMessages(
 | |
|         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
 | |
|                 SnodeAPI.getSwarm(for: groupPublicKey)
 | |
|                     .tryFlatMap { swarm -> AnyPublisher<[Message], Error> in
 | |
|                         guard let snode: Snode = swarm.randomElement() else {
 | |
|                             throw OnionRequestAPIError.insufficientSnodes
 | |
|                         }
 | |
|                         
 | |
|                         return ClosedGroupPoller.poll(
 | |
|                             namespaces: ClosedGroupPoller.namespaces,
 | |
|                             from: snode,
 | |
|                             for: groupPublicKey,
 | |
|                             calledFromBackgroundPoller: true,
 | |
|                             isBackgroundPollValid: { BackgroundPoller.isValid },
 | |
|                             using: dependencies
 | |
|                         )
 | |
|                     }
 | |
|                     .map { _ in () }
 | |
|                     .eraseToAnyPublisher()
 | |
|             }
 | |
|     }
 | |
| }
 |