Coredata Nsdate Issue When Uploading to Appstore
Imagine jotting downwardly something important in Notes, only to find your information is gone the next time yous open up the app! Fortunately, persistence is in first-class hands on iOS. All your notes, photos and other data are prophylactic, thanks to Core Information. :]
At that place are a number of dissimilar technologies you can apply when you need to shop data across app launches. Core Data is the go-to solution on iOS. With impressive performance and a broad set of features, Apple's Core Data framework manages the unabridged model layer of your app and handles persistence to your device's storage deejay.
In this Cadre Data with SwiftUI tutorial, you'll refactor an app to add persistence and preclude the nightmare of losing your data when the app restarts. Forth the style, you lot'll learn to:
- Fix Core Data in a project.
- Utilize SwiftUI's data flow to admission what yous need in the Core Data framework.
- Define and create new model objects using Core Data.
- Use Fetch Requests to retrieve objects from disk.
So buckle up and learn more about Cadre Data's capabilities and how it works!
Getting Started
To start, download the project materials using the Download Materials button at the meridian or lesser of this tutorial. Then, open the starter projection in Xcode. Then build and run.
Welcome to FaveFlicks, your ain personal tally of your favorite movies. It'southward a uncomplicated app that lets y'all add or delete a movie to the list. Notwithstanding, it has one glaring problem.
Yes, you guessed it right: The app does not persist data! This means that if yous add some movies to your list, then restart the app, those movies yous carefully added have gone. :[
Testing FaveFlick's Persistence
To remove a picture from the list, swipe left and tap Delete.
Next, tap the Plus button on the top-right to add ane of your favorites.
You'll see the Add Motion-picture show screen.
Each Pic object exists but in memory. They aren't stored to disk, so endmost the app removes your changes and reverts to my tasteful list of favorite movies.
Notation: If you endeavor to open the "add pic" screen a 2nd time, and so nothing happens. Information technology's a known Apple problems in SwiftUI. As a workaround, you demand to update the UI in some mode to add together more movies. Yous can pull down the list to update the UI and then add more movies.
Force close the app to test its persistence. With the app in foreground, enter the fast app switcher. To do this, gently drag up from the bottom of the screen. If your device has i, double-tap the Home button to enable the fast app switcher.
At present, select FaveFlicks and swipe up to shut the app. On the home screen, tap FaveFlicks to open up it again.
Discover that your changes are gone, and the default movies are back.
Information technology's time to fix that. Begin by setting up Core Information.
Setting Up Cadre Data
Before you showtime setting upwards persistence, you should learn nearly the moving parts of Cadre Data, also known as the Core Data stack. The Core Data stack includes:
- A managed object model which defines model objects, chosen entities, and their relationships with other entities. Call back of it every bit your database schema. In FaveFlicks, y'all'll ascertain the
Movieentity as office of the managed object model within FaveFlicks.xcdatamodeld. You'll utilise the NSManagedObjectModel form to access your managed object model in the code. - An NSPersistentStoreCoordinator, which manages the actual database.
- An NSManagedObjectContext, which is an in-memory scratchpad that lets yous create, edit, delete or retrieve entities. Normally, you'll work with a managed object context when you lot're interacting with Cadre Data.
With that out of the way, it'due south time to get started!
Adding the Core Data stack
While it might seem daunting to ready the unabridged Core Data stack, it's like shooting fish in a barrel thanks to NSPersistentContainer. It can create everything for you. Open SceneDelegate.swift and add the post-obit after import SwiftUI:
import CoreData Core Data lives in its own framework, so you must import it in lodge to employ it.
At present, add the following at the end of SceneDelegate:
// 1 lazy var persistentContainer: NSPersistentContainer = { // 2 allow container = NSPersistentContainer(proper name: "FaveFlicks") // 3 container.loadPersistentStores { _, error in // 4 if permit fault = fault equally NSError? { // You should add your ain error handling lawmaking here. fatalError("Unresolved error \(error), \(error.userInfo)") } } return container }() Here's what that does:
- Add a
lazyproperty calledpersistentContainerto yourSceneDelegate. The first fourth dimension you reference the property, it will create anNSPersistentContainer. - Create a container named
FaveFlicks. You'll run into a file called FaveFlicks.xcdatamodeld if you look in the app's listing of files in the Project navigator. This file is where y'all will after pattern your Core Data model schema. The proper name of this file needs to match the name of the container. - Instruct the container to load the persistent shop, which only sets up the Core Data stack.
- If an fault occurs, information technology'southward logged and the app is killed. In a real app, you should handle this by showing a dialog indicating the app is in a weird state and needs reinstalling. Any errors here should exist rare and consequence from developer mistakes, and so you'll take hold of them before submitting your app to the App Store.
That's it. That's all y'all need to gear up Core Data stack. Quite a journeying, huh? :]
Yous're going to also need a mode of saving any data to disk, because Core Data does not handle that automatically. Still in SceneDelegate.swift, add together the following method at the end of the class:
func saveContext() { // 1 let context = persistentContainer.viewContext // 2 if context.hasChanges { do { // 3 try context.salvage() } take hold of { // 4 // The context couldn't be saved. // You should add your own fault handling here. let nserror = mistake as NSError fatalError("Unresolved error \(nserror), \(nserror.userInfo)") } } } This creates a method called saveContext() which does the following:
- Obtain the
viewContextof the persistent container. This is a special managed object context which is designated for use only on the chief thread. You'll use this 1 to relieve any unsaved information. - Save only if there are changes to save.
- Salve the context. This telephone call may throw an error, and then information technology's wrapped in a
try/catch. - In the event of an mistake, information technology's logged and the app killed. Just like in the previous method, any errors hither should simply happen during evolution, but should be handled appropriately in your application but in example.
Now that you have the Cadre Data stack prepare and a method to save changes, it'due south time to wire this into the residue of the app.
At present, in scene(_:willConnectTo:options:), replace permit contentView = MovieList() with the following:
let context = persistentContainer.viewContext allow contentView = MovieList().environment(\.managedObjectContext, context) This only grabs the same viewContext you used before and sets it as an environment variable on the MovieList SwiftUI view. The view will use this later to add together and remove movies from the Core Data shop.
Now add the following method to the end of SceneDelegate:
func sceneDidEnterBackground(_ scene: UIScene) { saveContext() } This instructs the app to call the save method yous previously added when the app goes into the background. This is a good fourth dimension to save information to deejay. Later on, you'll see how to save more often.
Build and run to bank check that the app however works. At that place are no functional changes just all the same!
Creating the Information Model
Phew! Now that the Cadre Data stack is out of the way, information technology's finally fourth dimension to work on the main part of the app. In Xcode, open up FaveFlicks.xcdatamodel. Right at present it'south empty, but you'll declare the Movie entity beneath. This is where you define the schema of your data model. Y'all'll add the relevant entities, which are the types of objects you can create, and define relationships to signal how the entities are connected.
Click Add Entity.
Xcode creates a new entity in the data model, named Entity by default. Double-click the name and modify information technology to Movie.
Next, click the + icon nether Attributes to add a new attribute. Proper noun information technology title and set the type to Cord.
Finally, add 2 more attributes: Ane named genre of type String and another named releaseDate of type Appointment. Once you're washed, the Movie entity's attributes will match the following:
Relationships and Fetched Properties
Although FaveFlicks only has a single Flick entity, you may run into relationships and fetched properties in apps with larger data models. A relationship is the same every bit a relationship in any database: It lets you lot define relationships between two entities.
Nonetheless, Fetched properties is a more than advanced Cadre Data topic. You tin can call up of it as computed backdrop that work like weak one-manner relationships. For instance, if FaveFlicks had a Movie theater entity, it might take a currentlyShowingMovies fetched belongings that would fetch Movies that are currently in movie theaters.
Removing the One-time Motion-picture show Struct
Open up Movie.swift. At the starting time of this tutorial, Pic struct was the model object. Core Data creates its own Moving picture class so you need to remove Movie.swift. Delete Moving picture.swift by right-clicking it in the Project navigator and selecting Delete. In the resulting dialog, click Movement to Trash.
Build the app. Y'all'll come across a couple of errors that need fixing because you've just removed Movie.
Note: Yous'll need to be precise and delete quite a bit of code for the old Movie struct in this section, so follow closely!
First, open MovieList.swift. Yous'll find the movies for the listing stored in a uncomplicated movies array. At the top of MovieList, change the line declaring the movies array to an empty array like below:
@State var movies: [Movie] = [] The @State property wrapper is a vital slice of the SwiftUI data flow. The class that declares this local property owns information technology. If annihilation changes the value of movies, the view that owns it will trigger an update of the UI.
Now, delete makeMovieDefaults(), since information technology's no longer in use.
In addMovie(title:genre:releaseDate:), movies are created and added to the movies assortment. Remove its contents and leave it equally a bare method. Y'all'll apply it to create new instances of the Movie entity in a afterward section.
Finally, remove the contents of deleteMovie(at:). Y'all'll supervene upon information technology afterward with code that deletes Core Data entities.
Using the New Film Entity
Now that you created a Moving picture entity in the information model, Xcode will auto-generate it'due south own Movie course that you'll employ instead. All entities in the data model are subclasses of NSManagedObject. It's a managed object because Core Data handles its lifecycle and persistence for you, mainly through the utilize of the Managed Object Context.
The old Movie struct didn't utilize optional properties. Just, all NSManagedObject subclasses use optional backdrop for their attributes. This means you'll demand to brand some changes in files that apply Flick.
Notation: In that location is an exception to all properties being optional: scalar types. For case, if your entity has a Bladder property and Utilize Scalar Blazon is enabled, then the generated holding will exist a non-optional of type Float.
Using an Entity'south Attributes in a View
Now, you'll learn to use an entity's attributes in a view. Open MovieRow.swift. Then, replace the body belongings with:
var body: some View { VStack(alignment: .leading) { // 1 picture show.championship.map(Text.init) .font(.title) HStack { // ii motion picture.genre.map(Text.init) .font(.caption) Spacer() // 3 moving picture.releaseDate.map { Text(Self.releaseFormatter.string(from: $0)) } .font(.caption) } } } The construction of the view is exactly the aforementioned, only you'll notice that all the pic attributes are mapped to Viewsouthward.
All attributes on a Cadre Data entity are optional. That is to say, the title attribute is type Cord?, referenceDate is type Date? then on. And then, now you'll need a method to go the optional'south value.
Inside a ViewBuilder, like MovieRowsouth body property, you tin can't add control flow statements such as if let. Each line should be either a View or nil.
The line marked with ane, two and 3 above are a Text view if the attributes are not-zippo. Otherwise, information technology'south nil. Information technology's a handy way to deal with optionals in SwiftUI lawmaking.
Finally, build and run. You removed the erstwhile Movie struct and replaced it with a Core Information entity. As your reward, you now have an empty view rather than a list of swish films. :]
If you create a movie, nothing happens. Y'all'll fix this adjacent.
Using Environment to Access Managed Object Context
Next, you'll acquire to access objects from a managed object context. Back in MovieList.swift, add the post-obit line under the declaration of movies:
@Environs(\.managedObjectContext) var managedObjectContext Think earlier you set the managedObjectContext environs variable on MovieList? Well, now y'all're declaring that it exists and, therefore, can access it.
@Environment is another of import piece of SwiftUI data catamenia that lets you access global properties. When you want to pass an environment object to a view, you pass it in when creating an object.
At present add the post-obit method to MovieList.swift:
func saveContext() { practise { try managedObjectContext.save() } catch { print("Error saving managed object context: \(error)") } } When you create, update or delete entities, you do so in your managed object context — the in-memory scratchpad. To actually write the changes to disk, you lot must save the context. This method saves new or updated objects to the persistent store.
Adjacent, detect addMovie(championship:genre:releaseDate:). The method is even so blank from when you removed the old Movie, so replace information technology with the method below to create new Movie entities:
func addMovie(title: String, genre: String, releaseDate: Date) { // 1 let newMovie = Movie(context: managedObjectContext) // 2 newMovie.championship = title newMovie.genre = genre newMovie.releaseDate = releaseDate // 3 saveContext() } Here, you lot:
- Create a new
Picturein your managed object context. - Ready all the properties of the
Moving picturethat are passed as parameters intoaddMovie(title:genre:releaseDate:). - Save the managed object context.
Build and run and create a new picture show. You'll notice a blank list.
That's because you're creating Movies, but you're non retrieving them to brandish in the list. In the adjacent section, you'll set up this, and you'll finally see movies in the app again.
Fetching Objects
Now y'all'll learn to display the movies you've created. Y'all need to fetch them from the persistent store with a FetchRequest.
At the top of MovieList, remove the line declaring the movies array. Replace it with this FetchRequest:
// ane @FetchRequest( // two entity: Picture.entity(), // three sortDescriptors: [ NSSortDescriptor(keyPath: \Flick.title, ascending: true) ] // iv ) var movies: FetchedResults<Moving-picture show> When you need to remember entities from Cadre Data, you create a FetchRequest. Hither, y'all:
- Declare the property using the
@FetchRequestproperty wrapper, which lets you use the results directly in your SwiftUI view. - Inside the property wrapper, specify which entity yous'd like Cadre Data to fetch. This will fetch instances of the
Movieentity. - Add an assortment of sort descriptors to determine the gild of the results. For instance, you could sort the
Moviesouth by genre, then by championship forPicture showdue south with the same genre. But here, you simply order by title. - Finally, after the holding wrapper, you declare the
moviesproperty of typeFetchedResults.
Predicates
This will fetch all Motion-picture showdue south stored by Cadre Data. Merely, what if you need to filter the objects, or simply retrieve one specific entity? Yous can besides configure a fetched asking with a predicate to limit the results, such as merely fetching Movies from a certain year or matching a sure genre. To do and then, you would add the predicate parameter at the end of the @FetchRequest holding wrapper, like so:
predicate: NSPredicate(format: "genre contains 'Activity'") In that location'southward no need to add this now, equally your fetch request should fetch all Moving-picture shows. Merely if you want to play effectually with this then by all means do!
Testing the Results
Build and run. You'll see your list of movies. Congratulations!
Well, this just gets yous back to where you started. To test that the movies are storing to disk, add together a few movies and then kill the app by pressing stop in Xcode. Then build and run again. All your movies will notwithstanding be there!
Deleting Objects
Next, you'll learn to delete objects. If y'all swipe left and try to delete a moving-picture show, nothing happens. To fix this, replace deleteMovie(at:) with:
func deleteMovie(at offsets: IndexSet) { // one offsets.forEach { index in // 2 let picture show = self.movies[index] // 3 cocky.managedObjectContext.delete(movie) } // 4 saveContext() } Hither's what'due south happening:
- A SwiftUI
Listprovides you lot with anIndexSetof deletions when you swipe to delete an object in the list. Iterate over theIndexSetwithforEach. - Go the flick for the current
index. - Delete the flick from the managed object context.
- Save the context to persist your changes to disk.
Build and run. And so, delete a movie.
You've restored all the functionality of the app, and what's more, information technology'll nevertheless be in that location in the morn thanks to Cadre Data! And you lot're done!
Where to Go From Here?
You lot used Core Data to introduce persistence into a SwiftUI projection and store entities to deejay. You lot can download the completed project using the Download Materials button at the top or bottom of this tutorial.
If you want more hands-on experience with Core Data and SwiftUI, check out these tutorials:
- Commencement Core Information
- SwiftUI: Getting Started
Core Data is a huge topic and there'due south still a lot to larn. Take a look at Core Data by Tutorials for a deep dive into Core Information using UIKit.
You've gone over a off-white bit of data flow in this tutorial, just if you lot'd like to larn more than, have a look at WWDC 2019's Data Menstruation Through SwiftUI video.
For data period, as well as everything else yous need to get a SwiftUI main, SwiftUI by Tutorials will get y'all where you demand to get.
Getting set upward with Core Data is easier than ever before, thanks to SwiftUI. I hope y'all enjoyed this Cadre Data with SwiftUI tutorial. If you lot have any questions or insights to share, please join the forum below.
Source: https://www.raywenderlich.com/9335365-core-data-with-swiftui-tutorial-getting-started
0 Response to "Coredata Nsdate Issue When Uploading to Appstore"
Post a Comment