From c94da92f9ebec23c305b3e2cbb79d1e22404c58c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Coye=20de=20Brune=CC=81lis?= Date: Thu, 21 Mar 2024 11:22:16 +0100 Subject: [PATCH] feat: New ItemProviderPropertyValueRepresentation to Use an ItemProvider wrapping a property list test: Testing ItemProviderPropertyValueRepresentation --- ...mProviderPropertyValueRepresentation.swift | 86 ++++++++++++++++++ ...mProviderPropertyValueRepresentation.swift | 91 +++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 Sources/InfomaniakCore/ItemProviderRepresentation/ItemProviderPropertyValueRepresentation.swift create mode 100644 Tests/InfomaniakCoreTests/UTItemProviderRepresentation/UTItemProviderPropertyValueRepresentation.swift diff --git a/Sources/InfomaniakCore/ItemProviderRepresentation/ItemProviderPropertyValueRepresentation.swift b/Sources/InfomaniakCore/ItemProviderRepresentation/ItemProviderPropertyValueRepresentation.swift new file mode 100644 index 0000000..5eb664e --- /dev/null +++ b/Sources/InfomaniakCore/ItemProviderRepresentation/ItemProviderPropertyValueRepresentation.swift @@ -0,0 +1,86 @@ +/* + Infomaniak Mail - iOS App + Copyright (C) 2024 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 . + */ + +import Foundation + +/// Something that can provide a `Progress` and an async `Result` +/// from a `NSItemProvider` that is conforming to propertyList UTI +/// +/// Returns arbitrary Dictionary +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +public final class ItemProviderPropertyValueRepresentation: NSObject, ProgressResultable { + /// Progress increment size + private static let progressStep: Int64 = 1 + + /// Number of steps to complete the task + private static let totalSteps: Int64 = 1 + + /// Something to transform events to a nice `async Result` + private let flowToAsync = FlowToAsyncResult() + + private let itemProvider: NSItemProvider + + /// Domain specific errors + public enum ErrorDomain: Error, Equatable { + /// loadItem cast failed + case unableToReadDictionary + } + + public typealias Success = NSDictionary + public typealias Failure = Error + + public init(from itemProvider: NSItemProvider) { + progress = Progress(totalUnitCount: Self.totalSteps) + + self.itemProvider = itemProvider + super.init() + + Task { + let completionProgress = Progress(totalUnitCount: Self.totalSteps) + progress.addChild(completionProgress, withPendingUnitCount: Self.progressStep) + + defer { + completionProgress.completedUnitCount += Self.progressStep + } + + let propertyListIdentifier = UTI.propertyList.identifier + if self.itemProvider.hasItemConformingToTypeIdentifier(propertyListIdentifier) { + guard let resultDictionary = try await self.itemProvider + .loadItem(forTypeIdentifier: propertyListIdentifier) as? NSDictionary else { + flowToAsync.sendFailure(ErrorDomain.unableToReadDictionary) + return + } + + flowToAsync.sendSuccess(resultDictionary) + + } else { + flowToAsync.sendFailure(ErrorDomain.unableToReadDictionary) + } + } + } + + // MARK: ProgressResultable + + public var progress: Progress + + public var result: Result { + get async { + return await flowToAsync.result + } + } +} diff --git a/Tests/InfomaniakCoreTests/UTItemProviderRepresentation/UTItemProviderPropertyValueRepresentation.swift b/Tests/InfomaniakCoreTests/UTItemProviderRepresentation/UTItemProviderPropertyValueRepresentation.swift new file mode 100644 index 0000000..0c36bc0 --- /dev/null +++ b/Tests/InfomaniakCoreTests/UTItemProviderRepresentation/UTItemProviderPropertyValueRepresentation.swift @@ -0,0 +1,91 @@ +/* + Infomaniak Core - iOS + Copyright (C) 2024 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 . + */ + +import Foundation +import InfomaniakCore +@testable import InfomaniakDI +import XCTest + +@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +final class UTItemProviderPropertyValueRepresentation: XCTestCase { + private let fileManager = FileManager.default + + override func setUp() { + let factory = Factory( + type: AppGroupPathProvidable.self + ) { _, _ in + return AppGroupPathProvider(realmRootPath: "realm", appGroupIdentifier: "com.ik.test")! + } + SimpleResolver.sharedResolver.store(factory: factory) + } + + override func tearDown() { + SimpleResolver.sharedResolver.removeAll() + } + + // MARK: - + + func testProvideDictionary() async { + // GIVEN + let someDictionary: NSDictionary = ["key": "value"] + let item = NSItemProvider(item: someDictionary, typeIdentifier: UTI.propertyList.identifier) + + let provider = ItemProviderPropertyValueRepresentation(from: item) + XCTAssertFalse(provider.progress.isFinished) + + do { + // WHEN + let result = try await provider.result.get() + + // THEN + XCTAssertEqual(result, someDictionary) + XCTAssertTrue(provider.progress.isFinished) + } catch { + XCTFail("Unexpected error:\(error)") + } + } + + func testUnableToProvideDictionary() async { + // GIVEN + let garbageInput = "la li lu le lo" as NSSecureCoding + let item = NSItemProvider(item: garbageInput, typeIdentifier: UTI.propertyList.identifier) + + let provider = ItemProviderPropertyValueRepresentation(from: item) + XCTAssertFalse(provider.progress.isFinished) + + do { + // WHEN + let result = try await provider.result.get() + + // THEN + XCTFail("Should throw. got \(result)") + } catch { + guard let error = error as? ItemProviderPropertyValueRepresentation.ErrorDomain else { + XCTFail("Unexpected error :\(error)") + return + } + + guard error == .unableToReadDictionary else { + XCTFail("Unexpected domain error :\(error)") + return + } + + XCTAssertTrue(provider.progress.isFinished) + } + } +}