Unable to open a file using .fileImporter on actual device - swiftui

This use to work when deployed on a real device when I was testing iOS 14 beta, but now in production it only works on the emulator. Did something change in IOS 14? I received the following error:
FilePickerTest[15486:5885508] [AXRuntimeCommon] Unknown client: FilePickerTest
The file “16010282771_20190702_23_38.csv” couldn’t be opened because you don’t have permission to view it.
Is there a setting I need to add to give my app permission to access the file?
import SwiftUI
struct ContentView: View {
#State private var openFile = false
#StateObject var doc = ViewModel()
var body: some View {
VStack {
Text("Tap to pick a csv file")
.padding()
.onTapGesture(perform: {
openFile.toggle()
})
}
.fileImporter(isPresented: $openFile, allowedContentTypes: [.commaSeparatedText]) { (res) in
do {
let fileUrl = try res.get()
print(fileUrl)
doc.viewFile(fileUrl: fileUrl)
} catch {
print("Error reading file")
print(error.localizedDescription)
}
}
}
}
import SwiftUI
class ViewModel: ObservableObject {
var readString = ""
func viewFile(fileUrl: URL) {
do {
readString = try String(contentsOf: fileUrl)
} catch {
print("Error reading file")
print(error.localizedDescription)
}
print("File contents: \(readString)")
}
}

add fileUrl.startAccessingSecurityScopedResource()
before
doc.viewFile(fileUrl: fileUrl)

Related

Error: Thread 1: "executeFetchRequest:error: A fetch request must have an entity."

I'm trying to use CoreData in my Xcode-project (SwiftUI). I've created a Player entity and wanna use it in my View called "YouView". But when I'm trying to fetch the data, I get the error from Title. My app is called Dart Tools. My Application Language is German, so don't worry if you don't understand everything of the ui :).
Thanks for helping!
I already tried the .shared variant - same error
This is my .xcdatamodeld file (called DataModel.xcdatamodeld)
This is the Code of my DataController file:
import Foundation
import CoreData
class DataController: ObservableObject {
let container = NSPersistentContainer(name: "DataModel")
init() {
container.loadPersistentStores { desc, error in
if let error = error {
print("Daten wurden nicht geladen: \(error.localizedDescription)")
}
}
}
func save(context: NSManagedObjectContext) {
do {
try context.save()
print("Daten wurden gespiechert!")
} catch {
print("Daten konnten nicht gespeichert werden.")
}
}
func addPlayer(name: String, isUser: Bool, context: NSManagedObjectContext) {
let player = Player(context: context)
player.id = UUID()
player.name = name
player.isUser = isUser
save(context: context)
}
func editPlayerName(player: Player, name: String, context: NSManagedObjectContext) {
player.name = name
save(context: context)
}
}
Here is my DartToolsApp.swift file:
import SwiftUI
//The error is here:
#main
struct DartToolsApp: App {
#StateObject private var userDefaults = UserDefaults()
#StateObject private var dataController = DataController()
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, DataController().container.viewContext)
.environmentObject(UserDefaults())
}
}
}
This is the file where I want to use the Data:
Btw, this view is a piece of a TabView.
import SwiftUI
import CoreData
struct YouView: View {
#EnvironmentObject var userDefaults: UserDefaults
// I guess the error is because of this line:
#FetchRequest(sortDescriptors: []) var players: FetchedResults<Player>
#Environment(\.managedObjectContext) var managedObjectContext
var body: some View {
NavigationStack {
Form {
}
.navigationTitle("\(findUser()) (Du)")
}
}
func findUser() -> String {
if let index = players.firstIndex(where: { $0.isUser }) {
let output = players[index].name!
return output
}
else {
return ""
}
}
}
This is the code to create the user:
DataController().addPlayer(name: nameText, isUser: true, context: managedObjectContext)
Wow, that was a lot of code. I hope you can help me!
You are creating new instances of DataController everywhere which is a problem in itself but what think causes your particular error is when you assign the \.managedObjectContext environment variable.
So instead of creating a new instance you should use your #StateObject instance instead.
So change this in the App code
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, dataController.container.viewContext)
}
}

Why is my app failing to find a user during authentication?

I've had a problem on and off for the past week where my else statement is executing in the MainTabView upon login/signup (meaning it can't find the currentuser??) So rather than logging in and showing the main navigation, I see a white "loading.." screen after logging in. It's odd cause some log in's have worked fine and others crash the app. Any help is very appreciated!
I don't think the problem is within EmailAuth or CreateAccountAuth but let me know if you'd like to see the code for those too.
AuthViewModel:
import SwiftUI
import FirebaseAuth
import FirebaseCore
import FirebaseStorage
import FirebaseFirestore
import FirebaseFirestoreSwift
class AuthViewModel: NSObject, ObservableObject {
#Published var userSession: FirebaseAuth.User?
#Published var currentUser: User?
#Published var selectedImage: UIImage?
private let service = UserService()
static let shared = AuthViewModel()
override init() {
super.init()
userSession = Auth.auth().currentUser
fetchUser()
}
func login(withEmail email: String, password: String) {
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to sign in with error \(error.localizedDescription)")
return
}
self.userSession = result?.user
self.fetchUser()
}
}
func register(withEmail email: String, password: String, fullname: String) {
Auth.auth().createUser(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to register with error \(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
self.userSession = user
let data: [String: Any] = ["email": email,
"fullname": fullname]
COLLECTION_USERS
.document(user.uid)
.setData(data)
self.uploadProfileImage(self.selectedImage)
}
}
func signOut() {
// sets user session to nil so we show login view
self.userSession = nil
// signs user out on server
try? Auth.auth().signOut()
}
func uploadProfileImage(_ image: UIImage?) {
guard let uid = userSession?.uid else { return }
ImageUploader.uploadImage(image: image) { profileImageUrl in
COLLECTION_USERS
.document(uid)
.updateData(["profileImageUrl": profileImageUrl])
//{ _ in self.userSession = user }
}
}
func fetchUser() {
guard let uid = userSession?.uid else { return }
COLLECTION_USERS.document(uid).getDocument { snapshot, _ in
guard let user = try? snapshot?.data(as: User.self) else { return }
self.currentUser = user
}
}
}
App File:
struct Page_TurnerApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
NavigationView {
ContentView().environmentObject(AuthViewModel())
}
}
}
}
ContentView
struct ContentView: View {
#EnvironmentObject var viewModel: AuthViewModel
var body: some View {
Group {
if viewModel.userSession != nil {
MainTabView()
} else {
EmailAuth()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
MainTabView
struct MainTabView: View {
#State private var selectedIndex = 0
#EnvironmentObject var viewModel: AuthViewModel
var body: some View {
if let user = viewModel.currentUser {
TabView(selection: $selectedIndex) {
ExploreView()
.onTapGesture {
self.selectedIndex = 0
}
.tabItem {
Image(systemName: "house")
}.tag(0)
SearchView()
.onTapGesture {
self.selectedIndex = 1
}
.tabItem {
Image(systemName: "magnifyingglass")
}.tag(1)
ConversationsView()
.onTapGesture {
self.selectedIndex = 2
}
.tabItem {
Image(systemName: "message")
}.tag(2)
AccountView(user: user)
.onTapGesture {
self.selectedIndex = 3
}
.tabItem {
Image(systemName: "person.crop.circle")
}.tag(3)
}
} else {
Text("loading...")
}
}
}
My problem was that my User wasn't being created unless there was a profile image. That's why it worked for certain users and crashed for others. I changed let profileImageUrl: String
to let profileImageUrl: String? and all users can log in now

How do I fix a value member not found error in iOS foundation with swift while working with notifications?

I'm trying to experiment with iOS notifications, so I tried making a swiftUI view that would send a notification, basing it off a tutorial Found Here. I have wrote this so far:
import SwiftUI
import Foundation
class LocalNotificationManager: ObservableObject {
var notifications = [Notification]()
init() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound])
{ granted, error in
if granted == true && error == nil {
print("yay")
} else {
print("boo")
}
}
func sendNotification(title: String, subtitle: String?, body: String, LaunchIn: Double) {
let content = UNMutableNotificationContent()
content.title = title
if let subtitle = subtitle {
content.subtitle = subtitle
}
content.body = body
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: LaunchIn, repeats: false)
let request = UNNotificationRequest(identifier: "demoNotification", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
}
}
struct Screen: View {
#ObservedObject var NotificationManager = LocalNotificationManager()
var body: some View {
Button(action: {
self.notificationManager.sendNotification(title: "It worked", subtitle: ":D", body: "If you see this text, launching the local notification worked!", launchIn: 5)
}) {
Text("Test Notification")
}
}
}
struct Screen_Previews: PreviewProvider {
static var previews: some View {
Screen()
}
}
Its this line where I get problems:
self.notificationManager.sendNotification(title: "It worked", subtitle: ":D", body: "If you see this text, launching the local notification worked!", launchIn: 5)
I get an error that says:
Value of type 'Screen' has no member 'notificationManager'
Change
#ObservedObject var NotificationManager = LocalNotificationManager()
To
#StateObject var notificationManager = LocalNotificationManager()
Variables are case-sensitive and should start with a lowercase
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app
https://swift.org/documentation/api-design-guidelines/

swiftui list doens't appear but array isn't empty

I am working on a Swiftui file that loads data from Firebase.
It did work but when I added things it suddenly stopt working...
I tried to strip it back down but I can't get it working again.
Does anyone know what I do wrong?
import SwiftUI
import Firebase
struct Fav: View {
#StateObject var loader = Loader()
var body: some View {
ScrollView {
if loader.userfav.count != 0 {
List (loader.userfav, id: \.id) { fav in
Text(fav.name.capitalized)
}
}
else
{
Text("You haven't added favorits yet...")
}
}
.onAppear{
loader.loadfav(loadfavorits: "asd")
}
.navigationBarTitle("")
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
func deletefav (docid: String) {
print(docid)
}
}
struct Fav_Previews: PreviewProvider {
static var previews: some View {
Fav()
}
}
and the loader file
import Foundation
import Firebase
import FirebaseFirestore
class Loader : ObservableObject {
private var db = Firestore.firestore()
#Published var userfav = [fav]()
func loadfav (loadfavorits: String) {
userfav = [fav]()
db.collection("favo").whereField("user", isEqualTo: loadfavorits).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting favorits: \(err.localizedDescription)")
}
else
{
for fav in querySnapshot!.documents {
let brand = fav.get("brand") as! String
let store = fav.get("store") as! String
let name = fav.get("name") as! String
let type = fav.get("type") as! String
let docid = fav.get("docid") as! String
self.userfav.append(fav(brand: brand, store: store, name: name, type: type, docid: docid))
}
}
}
}
}
It doesn't show the Text("You haven't added favorits yet...")
So that means dat loader.userfav.count is not empty
Having a List embedded in a ScrollView (which also scrolls) can lead to layout problems. Remove the outer ScrollView and the issue will be solved.

How to make a SwiftUI DocumentGroup app without starting on the file picker?

If a user uses the Document App template in Xcode to create a SwiftUI app, macOS starts them off with a new document. This is good. I can work with that to present onboarding UI within a new document.
However, with that same app running on iOS, the user is instead greeted by the stock document view controller to create or pick a document.
This is not helpful in that I don't have a way to present onboarding or any other custom information.
I did notice that if you add a WindowGroup to the Scene, the app will display that window group. But then I don't know how to get the user to the picker UI.
Has anyone figured out how to do things like present onboarding on top of this DocumentGroup-based app?
Here is a full document app
import SwiftUI
import UniformTypeIdentifiers
#main
struct DocumentTestApp: App {
var body: some Scene {
DocumentGroup(newDocument: DocumentTestDocument()) { file in
ContentView(document: file.$document)
}
}
}
struct ContentView: View {
#Binding var document: DocumentTestDocument
var body: some View {
TextEditor(text: $document.text)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(document: .constant(DocumentTestDocument()))
}
}
extension UTType {
static var exampleText: UTType {
UTType(importedAs: "com.example.plain-text")
}
}
struct DocumentTestDocument: FileDocument {
var text: String
init(text: String = "Hello, world!") {
self.text = text
}
static var readableContentTypes: [UTType] { [.exampleText] }
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let string = String(data: data, encoding: .utf8)
else {
throw CocoaError(.fileReadCorruptFile)
}
text = string
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
let data = text.data(using: .utf8)!
return .init(regularFileWithContents: data)
}
}
App shows first window scene by default, so place first on-boarding window scene and afterwards a DocumentGroup. Somewhere at the end of on boarding process (success path) call document controller to create new document (DocumentGroup is based on same NSDocumentController engine inside).
Update: below is for macOS
*just recognised that original question is for iOS
So a possible approach is
#main
struct DocumentTestApp: App {
var body: some Scene {
WindowGroup("On-Boarding") {
// ContentView()
// In some action at the end of this scene flow
// just close current window and open new document
Button("Demo") {
NSApp.sendAction(#selector(NSWindow.performClose(_:)), to: nil, from: nil)
NSDocumentController.shared.newDocument(nil)
}
}
DocumentGroup(newDocument: DocumentTestDocument()) { file in
ContentView(document: file.$document)
}
}
}
Alright friends, here is a nice and hacky way to get things going, reaching into the key windows, and setting up onboarding/paywall/whatever you want!
import SwiftUI
#main
struct ExampleApp: App {
#StateObject var captain = Captain()
var body: some Scene {
DocumentGroup(newDocument: ExampleOfDocumentGroupAndOnboardingPaywallDocument()) { file in
ContentView(document: file.$document)
}
}
}
class Captain: ObservableObject {
var onboardingSheet: UIViewController?
#objc func loadData() {
onboardingSheet = try? OnboardingOrPaywall(dismissHandler: dismissSheet).presentFromDocumentGroup()
}
func dismissSheet() {
onboardingSheet?.dismiss(animated: true)
}
init() {
NotificationCenter.default.addObserver(self,
selector: #selector(loadData),
name: UIApplication.didBecomeActiveNotification,
object: nil)
}
}
public protocol DocumentGroupSheet: View {}
struct OnboardingOrPaywall: DocumentGroupSheet {
var dismissHandler: () -> Void
var body: some View {
Button("Done") {
dismissHandler()
}
Text("Let me introduce you to this delicious app!")
}
}
public enum DocumentGroupSheetError: Error {
case noParentWindow
}
public extension DocumentGroupSheet {
func presentFromDocumentGroup() throws -> UIViewController {
let window = UIApplication.shared.activeKeyWindows.first
let parent = window?.rootViewController
guard let parent = parent else { throw DocumentGroupSheetError.noParentWindow }
let sheet = UIHostingController(rootView: body)
sheet.modalPresentationStyle = .fullScreen
parent.present(sheet, animated: false, completion: nil)
return sheet
}
}
public extension UIApplication {
var activeWindowScenes: [UIWindowScene] {
connectedScenes
.filter { $0.activationState == .foregroundActive }
.compactMap { $0 as? UIWindowScene }
}
var activeWindows: [UIWindow] {
activeWindowScenes
.flatMap { $0.windows }
}
var activeKeyWindows: [UIWindow] {
activeWindows
.filter { $0.isKeyWindow }
}
}