Skip to content

Commit

Permalink
tests(UTItemProviderFileRepresentation): Testing image conversion for…
Browse files Browse the repository at this point in the history
… kDrive
  • Loading branch information
adrien-coye committed Jul 21, 2023
1 parent eb5d1e4 commit a86c6ae
Show file tree
Hide file tree
Showing 10 changed files with 527 additions and 117 deletions.
4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ let package = Package(
),
.testTarget(
name: "InfomaniakCoreTests",
dependencies: ["InfomaniakCore","ZIPFoundation"]
dependencies: ["InfomaniakCore","ZIPFoundation"],
resources: [Resource.process("Ressources/Matterhorn_as_seen_from_Zermatt,_Wallis,_Switzerland,_2012_August,Wikimedia_Commons.heic"),
Resource.process("Ressources/Matterhorn_as_seen_from_Zermatt,_Wallis,_Switzerland,_2012_August,Wikimedia_Commons.jpg")]
)
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,25 @@ import InfomaniakDI
public final class ItemProviderFileRepresentation: NSObject, ProgressResultable {
/// Something to transform events to a nice `async Result`
private let flowToAsync = FlowToAsyncResult<Success>()

/// Shorthand for default FileManager
private let fileManager = FileManager.default

/// Domain specific errors
public enum ErrorDomain: Error, Equatable{
public enum ErrorDomain: Error, Equatable {
case UTINotFound
case UnableToLoadFile
}

public typealias Success = URL
public typealias Failure = Error

public init(from itemProvider: NSItemProvider) throws {
/// Init method
/// - Parameters:
/// - itemProvider: The item provider we will be working with
/// - preferredImageFileFormat: Specify an output image file format. Supports HEIC and JPG. Will convert only if
/// itemProvider supports it.
public init(from itemProvider: NSItemProvider, preferredImageFileFormat: UTI? = nil) throws {
guard let typeIdentifier = itemProvider.registeredTypeIdentifiers.first else {
throw ErrorDomain.UTINotFound
}
Expand All @@ -49,24 +54,31 @@ public final class ItemProviderFileRepresentation: NSObject, ProgressResultable

super.init()

// Check if requested an image conversion, and if conversion is available.
let fileIdentifierToUse = self.preferredImageFileFormat(
itemProvider: itemProvider,
typeIdentifier: typeIdentifier,
preferredImageFileFormat: preferredImageFileFormat
)

// Set progress and hook completion closure to a combine pipe
progress = itemProvider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { [self] fileProviderURL, error in
progress = itemProvider.loadFileRepresentation(forTypeIdentifier: fileIdentifierToUse) { [self] fileProviderURL, error in
guard let fileProviderURL, error == nil else {
flowToAsync.sendFailure(error ?? ErrorDomain.UnableToLoadFile)
return
}

do {
let UTI = UTI(rawValue: typeIdentifier as CFString)
let uti = UTI(rawValue: fileIdentifierToUse as CFString)
@InjectService var pathProvider: AppGroupPathProvidable
let temporaryURL = pathProvider.tmpDirectoryURL
.appendingPathComponent(UUID().uuidString, isDirectory: true)
try fileManager.createDirectory(at: temporaryURL, withIntermediateDirectories: true)

let fileName = fileProviderURL.appendingPathExtension(for: UTI).lastPathComponent
let fileName = fileProviderURL.appendingPathExtension(for: uti).lastPathComponent
let temporaryFileURL = temporaryURL.appendingPathComponent(fileName)
try fileManager.copyItem(atPath: fileProviderURL.path, toPath: temporaryFileURL.path)

flowToAsync.sendSuccess(temporaryFileURL)
} catch {
flowToAsync.sendFailure(error)
Expand All @@ -80,7 +92,35 @@ public final class ItemProviderFileRepresentation: NSObject, ProgressResultable

public var result: Result<URL, Error> {
get async {
await self.flowToAsync.result
await flowToAsync.result
}
}

// MARK: Private

/// Check if a File conversion is possible for the provided `itemProvider` and `typeIdentifier`,
/// returns `typeIdentifier` if no conversion is possible.
///
/// - Parameters:
/// - itemProvider: The ItemProvider we work with
/// - typeIdentifier: top typeIdentifier for ItemProvider
/// - preferredImageFileFormat: The image format the user is requesting
private func preferredImageFileFormat(itemProvider: NSItemProvider,
typeIdentifier: String,
preferredImageFileFormat: UTI?) -> String {
if let preferredImageFileFormat = preferredImageFileFormat {
// Check that itemProvider supports the image types we ask of it
if itemProvider.hasItemConformingToAnyOfTypeIdentifiers([UTI.heic.identifier, UTI.jpeg.identifier]),
itemProvider.hasItemConformingToTypeIdentifier(preferredImageFileFormat.identifier) {
return preferredImageFileFormat.identifier
}
// No conversion if not possible
else {
return typeIdentifier
}
} else {
// No conversion
return typeIdentifier
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import Foundation
import InfomaniakDI

/// Extending NSItemProvider for detecting file type, business logic.
extension NSItemProvider {
public enum ItemUnderlyingType: Equatable {
public extension NSItemProvider {
/// Subset of types supported by the Apps
enum ItemUnderlyingType: Equatable {
/// The item is an URL
case isURL
/// The item is Text
Expand All @@ -43,7 +44,12 @@ extension NSItemProvider {
}

/// Wrapping business logic of supported types by the apps.
public var underlyingType: ItemUnderlyingType {
var underlyingType: ItemUnderlyingType {
// We expect to have a type identifier to work with
guard let typeIdentifier = registeredTypeIdentifiers.first else {
return .none
}

if hasItemConformingToTypeIdentifier(UTI.url.identifier) && registeredTypeIdentifiers.count == 1 {
return .isURL
} else if hasItemConformingToTypeIdentifier(UTI.plainText.identifier)
Expand All @@ -57,21 +63,29 @@ extension NSItemProvider {
} else if hasItemConformingToTypeIdentifier(UTI.zip.identifier)
|| hasItemConformingToTypeIdentifier(UTI.bz2.identifier)
|| hasItemConformingToTypeIdentifier(UTI.gzip.identifier)
|| hasItemConformingToTypeIdentifier(UTI.archive.identifier),
let typeIdentifier = registeredTypeIdentifiers.first {
|| hasItemConformingToTypeIdentifier(UTI.archive.identifier) {
return .isCompressedData(identifier: typeIdentifier)
} else if registeredTypeIdentifiers.count == 1 &&
registeredTypeIdentifiers.first == UTI.image.identifier {
return .isUIImage
} else if hasItemConformingToTypeIdentifier(UTI.heic.identifier) ||
hasItemConformingToTypeIdentifier(UTI.jpeg.identifier) {
return .isImageData
} else if let typeIdentifier = registeredTypeIdentifiers.first {
return .isMiscellaneous(identifier: typeIdentifier)
} else {
return .none
return .isMiscellaneous(identifier: typeIdentifier)
}
}

/// Check if item is conforming to *at least* one identifier provided
/// - Parameter collection: A collection of identifiers
/// - Returns: `true` if matches for at least one identifier
func hasItemConformingToAnyOfTypeIdentifiers(_ collection: [String]) -> Bool {
let hasItem = collection.contains(where: { identifier in
self.hasItemConformingToTypeIdentifier(identifier)
})

return hasItem
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public extension URL {
/// Try to append the correct file type extension for a given UTI
func appendingPathExtension(for contentType: UTI) -> URL {
guard let newExtension = contentType.preferredFilenameExtension,
pathExtension != newExtension else {
pathExtension.caseInsensitiveCompare(newExtension) != .orderedSame else {
return self
}

Expand Down
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit a86c6ae

Please sign in to comment.