Hi everyone,
I'm facing an issue where I cannot write a file to a shared App Group container in my tvOS app when running on a real device. My code works perfectly on the simulator, but fails on a physical device with a permissions error. I’ve set up an App Group with a custom identifier (e.g., group.<my.identifier>), and it’s correctly configured in the Capabilities section of Xcode for both my main app and widget targets.
Here’s the code I’m using to save a test file:
func saveTestFile() {
guard let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.<my.identifier>") else {
print("Couldn't access the Group URL.")
return
}
let containerURL = groupURL.appendingPathComponent("Library", isDirectory: true)
if FileManager.default.isWritableFile(atPath: containerURL.path) {
print("Directory IS writable")
} else {
print("Directory IS NOT writable")
}
let fileURL = containerURL.appendingPathComponent("test.txt")
let content = "Hello App Group!"
do {
try content.write(to: fileURL, atomically: true, encoding: .utf8)
print("File test.txt is saved at: \(fileURL.path)")
} catch {
print("Error while saving the file: \(error)")
}
}
Console:
Directory IS NOT writable
Error while saving the file: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “test.txt” in the folder “”." UserInfo={NSFilePath=/private/var/mobile/Containers/Shared/AppGroup//Library/test.txt, NSURL=file:///private/var/mobile/Containers/Shared/AppGroup//Library/test.txt, NSUnderlyingError=0x14387fbe0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
I’ve tried saving the file in different subdirectories within the App Group container:
Directly in groupURL (root of the container).
In groupURL.appendingPathComponent("Library").
In groupURL.appendingPathComponent("Caches").
Do you have any ideas what is the problem?
Thanks in advance for any help!
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'd like to customise text fields in my application, but I don't see any options for it. When I click text field, new window opens. And there is no title or description.
But there must be something that allows me to customize it, right? Apple applications contain customised text fields.
This is apple's text field (after clicking on it)
This is my text field (after clicking on it)
Here is my code:
Form {
//...
TextField("", text: $address)
//...
}
And there is one more problem. After filling this TextField I click enter and it immediately opens the next TextField. I don't want this behaviour.
I'm trying to create a simple app for tvOS. But I immediately encountered difficulties. At the top I have a tab selection, and at the bottom there is item management.
The problem is that all buttons in HStack are connected into one big button.
So I solved it by adding .plain style to the buttons.
But now I have a new problem - I can't focus on my buttons for tab selection.
Here is the code:
struct ContentView: View {
var body: some View {
VStack {
HStack (alignment: .center) {
Button("tab1"){}
Button("tab2"){}
}
.frame(width: 500) // works when remove
List {
HStack {
Text("Item 1")
Spacer()
Button("Button1"){}
.buttonStyle(.plain) // added
Button("Button2"){}
.buttonStyle(.plain) // added
}
HStack {
Text("Item 2")
Spacer()
Button("Button1"){}
.buttonStyle(.plain) // added
Button("Button2"){}
.buttonStyle(.plain) // added
}
}
}
}
}
I would appreciate any help. Unfortunately, there is very little information on the Internet about designing applications for tvOS, which confuses me.
I have a complex project that includes a background process sending requests to the backend for updates. It sends requests once per second.
So, I have the MyView where the PresetPicker is located. When I open the PresetPicker and try to scroll down, it keeps pushing me back up every second. PresetPicker redraws itself every second due to some changes from the background process. (Turning off the background process confirmed that everything works fine.)
I wrapped my PresetPicker in EquatableView, added a bunch of logs triggered by changes/redraws of the PresetPicker, but the logs are empty and EquatableView didn't help.
I tried setting breakpoints; they trigger when the PresetPicker first appears but don't trigger afterward. How can I fix/debug this?
Here is some code:
struct PresetPicker: View, Equatable {
var body: some View {
Menu {
Text("1")
Text("2")
Text("3")
Text("4")
} label: {
Text("menu")
}
}
}
struct MyView: View {
var body: some View {
EquatableView(content:
PresetPicker()
)
}
}
I have read through many forums but haven't found a solution for myself. When sending a notification, my NotificationService is not being called. I added it exactly as described in the official Apple tutorial and tried adding it again following other tutorials. Nothing helped. I haven't made any changes to it; it is in the same form as it is automatically created. Here it is:
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
// Modify the notification content here...
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
It does not print anything or hit any breakpoints. The notification payload looks like this:
{
"Simulator Target Bundle": "my.bundle",
"aps": {
"alert": {
"title": "Its title",
"body": "Very nice text"
},
"sound": "default",
"category": "CustomSamplePush",
"mutable-content": 1
}
}
Things I've tried:
Ensured the minimum version matches that of the application.
Configured info.plist, including UNNotificationExtensionCategory.
Ensured "Copy Only When Installed" is unchecked.
The Content extension works. However, prints and breakpoints are also not working.
I've shortened my code as much as possible. The problem is that when I click on the "Generate" button inside CreateView, I am transferred to WaitView for a tenth of a second, after which I am transferred back to CreateView.
I quickly realized that the problem was a modification of the DataManager, since when I deleted this code:
dataManager.data.append(SomeData(data: newData))
this problem disappeared. But this obviously doesn’t suit me, because I need to change the data.
I also noticed that if you remove this code from ListView, then everything will work:
NavigationLink(destination: CreateView(dataManager: dataManager)) {
Text(data_item.data)
}
I can't imagine how this should affect this at all.
What could be the problem?
code:
import Foundation
import SwiftUI
struct SomeData: Identifiable {
let id = UUID()
var data: String
}
class DataManager: ObservableObject {
@Published var data: [SomeData]
init() {
data = [SomeData(data: "test")]
}
}
struct ContentView: View {
@StateObject var dataManager: DataManager = DataManager()
var body: some View {
NavigationView {
ListView(dataManager: dataManager)
.navigationTitle("First View")
}
}
}
struct ListView: View {
@ObservedObject var dataManager: DataManager
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: CreateView(dataManager: dataManager)) {
Text("GENERATE")
.font(.title)
.foregroundColor(.white)
.padding()
.frame(width: 200, height: 200)
.background(Color.blue)
.clipShape(Circle())
}
.padding(100)
Spacer()
VStack (alignment: .leading) {
Text("History")
.font(.title3)
List {
ForEach(dataManager.data) {data_item in
NavigationLink(destination: CreateView(dataManager: dataManager)) { //When I remove this line everything works
Text(data_item.data)
}
}
}
.listStyle(.automatic)
.cornerRadius(20)
}
.padding()
}
}
}
}
struct CreateView: View {
@ObservedObject var dataManager: DataManager
@State var fieldData: String = "some data here"
var body: some View {
UITableView.appearance().backgroundColor = .clear
return VStack {
NavigationLink(destination: WaitView(dataManager: dataManager, newData: fieldData)) {
Text("Generate")
.padding()
.background(Color.red)
}
}
.navigationBarTitleDisplayMode(.inline)
}}
struct WaitView: View {
@ObservedObject var dataManager: DataManager
let newData: String
var body: some View {
Text("Wait for something...")
.navigationTitle("Third View")
.onAppear {
dataManager.data.append(SomeData(data: newData))//When I remove this line everything works
print("adding")
}
}
}