Skip to content

Commit

Permalink
Merge pull request #47 from Infomaniak/shareExtensionAbstractCode
Browse files Browse the repository at this point in the history
Abstract file sharing between ikMail and kDrive
  • Loading branch information
adrien-coye committed Jul 19, 2023
2 parents 3e858f4 + 55005de commit cd35528
Show file tree
Hide file tree
Showing 20 changed files with 1,543 additions and 69 deletions.
25 changes: 17 additions & 8 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire",
"state" : {
"revision" : "78424be314842833c04bc3bef5b72e85fff99204",
"version" : "5.6.4"
"revision" : "bc268c28fb170f494de9e9927c371b8342979ece",
"version" : "5.7.1"
}
},
{
Expand Down Expand Up @@ -41,26 +41,26 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/realm/realm-core.git",
"state" : {
"revision" : "dd91f5f967c4ae89c37e24ab2a0315c31106648f",
"version" : "13.6.0"
"revision" : "f1434caadda443b4ed2261b91ea4f43ab1ee2aa5",
"version" : "13.15.1"
}
},
{
"identity" : "realm-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/realm/realm-swift",
"state" : {
"revision" : "8ac6fe1aa5d0fb0100062d80863416a4d70de8ca",
"version" : "10.37.0"
"revision" : "b287dc102036ff425bd8a88483f0a5596871f05e",
"version" : "10.41.0"
}
},
{
"identity" : "sentry-cocoa",
"kind" : "remoteSourceControl",
"location" : "https://github.com/getsentry/sentry-cocoa",
"state" : {
"revision" : "2e7899aff930ed3b8d81be1909492f7684bbd481",
"version" : "8.3.1"
"revision" : "e46936ed191c0112cd3276e1c10c0bb7f865268e",
"version" : "8.9.1"
}
},
{
Expand All @@ -71,6 +71,15 @@
"revision" : "32e8d724467f8fe623624570367e3d50c5638e46",
"version" : "1.5.2"
}
},
{
"identity" : "zipfoundation",
"kind" : "remoteSourceControl",
"location" : "https://github.com/weichsel/ZIPFoundation.git",
"state" : {
"revision" : "43ec568034b3731101dbf7670765d671c30f54f3",
"version" : "0.9.16"
}
}
],
"version" : 2
Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ let package = Package(
.package(url: "https://github.com/realm/realm-swift", .upToNextMajor(from: "10.0.0")),
.package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack", .upToNextMajor(from: "3.7.0")),
.package(url: "https://github.com/matomo-org/matomo-sdk-ios", .upToNextMajor(from: "7.5.2")),
.package(url: "https://github.com/weichsel/ZIPFoundation.git", .upToNextMajor(from: "0.9.0"))
],
targets: [
.target(
Expand All @@ -37,7 +38,7 @@ let package = Package(
),
.testTarget(
name: "InfomaniakCoreTests",
dependencies: ["InfomaniakCore"]
dependencies: ["InfomaniakCore","ZIPFoundation"]
)
]
)
61 changes: 61 additions & 0 deletions Sources/InfomaniakCore/Asynchronous/AnyPublisher+Async.swift
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 Sources/InfomaniakCore/Asynchronous/FlowToAsyncResult.swift
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))
}
}
99 changes: 99 additions & 0 deletions Sources/InfomaniakCore/Asynchronous/SendableArray.swift
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
}
}
}
}
Loading

0 comments on commit cd35528

Please sign in to comment.