-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #47 from Infomaniak/shareExtensionAbstractCode
Abstract file sharing between ikMail and kDrive
- Loading branch information
Showing
20 changed files
with
1,543 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
Sources/InfomaniakCore/Asynchronous/AnyPublisher+Async.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
Infomaniak Core - iOS | ||
Copyright (C) 2021 Infomaniak Network SA | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
import Combine | ||
import Foundation | ||
|
||
public enum AnyPublisherError: Error, Equatable { | ||
case emptySteamError | ||
} | ||
|
||
/// Extending AnyPublisher for async await | ||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | ||
public extension AnyPublisher { | ||
/// Bridge `AnyPublisher` to `Result<>` | ||
/// - Returns: a `Result<>` wrapping the state of this `AnyPublisher` | ||
var result: Result<Output, Error> { | ||
get async { | ||
do { | ||
return .success(try await asyncCall()) | ||
} catch { | ||
return .failure(error) | ||
} | ||
} | ||
} | ||
|
||
/// Bridge `AnyPublisher` to async await | ||
/// - Returns: the first() Output of this AnyPublisher | ||
func asyncCall() async throws -> Output { | ||
try await withCheckedThrowingContinuation { continuation in | ||
var cancellable: AnyCancellable? | ||
|
||
cancellable = first() | ||
.sink(receiveCompletion: { completion in | ||
switch completion { | ||
case .finished: | ||
break | ||
case .failure(let error): | ||
continuation.resume(throwing: error) | ||
} | ||
cancellable?.cancel() | ||
}, receiveValue: { value in | ||
continuation.resume(with: .success(value)) | ||
}) | ||
} | ||
} | ||
} |
141 changes: 141 additions & 0 deletions
141
Sources/InfomaniakCore/Asynchronous/FlowToAsyncResult.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/* | ||
Infomaniak kDrive - iOS App | ||
Copyright (C) 2023 Infomaniak Network SA | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// Encapsulate a simple asynchronous event and provide a nice swift `async Result<>`. | ||
/// | ||
/// Useful when dealing with old xOS APIs that do not work well with swift native structured concurrency. | ||
/// | ||
/// The *first* event received will be forwarded. Thread safe. | ||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | ||
public final class FlowToAsyncResult<Success> { | ||
|
||
/// Something to deal with live observation | ||
typealias CompletionClosure = (Result<Success, Error>) -> Void | ||
|
||
// MARK: Private | ||
|
||
/// Serial locking queue | ||
private let lock = DispatchQueue(label: "com.infomaniak.core.FlowToAsyncResult.lock") | ||
|
||
/// The internal state of `FlowToAsyncResult` | ||
private enum State { | ||
/// Waiting for input events | ||
case wait | ||
/// We have a success event | ||
case success(wrapping: Success) | ||
/// We have a failure event | ||
case failure(wrapping: Error) | ||
} | ||
|
||
/// The state storage | ||
private var _state: State = .wait | ||
|
||
/// Something to deal with state _before_ observation started | ||
/// | ||
/// Thread safe | ||
private var state: State { | ||
get { | ||
lock.sync { | ||
return _state | ||
} | ||
} | ||
set { | ||
lock.sync { | ||
_state = newValue | ||
} | ||
} | ||
} | ||
|
||
/// Something to deal with state _after_ observation started | ||
private var liveObservation: CompletionClosure? | ||
|
||
/// Starts an observation | ||
private func observe(_ liveObservation: @escaping CompletionClosure) { | ||
self.liveObservation = liveObservation | ||
} | ||
|
||
/// Builds an `async` function to get a `Success`, given internal state. | ||
private func asyncResult() async throws -> Success { | ||
switch state { | ||
case .failure(wrapping: let error): | ||
/// We have an `error` to send right away | ||
throw error | ||
case .success(wrapping: let success): | ||
/// We have a `Success` type to send right away | ||
return success | ||
case .wait: | ||
// Nothing yet. We start to observe for changes | ||
return try await withCheckedThrowingContinuation { continuation in | ||
observe { result in | ||
switch result { | ||
case .success(let success): | ||
continuation.resume(with: .success(success)) | ||
case .failure(let error): | ||
continuation.resume(throwing: error) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// MARK: Public | ||
|
||
/// Provides a nice `async Result` public API. | ||
public var result: Result<Success, Error> { | ||
get async { | ||
do { | ||
let success = try await asyncResult() | ||
return .success(success) | ||
} catch { | ||
return .failure(error) | ||
} | ||
} | ||
} | ||
|
||
// MARK: Init | ||
|
||
public init() { | ||
// META keep SonarCloud happy | ||
} | ||
} | ||
|
||
/// Event handling | ||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | ||
extension FlowToAsyncResult { | ||
/// Send a successful event. | ||
func sendSuccess(_ input: Success) { | ||
guard case .wait = state else { | ||
return | ||
} | ||
|
||
state = .success(wrapping: input) | ||
liveObservation?(.success(input)) | ||
} | ||
|
||
/// Send an error event. | ||
func sendFailure(_ error: Error) { | ||
guard case .wait = state else { | ||
return | ||
} | ||
|
||
state = .failure(wrapping: error) | ||
liveObservation?(.failure(error)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
Infomaniak Core - iOS | ||
Copyright (C) 2023 Infomaniak Network SA | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation, either version 3 of the License, or | ||
(at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// A thread safe Array wrapper that does not require `await`. Conforms to Sendable. | ||
/// | ||
/// Please prefer using first party structured concurrency. Use this for prototyping or dealing with race conditions. | ||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | ||
public final class SendableArray<T>: @unchecked Sendable { | ||
/// Serial locking queue | ||
let lock = DispatchQueue(label: "com.infomaniak.core.SendableArray.lock") | ||
|
||
/// Internal collection | ||
private(set) var content = [T]() | ||
|
||
public init() { | ||
// META: keep SonarCloud happy | ||
} | ||
|
||
public var count: Int { | ||
lock.sync { | ||
return content.count | ||
} | ||
} | ||
|
||
public var values: [T] { | ||
lock.sync { | ||
return Array(content) | ||
} | ||
} | ||
|
||
public func append(_ newElement: T) { | ||
lock.sync { | ||
content.append(newElement) | ||
} | ||
} | ||
|
||
public func append(contentsOf collection: any Sequence<T>) { | ||
lock.sync { | ||
content.append(contentsOf: collection) | ||
} | ||
} | ||
|
||
public func popLast() -> T? { | ||
lock.sync { | ||
return content.popLast() | ||
} | ||
} | ||
|
||
public func insert(_ item: T, at index: Int) { | ||
lock.sync { | ||
content.insert(item, at: index) | ||
} | ||
} | ||
|
||
public var isEmpty: Bool { | ||
lock.sync { | ||
return content.isEmpty | ||
} | ||
} | ||
|
||
/// Bracket get / set pattern | ||
public subscript(_ index: Int) -> T? { | ||
get { | ||
lock.sync { | ||
return content[index] | ||
} | ||
} | ||
set { | ||
guard let newValue else { | ||
return | ||
} | ||
|
||
lock.sync { | ||
guard content[safe: index] != nil else { | ||
content.insert(newValue, at: index) | ||
return | ||
} | ||
content[index] = newValue | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.