SwiftUI: Sharing NSSharingService on macOS not receiving share - macos

I have a simple test application that attempts to share a CoreData record with another user using NSSharingService. I can create my share and that works,
but when I try to receive the share it opens up the application but doesn't do anything.
I have added CKSharingSupported to my plist.
I have also followed this link to no avail:
CloudKit CKShare userDidAcceptCloudKitShareWith Never Fires on Mac App
Here is my code:
SharingServiceApp:
final class AppDelegate: NSObject, NSApplicationDelegate
{
func application(_ application: NSApplication, userDidAcceptCloudKitShareWith metadata: CKShare.Metadata)
{
print ("userDidAcceptCloudKitShareWith")
let shareStore = persistenceController.sharedPersistentStore!
persistenceController.container.acceptShareInvitations(from: [metadata], into: shareStore)
{ _, error in
if let error = error
{
print("acceptShareInvitation error :\(error)")
}
}
}
}
ContentView:
import SwiftUI
import CoreData
import CloudKit
let persistenceController = PersistenceController()
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink {
VStack
{
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
Button("Share")
{
shareRecord(item: item)
}
}
} label: {
Text(item.timestamp!, formatter: itemFormatter)
}
}
}
.toolbar {
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
func shareRecord(item: Item)
{
Task
{
if let share = await createShare(item: item)
{
let container = CKContainer(identifier: "iCloud.com.xxxxxxxx.sharingservice")
let item = NSItemProvider()
item.registerCloudKitShare(share, container: container)
DispatchQueue.main.async
{
let sharingService = NSSharingService(named: .cloudSharing)
sharingService!.perform(withItems: [item])
}
}
}
}
private func createShare(item: Item) async -> CKShare?
{
do
{
let (_, share, _) = try await persistenceController.container.share([item], to: nil)
share[CKShare.SystemFieldKey.title] = "MyApp"
return share
}
catch
{
print("Failed to create share")
return nil
}
}

OK I have finally managed to get userDidAcceptCloudKitShareWith to be called.
You need to create the app delegate for your SwiftUI app using #NSApplicationDelegateAdaptor:
#main
struct Sharing_ServiceApp: App
{
#NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene
{
WindowGroup
{
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
I put that line in and my code instantly started receiving the share requests.

Related

NSContactPicker not displaying picker window in SwiftUI on macOS

I have tried to get the NSContactPicker to display a picker window in SwiftUI on macOS. Here is my code. If you click on the button nothing happens. What am I missing?
import SwiftUI
import Contacts
import ContactsUI
let d = MyContactPicker()
class MyContactPicker: NSObject, CNContactPickerDelegate
{
var contactName: String = "No user selected"
func pickContact()
{
let contactPicker = CNContactPicker()
contactPicker.delegate = self
}
func contactPicker(_ picker: CNContactPicker, didSelect contact: CNContact)
{
contactName = contact.givenName
}
}
struct ContentView: View
{
#State var contact: CNContact?
var picker = MyContactPicker()
var body: some View
{
VStack
{
Text(picker.contactName)
Button("Select Contact")
{
picker.pickContact()
}
}
}
}
Here's a possible starting point using NSViewRepresentable and an NSView subclass
class NSContactPickerView: NSView, CNContactPickerDelegate {
let didSelectContact: (CNContact) -> Void
init(didSelectContact: #escaping (CNContact) -> Void) {
self.didSelectContact = didSelectContact
super.init(frame: .zero)
Task {
showPicker()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showPicker() {
let picker = CNContactPicker()
picker.delegate = self
picker.showRelative(to: .zero, of: self, preferredEdge: .maxY)
}
func contactPicker(_ picker: CNContactPicker, didSelect contact: CNContact) {
didSelectContact(contact)
}
}
struct ContactPicker: NSViewRepresentable {
let didSelectContact: (CNContact) -> Void
func makeNSView(context: Context) -> NSContactPickerView {
NSContactPickerView(didSelectContact: didSelectContact)
}
func updateNSView(_ nsView: NSContactPickerView, context: Context) {
}
}
struct ContentView: View {
#State var contact: CNContact?
#State private var showPicker = false
var body: some View {
VStack {
Text(contact?.givenName ?? "")
Button("Select Contact") {
showPicker = true
}
}
.sheet(isPresented: $showPicker) {
ContactPicker { contact in
self.contact = contact
}
.frame(width: 1, height: 1)
}
}
}
It works, but it's not very elegant. Maybe someone else can improve on this.

SwiftUI Delete a List Section

Is there a way to delete an entire list section with a function in SwiftUI? I have a list that you can add sections to as you please, but don't know how to delete them when you want to. I know that you can delete a list item itself using the deleteItems function, but I would like to use a button to delete the entire section. When I try to use the standard "deleteItems" function, it asks for "offsets: IndexSet". I am unsure what this IndexSet would be.
I can't find out what should go in the IndexSet field.
Here is my code
struct SampleView: View {
#Environment(\.managedObjectContext) private var viewContext
#Environment(\.presentationMode) var presentationMode
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Category.categoryString, ascending: false)],
animation: .default)
private var listedCategory: FetchedResults<Category>
#State var categoryString: String = ""
var body: some View {
VStack{
TextField("Enter Text Here", text: $categoryString)
Button {
let newlistedCategory = Category(context: viewContext)
newlistedCategory.categoryString = categoryString
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
categoryString = ""
} label: {
Text("Save")
}
List{
ForEach(listedCategory) { listedCategory in
if listedCategory.typeString == "Item" {
Section(header: Text(listedCategory.categoryString!)) {
Button {
//deleteCategories(offsets: <#T##IndexSet#>)
} label: {
Text("Delete")
}
}
}
}
}
Spacer()
}
}
private func deleteCategories(offsets: IndexSet) {
withAnimation {
offsets.map { listedCategory[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}

SwiftUI CoreData Preview FetchedResults Crashing

I am having issues previewing my SwiftUI CoreData in the canvas. The app works as expected in the simulator and on device, but the preview is crashing any preview view that uses a FetchedResult or ObservedObject (which I may be using wrong?).
My Persistence is set up as such, using a preview controller with supplied preview data:
import CoreData
import CloudKit
struct PersistenceController {
//MARK: - 1. PERSISTENCE CONTROLLER
static let shared = PersistenceController()
//MARK: - 2. PERSISTENT CONTAINER
let container: NSPersistentContainer
//MARK: - 3. INIT (load the persistent store)
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "Training")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
//MARK: - 4. PREVIEW
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let newGoalView = GoalViews(context: viewContext)
newGoalView.startDate = K.init().EndOfWeek(weeksSinceAnchorDate: 1)
newGoalView.endDate = K.init().EndOfWeek(weeksSinceAnchorDate: 2)
newGoalView.id = UUID()
newGoalView.totalDistanceUnit = "meters"
newGoalView.totalDistanceDoubleValue = 30000
newGoalView.workoutActivityType = Int16(32)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo), The database failed to load.")
}
return result
}()
}
I am having issues both previewing it in the parent view here:
//
// GoalsView.swift
// Training
//
// Created by Liam Day on 03/05/2021.
//
import SwiftUI
import HealthKit
struct GoalsView: View {
//MARK: - PROPERTIES
#State var showNewGoalView: Bool = false
//MARK: - FETCHING DATA
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \GoalViews.endDate, ascending: true)],
animation: .default) var goalViews: FetchedResults<GoalViews>
func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { goalViews[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
//MARK: - BODY
var body: some View {
NavigationView {
List {
ForEach(goalViews) { goalView in
GoalViewListItemView(goalView: goalView)
}
.onDelete(perform: deleteItems)
}
.listStyle(PlainListStyle())
.navigationBarTitle(Text("Goals"), displayMode: .inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
self.showNewGoalView = true
}, label: {
Text("Add Goal")
})
}
ToolbarItem(placement: .navigationBarLeading) {
EditButton()
}
}
.sheet(isPresented: $showNewGoalView, content: {
CreateGoalView(isShowing: $showNewGoalView)
})
}
}
}
struct GoalsView_Previews: PreviewProvider {
static var previews: some View {
return GoalsView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
And in the individual child view, GoalViewListItemView:
//
// GoalViewListItemView.swift
// Training
//
// Created by Liam Day on 30/05/2021.
//
import SwiftUI
import HealthKit
import CoreData
struct GoalViewListItemView: View {
var goalView: FetchedResults<GoalViews>.Element // changed from observed object
#State var progressValue: Double = 0
#AppStorage("distanceUnitSystemMetric") var distanceUnitSystemMetric: Bool = true
func calcGoalTotal() {
...
}
var body: some View {
NavigationLink(destination: GoalViewDetailView(goalView: goalView, progressValue: $progressValue)) {
VStack(alignment: .leading) {
Text(HKWorkoutActivityType.init(rawValue: UInt(goalView.workoutActivityType))!.commonName)
.font(.title)
Text(K.goalUnit.init(rawValue: (goalView.goalUnit ?? String("totalDistanceDoubleValue")))?.name ?? "Unkown")
Text("Start: \(Bundle.main.getDynamicDayName(date: goalView.startDate ?? Date()))")
Text("Finish: \(Bundle.main.getDynamicDayName(date: goalView.endDate ?? Date()))")
if goalView.goalUnit == K.goalUnit.totalDistanceDoubleValue.rawValue {
Text("Goal of \(Bundle.main.getDistance(totalDistanceDoubleValue: goalView.totalDistanceDoubleValue, totalDistanceUnit: HKUnit.meter().unitString, returnUnit: distanceUnitSystemMetric ? K.init().kilometer : HKUnit.mile().unitString, significantDigits: 3))")
ProgressView(
"\(Bundle.main.getDistance(totalDistanceDoubleValue: progressValue, totalDistanceUnit: HKUnit.meter().unitString, returnUnit: distanceUnitSystemMetric ? K.init().kilometer : HKUnit.mile().unitString, significantDigits: 3))",
value: (progressValue / goalView.totalDistanceDoubleValue),
total: 1
)
} else if goalView.goalUnit == K.goalUnit.totalEnergyBurnedDoubleValue.rawValue {
Text("Goal of \(String(format: "%.0f", goalView.totalEnergyBurnedDoubleValue)) KCal")
ProgressView(
"\(String(format: "%.0f", progressValue)) KCal",
value: (progressValue / goalView.totalEnergyBurnedDoubleValue),
total: 1
)
} else if goalView.goalUnit == K.goalUnit.duration.rawValue {
Text("Goal of \(Bundle.main.getHoursMinutesSeconds(timeInSeconds: goalView.duration))")
ProgressView(
"\(Bundle.main.getHoursMinutesSeconds(timeInSeconds: progressValue))",
value: (progressValue / goalView.duration),
total: 1
)
} else {
EmptyView()
}
}
}
.onAppear(perform: {
calcGoalTotal()
})
}
}
struct GoalViewListItemView_Previews: PreviewProvider {
static var previews: some View {
let viewContext = PersistenceController.preview.container.viewContext
let newGoalView = GoalViews(context: viewContext)
return GoalViewListItemView(goalView: newGoalView)
}
}
I have already swapped the child view from an #ObservedObject with type GoalViews to a FetchedResults<GoalViews>.Element.
The app continues to function as expected as I swap and change FetchedResults, ObservedObjects and States at different levels.
What is the correct data flow from parent (fetching data) to child (needing to access update/delete data)? How are these outlined in the preview if using a preview Persistence Controller?

Unmodified SwiftUI / Core Data default app code does not run in simulator?

When I create and run a new, unmodified, Core Data / SwiftUI app project in Xcode (12.3), the simulator shows a blank screen. Code below.
Upon creation of the new app project, SwiftUI code is generated that includes a List along with Add and Edit buttons. The UI displays correctly in Xcode's preview but not in the simulator.
This is the default code in the ContentView.swift file:
import SwiftUI
import CoreData
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
List {
ForEach(items) { item in
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
}
.onDelete(perform: deleteItems)
}
.toolbar {
#if os(iOS)
EditButton()
#endif
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
} catch {
// Replace this...
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { items[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
// Replace this...
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
You can reproduce this simply by creating a new iOS App project with SwiftUI and Core Data enabled:
Trying to wrap my head around this stuff. Getting stuck on the default code is not promising!
IMHO Yes this seems like a bug with the example, might be worth submitting with a feedback
Problem:
Since the view is not inside the navigation view the navigation buttons are not visible
The tool bar modifier requires to use the ToolbarItem which is missing.
Overview:
Please wrap the view contents inside a NavigationView
Wrap the tool bar buttons inside ToolbarItem
Solution:
Replace the body computed property as follows:
var body: some View {
NavigationView {
List {
ForEach(items) { item in
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
}
.onDelete(perform: deleteItems)
}
.toolbar {
#if os(iOS)
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
#endif
ToolbarItem(placement: .navigationBarLeading) {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
}
}

Change State with DocumentPicker SwiftUI

I'm trying to get a List View to appear after selecting a document with documentPicker. Getting the following error...
Fatal error: No ObservableObject of type Switcher found.
A View.environmentObject(_:) for Switcher may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros/Monoceros-30.4/Core/EnvironmentObject.swift, line 55
It seems like I should use an EnviromentObject binding to have all views be able to read, access and update the Switcher class. Under the Coordinator Class in CSVDocumentPicker.swift is where things seem to go wrong.
I'm using #EnvironmentObject var switcher:Switcher and using the documentPicker function to toggle the switcher state so the Lists View will be displayed. I'm stumped.
SceneDelegate.swift
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var switcher = Switcher()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView().environmentObject(Switcher())
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(switcher))
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
}
func sceneDidBecomeActive(_ scene: UIScene) {
}
func sceneWillResignActive(_ scene: UIScene) {
}
func sceneWillEnterForeground(_ scene: UIScene) {
}
func sceneDidEnterBackground(_ scene: UIScene) {
}
}
CSVDocumentPicker.swift
import Combine
import SwiftUI
class Switcher: ObservableObject {
var didChange = PassthroughSubject<Bool, Never>()
var isEnabled = false {
didSet {
didChange.send(self.isEnabled)
}
}
}
struct CSVDocumentPicker: View {
#EnvironmentObject var switcher:Switcher
#State private var isPresented = false
var body: some View {
VStack{
Text("csvSearch")
Button(action: {self.isPresented = true
})
{Text("import")
Image(systemName: "folder").scaledToFit()
}.sheet(isPresented: $isPresented) {
() -> DocumentPickerViewController in
DocumentPickerViewController.init(onDismiss: {
self.isPresented = false
})
}
if switcher.isEnabled {
ListView()
}
}
}
}
struct CSVDocumentPicker_Previews: PreviewProvider {
static var previews: some View {
CSVDocumentPicker().environmentObject(Switcher())
}
}
/// Wrapper around the `UIDocumentPickerViewController`.
struct DocumentPickerViewController {
private let supportedTypes: [String] = ["public.item"]
// Callback to be executed when users close the document picker.
private let onDismiss: () -> Void
init(onDismiss: #escaping () -> Void) {
self.onDismiss = onDismiss
}
}
// MARK: - UIViewControllerRepresentable
extension DocumentPickerViewController: UIViewControllerRepresentable {
typealias UIViewControllerType = UIDocumentPickerViewController
func makeUIViewController(context: Context) -> DocumentPickerViewController.UIViewControllerType {
let documentPickerController = UIDocumentPickerViewController(documentTypes: supportedTypes, in: .import)
documentPickerController.allowsMultipleSelection = false
documentPickerController.delegate = context.coordinator
return documentPickerController
}
func updateUIViewController(_ uiViewController: DocumentPickerViewController.UIViewControllerType, context: Context) {}
// MARK: Coordinator
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIDocumentPickerDelegate, ObservableObject {
#EnvironmentObject var switcher:Switcher
var parent: DocumentPickerViewController
init(_ documentPickerController: DocumentPickerViewController) {
parent = documentPickerController
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
globalPathToCsv = url
loadCSV()
switcher.isEnabled.toggle()
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
parent.onDismiss()
}
}
}
ContentView.swift
import SwiftUI
import UIKit
var globalPathToCsv:URL!
var csvArray = [[String:String]]()
var csv = CSVaccessability()
func loadCSV(){
csv.csvToList()
// print(csvArray)
}
struct ContentView: View {
#EnvironmentObject var switcher:Switcher
var body: some View {
VStack{
CSVDocumentPicker().environmentObject(Switcher())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(Switcher())
}
}
ListView.swift
import SwiftUI
struct ListView: View {
var body: some View {
HStack{
List {
ForEach(csvArray, id:\.self) { dict in Section {DataList(dict: dict)} }
}
}}
}
struct DataList : View {
#State var dict = [String: String]()
var body: some View {
let keys = dict.map{$0.key}
let values = dict.map {$0.value}
return ForEach(keys.indices) {index in
HStack {
Text(keys[index])
Text("\(values[index])")
}
}
}
}
struct ListView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
CSVaccessability.swift
import Foundation
import SwiftCSV
var csvData:[[String]]!
var headers:[String] = []
class CSVaccessability {
var numberOfColumns:Int!
var masterList = [[String:Any]]()
func csvToList(){
if let url = globalPathToCsv {
do {
print(url)
let csvFile: CSV = try CSV(url: globalPathToCsv)
let csv = csvFile
//print(stream)
//print(csvFile)
headers = csv.header
csvArray=csv.namedRows
} catch {print("contents could not be loaded")}}
else {print("the URL was bad!")}
}
}
I've imported SwiftCSV for this project...
Created a new class...
ToggleView.swift
import Foundation
class ToggleView: ObservableObject {
#Published var toggleView: Bool = false
}
Added the Environment Object to ContentView.swift
#EnvironmentObject var viewToggle: ToggleView
Also added .environmentObject(ToggleView()) to any view that would be called and cause a crash the crash logs helped with this...
Text("csvSearch")
Button(action: {self.isPresented = true
self.viewToggle.toggleView.toggle()
// self.switcher = true
})
{Text("import")
Image(systemName: "folder").scaledToFit()
}.sheet(isPresented: $isPresented) {
() -> DocumentPickerViewController in
DocumentPickerViewController.init()
}
if self.picker {
DocumentPickerViewController().environmentObject(ToggleView())
}
if self.viewToggle.toggleView{
ListView()
}
}
}
}
Did you ever get this working? The only problem I found was with the line var csv = CSVaccessability() in the ContentView. CSVaccessability does not exist.
This is my solution for the Catalyst app for Mac, but to avoid pressing theImage (systemName: "book")button a second time to update the data in the text fields, I have implemented a hidden view in GeoFolderReadFileView to force the view update.
//File: GeoFolderCodStruct
import Foundation
struct GeoFolderCodStruct:Codable {
var isActive:Bool = true
var dataCreazione:Date = Date()
var geoFolderPath:String = ""
var nomeCartella:String = ""
var nomeCommittente:String = ""
var nomeArchivio:String = ""
var note:String = ""
var latitudine:Double? = nil
var longitudine:Double? = nil
var radiusCircle:Int16? = nil
//Roma 42.1234 13.1234
}
//File: PickerForReadFile
import SwiftUI
final class PickerForReadFile: NSObject, UIViewControllerRepresentable, ObservableObject {
#Published var geoFolder = GeoFolderCodStruct()
lazy var viewController:UIDocumentPickerViewController = {
let vc = UIDocumentPickerViewController(documentTypes: ["geof"], in: .open)
vc.allowsMultipleSelection = false
vc.delegate = self
return vc
}()
func makeUIViewController(context: UIViewControllerRepresentableContext<PickerForReadFile>) -> UIDocumentPickerViewController {
viewController.delegate = self
return viewController
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<PickerForReadFile>) {
print("updateUIViewController")
}
}
extension PickerForReadFile: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
if urls.count > 0 {
DispatchQueue.main.async {
let url = urls[0]
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let jsonData = try decoder.decode(GeoFolderCodStruct.self, from: data)
self.geoFolder = jsonData
print("geoFolder: \(self.geoFolder.nomeArchivio)")
} catch {
print("error:\(error)")
}
}
}
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true) {
print("Cancel from picker view controller")
}
}
}
//File: GeoFolderReadFileView
import SwiftUI
struct GeoFolderReadFileView: View {
#ObservedObject var picker = PickerForReadFile()
#Binding var geoFolder: GeoFolderCodStruct
var body: some View {
VStack(alignment: .trailing){
Button(action: {
#if targetEnvironment(macCatalyst)
print("Press open file")
let vc = UIApplication.shared.windows[0].rootViewController!
vc.present(self.picker.viewController, animated: true) {
self.geoFolder = self.picker.geoFolder
}
#endif
}) {
Image(systemName: "book")
}
urlPickedView()
.hidden()
}
}
private func urlPickedView() -> some View {
DispatchQueue.main.async {
let geoF = self.picker.geoFolder
print("Committente: \(geoF.nomeCommittente) - Archivio: \(geoF.nomeArchivio)")
self.geoFolder = geoF
}
return TextField("", text: $geoFolder.geoFolderPath)
}
}
//File: ContentView
import SwiftUI
struct ContentView: View {
#State private var geoFolder = GeoFolderCodStruct()
var body: some View {
VStack {
HStack {
Text("Open GeoFolder File:")
.padding()
Spacer()
GeoFolderReadFileView(geoFolder: $geoFolder)
.padding()
}
.padding()
Group {
TextField("", text: $geoFolder.geoFolderPath)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeCartella)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeCommittente)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeArchivio)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.note)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
.padding()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and the last, the json file to read for test the code.
{
"nomeCommittente" : "Appple",
"note" : "Image from Cupertino (CA).",
"latitudine" : 37.332161,
"longitudine" : -122.030352,
"nomeCartella" : "Foto",
"geoFolderPath" : "\/Users\/cesare\/Desktop",
"radiusCircle" : 50,
"dataCreazione" : "20\/03\/2020",
"nomeArchivio" : "AppleCampus-Image",
"isActive" : true
}
I was unable to implement the solution proposed by #Mdoyle1. I hope someone can edit the code to make it work as it should, without create hidden view.

Resources