mirror of
https://github.com/lux-tel/session-ios.git
synced 2026-10-30 20:38:32 +00:00
# Conflicts: # Session.xcodeproj/project.pbxproj # Session/Conversations/ConversationVC+Interaction.swift # Session/Conversations/ConversationVC.swift # Session/Conversations/Settings/ThreadSettingsViewModel.swift # Session/Home/App Review/AppReviewPromptModel.swift # Session/Home/HomeViewModel.swift # Session/Media Viewing & Editing/MessageInfoScreen.swift # Session/Meta/AppDelegate.swift # Session/Settings/DeveloperSettings/DeveloperSettingsProViewModel.swift # Session/Settings/SettingsViewModel.swift # Session/Shared/BaseVC.swift # Session/Utilities/UIContextualAction+Utilities.swift # SessionMessagingKit/Database/Models/SessionThread.swift # SessionMessagingKit/SessionPro/SessionProPaymentScreenContent.swift # SessionMessagingKit/SessionPro/SessionProSettingsViewModel.swift # SessionMessagingKit/SessionPro/SessionProState.swift # SessionMessagingKit/Shared Models/SessionThreadViewModel.swift # SessionNotificationServiceExtension/NotificationServiceExtension.swift # SessionUIKit/Components/Input View/InputView.swift # SessionUIKit/Components/SwiftUI/ProCTAModal.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+CancelPlan.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+Models.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+Purchase.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+Renew.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+RequestRefund.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen+UpdatePlan.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPaymentScreen.swift # SessionUIKit/Screens/Settings/SessionProSettings/SessionProPlanUpdatedScreen.swift # SessionUIKit/Style Guide/Constants+Apple.swift # SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentApprovalViewController.swift # SignalUtilitiesKit/Media Viewing & Editing/Attachment Approval/AttachmentTextToolbar.swift
199 lines
7.4 KiB
Swift
199 lines
7.4 KiB
Swift
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
//
|
|
// stringlint:disable
|
|
|
|
import Foundation
|
|
import Combine
|
|
|
|
public protocol CombineCompatible {}
|
|
|
|
public enum PublisherError: Error, CustomStringConvertible {
|
|
case targetPublisherIsNull
|
|
case invalidCollectionType
|
|
|
|
// stringlint:ignore_contents
|
|
public var description: String {
|
|
switch self {
|
|
case .targetPublisherIsNull: return "The target publisher is null, likely due to a 'weak self' (PublisherError.targetPublisherIsNull)."
|
|
case .invalidCollectionType: return "Failed to convert array literal to desired Publisher type (PublisherError.invalidCollectionType)."
|
|
}
|
|
}
|
|
}
|
|
|
|
public extension Publisher {
|
|
func sink(into subject: PassthroughSubject<Output, Failure>, includeCompletions: Bool = false) -> AnyCancellable {
|
|
return sink(
|
|
receiveCompletion: { completion in
|
|
guard includeCompletions else { return }
|
|
|
|
subject.send(completion: completion)
|
|
},
|
|
receiveValue: { value in subject.send(value) }
|
|
)
|
|
}
|
|
|
|
func flatMapOptional<T, P>(
|
|
maxPublishers: Subscribers.Demand = .unlimited,
|
|
_ transform: @escaping (Self.Output) -> P?
|
|
) -> AnyPublisher<T, Error> where T == P.Output, P : Publisher, P.Failure == Error {
|
|
return self
|
|
.mapError { $0 }
|
|
.flatMap(maxPublishers: maxPublishers) { output -> AnyPublisher<P.Output, Error> in
|
|
do {
|
|
guard let result: AnyPublisher<T, Error> = transform(output)?.eraseToAnyPublisher() else {
|
|
throw PublisherError.targetPublisherIsNull
|
|
}
|
|
|
|
return result
|
|
}
|
|
catch {
|
|
return Fail<P.Output, Error>(error: error)
|
|
.eraseToAnyPublisher()
|
|
}
|
|
}
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func tryFlatMap<T, P>(
|
|
maxPublishers: Subscribers.Demand = .unlimited,
|
|
_ transform: @escaping (Self.Output) throws -> P
|
|
) -> AnyPublisher<T, Error> where T == P.Output, P : Publisher, P.Failure == Error {
|
|
return self
|
|
.mapError { $0 }
|
|
.flatMap(maxPublishers: maxPublishers) { output -> AnyPublisher<P.Output, Error> in
|
|
do {
|
|
return try transform(output)
|
|
.eraseToAnyPublisher()
|
|
}
|
|
catch {
|
|
return Fail<P.Output, Error>(error: error)
|
|
.eraseToAnyPublisher()
|
|
}
|
|
}
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func tryFlatMapOptional<T, P>(
|
|
maxPublishers: Subscribers.Demand = .unlimited,
|
|
_ transform: @escaping (Self.Output) throws -> P?
|
|
) -> AnyPublisher<T, Error> where T == P.Output, P : Publisher, P.Failure == Error {
|
|
return self
|
|
.mapError { $0 }
|
|
.flatMap(maxPublishers: maxPublishers) { output -> AnyPublisher<P.Output, Error> in
|
|
do {
|
|
guard let result: AnyPublisher<T, Error> = try transform(output)?.eraseToAnyPublisher() else {
|
|
throw PublisherError.targetPublisherIsNull
|
|
}
|
|
|
|
return result
|
|
}
|
|
catch {
|
|
return Fail<P.Output, Error>(error: error)
|
|
.eraseToAnyPublisher()
|
|
}
|
|
}
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func catchOptional<P>(
|
|
_ handler: @escaping (Self.Failure) -> P?
|
|
) -> AnyPublisher<P.Output, Error> where P : Publisher, Self.Output == P.Output, P.Failure == Error {
|
|
return self
|
|
.catch { error in
|
|
guard let result: AnyPublisher<P.Output, Error> = handler(error)?.eraseToAnyPublisher() else {
|
|
return Fail<P.Output, Error>(error: PublisherError.targetPublisherIsNull)
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
return result
|
|
}
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func subscribe<S>(
|
|
on scheduler: S,
|
|
options: S.SchedulerOptions? = nil,
|
|
using dependencies: Dependencies
|
|
) -> AnyPublisher<Output, Failure> where S: Scheduler {
|
|
guard !dependencies.forceSynchronous else { return self.eraseToAnyPublisher() }
|
|
|
|
return self.subscribe(on: scheduler, options: options)
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func receive<S>(
|
|
on scheduler: S,
|
|
options: S.SchedulerOptions? = nil,
|
|
using dependencies: Dependencies
|
|
) -> AnyPublisher<Output, Failure> where S: Scheduler {
|
|
guard !dependencies.forceSynchronous else { return self.eraseToAnyPublisher() }
|
|
|
|
return self.receive(on: scheduler, options: options)
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func manualRefreshFrom(_ refreshTrigger: some Publisher<Void, Never>) -> AnyPublisher<Output, Failure> {
|
|
return Publishers
|
|
.CombineLatest(refreshTrigger.prepend(()).setFailureType(to: Failure.self), self)
|
|
.map { _, value in value }
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func withPrevious() -> AnyPublisher<(previous: Output?, current: Output), Failure> {
|
|
scan(Optional<(Output?, Output)>.none) { ($0?.1, $1) }
|
|
.compactMap { $0 }
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func withPrevious(_ initialPreviousValue: Output) -> AnyPublisher<(previous: Output, current: Output), Failure> {
|
|
scan((initialPreviousValue, initialPreviousValue)) { ($0.1, $1) }.eraseToAnyPublisher()
|
|
}
|
|
}
|
|
|
|
// MARK: - Convenience
|
|
|
|
public extension Publisher {
|
|
func sink(into subject: PassthroughSubject<Output, Failure>?, includeCompletions: Bool = false) -> AnyCancellable {
|
|
guard let targetSubject: PassthroughSubject<Output, Failure> = subject else { return AnyCancellable {} }
|
|
|
|
return sink(into: targetSubject, includeCompletions: includeCompletions)
|
|
}
|
|
|
|
/// Automatically retains the subscription until it emits a 'completion' event
|
|
func sinkUntilComplete(
|
|
receiveCompletion: ((Subscribers.Completion<Failure>) -> Void)? = nil,
|
|
receiveValue: ((Output) -> Void)? = nil
|
|
) {
|
|
var retainCycle: Cancellable? = nil
|
|
retainCycle = self
|
|
.sink(
|
|
receiveCompletion: { result in
|
|
receiveCompletion?(result)
|
|
|
|
// Redundant but without reading 'retainCycle' it will warn that the variable
|
|
// isn't used
|
|
if retainCycle != nil { retainCycle = nil }
|
|
},
|
|
receiveValue: (receiveValue ?? { _ in })
|
|
)
|
|
}
|
|
}
|
|
|
|
extension AnyPublisher: @retroactive ExpressibleByArrayLiteral where Output: RangeReplaceableCollection {
|
|
public init(arrayLiteral elements: Output.Element...) {
|
|
self = Just(Output(elements)).setFailureType(to: Failure.self).eraseToAnyPublisher()
|
|
}
|
|
}
|
|
|
|
public extension AnyPublisher where Failure == Error {
|
|
static func lazy(_ closure: @escaping () throws -> Output) -> Self {
|
|
return Deferred {
|
|
Future { promise in
|
|
do { promise(.success(try closure())) }
|
|
catch { promise(.failure(error)) }
|
|
}
|
|
}
|
|
.eraseToAnyPublisher()
|
|
}
|
|
}
|