Skip to content

feat: additional picker options #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion example/tests/benchmark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,10 @@ const styles = StyleSheet.create({

// tbd: playground
setTimeout(async () => {
const files = await showOpenFilePicker()
const files = await showOpenFilePicker({
startIn: 'desktop',
types: [{ description: 'PDF files', accept: { 'text/plain': ['.txt'] } }],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] PDF files have a different MIME type: application/pdf 😅

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha, nit of the year!

})

if (!files[0]) {
console.log('No file')
Expand Down
43 changes: 40 additions & 3 deletions packages/react-native-fast-io/ios/HybridFileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import UniformTypeIdentifiers
import NitroModules
import FastIOPrivate

Expand All @@ -17,7 +18,7 @@ class HybridFileSystem : NSObject, UIDocumentPickerDelegate, HybridFileSystemSpe
return HybridInputStream(stream: stream)
}

func getFileMetadata(path: String) throws -> Metadata {
func getMetadata(path: String) throws -> Metadata {
let attributes = try FileManager.default.attributesOfItem(atPath: path)
let fileURL = URL(fileURLWithPath: path)

Expand All @@ -30,20 +31,56 @@ class HybridFileSystem : NSObject, UIDocumentPickerDelegate, HybridFileSystemSpe
)
}

func getWellKnownDirectoryPath(directory: WellKnownDirectory) throws -> String {
let url = switch directory {
case .desktop:
FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first
case .documents:
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
case .downloads:
FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first
case .music:
FileManager.default.urls(for: .musicDirectory, in: .userDomainMask).first
case .pictures:
FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask).first
case .videos:
FileManager.default.urls(for: .moviesDirectory, in: .userDomainMask).first
}

guard let url else {
throw RuntimeError.error(withMessage: "Directory '\(directory)' is not available")
}

return url.path
}

private var filePicker: (promise: Promise<[String]>, vc: UIDocumentPickerViewController)?
func showOpenFilePicker() throws -> Promise<[String]> {
func showOpenFilePicker(options: NativeFilePickerOptions?) throws -> Promise<[String]> {
if filePicker != nil {
return Promise.rejected(withError: RuntimeError.error(withMessage: "File picker already open"))
}

let promise = Promise<[String]>()

DispatchQueue.main.async {
let utTypes: [UTType] = options?.extensions?
.compactMap { ext in
let cleanExt = ext.hasPrefix(".") ? String(ext.dropFirst()) : ext
return UTType(filenameExtension: cleanExt)
} ?? [.item]


let documentPicker = UIDocumentPickerViewController(
forOpeningContentTypes: [.item],
forOpeningContentTypes: utTypes,
asCopy: true
)
documentPicker.delegate = self

documentPicker.allowsMultipleSelection = options?.multiple ?? false

if let startIn = options?.startIn {
documentPicker.directoryURL = URL(fileURLWithPath: startIn, isDirectory: true)
}

guard let vc = RCTUtilsWrapper.getPresentedViewController() else {
promise.reject(withError: RuntimeError.error(withMessage: "Cannot present file picker"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ namespace margelo::nitro::fastio { class HybridPassThroughStreamSpec; }
namespace margelo::nitro::fastio { class HybridWebSocketManagerSpec; }
// Forward declaration of `HybridWebSocketSpec` to properly resolve imports.
namespace margelo::nitro::fastio { class HybridWebSocketSpec; }
// Forward declaration of `NativeFilePickerOptions` to properly resolve imports.
namespace margelo::nitro::fastio { struct NativeFilePickerOptions; }

// Forward declarations of Swift defined types
// Forward declaration of `HybridFileSystemSpecCxx` to properly resolve imports.
Expand All @@ -51,12 +53,14 @@ namespace FastIO { class HybridWebSocketSpecCxx; }
#include "HybridPassThroughStreamSpec.hpp"
#include "HybridWebSocketManagerSpec.hpp"
#include "HybridWebSocketSpec.hpp"
#include "NativeFilePickerOptions.hpp"
#include <NitroModules/ArrayBuffer.hpp>
#include <NitroModules/ArrayBufferHolder.hpp>
#include <NitroModules/PromiseHolder.hpp>
#include <functional>
#include <future>
#include <memory>
#include <optional>
#include <string>
#include <vector>

Expand Down Expand Up @@ -94,6 +98,42 @@ namespace margelo::nitro::fastio::bridge::swift {
return PromiseHolder<std::vector<std::string>>();
}

// pragma MARK: std::optional<bool>
/**
* Specialized version of `std::optional<bool>`.
*/
using std__optional_bool_ = std::optional<bool>;
inline std::optional<bool> create_std__optional_bool_(const bool& value) {
return std::optional<bool>(value);
}

// pragma MARK: std::optional<std::string>
/**
* Specialized version of `std::optional<std::string>`.
*/
using std__optional_std__string_ = std::optional<std::string>;
inline std::optional<std::string> create_std__optional_std__string_(const std::string& value) {
return std::optional<std::string>(value);
}

// pragma MARK: std::optional<std::vector<std::string>>
/**
* Specialized version of `std::optional<std::vector<std::string>>`.
*/
using std__optional_std__vector_std__string__ = std::optional<std::vector<std::string>>;
inline std::optional<std::vector<std::string>> create_std__optional_std__vector_std__string__(const std::vector<std::string>& value) {
return std::optional<std::vector<std::string>>(value);
}

// pragma MARK: std::optional<NativeFilePickerOptions>
/**
* Specialized version of `std::optional<NativeFilePickerOptions>`.
*/
using std__optional_NativeFilePickerOptions_ = std::optional<NativeFilePickerOptions>;
inline std::optional<NativeFilePickerOptions> create_std__optional_NativeFilePickerOptions_(const NativeFilePickerOptions& value) {
return std::optional<NativeFilePickerOptions>(value);
}

// pragma MARK: std::shared_ptr<margelo::nitro::fastio::HybridFileSystemSpec>
/**
* Specialized version of `std::shared_ptr<margelo::nitro::fastio::HybridFileSystemSpec>`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ namespace margelo::nitro::fastio { class HybridWebSocketManagerSpec; }
namespace margelo::nitro::fastio { class HybridWebSocketSpec; }
// Forward declaration of `Metadata` to properly resolve imports.
namespace margelo::nitro::fastio { struct Metadata; }
// Forward declaration of `NativeFilePickerOptions` to properly resolve imports.
namespace margelo::nitro::fastio { struct NativeFilePickerOptions; }
// Forward declaration of `RequestMethod` to properly resolve imports.
namespace margelo::nitro::fastio { enum class RequestMethod; }
// Forward declaration of `RequestOptions` to properly resolve imports.
namespace margelo::nitro::fastio { struct RequestOptions; }
// Forward declaration of `WellKnownDirectory` to properly resolve imports.
namespace margelo::nitro::fastio { enum class WellKnownDirectory; }

// Include C++ defined types
#include "HybridFileSystemSpec.hpp"
Expand All @@ -40,12 +44,15 @@ namespace margelo::nitro::fastio { struct RequestOptions; }
#include "HybridWebSocketManagerSpec.hpp"
#include "HybridWebSocketSpec.hpp"
#include "Metadata.hpp"
#include "NativeFilePickerOptions.hpp"
#include "RequestMethod.hpp"
#include "RequestOptions.hpp"
#include "WellKnownDirectory.hpp"
#include <NitroModules/ArrayBuffer.hpp>
#include <functional>
#include <future>
#include <memory>
#include <optional>
#include <string>
#include <vector>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,21 @@ namespace FastIO { class HybridFileSystemSpecCxx; }
namespace margelo::nitro::fastio { class HybridInputStreamSpec; }
// Forward declaration of `Metadata` to properly resolve imports.
namespace margelo::nitro::fastio { struct Metadata; }
// Forward declaration of `WellKnownDirectory` to properly resolve imports.
namespace margelo::nitro::fastio { enum class WellKnownDirectory; }
// Forward declaration of `NativeFilePickerOptions` to properly resolve imports.
namespace margelo::nitro::fastio { struct NativeFilePickerOptions; }

#include <memory>
#include "HybridInputStreamSpec.hpp"
#include <string>
#include "Metadata.hpp"
#include "WellKnownDirectory.hpp"
#include <future>
#include <vector>
#include <NitroModules/PromiseHolder.hpp>
#include <optional>
#include "NativeFilePickerOptions.hpp"

#if __has_include(<NitroModules/HybridContext.hpp>)
#include <NitroModules/HybridContext.hpp>
Expand Down Expand Up @@ -72,12 +79,16 @@ namespace margelo::nitro::fastio {
auto __result = _swiftPart.createInputStream(path);
return __result;
}
inline Metadata getFileMetadata(const std::string& path) override {
auto __result = _swiftPart.getFileMetadata(path);
inline Metadata getMetadata(const std::string& path) override {
auto __result = _swiftPart.getMetadata(path);
return __result;
}
inline std::future<std::vector<std::string>> showOpenFilePicker() override {
auto __result = _swiftPart.showOpenFilePicker();
inline std::string getWellKnownDirectoryPath(WellKnownDirectory directory) override {
auto __result = _swiftPart.getWellKnownDirectoryPath(static_cast<int>(directory));
return __result;
}
inline std::future<std::vector<std::string>> showOpenFilePicker(const std::optional<NativeFilePickerOptions>& options) override {
auto __result = _swiftPart.showOpenFilePicker(options);
return __result.getFuture();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public protocol HybridFileSystemSpec: AnyObject, HybridObjectSpec {

// Methods
func createInputStream(path: String) throws -> (any HybridInputStreamSpec)
func getFileMetadata(path: String) throws -> Metadata
func showOpenFilePicker() throws -> Promise<[String]>
func getMetadata(path: String) throws -> Metadata
func getWellKnownDirectoryPath(directory: WellKnownDirectory) throws -> String
func showOpenFilePicker(options: NativeFilePickerOptions?) throws -> Promise<[String]>
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ public class HybridFileSystemSpecCxx {
}

@inline(__always)
public func getFileMetadata(path: std.string) -> Metadata {
public func getMetadata(path: std.string) -> Metadata {
do {
let __result = try self.__implementation.getFileMetadata(path: String(path))
let __result = try self.__implementation.getMetadata(path: String(path))
return __result
} catch {
let __message = "\(error.localizedDescription)"
Expand All @@ -125,9 +125,26 @@ public class HybridFileSystemSpecCxx {
}

@inline(__always)
public func showOpenFilePicker() -> bridge.PromiseHolder_std__vector_std__string__ {
public func getWellKnownDirectoryPath(directory: Int32) -> std.string {
do {
let __result = try self.__implementation.showOpenFilePicker()
let __result = try self.__implementation.getWellKnownDirectoryPath(directory: margelo.nitro.fastio.WellKnownDirectory(rawValue: directory)!)
return std.string(__result)
} catch {
let __message = "\(error.localizedDescription)"
fatalError("Swift errors can currently not be propagated to C++! See https://github.com/swiftlang/swift/issues/75290 (Error: \(__message))")
}
}

@inline(__always)
public func showOpenFilePicker(options: bridge.std__optional_NativeFilePickerOptions_) -> bridge.PromiseHolder_std__vector_std__string__ {
do {
let __result = try self.__implementation.showOpenFilePicker(options: { () -> NativeFilePickerOptions? in
if let __unwrapped = options.value {
return __unwrapped
} else {
return nil
}
}())
return { () -> bridge.PromiseHolder_std__vector_std__string__ in
let __promiseHolder = bridge.create_PromiseHolder_std__vector_std__string__()
__result
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
///
/// NativeFilePickerOptions.swift
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
/// https://github.com/mrousavy/nitro
/// Copyright © 2024 Marc Rousavy @ Margelo
///

import NitroModules

/**
* Represents an instance of `NativeFilePickerOptions`, backed by a C++ struct.
*/
public typealias NativeFilePickerOptions = margelo.nitro.fastio.NativeFilePickerOptions

public extension NativeFilePickerOptions {
private typealias bridge = margelo.nitro.fastio.bridge.swift

/**
* Create a new instance of `NativeFilePickerOptions`.
*/
init(multiple: Bool?, startIn: String?, extensions: [String]?) {
self.init({ () -> bridge.std__optional_bool_ in
if let __unwrappedValue = multiple {
return bridge.create_std__optional_bool_(__unwrappedValue)
} else {
return .init()
}
}(), { () -> bridge.std__optional_std__string_ in
if let __unwrappedValue = startIn {
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
} else {
return .init()
}
}(), { () -> bridge.std__optional_std__vector_std__string__ in
if let __unwrappedValue = extensions {
return bridge.create_std__optional_std__vector_std__string__({ () -> bridge.std__vector_std__string_ in
var __vector = bridge.create_std__vector_std__string_(__unwrappedValue.count)
for __item in __unwrappedValue {
__vector.push_back(std.string(__item))
}
return __vector
}())
} else {
return .init()
}
}())
}

var multiple: Bool? {
@inline(__always)
get {
return self.__multiple.value
}
@inline(__always)
set {
self.__multiple = { () -> bridge.std__optional_bool_ in
if let __unwrappedValue = newValue {
return bridge.create_std__optional_bool_(__unwrappedValue)
} else {
return .init()
}
}()
}
}

var startIn: String? {
@inline(__always)
get {
return { () -> String? in
if let __unwrapped = self.__startIn.value {
return String(__unwrapped)
} else {
return nil
}
}()
}
@inline(__always)
set {
self.__startIn = { () -> bridge.std__optional_std__string_ in
if let __unwrappedValue = newValue {
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
} else {
return .init()
}
}()
}
}

var extensions: [String]? {
@inline(__always)
get {
return { () -> [String]? in
if let __unwrapped = self.__extensions.value {
return __unwrapped.map({ __item in String(__item) })
} else {
return nil
}
}()
}
@inline(__always)
set {
self.__extensions = { () -> bridge.std__optional_std__vector_std__string__ in
if let __unwrappedValue = newValue {
return bridge.create_std__optional_std__vector_std__string__({ () -> bridge.std__vector_std__string_ in
var __vector = bridge.create_std__vector_std__string_(__unwrappedValue.count)
for __item in __unwrappedValue {
__vector.push_back(std.string(__item))
}
return __vector
}())
} else {
return .init()
}
}()
}
}
}
Loading