SwiftUI und SwiftData

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • SwiftUI und SwiftData

    Guten Abend,
    da das Ganze etwas länger geraten ist bitte ich um Verzeihung, aber ich versuche es möglichst verständlich auszudrücken und das kann dann schon etwas umfangreicher werden.

    ich hoffe mir kann jemand erklären, was ich hier falsch mache, oder was an meinem Ansatz falsch ist.

    Da ich nun doch schon seit einigen Wochen mit SwiftI eine kleine APP fürs iPhone programmiere und ich mich bis jetzt immer erfolgreich verbessert habe (hoffe ich zumindestens), und eigentlich bis jetzt nie aufgegeben habe und nach längerem Suchen immer eine Lösung gefunden habe, bin ich eigentlich mit SwiftUI sehr zufrieden. Bis jetzt. Nun habe ich begonnen, die Daten die ich in der App eingebe, per SwiftData zu speichern. Alles funktioniert eigentlich, wenn da nicht das Thema Datenüberprüfung bei der Eingabe von Daten wäre.

    Es ist halt in SwiftUI nicht so einfach möglich Daten während der Eingabe zu überprüfen. Das View Konzept von SwiftUI macht das Ganze nicht einfach. Als Lösung habe ich in der View in der die Daten eingegeben werden einen Button mit einer Funktion eingebaut. Soweit so gut. Nur, da man ja in einer Funktion kein Quer'y Macro verwenden kann, habe ich versucht das per FetchReques zu lösen. Dabei will ich nur feststellen ob es einen Eintrag (in dem Fall Usernamen) bereits in der DB gibt. Die DB habe per SQLITE Kommandos bereits überprüft sieht alles gut aus und wenn ich den Usernamen per SQLITE suche finde ich den auch, aber der FecthRequest gibt mir als Ergebnis, dass der Userrname noch nicht existieren würde.

    Ist mein Ansatz das per Fetch Request zu machen nicht der richtige, gibt es andere Lösungsmöglichkeiten, ich kann doch nicht nachdem ich in einer View einen Namen eingegeben habe wieder eine neue View aufrufen Inder die Prüfung erfolgt, wie habt ihr solche Probleme gelöst?

    Ich würde mich über jede Antwort freuen die mit irgendwie weiterhilft.

    hier meine Beispiel:
    Das ist die View mit den Eingaben:

    [
    Achso, auch vergessen, vielleicht besser doch mal die Datendefinition, wie gesagt diese funktioniert eigentlich


    @Model
    class fcDB03: Codable, Identifiable {
    var fc_UR_UID: UUID
    var fc_UR_IniDate: Date
    var fc_UR_ModDate: Date
    var fc_UR_UseDate: Date
    var fc_UR_UFName: String // Vorname
    var fc_UR_USName: String // Nachname
    var fc_UR_UseName: String // Username
    var fc_UR_EMail: String
    var fc_UR_PWord: String
    var fc_UR_DBNR: String
    var fullName: String {
    "\(fc_UR_UFName) \(fc_UR_USName)"
    }
    enum CodingKeys: CodingKey {
    case fc_UR_UID, fc_UR_IniDate,fc_UR_ModDate, fc_UR_UseDate, fc_UR_UFName, fc_UR_USName, fc_UR_UseName, fc_UR_EMail, fc_UR_PWord, fc_UR_DBNR
    }
    init(fc_UR_UID: UUID = UUID(), fc_UR_IniDate: Date = Date(), fc_UR_ModDate: Date = Date(), fc_UR_UseDate: Date = Date(), fc_UR_UFName: String = " ", fc_UR_USName: String = " ", fc_UR_UseName: String = " ", fc_UR_EMail: String = " ", fc_UR_PWord: String = " ", fc_UR_DBNR: String = " ")
    {
    self.fc_UR_UID = fc_UR_UID
    self.fc_UR_IniDate = fc_UR_IniDate
    self.fc_UR_ModDate = fc_UR_ModDate
    self.fc_UR_UseDate = fc_UR_UseDate
    self.fc_UR_UFName = fc_UR_UFName
    self.fc_UR_USName = fc_UR_USName
    self.fc_UR_UseName = fc_UR_UseName
    self.fc_UR_EMail = fc_UR_EMail
    self.fc_UR_PWord = fc_UR_PWord
    self.fc_UR_DBNR = fc_UR_DBNR
    }
    // Erforderlich für Decodable (Import)
    required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.fc_UR_UID = try container.decode(UUID.self, forKey: .fc_UR_UID )
    self.fc_UR_IniDate = try container.decode(Date.self, forKey: .fc_UR_IniDate )
    self.fc_UR_ModDate = try container.decode(Date.self, forKey: .fc_UR_ModDate )
    self.fc_UR_UseDate = try container.decode(Date.self, forKey: . fc_UR_UseDate)
    self.fc_UR_UFName = try container.decode(String.self, forKey: .fc_UR_UFName )
    self.fc_UR_USName = try container.decode(String.self, forKey: .fc_UR_USName )
    self.fc_UR_UseName = try container.decode(String.self, forKey: .fc_UR_UseName )
    self.fc_UR_EMail = try container.decode(String.self, forKey: .fc_UR_EMail )
    self.fc_UR_PWord = try container.decode(String.self, forKey: .fc_UR_PWord )
    self.fc_UR_DBNR = try container.decode(String.self, forKey: .fc_UR_DBNR)
    }
    // erforderlich für Encodable (Export)
    func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(fc_UR_UID, forKey: .fc_UR_UID)
    try container.encode(fc_UR_IniDate, forKey: .fc_UR_IniDate)
    try container.encode(fc_UR_ModDate, forKey: .fc_UR_ModDate)
    try container.encode(fc_UR_UseDate, forKey: .fc_UR_UseDate)
    try container.encode(fc_UR_UFName, forKey: .fc_UR_UFName)
    try container.encode(fc_UR_USName, forKey: .fc_UR_USName)
    try container.encode(fc_UR_UseName, forKey: .fc_UR_UseName)
    try container.encode(fc_UR_EMail, forKey: .fc_UR_EMail)
    try container.encode(fc_UR_PWord, forKey: .fc_UR_PWord)
    try container.encode(fc_UR_DBNR, forKey: .fc_UR_DBNR)
    }
    struct Settings: Codable {
    var wishlist, collection, findable: Bool// <-- here wishlist, not s
    }
    }


    Quellcode

    1. Form {
    2. Section {
    3. TextField("Vorname: ", text: $vName)
    4. TextField("Nachname: ", text: $nName)
    5. TextField("Username: ", text: $uName)
    6. .alert(isPresented: $ohneFail){
    7. return Alert(title: Text("die Eingaben sind falsch"))
    8. }
    9. Section {
    10. Button("Speichern") {
    11. ohneFail = plausi(ID: eUUID, Vorname: vName, Nachname: nName,Username: uName )
    12. if ohneFail {
    13. let newTT = fcDB03(fc_UR_UID: eUUID , fc_UR_IniDate: Date(), fc_UR_ModDate: Date(), fc_UR_UseDate: Date(), fc_UR_UFName: vName, fc_UR_USName: nName, fc_UR_UseName: uName, fc_UR_DBNR: "43")
    14. modCon.insert(newTT)
    15. dismiss()
    16. }
    17. self.ohneFail.toggle()
    18. }
    19. }
    20. }
    21. }
    22. }
    23. }
    24. das ist die Plausiprüftng:
    25. // Eingaben auf Plausibilität überprüfen
    26. func plausi(ID: UUID, Vorname: String, Nachname: String, Username: String) -> Bool {
    27. @Environment(\.modelContext) var modCon2
    28. if Vorname.isEmpty Nachname.isEmpty Username.isEmpty{
    29. return false }
    30. else {
    31. // Beispiel: Überprüfen, ob ein Name schon existiert
    32. let fetchDescriptor = FetchDescriptor<fcDB03>(predicate: #Predicate { $0.fc_UR_UseName == Username})
    33. let existingUsers = try? modCon2.fetch(fetchDescriptor)
    34. if existingUsers?.isEmpty == true {
    35. return true
    36. } else {
    37. print("Benutzer existiert bereits")
    38. return false
    39. }
    40. }
    41. }
    Alles anzeigen







    Viele Grüße und ich hoffe, ich habe mich verständlich ausgedrückt

    ein verzweifelter Anfänger, bitte habt etwas Geduld mit mir, wenn ich etwas vergessen haben sollte werde Ich das dann auf jeden Fall nachreichen. Ich bin auch für Vorschläge
    mit anderen Lösungsansätzen offen und dankbar. Vielleicht ist es mit CoreData möglich?

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von Level0 ()

  • Ich kann Dir inhaltlich leider nicht helfen, aber könntest Du bitte Deine Code-Fragmente mit der entsprechenden Formatierung versehen (Schaltfläche in der Toolbar des Forums-Editors)? Das macht sie deutlich lesbarer - vielleicht kommt dann auch jemand mit inhaltlichen Tipps.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Bei SwiftUI sollte der View stärker vom Model getrennt werden, also möglichst wenig Code innerhalb der View Deklaration und mehr Code im Model zu dem View. Daher dann auch besser anstelle einzelner Variablen im View eine Klasse als Model für den View verwenden.

    Dies könnte dann z.B. so aussehen:

    C-Quellcode

    1. class NewUser: ObservableObject {
    2. var username: String = ""
    3. var firstname: String = ""
    4. var lastname: String = ""
    5. @Published var errorText: String? = nil
    6. func save() {
    7. // Überprüfen, ob ein Name schon existiert ...
    8. errorText = "Die Eingaben sind falsch"
    9. }
    10. }
    11. struct NewUserView: View {
    12. @StateObject var user = NewUser()
    13. var body: some View {
    14. Form {
    15. Section {
    16. TextField("Vorname: ", text: $user.firstname)
    17. TextField("Nachname: ", text: $user.lastname)
    18. TextField("Username: ", text: $user.username)
    19. }
    20. Section {
    21. Button("Speichern") {
    22. user.save()
    23. }
    24. .alert("Speichern", isPresented: .constant(user.errorText != nil), actions: {
    25. // Leave empty to use the default "OK" action.
    26. }, message: {
    27. Text(user.errorText ?? "")
    28. })
    29. }
    30. }
    31. }
    32. }
    Alles anzeigen
  • Hej Level0,

    mal so als Anmerkung, aber mit `var fc_UR_USName: String // Nachname` tust Du dir keinen Gefallen. Nenn die Variablen einfach `lastname` und gut ist.

    Wenn Du etwas mit SQLite vor hast, dann behaupte ich einfach mal pauschal, dass Du etwas falsch machst. Das geht auch mit SwiftData einfach so. Frag einfach eine aktuelle LLM, um Beispiele zu bekommen, wie die Abfrage aussehen kann.

    Ansonsten bin ich bei MCDan Lösung dabei, wobei ich die validations-Methode oder save-Methode in den NewUserView implementieren würde und in dieser Validierungs-Methode gleich die entsprechende Fehlermeldung bzw. Fehlervariablen setzen. Kann man halt dann nicht teilen.


    Und von irgendwo habe ich folgende Bespiele, aber keine Ahnung wie gut die sind bzw. ob es noch Seiteneffekte gibt, wenn man das so macht. Aber im Allgemein hat das bei mir funktioniert.

    String Binding Max Length

    Quellcode

    1. import SwiftUI
    2. extension Binding where Value == String {
    3. /// Maximal length of the string
    4. func max(_ limit: Int) -> Self {
    5. // drop last characters if it is too long
    6. let count = self.wrappedValue.count - limit
    7. if count > 0 {
    8. DispatchQueue.main.async {
    9. self.wrappedValue = String(self.wrappedValue.dropLast(count))
    10. }
    11. }
    12. return self
    13. }
    14. }
    Alles anzeigen
    Limit to min, max

    Quellcode

    1. extension Binding where Value == Int {
    2. /// limit to numbers between min, max
    3. func limit(min: Int, max: Int) -> Self {
    4. if self.wrappedValue < min {
    5. self.wrappedValue = min
    6. } else if self.wrappedValue > max {
    7. self.wrappedValue = max
    8. }
    9. return self
    10. }
    11. }
    Alles anzeigen
    Beispiel zur Anwendung

    Quellcode

    1. Form {
    2. // FIXME: limit address to only valid characters
    3. TextField("Host:", text: $host.max(100))
    4. TextField("Port:", value: $port.limit(min: 0, max: 65536), format: .number)