Post not yet marked as solved
I referred to the code provided in the article Adding and Editing Persistent Data in Your App in the SwiftData documentation and tested the "/PreviewHelper" approach. I found that if a model class contains a property of a struct, and that struct includes a Set property or an Array property, the Preview would throw an error. Here is an example of the model:
@Model
class Book {
// ...
var someStruct: SomeStruct
// ...
}
struct SomeStruct: Codable {
// ...
var someCollection: Set<Int>
// ...
}
In actuality, this model can run and store data without any issues, but it fails to run in the Preview. Is there a problem with the implementation of this Preview or is there any other reason? Are there better ways to perform Preview in SwiftData?
Post not yet marked as solved
I know that this has been posted many times, but I am facing an issue where I can't save a model instance. I know that this is due to enums from others trying to find solutions.
The error that shows:
CoreData: error: Row (pk = 2) for entity 'ResidentInfo' is missing mandatory text data for property 'name'
Solutions that I've tried:
removed all enums from their respective structs to being standalone.
making sure all enum calls use the full keyPath
removing CustomStringConvertible from all enums and moving them to rawValue strings
updating Xcode to 15.4b
probably other things I've forgotten at this point
File is too long to fit in post or comments (I'm sorry it's insanely long, it's a complex model), so I'll try to fit what I can where, are there anythings that stand out that may be an issue?
Enums:
enum Gender: String, CaseIterable, Codable {
case male = "Male"
case female = "Female"
case transmale = "Trans Male"
case transfemale = "Trans Female"
case nonbinary = "Nonbinary"
case other = "Other"
}
enum BodyBuild: String, CaseIterable, Codable {
case small = "Small"
case medium = "Medium"
case large = "Large"
}
enum Status: String, Codable {
case incomplete, complete
}
enum HairColor: String, CaseIterable, Codable {
case black = "Black"
case blonde = "Blonde"
case dirtyBlonde = "Dirty Blonde"
case brown = "Brown"
case lightBrown = "Light Brown"
case darkBrown = "Dark Brown"
case red = "Red"
}
enum EyeColor: String, CaseIterable, Codable {
case blue = "Blue"
case brown = "Brown"
case black = "Black"
case green = "Green"
case hazel = "Hazel"
case gray = "Gray"
case heterochromatic = "Heterochromatic"
}
enum RaceEthnicity: String, CaseIterable, Codable {
case native = "American Indian or Alaska Native"
case asian = "Asian"
case black = "Black or African American"
case pi = "Native Hawaiian or Other Pacific Islander"
case white = "White"
case mixed = "2 or More Races/Mixed"
}
enum MarkType: String, CaseIterable, Codable {
case birthMark = "Birth Mark"
case scar = "Scar"
case tattoo = "Tattoo"
case piercing = "Piercing"
case other = "Other"
}
enum heightFormatStyle: String {
case short, long, hash
}
enum suicideRiskLevel: String, Codable {
case low = "Low"
case medium = "Medium"
case high = "High"
}
enum MedicationFrequency: String, CaseIterable, Codable {
case daily = "Daily"
case weekly = "Weekly"
case biweekly = "Bi-Weekly"
case monthly = "Monthly"
case asNeeded = "As Needed"
case temporary = "Temporary Use"
case other = "Other"
}
enum SchoolOption: String, CaseIterable, Codable {
case ged = "GED"
case communityCollege = "Community College"
case university = "4-Year College"
}
enum FamilyDiseaseTypes: String, CaseIterable, Codable {
case alcoholism = "Alcoholism"
case asthma = "Asthma"
case cancer = "Cancer"
case drugAbuse = "Drug Abuse"
case hypertension = "High Blood Pressure"
case nervousBreakdown = "Nervous Breakdown"
case sca = "Sickle Cell Anemia"
case seizures = "Convulsions, Seizures Epilepsy"
case allergies = "Allergies"
case birthDefect = "Birth Defect"
case diabetes = "Diabetes"
case heartDisease = "Heart Disease"
case migraines = "Migraine Headaches"
case obesity = "Obesity"
case tuberculosis = "Tuberculosis"
case thyroid = "Glandular/Thyroid Issues"
}
Structs:
struct IdentifyingInformation: Codable {
var hairColor: HairColor = HairColor.black
var height: Height = Height(feet: 5, inches: 8)
var race: RaceEthnicity = RaceEthnicity.native
var build: BodyBuild = BodyBuild.small
var eyeColor: EyeColor = EyeColor.blue
var hispanic: Bool = false
var distinguishingMarks: [DistinguishingMark] = []
var currentLivingSitutation: String = ""
var currentSupportServices: String = ""
struct DistinguishingMark: Codable, Identifiable {
var id: UUID = UUID()
var type: MarkType = MarkType.tattoo
var location: String = ""
var description: String = ""
var photos: [Data] = []
}
struct Height: Codable {
var feet: Int
var inches: Int
func formatted(formatStyle: HeightFormatStyle = .short) -> String {
switch formatStyle {
case HeightFormatStyle.hash:
return "\(feet)'\(inches)\""
case HeightFormatStyle.long:
return "\(feet) feet \(inches) inches"
case HeightFormatStyle.short:
return "\(feet)ft \(inches)in"
}
}
}
}
struct EmergencyNeeds: Codable {
var food: Bool = false
var shelter: Bool = false
var clothing: Bool = false
var hygeine: Bool = false
var medical: Bool = false
var mentalHealth: Bool = false
var otherNeeds: String = ""
var abuseNeglectAllegations: Bool = false
var abuseReport: AbuseReport? = nil
struct AbuseReport: Codable {
var reportAccepted: Bool = false
var whoAccepted: String = ""
var reportNumber: String = ""
}
}
struct RiskAssessment: Codable {
var selfHarm: Bool = false
var selfHarmAttempt: Bool = false
var selfHarmExplanation: String? = nil
var currentFeelings: String? = nil
var dangerToOthers: Bool = false
var dangerToOthersExplanation: String? = nil
var suicideAssessmentRequired: Bool {
return selfHarm || dangerToOthers
}
}
struct SuicideAssessment: Loopable, Codable {
var dateCompleted: Date = Date()
var familyMember: Bool = false
var treatedMentalIllness: Bool = false
var chronicIllness: Bool = false
var historyOfSelfDestructiveBehavior: Bool = false
var recentLoss: Bool = false
var isolatedAloneHopeless: Bool = false
var goodbyeNoteOrPosessionDelegation: Bool = false
var plan: Bool = false
var betterOff: Bool = false
var wishToDie: Bool = false
var manualReview: Bool = false
var comments: String = ""
var helpCallMade: Bool = false
var riskLevel: suicideRiskLevel {
let riskScore = try? allProperties().reduce(0) { result, property in
if let isTrue = property.value as? Bool, isTrue {
return result + 1
} else {
return result
}
}
switch riskScore! {
case 0 ... 3:
return .low
case 4 ... 7:
return .medium
default:
return .high
}
}
}
[Continued in comments]
Post not yet marked as solved
Hi,
I'm new to Swift development and encountering an issue that I believe may be due to an error on my part.
I have two questions regarding the following code:
import AppIntents
import SwiftUI
import SwiftData
@available(iOS 17.0, *)
struct CopiarEventoIntent: AppIntent {
@Environment(\.modelContext) private var context
@Parameter(title: "Nome do Evento")
var name: String
@Parameter(title: "Data Inicial")
var datai: Date
@Parameter(title: "Data Final")
var dataf: Date
@Parameter(title: "Tipo de Evento")
var tipo: String
@Parameter(title: "Endereço")
var endereco: String
@Parameter(title: "Lembrete")
var reminder: Bool
static var title: LocalizedStringResource = "Adicionar Eventos"
static var description = IntentDescription("Copiar Eventos e alterar datas", resultValueName: "Resultado")
@MainActor
func perform() async throws -> some IntentResult & ProvidesDialog {
let calData = CalData(title: name, datei: datai, datef: dataf, tipo: tipo, endereco: endereco,reminder: reminder)
context.insert(calData)
return .result(dialog: "Evento copiado com sucesso!")
}
}
@available(iOS 15.0, *)
struct AppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: CopiarEventoIntent(),
phrases: [
"Copiar Evento"
],
shortTitle: "Copiar Evento",
systemImageName: "square.and.arrow.down"
)
}
}
@available(iOS 15.0, *)
struct ShortcutSelectionView: View {
@Environment(\.modelContext) private var context
@Query(sort: \CalData.title) private var caldatas: [CalData]
@State private var selectedEvent: CalData?
var body: some View {
List(caldatas, id: \.self) { eventData in
Button(action: {
selectedEvent = eventData
handleEventSelection()
}) {
Text(eventData.title)
}
}
}
private func handleEventSelection() {
guard selectedEvent != nil else { return }
}
}
When I run the shortcut, gives me the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Cannot insert 'CalData' in this managed object context because it is not found in the associated managed object model.
I attempted to save this using SwiftData with the following model:
import SwiftData
@Model
class CalData {
var title : String
var datei : Date
var datef : Date
var tipo : String
var endereco : String
@Relationship(deleteRule: .cascade) var caldataval = [CalDataVal]()
var reminder: Bool = false
init(title:String, datei:Date, datef:Date, tipo:String, endereco:String, reminder:Bool){
self.title = title
self.datei = datei
self.datef = datef
self.tipo = tipo
self.endereco = endereco
self.reminder = reminder
}
}
As for the second question, how can I add a parameter that can have multiple values to save them to EventDataVal, resembling a one-to-many relationship:
import SwiftData
@Model
class CalDataVal {
var name : String
var value : Double
init(name:String, value:Double){
self.name = name
self.value = value
}
}
Thanks in advanced
Post not yet marked as solved
Hi everyone,
I'm trying to make use of a background actor in my SwiftUI project. Inserting data works with the ModelContainer's mainContext. Another context in a ModelActor, however, fails to write into the same database.
I verify the results by opening the SQLite file on the file system. While the mainContext.insert call does indeed insert a row into the table, the ModelActor's context fails to do so.
There is no error or message received in the ModelActor. The property autosaveEnabled is set to true.
I wrote a sample project to reproduce the issue in isolation. It consists of a single source file that introduces the Model ToDo, the ToDoView, initializes the ModelContainer and ModelActor. Please find the source code below.
Is there any mistake in my approach?
import SwiftUI
import SwiftData
@Model
final class ToDo {
let title: String
init(title: String) {
self.title = title
}
}
@main
struct testSwiftDataApp: App {
@State
var modelContainer: ModelContainer
@State
var backgroundData: BackgroundDataActor
init() {
let modelContainer: ModelContainer =
try! ModelContainer(for: ToDo.self)
self.modelContainer = modelContainer
self.backgroundData = BackgroundDataActor(
modelContainer: modelContainer
)
}
var body: some Scene {
WindowGroup {
ToDoView(backgroundData: self.backgroundData)
.modelContainer(modelContainer)
}
}
}
struct ToDoView: View {
@Environment(\.modelContext)
var modelContext
@Query
var todos: [ToDo]
let backgroundData: BackgroundDataActor
var modelContainer: ModelContainer {
self.modelContext.container
}
var body: some View {
VStack {
Text("Add ToDo")
TextField(
"",
text: .constant(
"URL to database: " +
"\(self.modelContainer.configurations.first!.url)"
)
)
// This action will be invoked on the ModelActor's context
Button {
Task {
let todo = ToDo(title: "Step 1")
await self.backgroundData.store(
id: todo.persistentModelID
)
}
} label: {
Text("Create ToDo in background")
}
// This action will be invoked on the mainContext
Button {
Task {
let todo = ToDo(title: "Step 2")
self.modelContainer.mainContext.insert(todo)
}
} label: {
Text("Create ToDo in foreground")
}
// Show the query results
VStack {
Text("Available ToDos")
ForEach(self.todos) {
Text($0.title)
}
}
}
}
}
@ModelActor
actor BackgroundDataActor: ModelActor {
func store(id: PersistentIdentifier) {
print("Trying to save")
print("Is auto save enabled: \(self.modelContext.autosaveEnabled)")
if let dbo = self[id, as: ToDo.self] {
self.modelContext.insert(dbo)
try! self.modelContext.save()
print("Saved into database")
}
}
}
Post not yet marked as solved
Hello,
I have been working on SwiftData since a month now and i found very weird that every time i update a data inside a SwiftData model my app became very slow.
I used the instrument if something was wrong, and i see memory increasing without releasing.
My app have a list with check and unchecked elements (screeshot below).
I just press check and unchecked 15 times and my memory start from 149mb to 361mb (screenshots below).
For the code i have this model.
final class Milestone: Identifiable {
// *********************************************************************
// MARK: - Properties
@Transient var id = UUID()
@Attribute(.unique) var uuid: UUID
var text: String
var goalId: UUID
var isFinish: Bool
var createdAt: Date
var updatedAt: Date
var goal: Goal?
init(from todoTaskResponse: TodoTaskResponse) {
self.uuid = todoTaskResponse.id
self.text = todoTaskResponse.text
self.goalId = todoTaskResponse.goalId
self.isFinish = todoTaskResponse.isFinish
self.createdAt = todoTaskResponse.createdAt
self.updatedAt = todoTaskResponse.updatedAt
}
init(uuid: UUID, text: String, goalId: UUID, isFinish: Bool, createdAt: Date, updatedAt: Date, goal: Goal? = nil) {
self.uuid = uuid
self.text = text
self.goalId = goalId
self.isFinish = isFinish
self.createdAt = createdAt
self.updatedAt = updatedAt
self.goal = goal
}
}
For the list i have
var milestonesView: some View {
ForEach(milestones) { milestone in
MilestoneRowView(task: milestone) {
milestone.isFinish.toggle()
}
.listRowBackground(Color.backgroundComponent)
}
.onDelete(perform: deleteMilestone)
}
And this is the cell
struct MilestoneRowView: View {
// *********************************************************************
// MARK: - Properties
var task: Milestone
var action: () -> Void
init(task: Milestone, action: @escaping () -> Void) {
self.task = task
self.action = action
}
var body: some View {
Button {
action()
} label: {
HStack(alignment: .center, spacing: 8) {
Image(systemName: task.isFinish ? "checkmark.circle.fill" : "circle")
.font(.title2)
.padding(3)
.contentShape(.rect)
.foregroundStyle(task.isFinish ? .gray : .primary)
.contentTransition(.symbolEffect(.replace))
Text(task.text)
.strikethrough(task.isFinish)
.foregroundStyle(task.isFinish ? .gray : .primary)
}
.foregroundStyle(Color.backgroundComponent)
}
.animation(.snappy, value: task.isFinish)
}
}
Does someone have a idea?
Thanks
The deletion is working, but it does not refresh the view. This is similar to a question I asked previously but I started a new test project to try and work this out.
@Model
class Transaction {
var timestamp: Date
var note: String
@Relationship(deleteRule: .cascade) var items: [Item]?
init(timestamp: Date, note: String, items: [Item]? = nil) {
self.timestamp = timestamp
self.note = note
self.items = items
}
func getModifierCount() -> Int {
guard let items = items else { return 0 }
return items.reduce(0, {result, item in
result + (item.modifiers?.count ?? 0)
})
}
}
@Model
class Item {
var timestamp: Date
var note: String
@Relationship(deleteRule: .nullify) var transaction: Transaction?
@Relationship(deleteRule: .noAction) var modifiers: [Modifier]?
init(timestamp: Date, note: String, transaction: Transaction? = nil, modifiers: [Modifier]? = nil) {
self.timestamp = timestamp
self.note = note
self.transaction = transaction
self.modifiers = modifiers
}
}
@Model
class Modifier {
var timestamp: Date
var value: Double
@Relationship(deleteRule: .nullify) var items: [Item]?
init(timestamp: Date, value: Double, items: [Item]? = nil) {
self.timestamp = timestamp
self.value = value
self.items = items
}
}
struct ContentView: View {
@Environment(\.modelContext) private var context
@Query private var items: [Item]
@Query private var transactions: [Transaction]
@Query private var modifiers: [Modifier]
@State private var addItem = false
@State private var addTransaction = false
var body: some View {
NavigationStack {
List {
Section(content: {
ForEach(items) { item in
LabeledText(label: item.timestamp.formatAsString(), value: .int(item.modifiers?.count ?? -1))
}
.onDelete(perform: { indexSet in
withAnimation {
for index in indexSet {
context.delete(items[index])
}
}
})
}, header: {
LabeledView(label: "Items", view: {
Button("", systemImage: "plus", action: {})
})
})
Section(content: {
ForEach(modifiers) { modifier in
LabeledText(label: modifier.timestamp.formatAsString(), value: .currency(modifier.value))
}
.onDelete(perform: { indexSet in
indexSet.forEach { index in
context.delete(modifiers[index])
}
})
}, header: {
LabeledView(label: "Modifiers", view: {
Button("", systemImage: "plus", action: {})
})
})
Section(content: {
ForEach(transactions) { transaction in
LabeledText(label: transaction.note, value: .int(transaction.getModifierCount()))
}
.onDelete(perform: { indexSet in
withAnimation {
for index in indexSet {
context.delete(transactions[index])
}
}
})
}, header: {
LabeledView(label: "Transactions", view: {
Button("", systemImage: "plus", action: {addTransaction.toggle()})
})
})
}
.navigationTitle("Testing")
.sheet(isPresented: $addTransaction, content: {
TransactionEditor()
})
}
}
}
}
Here's the scenario. Create a transaction with 1 item. That item will contain 1 modifier. ContentView will display Items, Modifiers, and Transactions. For Item, it will display the date and how many modifiers it has. Modifier will display the date and its value. Transactions will display a date and how many modifiers are contained inside of its items.
When I delete a modifier, in this case the only one that exist, I should see the count update to 0 for both the Item and the Transaction. This is not happening unless I close the application and reopen it. If I do that, it's updated to 0. I tried to add an ID variable to the view and change it to force a refresh, but it's not updating.
This issue also seems to be only with this many to many relationship. Previously, I only had the Transaction and Item models. Deleting an Item would correctly update Transaction, but that was a one to many relationship.
I would like for Modifier to have a many to many relationship with Items, so they can be reused.
Why is deleting a modifier not updating the items correctly? Why is this not refreshing the view? How can I resolve this issue?
Post not yet marked as solved
I have a TabView which consists of a few different tabs. One of which does an @Query to retrieve an array of Transaction models. These are then displayed in a list using a ForEach.
struct TransactionsTab: View {
@Query private var transactions: [Transaction]
... other code
Section(content: {
ForEach(transactions) { transaction in
transaction.getListItem()
}
}, header: {
LabeledView(label: "Recent Transactions", view: {
ListButton(mode: .link(destination: {
ListView(list: transactions)
.navigationTitle("All Transactions")
}))
})
})
Transaction contains a different model called TransactionItem and that has a variable called amount. That amount variable is used in the getListItem() function to show how much the total transaction was in the list item.
The issue is that I can delete a Transaction and the ForEach will update to reflect that. However, if I delete an TransactionItem separately, that getListItem() will not show that it's been deleted. The total amount shown will still be as if the TransactionItem was never deleted. It will only update when the app is closed and reopened. Below is the code that's ran when deleting a model, in this case a TransactionItem.
// Deletes a single item
private func delete() {
deleteWarning = false
if let item = itemToDelete {
// If last item is being delete, dismiss the view
if list.count == 1 { dismissView() }
context.delete(item)
context.saveContext()
itemToDelete = nil
}
mode = .view
}
I would think that deleting the model and having it save will cause the transaction query to update. What's going on here to cause it to not update?
By the way, saveContext() just calls the ModelContext save function.
extension ModelContext {
func saveContext() {
do {
try self.save()
} catch {
print("Could not save context: \(error.localizedDescription)")
}
}
}
I have a driving tracking app I'm working on that properly starts tracking and logging location when the app is in the foreground or background. I use a buffer/queue to keep recent locations so when a trip ramps up to driving speed I can record that to work back to the start location just before the trip starts. This works great, however, in background mode when the user does not have the app open it will record locations but not until a significant location change is detected. The buffering I do is lost and the location only starts tracking several hundred yards or more after the trip has started. Does anyone have any suggestions or strategies to handle this chicken and the egg scenario?
Post not yet marked as solved
Hello, I developed an application that uses iBeacons to create events, however when the app is in the terminated state I notice that part of my code is activated but the rest of the process is not activated ex : ranging beacon and notification. Is there a way to completely wake up my app when it is in terminated state or at least send a notification to the user to inform them that the app must be opened to put it back in background state so that the app working correctly?
Post not yet marked as solved
Looking at having editable text as the detail view within a master detail interface.
There are various examples out there that show how to build the NSViewRepresentable wrapper around NSTextView.
e.g. https://developer.apple.com/forums/thread/125920
However, when I try to embed the NSViewRepresentable within a NavigationSplitView the various examples (and my own attempt) don't work properly. There seems to be a race condition competing to update the internal bindings. I also see updateNSView being called multiple times when only one event, say selection change, is called once.
Using the SwiftUI TextEditor view actually works. (it is just so limited)
When the master selected item is changed the change isn't automatically propagated through to update the Coordinator. That's because there isn't a delegate for "master did change".
Take any of the TextViews on their own (not within a split view) and they work as you would expect.
The SwiftUI TextEditor does this without any obvious connection to the master.
So the first question is: has anybody solved this type of problem? I haven't yet found a link to the solution or a discussion on the internal trick to make this work.
In principle the view structure is:
NavigationSplitView {
// master list
List(selection : $selectedItem {
}
}
content: {
TextEditor(text: $selectedItem.text) // works
// or
CustomTextView(text: $selectedItem.text)
// all examples I've found so far fail to properly display changes to the text when edited
}
My current thought is that the internal Coordinator needs to know that there has been a selection change so you can synchronise the significant change in the contents of the text binding. Again TextEditor doesn't need to know that. makeNSView is only called once, so there isn't any regeneration of the NSTextView that you could rely on.
Have tried on both macOS and iOS and see the same results.
Post not yet marked as solved
This possibly seems like a regression but from iOS 17.1.0+, I'm having issues from Xcode 15.2 Beta &where when using a transformable property I'm getting a crash when trying to create a model container. This worked fine for me in Xcode 15.1 Beta when testing on iOS OS 17.0.1 and below.
I have a simple model where I'm trying to save a UIColor, below is an example of this model.
class Category: Codable {
@Attribute(.unique)
var title: String
var items: [Item]?
@Attribute(.transformable(by: ColorValueTransformer.self))
var color: UIColor?
init(title: String = "",
color: UIColor) {
self.title = title
self.color = color
}
enum CodingKeys: String, CodingKey {
case title
}
required init(from decoder: Decoder) throws { ... }
func encode(to encoder: Encoder) throws { ... }
}
Within my value transformer, I'm handling setting and getting the value.
final class ColorValueTransformer: ValueTransformer {
static let name = NSValueTransformerName(rawValue: String(describing: ColorValueTransformer.self))
override func transformedValue(_ value: Any?) -> Any? {
guard let color = value as? UIColor else { return nil }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: true)
return data
} catch {
return nil
}
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? Data else { return nil }
do {
let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data)
return color
} catch {
return nil
}
}
public static func register() {
let transformer = ColorValueTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
Then within my root app entry point, I register this transformer.
@main
struct ToDosApp: App {
......
init() {
ColorValueTransformer.register()
}
......
Unfortunately in my custom container object I get a crash on this line
let container = try ModelContainer(for: ....)
With an error of Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
Like i said before previously this was working fine but now it's not... I have a feedback open also FB13471979 but it would be great if someone on the SwiftData team at Apple could look into this issue since it's a pretty big regression...
Post not yet marked as solved
I've been coding for a while after creating the project, but I can't remember clearly if I selected SwiftData initially.
Post not yet marked as solved
Many of Apple's tutorials on SwiftData are written alongside SwiftUI, including the sample code.
Would it be a bad idea to use SwiftData separately? If I don't use SwiftUI, would it be wiser to choose a different database instead?
Post not yet marked as solved
I'm developing an application using SwiftUI and SwiftData. The app includes a pricing section, and I'd like it to have an initial default value for pricing. Additionally, when updating the app on the App Store, I also want to update the prices. However, I'd like users to have the option to create their own prices if they don't want to use the ones I suggest. I want to save these prices obtained from the user because I'll be using these values for some operations later on.
I'm wondering how to achieve this. Should I use SwiftData or UserDefaults for managing the data, or should I save the prices to a JSON file? If I opt for saving to a JSON file, would it be possible to update the JSON file when updating the app? Please feel free to ask any questions. Thank you in advance for your responses.
Post not yet marked as solved
Dear all,
I'm quite new to SwiftUI.
I have a view where I'd like to open a sheet to edit data in a List. Here below the code of the view:
import SwiftUI
import SwiftData
struct SettingsView: View {
@Query(sort: \Stagione.idStagione) private var usoStagione: [Stagione]
@Environment(\.modelContext) private var context
@State private var stagione = String(Calendar.current.component(.year, from: Date()))
@State private var miaSquadra = ""
@State private var categoria = ""
@State private var selStagione = Set<Stagione>()
@State private var modificaStagione = false
var body: some View {
NavigationStack {
GroupBox("Stagione") {
Form {
TextField("Stagione:", text: $stagione)
.frame(width: 150)
TextField("Categoria:", text: $categoria)
.frame(width: 400)
TextField("Mia squadra:", text: $miaSquadra)
.frame(width: 400)
Button("Salva") {
let nuovaStagione = Stagione(idStagione: stagione, categoriaStagione: categoria, miaSquadra: miaSquadra)
context.insert(nuovaStagione)
miaSquadra = ""
categoria = ""
stagione = String(Calendar.current.component(.year, from: Date()))
}
.frame(maxWidth: .infinity, alignment: .trailing)
.buttonStyle(.borderedProminent)
.disabled(categoria.isEmpty || miaSquadra.isEmpty)
}
List(selection: $selStagione) {
ForEach(usoStagione, id: \.self) { stag in
VStack(alignment: .leading) {
Text("\(stag.idStagione)").font(.title2)
Text("\(stag.miaSquadra)").foregroundStyle(.secondary)
Text("\(stag.categoriaStagione)").foregroundStyle(.secondary)
}
}
.contextMenu() {
Button(action: {
selStagione.forEach(context.delete)
}) {
Text("Cancella")
}
Button(action: {
self.modificaStagione = true
}) {
Text("Modifica")
}
}
}
.sheet(isPresented: $modificaStagione) {
ModificaStagione(stagione: Stagione)
}
.listStyle(.plain)
.textFieldStyle(.roundedBorder)
.padding()
}
}
.padding()
.textFieldStyle(.roundedBorder)
.navigationTitle("Impostazioni")
GroupBox("Squadre") {
}
}
}
#Preview {
SettingsView()
.environmentObject(NavigationStateManager())
.modelContainer(for: Stagione.self, inMemory: true)
}
While here below the code of the view ModificaStagione:
import SwiftUI
struct ModificaStagione: View {
@Environment(\.dismiss) private var dismiss
let stagione: Stagione
@State private var idStagione = ""
@State private var categoria = ""
@State private var miaSquadra = ""
@State private var vistaPrecedente = true
var body: some View {
VStack (alignment: .leading) {
GroupBox {
LabeledContent {
TextField("", text: $categoria)
.frame(width: 400)
} label: {
Text("Categoria:")
}
LabeledContent {
TextField("", text: $miaSquadra)
.frame(width: 400)
} label: {
Text("Mia squadra:")
}
if modifica {
Button("Aggiorna dati") {
stagione.idStagione = idStagione
stagione.categoriaStagione = categoria
stagione.miaSquadra = miaSquadra
dismiss()
}
.buttonStyle(.borderedProminent)
}
}
.padding()
.textFieldStyle(.roundedBorder)
.navigationTitle("Modifica dati stagione")
}
.onAppear {
idStagione = stagione.idStagione
categoria = stagione.categoriaStagione
miaSquadra = stagione.miaSquadra
}
}
var modifica: Bool {
categoria != stagione.categoriaStagione
|| miaSquadra != stagione.miaSquadra
}
}
In the "Setting" view I receive an error message when I call the view "Modifica Stagione "saying that "Cannot convert value of type 'Stagione.Type' to expected argument type 'Stagione'".
What am I doing wrong?
Thanks in advance,
A.
Post not yet marked as solved
In Xcode 15.0.1, I created a new project to start working with SwiftData. I did this by creating a default App project and checking the Use SwiftData checkbox. The resulting project contains just three files: an app entry point file, a ContentView SwiftUI view file, and an Item model file.
The only change I made was to annotate the default Item timestamp property with a .transformable attribute.
Here is the resulting model:
@Model
final class Item {
@Attribute(.transformable(by: TestVT.self)) var timestamp: Date // Only updated this line
init(timestamp: Date) {
self.timestamp = timestamp
}
}
And here is the definition of TestVT. It is a basic ValueTransformer that simply tries to store the Date as a NSNumber:
// Added this
class TestVT: ValueTransformer {
static let name = NSValueTransformerName("TestVT")
override class func transformedValueClass() -> AnyClass {
NSNumber.self
}
override class func allowsReverseTransformation() -> Bool {
true
}
override func transformedValue(_ value: Any?) -> Any? {
guard let date = value as? Date else {
return nil
}
let ti = date.timeIntervalSince1970
return NSNumber(value: ti)
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let num = value as? NSNumber else {
return nil
}
let ti = num.doubleValue as TimeInterval
return Date(timeIntervalSince1970: ti)
}
}
And finally, I made sure to register my ValueTransformer but updating the sharedModelContainer definition in the App:
var sharedModelContainer: ModelContainer = {
ValueTransformer.setValueTransformer(TestVT(), forName: TestVT.name) // Only added this line
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
Prior to Xcode 15.1, this was working fine. However, now when I try to create an item when running the app I get the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "timestamp"; desired type = NSNumber; given type = __NSTaggedDate; value = 2023-12-14 01:47:11 +0000.'
I'm unsure of why this stopped working. The error seems to be complaining about the input being of type Date when NSNumber was expected, but I thought that's what the ValueTransformer was supposed to be doing.
Important note: prior to Xcode 15.1, I did not originally override the transformedValueClass() and everything was working but in the new Xcode when launching the app I was getting a Thread 1: EXC_BAD_ACCESS (code=1, address=0x0) on the return try ModelContainer(...) line. Removing the .transformable property from my model fixed the issue. That's why I added the override here, because I think the docs indicate overriding it as well and I missed that the first time. This being said, I think the code I have is what a correct ValueTransformer would look like.
If anyone has experienced this issue, or has a working ValueTransformer for SwiftData in Xcode 15.1, please let me know. Appreciate any help with this issue. Thanks so much!
Post not yet marked as solved
iOS17 using swiftData & CloudKit.
compiles correctly, then throws error "App installation failed: Unable To Install "
Please try again later. Failed to load Info.plist from bundle at path
...
/.../simulatorlocation/.app/Frameworks/SwiftData.framework/Info.plist: No such file or directory.
same error occurs on physical device 15Pro running iOS17.5
how do I fix this?
here is the error:
Please try again later. Failed to load Info.plist from bundle at path /Users//Library/Developer/CoreSimulator/Devices/996C811C-EE2D-47AE-881A-D1D2FA830BCC/data/Library/Caches/com.apple.mobile.installd.staging/temp.kkR2I8/extracted/Payload/.app/Frameworks/SwiftData.framework; Extra info about "/Users//Library/Developer/CoreSimulator/Devices/996C811C-EE2D-47AE-881A-D1D2FA830BCC/data/Library/Caches/com.apple.mobile.installd.staging/temp.kkR2I8/extracted/Payload/.app/Frameworks/SwiftData.framework/Info.plist": Couldn't stat /Users//Library/Developer/CoreSimulator/Devices/996C811C-EE2D-47AE-881A-D1D2FA830BCC/data/Library/Caches/com.apple.mobile.installd.staging/temp.kkR2I8/extracted/Payload/.app/Frameworks/SwiftData.framework/Info.plist:
No such file or directory
Post not yet marked as solved
Hello. See the code below.
struct ContentView: View {
var body: some View {
TabView {
VehicleTab()
.tabItem({ Label("Vehicles", systemImage: "car.fill")})
.modelContainer(for: Vehicle.self)
TransactionsTab()
.tabItem { Label("Transactions", systemImage: "dollarsign") }
.modelContainer(for: Transaction.self)
}
}
}
Using the .modelContainer() in this way seems to be causing some issue. I was under the assumption that this would just create a container for each view. I get the error below in this configuration. If I comment out either one of the .modelContainer() modifiers, it works fine.
Query encountered an error: Error Domain=NSCocoaErrorDomain Code=256 "The file “default.store” couldn’t be opened."
Are you not able to do what I'm doing? Is there a way to have two separate containers?
Post not yet marked as solved
Hi, just trying to learn how to work with mainActor. I am in a need of analyzing users data with API service one a background. Whenever user saves a post into SwiftData, I need to analyze that posts asynchronously. Here is my current code, which by the way works, but I am getting warning here;
actor DatabaseInteractor {
let networkInteractor: any NetworkInteractor = NetworkInteractorImpl()
func loadUserProfile() async -> String {
do {
let objects = try await modelContainer.mainContext.fetch(FetchDescriptor<ProfileSwiftData>())
if let profileTest = objects.first?.profile {
return profileTest
}
} catch {
}
return ""
}
I get a warning on let objects line.
Warning: Non-sendable type 'ModelContext' in implicitly asynchronous access to main actor-isolated property 'mainContext' cannot cross actor boundary
I used Query(filter:sortBy:) and show them in the ListView.
When I change the property of the model, because I fetched filtered and sorted array, the model immediately reflect the change and find its position.
What I want is at initializing it fetches filtered and sorted array. And when I change the property nothing happens. But when I tap on refresh button, then the array should change.
For example in ToDoList app, the array consists of not done works. when I tap done, it should stay until I refresh.
Is it possible?