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()
 | 
						|
            }
 | 
						|
    }
 | 
						|
}
 |