Skip to content

Commit 85df139

Browse files
committedApr 21, 2021
Initial commit of version 1.0.
1 parent c6bfcea commit 85df139

File tree

5 files changed

+214
-3
lines changed

5 files changed

+214
-3
lines changed
 

‎LICENCE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Mark Renaud
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎Package.swift

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import PackageDescription
55

66
let package = Package(
77
name: "FilePicker",
8+
platforms: [
9+
.macOS(.v10_15),
10+
.iOS(.v14)
11+
],
812
products: [
913
// Products define the executables and libraries a package produces, and make them visible to other packages.
1014
.library(

‎README.md

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,52 @@
11
# FilePicker
2+
> A cross platform file picker for SwiftUI.
23
3-
A description of this package.
4+
[![License][license-image]][license-url]
5+
6+
The `FilePicker` package implements a SwiftUI view that can be used in **both** iOS and macOS.
7+
The `FilePicker` provides a `Button` that presents a platform-native file picker that is a modern way for use in SwiftUI.
8+
9+
## Installation
10+
11+
Add the `FilePicker` package to your Xcode project using the package manager.
12+
Import `FilePicker` to your file.
13+
14+
If you are using the package with macOS, ensure that you grant appropriate `File Access` permissions for `User Selected File` under `Signing & Capabilities` (on the macOS target)
15+
16+
## Example Usage
17+
18+
```swift
19+
import SwiftUI
20+
import FilePicker
21+
22+
struct DebugView: View {
23+
24+
var body: some View {
25+
HStack {
26+
//
27+
28+
// Use custom content for the button label
29+
FilePicker(types: [.plainText], allowMultiple: true) { urls in
30+
print("selected \(urls.count) files")
31+
} label: {
32+
HStack {
33+
Image(systemName: "doc.on.doc")
34+
Text("Pick Files")
35+
}
36+
}
37+
38+
FilePicker(types: [.plainText], allowMultiple: false, title: "pick single file") { urls in
39+
print("selected \(urls.count) files")
40+
}
41+
42+
}
43+
}
44+
45+
}
46+
```
47+
48+
## Licence
49+
Distributed under the MIT license. See ``LICENSE`` for more information.
50+
51+
[license-image]: https://img.shields.io/badge/License-MIT-blue.svg
52+
[license-url]: LICENSE

‎Sources/FilePicker/FilePicker.swift

+83-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,84 @@
1-
struct FilePicker {
2-
var text = "Hello, World!"
1+
// FilePicker.swift
2+
// FilePicker Package
3+
//
4+
// Created by Mark Renaud on 20/4/21.
5+
//
6+
// Licence: MIT
7+
8+
import SwiftUI
9+
import UniformTypeIdentifiers
10+
11+
public struct FilePicker<LabelView: View>: View {
12+
13+
public typealias PickedURLsCompletionHandler = (_ urls: [URL]) -> Void
14+
public typealias LabelViewContent = () -> LabelView
15+
16+
@State private var isPresented: Bool = false
17+
18+
public let types: [UTType]
19+
public let allowMultiple: Bool
20+
public let pickedCompletionHandler: PickedURLsCompletionHandler
21+
public let labelViewContent: LabelViewContent
22+
23+
public init(types: [UTType], allowMultiple: Bool, onPicked completionHandler: @escaping PickedURLsCompletionHandler, @ViewBuilder label labelViewContent: @escaping LabelViewContent) {
24+
self.types = types
25+
self.allowMultiple = allowMultiple
26+
self.pickedCompletionHandler = completionHandler
27+
self.labelViewContent = labelViewContent
28+
}
29+
30+
public init(types: [UTType], allowMultiple: Bool, title: String, onPicked completionHandler: @escaping PickedURLsCompletionHandler) where LabelView == Text {
31+
self.init(types: types, allowMultiple: allowMultiple, onPicked: completionHandler) { Text(title) }
32+
}
33+
34+
#if os(iOS)
35+
36+
public var body: some View {
37+
Button(
38+
action: {
39+
if !isPresented { isPresented = true }
40+
},
41+
label: {
42+
labelViewContent()
43+
}
44+
)
45+
.disabled(isPresented)
46+
.sheet(isPresented: $isPresented) {
47+
FilePickerUIRepresentable(types: types, allowMultiple: allowMultiple, onPicked: pickedCompletionHandler)
48+
}
49+
}
50+
51+
#endif
52+
53+
#if os(macOS)
54+
public var body: some View {
55+
Button(
56+
action: {
57+
if !isPresented { isPresented = true }
58+
},
59+
label: {
60+
labelViewContent()
61+
}
62+
)
63+
.disabled(isPresented)
64+
.onChange(of: isPresented, perform: { presented in
65+
// binding changed from false to true
66+
if presented == true {
67+
let panel = NSOpenPanel()
68+
panel.allowsMultipleSelection = allowMultiple
69+
panel.canChooseDirectories = false
70+
panel.canChooseFiles = true
71+
panel.allowedFileTypes = types.map { $0.identifier }
72+
panel.begin { reponse in
73+
if reponse == .OK {
74+
pickedCompletionHandler(panel.urls)
75+
}
76+
// reset the isPresented variable to false
77+
isPresented = false
78+
}
79+
}
80+
})
81+
}
82+
#endif
83+
384
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// FilePicker Package
2+
//
3+
// Created by Mark Renaud on 20/4/21.
4+
//
5+
// Licence: MIT
6+
7+
8+
import SwiftUI
9+
import UniformTypeIdentifiers
10+
11+
#if os(iOS)
12+
13+
public struct FilePickerUIRepresentable: UIViewControllerRepresentable {
14+
public typealias UIViewControllerType = UIDocumentPickerViewController
15+
public typealias PickedURLsCompletionHandler = (_ urls: [URL]) -> Void
16+
17+
@Environment(\.presentationMode) var presentationMode
18+
19+
public let types: [UTType]
20+
public let allowMultiple: Bool
21+
public let pickedCompletionHandler: PickedURLsCompletionHandler
22+
23+
public init(types: [UTType], allowMultiple: Bool, onPicked completionHandler: @escaping PickedURLsCompletionHandler) {
24+
self.types = types
25+
self.allowMultiple = allowMultiple
26+
self.pickedCompletionHandler = completionHandler
27+
}
28+
29+
public func makeCoordinator() -> Coordinator {
30+
Coordinator(parent: self)
31+
}
32+
33+
public func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
34+
let picker = UIDocumentPickerViewController(forOpeningContentTypes: types, asCopy: true)
35+
picker.delegate = context.coordinator
36+
picker.allowsMultipleSelection = allowMultiple
37+
return picker
38+
}
39+
40+
public func updateUIViewController(_ controller: UIDocumentPickerViewController, context: Context) {}
41+
42+
public class Coordinator: NSObject, UIDocumentPickerDelegate {
43+
var parent: FilePickerUIRepresentable
44+
45+
init(parent: FilePickerUIRepresentable) {
46+
self.parent = parent
47+
}
48+
49+
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
50+
parent.pickedCompletionHandler(urls)
51+
parent.presentationMode.wrappedValue.dismiss()
52+
}
53+
}
54+
}
55+
56+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.