Hi. The binding in a ForEach or List view doesn't work anymore when using the @Observable macro to create the observable object. For example, the following are the modifications I introduced to the Apple's example called "Migrating from the Observable Object Protocol to the Observable Macro" https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro
struct LibraryView: View {
@Environment(Library.self) private var library
var body: some View {
List($library.books) { $book in
BookView(book: book)
}
}
}
All I did was to add the $ to turn the reference to library.books into a binding but I got the error "Cannot find '$library' in scope"
Is this a bug or the procedure to use binding in lists changed?
Thanks
Post
Replies
Boosts
Views
Activity
Hi, guys. I installed iOS Beta 18.0 on my iPhone SE and iPad 9th, but the system does not upgrade to 18.1. These devices can't run Apple Intelligence. Is it that the reason? And if so, does anyone know how Apple is going to manage this situation?
Thanks
Hi guys. Can someone please confirm this bug so I report it? The issue is that SwiftData relationships don't update the views in some specific situations on devices running iOS 18 Beta. One clear example is with CloudKit. I created a small example for testing. The following code creates two @models, one to store bands and another to store their records. The following code works with no issues. (You need to connect to a CloudKit container and test it on two devices)
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var records: [Record]
var body: some View {
NavigationStack {
List(records) { record in
VStack(alignment: .leading) {
Text(record.title)
Text(record.band?.name ?? "Undefined")
}
}
.toolbar {
ToolbarItem {
Button("Add Record") {
let randomNumber = Int.random(in: 1...100)
let newBand = Band(name: "New Band \(randomNumber)", records: nil)
modelContext.insert(newBand)
let newRecord = Record(title: "New Record \(randomNumber)", band: newBand)
modelContext.insert(newRecord)
}
}
}
}
}
}
@Model
final class Record {
var title: String = ""
var band: Band?
init(title: String, band: Band?) {
self.title = title
self.band = band
}
}
@Model
final class Band {
var name: String = ""
var records: [Record]?
init(name: String, records: [Record]?) {
self.name = name
self.records = records
}
}
This view includes a button at the top to add a new record associated with a new band. The data appears on both devices, but if you include more views inside the List, the views on the second device are not updated to show the values of the relationships. For example, if you extract the row to a separate view, the second device shows the relationships as "Undefined". You can try the following code.
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var records: [Record]
var body: some View {
NavigationStack {
List {
ForEach(records) { record in
RecordRow(record: record)
}
}
.toolbar {
ToolbarItem {
Button("Add Record") {
let randomNumber = Int.random(in: 1...100)
let newBand = Band(name: "New Band \(randomNumber)", records: nil)
modelContext.insert(newBand)
let newRecord = Record(title: "New Record \(randomNumber)", band: newBand)
modelContext.insert(newRecord)
}
}
}
}
}
}
struct RecordRow: View {
let record: Record
var body: some View {
VStack(alignment: .leading) {
Text(record.title)
Text(record.band?.name ?? "Undefined")
}
}
}
Here I use a ForEach loop and move the row to a separate view. Now on the second device the relationships are nil, so the row shows the text "Undefined" instead of the name of the band.
I attached an image from my iPad. I inserted all the information on my iPhone. The first three rows were inserted with the first view. But the last two rows were inserted after I extracted the rows to a separate view. Here you can see that the relationships are nil and therefore shown as "Undefined". The views are not updated to show the real value of the relationship.
This example shows the issue with CloudKit, but this also happens locally in some situations. The system doesn't detect updates in relationships and therefore doesn't refresh the views.
Please, let me know if you can reproduce the issue. I'm using Mac Sequoia 15.1, and two devices with iOS 18.0.
Hi, guys. When I update a value in a Core Data object, the view is not updated to show the change. I don't understand what I'm doing wrong, since this worked before. Right now I have Xcode 16 Beta installed, but it happens with Xcode 15. Here is an example.
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@State private var selectedItem: Item?
var body: some View {
NavigationStack {
VStack {
Text("Year: \(selectedItem?.year ?? 0)")
.onTapGesture {
changeYear()
}
Spacer()
}
.onAppear {
// Load an item from the database
let request: NSFetchRequest<Item> = Item.fetchRequest()
request.fetchLimit = 1
if let item = try? viewContext.fetch(request).first {
selectedItem = item
}
}
}
}
private func changeYear() {
let year = Int16.random(in: 1900..<2020)
print("Random year: \(year)")
selectedItem?.year = year
try? viewContext.save()
}
}
#Preview {
ContentView()
.environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
I think it is easy to follow. There is an entity called Item with an attribute called year. I preloaded a few objects for the preview. This view loads one item and assigns it to a state property. When you tap on the Text view, the changeYear() method is executed. In this method I select a random year, assign it to the object in the selectedItem property and save the context. A message is printed on the console with the new value, but the view never updates. I'm also having similar problems with SwiftData and To Many relationships. They also do not update the views when they are modified.
In case you want to test it, you can just create a new project with Core Data set in, change the name of the attribute in the Item entity to year and select Int16 as the data type, and then update the following method to preload some values.
@MainActor
static let preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
for _ in 0..<10 {
let newItem = Item(context: viewContext)
newItem.year = Int16.random(in: 1900..<2020)
}
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
Thanks for any help.
There is a new Relationship macro in Xcode Beta 6. The macro includes two new arguments: minimumModelCount and maximumModelCount. I wonder if anyone knows what these values are for and if there is another change under the hood.
Relationship(
_ options: PropertyOptions...,
deleteRule: Schema.Relationship.DeleteRule = .nullify,
minimumModelCount: Int? = 0,
maximumModelCount: Int? = 0,
originalName: String? = nil,
inverse: AnyKeyPath? = nil,
hashModifier: String? = nil
)
Hi, guys. In Xcode Beta 4, the PhotosPicker view doesn't modify the state property when all the items are deselected. This seems to be a bug, but I don't know how to file a bug for a beta API.
This code generates a list with the selected pictures. If you select a picture and then deselect it, the item is not removed from the list.
import SwiftUI
import PhotosUI
struct ContentView: View {
@State private var selected: [PhotosPickerItem] = []
var body: some View {
NavigationStack {
List(selected, id: \.itemIdentifier) { item in
Text(item.itemIdentifier ?? "")
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
PhotosPicker(selection: $selected, matching: .images, photoLibrary: .shared()) { Text("Select Photos") }
}
}
}
}
}
Hi, guys. Can someone confirm this problem so I submit the bug to Apple. When the Core Data Persistent Store is empty, the canvas preview crashes. To test it, you just need to create a new project with the Core Data option enabled. Then go to the Persistence.swift file created by the template, and remove the for in loop in the closure assigned to the preview property that creates 10 new items. The property will look like this:
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
return result
}()
Now, you will get an error when the system tries to fetch items [NSManagedObjectContext executeFetchRequest:error:].
You only need to add one single item for the error to disappear. It looks like a pretty bad bug, so I want to be sure it is not my computer.
Thanks.
Hi, guys. I'm testing the new pop-up and pull-down buttons in iOS 15, but they do not show the pop-up menu when pressed (all the options in the Attributes Inspector panel are checked). They only work when I define the entire menu in code. My questions are:
Is this because the feature was not implemented yet?
If this is implemented later and we can define the menu in Interface Builder, how do you respond to a selection? In code, I can specify the action in the closure, but how do I do it if the menu is defined in Interface Builder?
This is how I define the menu from code, just in case someone needs to know:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let optionsClosure = { (action: UIAction) in
print(action.title)
}
myButton.menu = UIMenu(children: [
UIAction(title: "Option 1", state: .on, handler: optionsClosure),
UIAction(title: "Option 2", handler: optionsClosure),
UIAction(title: "Option 3", handler: optionsClosure)
])
}
}
Thanks
JD
Hi, guys. While trying to update the old code to the new AttributedString structure I realized that there are properties in UIKit that take a dictionary of NSAttributedString.Key values. For instance, the code below assigns a dictionary to the titleTextAttributes property of the UINavigationBarAppearance object. This property doesn't take an AttributeContainer value and I couldn't find any way to convert the container into a dictionary. Is there a way to do it or do we still have to use the old NSAttributedString.Key values in these cases?
Thanks!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let standard = UINavigationBarAppearance()
standard.titleTextAttributes = [.foregroundColor: UIColor.red]
navigationController?.navigationBar.standardAppearance = standard
}
}
Hi, guys. I want to put my apps on the cloud so I can develop them from any device, but after a while, the files are removed locally and I get errors. I need to store the files locally on every computer and get them to automatically synchronize with iCloud, but I can't find the option "Optimize Mac Storage" anymore. Also, if I deactivate that option are the files going to stay locally or there is anything else I have to do to make sure that happens?
Thanks!
Hi, guys. The use of @MainActor is confusing? Should I use it only to turn classes into main actors? What if one of my functions access the interface from a closure that could run on a different thread? How do I tell the system to access a label from the main thread? Should I mark the entire view controller class with the @MainActor? Is there anything similar to use with functions or statements or should I still use DispatchQueue.main? Thanks.
Hi, guys. How can I disable the popup windows that appear when you keep the mouse over an option in the Attributes Inspector panel?https://ibb.co/cwWRTFqI don't remember these windows poping up in previous versions. I think is a new "feature" but it becomes extremelly annoying after just a few minutes working on the storyboard. They do not only appear when you just leave the mouse over the option for a while but also when you are working with the option. For instance, try to change the tag value of an element. After you press the button three or four times, the help window appears and covers the field, not letting you see what you are doing. Probably the worse idea in the history of Xcode.Thanks