Initializers with different stored properties - swift

I have the following view where I pass a binding to an item that I need to be selected.
struct SelectionListView<Data>: View where Data: RandomAccessCollection, Data.Element: Identifiable, Data.Element: Named {
private let data: Data
#Binding private var isPresented: Bool
#Binding private var selectedElement: Data.Element
init(
data: Data,
selectedElement: Binding<Data.Element>,
isPresented: Binding<Bool>
) {
self.data = data
_selectedElement = selectedElement
_isPresented = isPresented
}
var body: some View {
VStack {
ForEach(data) { element in
Button(element.name) {
selectedElement = element
isPresented.toggle()
}
.foregroundColor(
selectedElement.id == item.id
? .black
: .white
)
}
}
}
}
I would need a slightly different initializer of this view where I can only pass the element ID, instead of the whole element. I'm having trouble achieving this solution. To make it even more clear, it would be great if I could have a second initializer such that:
init(
data: Data,
selectedId: Binding<Data.Element.ID>,
isPresented: Binding<Bool>
)

Here is a working version. I decided to store the element or id in their own enum cases. I made the view separate just so it is a little easier to understand what I did.
Working code:
struct SelectionListView<Data>: View where Data: RandomAccessCollection, Data.Element: Identifiable, Data.Element: Named {
enum Selected {
case element(Binding<Data.Element>)
case id(Binding<Data.Element.ID>)
}
#Binding private var isPresented: Bool
private let data: Data
private let selected: Selected
init(
data: Data,
selectedElement: Binding<Data.Element>,
isPresented: Binding<Bool>
) {
self.data = data
selected = .element(selectedElement)
_isPresented = isPresented
}
init(
data: Data,
selectedId: Binding<Data.Element.ID>,
isPresented: Binding<Bool>
) {
self.data = data
selected = .id(selectedId)
_isPresented = isPresented
}
var body: some View {
SelectionListItem(data: data) { dataElement in
switch selected {
case .element(let element):
element.wrappedValue = dataElement
print("Selected element:", element.wrappedValue)
case .id(let id):
id.wrappedValue = dataElement.id
print("Selected element ID:", id.wrappedValue)
}
isPresented.toggle()
}
}
}
struct SelectionListItem<Data>: View where Data: RandomAccessCollection, Data.Element: Identifiable, Data.Element: Named {
let data: Data
let action: (Data.Element) -> Void
var body: some View {
VStack {
ForEach(data) { element in
Button(element.name) {
action(element)
}
.foregroundColor(
.red // Temporary because I don't know what `item.id` is
// selectedElement.id == item.id
// ? .black
// : .white
)
}
}
}
}
Other code for minimal working example:
struct ContentView: View {
#State private var selection: StrItem
#State private var selectionId: StrItem.ID
#State private var isPresented = true
private let data: [StrItem]
init() {
data = [StrItem("Hello"), StrItem("world!")]
_selection = State(initialValue: data.first!)
_selectionId = State(initialValue: data.first!.id)
}
var body: some View {
// Comment these to try each initializer
//SelectionListView(data: data, selectedElement: $selection, isPresented: $isPresented)
SelectionListView(data: data, selectedId: $selectionId, isPresented: $isPresented)
}
}
protocol Named {
var name: String { get }
}
struct StrItem: Identifiable, Named {
let id = UUID()
let str: String
var name: String { id.uuidString }
init(_ str: String) {
self.str = str
}
}

I'm not really sure what you are trying to achieve. Something feels off :) But anyway, here's a variant of your code that would do what you want:
struct SelectionListView<Data>: View where Data: RandomAccessCollection, Data.Element: Identifiable, Data.Element: Named {
private let data: Data
#Binding private var isPresented: Bool
#Binding private var selectedElement: Data.Element
#Binding private var selectedId: Data.Element.ID
init(
data: Data,
selectedElement: Binding<Data.Element>,
isPresented: Binding<Bool>
) {
self.data = data
_selectedElement = selectedElement
_selectedId = .constant(selectedElement.wrappedValue.id)
_isPresented = isPresented
}
init(
data: Data,
selectedId: Binding<Data.Element.ID>,
isPresented: Binding<Bool>
) {
self.data = data
_selectedElement = .constant(data.first(where: { $0.id == selectedId.wrappedValue })!)
_selectedId = selectedId
_isPresented = isPresented
}
var body: some View {
VStack {
ForEach(data) { element in
Button(element.name) {
selectedElement = element
selectedId = element.id
isPresented.toggle()
}
.foregroundColor(
selectedElement.id == element.id
? .black
: .gray
)
}
}
}
}

Related

SwiftUI: How to get notified when a field in a singleton object get changed?

class SharedData: ObservableObject {
static let shared = SharedData()
#Published var sharedState = SharedState()
}
struct SharedState {
var allMMS: [MMS] = []
var typeTrees: [TTMaker] = []
var sampleInputs: [String: String] = [:]
var selectedTypeTreeName: String?
var selectedMMSPathName: String?
var maps: [String: FunctionalMap] = [:]
var mapId: String?
var selectedMenuItem: String? = nil
}
struct ContentView: View {
#ObservedObject var sharedData = SharedData.shared
set a file / string on the view:
SharedData.shared.sharedState.typeTrees.append(ttMaker)
I would expect the List in the same would get updated:
List {
ForEach(SharedData.shared.sharedState.typeTrees, id: \.self) { tree in
Button(action: {
SharedData.shared.sharedState.selectedTypeTreeName = tree.newTree.filename
}) {
HStack {
Text(tree.newTree.filename)
Spacer()
if tree.newTree.filename == SharedData.shared.sharedState.selectedTypeTreeName {
Image(systemName: "checkmark")
}
}
}
}
}
Is it any similar oslution than context in React?

Is there a way to execute a task before anything else happens?

I need to fetch some data before passing it to a View. I created a ViewModel BoughtItemViewModel for the my BoughtItemView.
For simplicity I only provide one case, my enum has some more cases I switch through depending on what item is bought. This is the BoughtItemView:
enum SomeType {
case song(Song)
}
struct BoughtItemCard: View {
#State var type: SomeType
#StateObject var vm = BoughtItemViewModel()
var body: some View {
switch type {
case .song(let song):
WebImage(url: URL(string: song.image))
.resizable()
.frame(width: 150, height: 150)
.overlay(BoughtItemOverlay(type: type)
.environmentObject(vm)
.onAppear() {
vm.fetchUnlockDate(path: "songs", docId: song.id ?? "")
}
}
}
This is the BoughtItemViewModel:
class BoughtItemViewModel: ObservableObject {
#Published var unlockDate = Date()
func fetchUnlockDate(path: String, docId: String) {
let uid = FirebaseManager.shared.auth.currentUser?.uid ?? ""
FirebaseManager.shared.firestore.collection(path)
.document(docId).collection("unlocked").document(uid).getDocument { docSnapshot, error in
if let error = error {
print(error)
return
}
let data = docSnapshot?.data()
self.unlockDate = (data?["unlockDate"] as? Date ?? Date())
}
}
}
Now I want the unlockDatein my BoughtItemOverlay View to show the fetched date.
Again for simplicity I provide one case, this is the BoughtItemOverlay:
struct BoughtItemOverlay: View {
#State var showModal: Bool = false
#State var type: SomeType
#State var unlockDate = Date()
#EnvironmentObject var vm: BoughtItemViewModel
var body: some View {
switch type {
case .song(let song):
VStack {
Spacer().onAppear() {
unlockDate = vm.unlockDate
}
Text("Purchased \(unlockDate.formatted(.dateTime.day().month(.defaultDigits).year()))")
}
}
}
}
Instead of displaying the unlockDate it always displays the date of today. I'm not sure why that is since the BoughtItemOverlayshould refresh after the State changes in the onAppear() setting the value to the BoughtItemViewModel value. Atleast that I think it should work that way, but correct me if I'm wrong.
With #State var unlockDate = Date() you are creating a new source of truth and try to synchronize it with unlockDate = vm.unlockDate.
Don´t do that, use the Viewmodel itself in your Childview:
struct BoughtItemOverlay: View {
#State var showModal: Bool = false
#State var type: SomeType
#EnvironmentObject var vm: BoughtItemViewModel
var body: some View {
switch type {
case .song(let song):
VStack {
Spacer()
// use the vm value here
Text("Purchased \(vm.unlockDate.formatted(.dateTime.day().month(.defaultDigits).year()))")
}
}
}
}
The Viewmodel will notify the View when unlockDate has changed and the View will show the new date.
Firebase returns a FIRTimestamp so I had to change my code to:
import Foundation
import FirebaseFirestore
class BoughtItemViewModel: ObservableObject {
#Published var unlockDate = Date()
func fetchUnlockDate(path: String, docId: String) {
let uid = FirebaseManager.shared.auth.currentUser?.uid ?? ""
FirebaseManager.shared.firestore.collection(path).document(docId).collection("unlocked").document(uid).getDocument { docSnapshot, error in
if let error = error {
print(error)
return
}
let data = docSnapshot?.data()
let timestamp = data?["unlockDate"] as? Timestamp
self.unlockDate = timestamp?.dateValue() ?? Date()
}
}
}

Save objects in Realm only when Save button is pressed

I'm trying to create a really simple app in SwifUI + Realm but I cannot understand how can I make it work. Basically I have a list of objects. When I click on a row it will open a sheet to edit the underlying object, if i press "+" it will open same sheet to create a new object.
In the sheet object will be save only if "Save" button is pressed.
Here is my model
class PayableEntityType: Object {
#Persisted(primaryKey: true) var _id: ObjectId
#Persisted var name: String
#Persisted var iconName: String
#Persisted var mainColor: String
#Persisted var secondaryColor: String
#Persisted var _partitionValue: String = AppInfo.partitionValue
convenience init(name: String, mainColor: String, secondaryColor: String, iconName: String) {
self.init()
self.name = name
self.mainColor = mainColor
self.secondaryColor = secondaryColor
self.iconName = iconName
}
}
This is my list view
struct PayableEntityTypesListScreen: View {
#Environment(\.presentationMode) private var presentationMode
#ObservedResults(PayableEntityType.self) private var payableEntityTypes
#State var selectedPayableEntityType: PayableEntityType
#State var forEdit: PayableEntityType?
#State var openForSelect: Bool = false
#State private var showEdit: Bool = false
#State private var isNew: Bool = false
var body: some View {
NavigationView {
List {
ForEach(self.payableEntityTypes.sorted(byKeyPath: "name")) { payableEntityType in
Text(payableEntityType.name).onTapGesture {
self.rowTap(payableEntityType)
}
}
}.padding(.top, 1)
}.navigationTitle("Entity type")
.toolbar {
if !openForSelect {
self.newPayableEntityTypeButton()
}
}
}
private func rowTap(_ payableEntityType: PayableEntityType) {
self.selectedPayableEntityType = payableEntityType
if self.openForSelect {
self.presentationMode.wrappedValue.dismiss()
} else {
self.isNew = false
self.showEdit = true
}
}
#ToolbarContentBuilder
private func newPayableEntityTypeButton() -> some ToolbarContent {
ToolbarItem(placement: .navigationBarTrailing) {
AddButton(action: {
self.isNew = true
self.showEdit = true
}).sheet(isPresented: self.$showEdit) {
EditPayableEntityTypeScreen(payableEntityType: self.isNew ? PayableEntityType() : self.selectedPayableEntityType)
}
}
}
}
and this is the edit\create view
struct EditPayableEntityTypeScreen: View {
#Environment(\.realm) var realm
#Environment(\.presentationMode) private var presentationMode
#ObservedRealmObject var payableEntityType: PayableEntityType
#State private var errorMessage: String = ""
#State private var showingAlert: Bool = false
#State private var name: String = ""
#State private var iconName: String = ""
#State private var selectedMainColor: Color = .green
#State private var selectedSecondaryColor: Color = .black
private var isUpdating: Bool {
self.payableEntityType.realm != nil
}
init(payableEntityType: PayableEntityType) {
self.payableEntityType = payableEntityType
if self.isUpdating {
self._name = .init(initialValue: self.payableEntityType.name)
self._iconName = .init(initialValue: self.payableEntityType.iconName)
self._selectedMainColor = State<Color>.init(initialValue: Color(hex: self.payableEntityType.mainColor))
self._selectedSecondaryColor = State<Color>.init(initialValue: Color(hex: self.payableEntityType.secondaryColor))
}
}
var body: some View {
NavigationView {
Form {
Section(header: Text("Info")) {
TextField("name", text: self.$name)
.disableAutocorrection(true)
}
Button(action: {
self.save()
}, label: {
Text("SAVE")
}).alert(isPresented: $showingAlert) {
Alert(title: Text("Warning"), message: Text(self.errorMessage), dismissButton: .default(Text("OK")))
}
}
}
}
private func save() {
if self.name.isEmpty {
self.errorMessage = "Missing name 😅"
self.showingAlert = true
return
}
self.errorMessage = ""
do {
if self.isUpdating {
try realm.write {
self.$payableEntityType.name.wrappedValue = self.name
self.$payableEntityType.iconName.wrappedValue = self.iconName
self.$payableEntityType.mainColor.wrappedValue = self.selectedMainColor.toHex()
self.$payableEntityType.secondaryColor.wrappedValue = self.selectedSecondaryColor.toHex()
}
} else {
self.payableEntityType.name = self.name
self.payableEntityType.iconName = self.iconName
self.payableEntityType.mainColor = self.selectedMainColor.toHex()
self.payableEntityType.secondaryColor = self.selectedSecondaryColor.toHex()
try realm.write {
realm.add(self.payableEntityType)
}
}
self.presentationMode.wrappedValue.dismiss()
} catch {
self.errorMessage = error.localizedDescription
self.showingAlert = true
}
}
}
This code works, but I think is not the right way to use Realm property wrappers.
Note: the PayableEntityTypesListScreen has a openForSelect variable because I want to reuse that view to only select one of list elements and the close it.

SwiftUI: #State value doesn't update after async network request

My aim is the change data in DetailView(). Normally in this case I'm using #State + #Binding and it's works fine with static data, but when I trying to update ViewModel with data from network request I'm loosing functionality of #State (new data doesn't passing to #State value it's stays empty). I checked network request and decoding process - everything ok with it. Sorry my code example a bit long but it's the shortest way that I found to recreate the problem...
Models:
struct LeagueResponse: Decodable {
var status: Bool?
var data: [League] = []
}
struct League: Codable, Identifiable {
let id: String
let name: String
var seasons: [Season]?
}
struct SeasonResponse: Codable {
var status: Bool?
var data: LeagueData?
}
struct LeagueData: Codable {
let name: String?
let desc: String
let abbreviation: String?
let seasons: [Season]
}
struct Season: Codable {
let year: Int
let displayName: String
}
ViewModel:
class LeagueViewModel: ObservableObject {
#Published var leagues: [League] = []
init() {
Task {
try await getLeagueData()
}
}
private func getLeagueData() async throws {
let (data, _) = try await URLSession.shared.data(from: URL(string: "https://api-football-standings.azharimm.site/leagues")!)
guard let leagues = try? JSONDecoder().decode(LeagueResponse.self, from: data) else {
throw URLError(.cannotParseResponse)
}
await MainActor.run {
self.leagues = leagues.data
}
}
func loadSeasons(forLeague id: String) async throws {
let (data, _) = try await URLSession.shared.data(from: URL(string: "https://api-football-standings.azharimm.site/leagues/\(id)/seasons")!)
guard let seasons = try? JSONDecoder().decode(SeasonResponse.self, from: data) else {
throw URLError(.cannotParseResponse)
}
await MainActor.run {
if let responsedLeagueIndex = leagues.firstIndex(where: { $0.id == id }),
let unwrappedSeasons = seasons.data?.seasons {
leagues[responsedLeagueIndex].seasons = unwrappedSeasons
print(unwrappedSeasons) // successfully getting and parsing data
}
}
}
}
Views:
struct ContentView: View {
#StateObject var vm = LeagueViewModel()
var body: some View {
NavigationView {
VStack {
if vm.leagues.isEmpty {
ProgressView()
} else {
List {
ForEach(vm.leagues) { league in
NavigationLink(destination: DetailView(league: league)) {
Text(league.name)
}
}
}
}
}
.navigationBarTitle(Text("Leagues"), displayMode: .large)
}
.environmentObject(vm)
}
}
struct DetailView: View {
#EnvironmentObject var vm: LeagueViewModel
#State var league: League
var body: some View {
VStack {
if let unwrappedSeasons = league.seasons {
List {
ForEach(unwrappedSeasons, id: \.year) { season in
Text(season.displayName)
}
}
} else {
ProgressView()
}
}
.onAppear {
Task {
try await vm.loadSeasons(forLeague: league.id)
}
}
.navigationBarTitle(Text("League Detail"), displayMode: .inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
ChangeButton(selectedLeague: $league)
}
}
}
}
struct ChangeButton: View {
#EnvironmentObject var vm: LeagueViewModel
#Binding var selectedLeague: League // if remove #State the data will pass fine
var body: some View {
Menu {
ForEach(vm.leagues) { league in
Button {
self.selectedLeague = league
} label: {
Text(league.name)
}
}
} label: {
Image(systemName: "calendar")
}
}
}
Main goals:
Show selected league seasons data in DetailView()
Possibility to change seasons data in DetailView() when another league was chosen in ChangeButton()
You update view model but DetailView contains a copy of league (because it is value type).
The simplest seems to me is to return in callback seasons, so there is possibility to update local league as well.
func loadSeasons(forLeague id: String, completion: (([Season]) -> Void)?) async throws {
// ...
await MainActor.run {
if let responsedLeagueIndex = leagues.firstIndex(where: { $0.id == id }),
let unwrappedSeasons = seasons.data?.seasons {
leagues[responsedLeagueIndex].seasons = unwrappedSeasons
completion?(unwrappedSeasons) // << here !!
}
}
}
and make task dependent on league id so selection would work, like:
struct DetailView: View {
#EnvironmentObject var vm: LeagueViewModel
#State var league: League
var body: some View {
VStack {
// ...
}
.task(id: league.id) { // << here !!
Task {
try await vm.loadSeasons(forLeague: league.id) {
league.seasons = $0 // << update local copy !!
}
}
}
Tested with Xcode 13.4 / iOS 15.5
Test module is here
One question is if you already made LeagueViewModel an ObservableObject, why don't you display from it directly, and simply pass an id to your DetailView?
So your detail view will be:
struct DetailView: View {
#EnvironmentObject var vm: LeagueViewModel
#State var id: String
var body: some View {
VStack {
if let unwrappedSeasons = vm.leagues.first { $0.id == id }?.seasons {
List {
ForEach(unwrappedSeasons, id: \.year) { season in
Text(season.displayName)
}
}
} else {
ProgressView()
}
}
.task(id: id) {
Task {
try await vm.loadSeasons(forLeague: id)
}
}
.navigationBarTitle(Text("League Detail"), displayMode: .inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
ChangeButton(selectedId: $id)
}
}
}
}
The view will automatically update season data as it loads them.

Cannot convert value of type 'Binding<[ContactEntity]>.Element' (aka 'Binding<ContactEntity>') to expected argument type 'ContactEntity'

Using Xcode 13.4.1 on macOS 12.5. I revised the working code to conform to MVVM. This was successful for the first Entity (all properties Optional) for all CRUD operations.
Using this code as a base, I tackled the second Entity (one Bool property NOT Optional), but it throws the compiler error inside the ForEach loop, against 'contact'. This code was error-free before the MVVM conversion. I've been at this for 4 days and am reaching out, but clearly my limited knowledge is inadequate.
ContactListView code below, supported by the ContactViewModel, which in turn relies on the CoreDataManager code.
import SwiftUI
import CoreData
//class FirstNameSort: ObservableObject {
// #Published var firstNameSort: Bool = false
//}
struct ContactsListView: View {
// MARK: - PROPERTIES
#Environment(\.managedObjectContext) var viewContext
#ObservedObject var contactVM = ContactViewModel()
#State private var totalContacts: Int = 0
#State private var search: String = ""
#State private var searchByChampions = false
#State var searchByFirstNames = false
#State private var totalChampions = 0
// MARK: - BODY
var body: some View {
NavigationView {
VStack {
// HStack {
// Toggle("Display Champions only", isOn: $searchByChampions)
// .toggleStyle(.switch)
// .foregroundColor(.blue)
// .padding()
// Toggle("Sort by First Names", isOn: $contactVM.sortFirstName)
// .toggleStyle(.switch)
// .foregroundColor(.blue)
// .padding()
//}
List {
HStack {
Text(searchByChampions ? "Total Champions" : "Total Contacts")
.foregroundColor(.gray)
Spacer()
Text("\(searchByChampions ? totalChampions : totalContacts)")
.bold()
}.foregroundColor(.green)
.padding()
ForEach($contactVM.listofContacts) { contact in
NavigationLink(destination:
ModifyContactView(contact: ***contact***)
.id(UUID()), label: {
ContactRowView(contact: ***contact***)
.id(UUID())
})
}
.onDelete(perform: contactVM.deleteContact)
}.navigationTitle("Contacts")
.toolbar {
#if os(iOS)
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
#endif
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: AddContactView(), label: {
Image(systemName: "plus.circle")
})
}
}
.onAppear {
countContacts()
countChampions()
}
.searchable(text: $search, prompt: Text("Contact Last Name?"))
// .onChange(of: search) { value in
// if !value.isEmpty {
// listofContacts.nsPredicate = NSPredicate(format: "contactLastName CONTAINS[dc] %#", value)
// } else {
// listofContacts.nsPredicate = nil
// }
// }
}
}.navigationViewStyle(.stack)
}
func countContacts() {
totalContacts = $contactVM.listofContacts.count
}
// func countChampions() {
// totalChampions = $contactVM.listOfChampions.count
// }
}
import CoreData
import SwiftUI
class ContactViewModel: ObservableObject {
#Environment(\.dismiss) var dismiss
#ObservedObject var dataVM = CoreDataManager()
#ObservedObject var qualifierVM = QualifierViewModel()
#Published var inputFirstName: String = ""
#Published var inputLastName: String = ""
#Published var inputCellNumber: String = ""
#Published var inputEmail: String = ""
#Published var inputChampion: Bool = false
#Published var inputComments: String = ""
#Published var inputCreated: Date = Date()
#Published var inputUpdated: Date = Date()
#Published var listOfFirstNames = []
#Published var listofContacts: [ContactEntity] = []
func fetchContacts() {
let request = NSFetchRequest<ContactEntity>(entityName: "ContactEntity")
do {
dataVM.listofContacts = try dataVM.container.viewContext.fetch(request)
} catch let error {
print("Error fetching. \(error)")
}
}
func addContact(
contactFirstName: String,
contactLastName: String,
contactCellNumber: String,
contactEmail: String,
contactChampion: Bool,
contactComments: String,
contactCreated: Date,
contactUpdated: Date) {
let newContact = ContactEntity(context: dataVM.container.viewContext)
newContact.contactFirstName = contactFirstName
newContact.contactLastName = contactLastName
newContact.contactCellNumber = contactCellNumber
newContact.contactEmail = contactEmail
newContact.contactChampion = contactChampion
newContact.contactComments = contactComments
newContact.contactUpdated = Date()
newContact.contactCreated = Date()
let uniqueClient = Set(dataVM.selectedClient)
for client in uniqueClient {
newContact.addToClients(client)
print("Client: \(client.clientName ?? "No client")")
}
saveContact()
dismiss()
}
func deleteContact(indexSet: IndexSet) {
guard let index = indexSet.first else { return }
let entity = dataVM.listofContacts[index]
dataVM.container.viewContext.delete(entity)
saveContact()
}
func saveContact() {
do {
try dataVM.container.viewContext.save()
fetchContacts()
} catch let error {
print("Error saving. \(error)")
}
}
func sortLastName() -> [ Array<Any>] {
let listOfLastNames = dataVM.listofContacts.sorted {
$0.contactLastName ?? "" < $1.contactLastName ?? ""
}
return [listOfLastNames]
}
func sortFirstName() -> [ Array<Any>] {
let listOfFirstNames = dataVM.listofContacts.sorted {
$0.contactFirstName ?? "" < $1.contactFirstName ?? ""
}
return [listOfFirstNames]
}
}
import Foundation
import CoreData
class CoreDataManager: ObservableObject {
let container: NSPersistentContainer
#Published var listOfQualifiers: [QQEntity] = []
#Published var listofContacts: [ContactEntity] = []
#Published var listOfClients: [ClientEntity] = []
#Published var listOfOpportunities: [OpportunityEntity] = []
//#Published var selectedClient: [ClientEntity] = []
init() {
container = NSPersistentContainer(name: "B2BContainer")
container.loadPersistentStores { (description, error) in
if let error = error {
fatalError("Error loading Core Data. \(error)")
} else {
print("Successfully loaded Core Data...")
}
}
}
}