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