UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Deleting Core Data Crash

Forums > SwiftUI

I've changed my app from a simple list view of Core Data to a LazyVGrid. When I did that, I lost the ability to easily delete an entry in the list, so I moved the delete function to the details view. My hope was to provide both Edit and Delete functions from the detail view; however, when I delete an entry, it now crashed the app, before processing the self.presentationMode.wrappedValue.dismiss() . Can any one take a look at this code and let me know how to handle this correctly?

--- LazyVGrid ----

import SwiftUI
import CoreData
import MapKit

enum NavBarItemChoosen: Identifiable {
    case newCard 
    var id: Int {
        hashValue
    }
}

struct ViewEventsView: View {
    @Environment(\.managedObjectContext) var moc
    @Environment(\.presentationMode) var presentationMode
    @FetchRequest private var events: FetchedResults<Event>

    private var blankCardFront = UIImage(contentsOfFile: "frontImage")
    private var recipient: Recipient

    @State var newEvent = false
    @State var frontView = false
    @State var backView = false
    @State var frontShown = true
    @State private var frontImageShown: UIImage?

    @State var navBarItemChoosen: NavBarItemChoosen?
    private var gridLayout: [GridItem]
    @State var isEditing = false

    @State var region: MKCoordinateRegion?

    static let eventDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        return formatter
    }()

    init(recipient: Recipient) {
        request.predicate =  NSPredicate(format: "%K == %@", #keyPath(Event.recipient), recipient)
        _events = FetchRequest<Event>(fetchRequest: request)
        if UIDevice.current.userInterfaceIdiom != .phone {
            self.gridLayout = [GridItem(.flexible()),
                               GridItem(.flexible()),
                               GridItem(.flexible())]
        } else {
            self.gridLayout = [GridItem(.flexible()), GridItem(.flexible())]
        }
    }

    var body: some View {
        GeometryReader { geo in
            VStack {
                HStack {
                    if let region = region {
                        MapView(region: region)
                            .frame(width: geo.size.width * 0.3, height: geo.size.height * 0.2)
                            .padding([.leading, .trailing], 10 )
                        AddressView(recipient: recipient)
                    }
                    Spacer()
                        .onAppear {
                            // swiftlint:disable:next line_length
                            let addressString = String("\(recipient.addressLine1 ?? "") \(recipient.city ?? "") \(recipient.state ?? "") \(recipient.zip ?? "") \(recipient.country ?? "")")
                            getLocation(from: addressString) { coordinates in
                                if let coordinates = coordinates {
                                    // swiftlint:disable:next line_length
                                    self.region = MKCoordinateRegion(center: coordinates, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005))
                                }
                            }
                        }
                }
                ScrollView {
                    LazyVGrid(columns: gridLayout, alignment: .center, spacing: 1) {
                        ForEach(events, id: \.self) { event in
                            NavigationLink(destination: ViewAnEventView(event: event, recipient: recipient)) {
                                HStack {
                                    VStack {
                                        ZStack {
                                            Spacer()
                                            Image(uiImage: (event.cardFrontImage ?? blankCardFront)!)
                                                .resizable()
                                                .aspectRatio(contentMode: .fit)
                                                .ignoresSafeArea(edges: [.vertical, .bottom])
                                            HStack {
                                                VStack {
                                                    Spacer()
                                                    Text("\(event.event ?? "")")
                                                    // swiftlint:disable:next line_length
                                                    Text("\(event.eventDate ?? NSDate(), formatter: ViewEventsView.eventDateFormatter)")
                                                    Spacer()
                                                }
                                                .padding(10)
                                                .font(.title)
                                                .foregroundColor(.white)
                                                .shadow(color: .black, radius: 2.0)
                                            }
                                        }
                                    }
                                }
                                .frame(height: geo.size.width * 0.3)
                                .contextMenu {
                                    Button {
                                        print("Edit pressed for \(event) and \(recipient)")
                                        _ = ViewAnEventView(event: event, recipient: recipient)
                                    } label: {
                                        Label("Edit", systemImage: "square.and.pencil")
                                            .foregroundColor(.green)
                                    }
                                }
                            }
                        }
                    }
                }
                .navigationTitle("\(recipient.firstName ?? "no first name") \(recipient.lastName ?? "no last name")")
                .navigationBarItems(trailing:
                                        HStack {
                    Button(action: {
                        navBarItemChoosen = .newCard
                    }, label: {
                        Image(systemName: "plus.circle.fill")
                            .foregroundColor(.green)
                    })
                })
            }
            .sheet(item: $navBarItemChoosen ) { item in
                switch item {
                case .newCard:
                    AddNewCardView(recipient: recipient)
                }
            }
        }
    }

    func getLocation(from address: String, completion: @escaping (_ location: CLLocationCoordinate2D?) -> Void) {
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(address) { (placemarks, _) in
            guard let placemarks = placemarks,
                  let location = placemarks.first?.location?.coordinate else {
                      completion(nil)
                      return
                  }
            completion(location)
        }
    }
}

struct ViewEventsView_Previews: PreviewProvider {
    static var previews: some View {
        ViewEventsView(recipient: Recipient())
    }
}

--- Details View ---


import SwiftUI

struct ViewAnEventView: View {
    enum NavBarItemChoosen: Identifiable {
        case editEvent
        var id: Int {
            hashValue
        }
    }

    @Environment(\.presentationMode) var presentationMode
    @Environment(\.managedObjectContext) var moc
    @State private var zoomed = false

    private var event: Event
    private var recipient: Recipient

    private var blankCardFront = UIImage(contentsOfFile: "frontImage")

    @State var navBarItemChoosen: NavBarItemChoosen?

    enum ShowCardView: Identifiable {
        case front, edit
        var id: Int {
            hashValue
        }
    }
    @State var showCardView: ShowCardView?

    static let eventDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        return formatter
    }()

    init(event: Event, recipient: Recipient) {
        let navBarApperance = UINavigationBarAppearance()
        navBarApperance.largeTitleTextAttributes = [
            .foregroundColor: UIColor.systemGreen,
            .font: UIFont(name: "ArialRoundedMTBold", size: 35)!
        ]
        navBarApperance.titleTextAttributes = [
            .foregroundColor: UIColor.systemGreen,
            .font: UIFont(name: "ArialRoundedMTBold", size: 15)!
        ]

        UINavigationBar.appearance().standardAppearance = navBarApperance
        UINavigationBar.appearance().scrollEdgeAppearance = navBarApperance
        UINavigationBar.appearance().compactAppearance = navBarApperance
        self.recipient = recipient
        self.event = event
    }

    var body: some View {
        VStack {
            HStack {
                Text("\(event.event ?? "no event") - \(event.eventDate!, formatter: Self.eventDateFormatter)") // <---- CRASH
                    .font(.title)
                    .foregroundColor(.green)
                Spacer()
                Button(action: {
                    showCardView = .edit
                }, label: {
                    Image(systemName: "square.and.pencil")
                        .font(.title)
                        .foregroundColor(.green)
                })
                Button {
                    self.presentationMode.wrappedValue.dismiss()
                    deleteCard(event: event)
                } label: {
                    Image(systemName: "trash")
                        .font(.title)
                        .foregroundColor(.red)
                }
            }
            .padding([.leading, .trailing], 10 )
            Image(uiImage: (event.cardFrontImage ?? blankCardFront)!)  // <-- Second crash if moved
            .resizable()
            .aspectRatio(contentMode: zoomed ? .fill : .fit)
            Spacer()
                .sheet(item: $showCardView) { item in
                    switch item {
                    case .front:
                        // This is no longer used...
                        // swiftlint:disable:next line_length
                        CardView(cardImage: (event.cardFrontImage ?? blankCardFront)!, event: event.event ?? "", eventDate: event.eventDate! as Date)
                    case .edit:
                        EditAnEvent(event: event, recipient: recipient)
                    }
                }
        }
    }

    func deleteCard(event: Event) {
        print("Delete the card \(event)")
        moc.delete(event)
        do {
            try moc.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}

struct ViewAnEventView_Previews: PreviewProvider {
    static var previews: some View {
        ViewAnEventView(event: Event(), recipient: Recipient())
    }
}

The crash happens at the line indicated above with the following message - /ViewAnEventView.swift:65: Fatal error: Unexpectedly found nil while unwrapping an Optional value.

I have tried to change the unwrapping to a default value, but it just moved the error to the Image line (which also crashes).

2      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.