+
button on the screen to add a new command and an Edit
button to delete. Here is the screen:+
button on the "Command List" screen to add a new command, or when you click on one of the commands from the list to edit it.+
, a screen for adding a new player to the team appears, and when you click on Edit
you can edit the player information.*.xcdatamodeld
in the xcode:Delete
key) an existing Event
entity that is not useful to us.Team
entity:Team
entity:Player
entity with three attributes:Team
and Player
entities, you must first select the Team
entity and in the Relationships
section, click +
and name the new communication players , for the Destination
select the Player
entity. On the right, set the “To-Many Relationship” flag with the selected players connection. For the delete rule, select Cascade
(when deleting a team, all players of the team will be automatically deleted).Player
entity). Add a Player
entity connection called team
, select Team
in the Destination
field, and select players
in the Invers
field (after that, don’t forget to set the Inverse
field for the players
connection in the Team
entity)Team
, and not the Event
.MasterViewController.m
file, without having forgotten to describe it in MasterViewController.h
.MasterViewController.h
now looks like this: #import <UIKit/UIKit.h> #import <CoreData/CoreData.h> @interface MasterViewController : UITableViewController <NSFetchedResultsControllerDelegate> @property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; - (void)insertTeamWithName:(NSString *)name uniformColor:(NSString *)uniformColor; - (void)saveContext; @end
self.title = NSLocalizedString(@"League Manager", @"League Manager");
- (void)insertTeamWithName:(NSString *)name uniformColor:(NSString *)uniformColor { NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext]; NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity]; NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context]; [newManagedObject setValue:name forKey:@"name"]; [newManagedObject setValue:uniformColor forKey:@"uniformColor"]; [self saveContext]; }
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Team" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] iniWithKey:@"timestamp" ascending:NO];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] iniWithKey:@"name" ascending:NO];
[newManagedObject setValue:name forKey:@"name"]; [newManagedObject setValue:uniformColor forKey:@"uniformColor"];
saveContext
method is saveContext
. - (void)saveContext { NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext]; NSError *error = nil; if(![context save:&error]){ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } }
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { [self saveContext]; } }
Event
entities, instead of the required Team
entities. We need to display two components in one cell of the table: the name of the team and the color of the form in which this team plays. In order to achieve this, you must first change the style of the displayed table cell, as well as the CellIdentifier
identifier used in the cellForRowAtIndexPath:
method. static NSString* CellIdentifier = @"Cell";
static NSString* CellIdentifier = @"TeamCell";
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
configureCell:atIndexPath:
method is present, in which the cell is actually configured for display. But in the current form, the method works with the essence of the Event
, and not the Team
, so you need to make some adjustments. - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = [[managedObject valueForKey:@"name"] description]; cell.detailTextLabel.text = [[managedObject valueForKey:@"uniformColor"] description]; cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; }
Master-Details View Application
template, another controller is created called DetailViewController
. You can use this class, but since we need to display information about the team and players, it is better to get rid of this controller and create new controllers with appropriate names.#import DetailViewController.h
line from MasterViewController.m
. Find the tableView:didSelectRowAtIndexPath:
method and clean its body. As follows: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { }
DetailViewController
(.h, .m, .xib). If we start the application and click on the + button, the application crashes. This + is still bound to the insertNewObject:
method that we deleted. We need to attach to this button the ability to create a new team, or rather, to show a modal window with fields for entering information (team name and form color) about the new team. The same window will be used to edit existing commands when you click on a cell with a command on the command list screen.UIViewController
:TeamViewController
and don't forget to tick the With XIB for user interface
option.TeamViewController.h
. In League Manager, the MasterViewController
class manages NSManagedObjectContext
, which means in TeamViewController
will need a reference to this object management environment and the corresponding initialization method. Since this controller will be responsible for editing the information about the command, during initialization it is worth passing the command object (for this we also need to create a property and add it to the initialization method). The user interface of the command to add / edit a command will contain two text fields - for the name of the command and the color of their form, for them you must create the corresponding properties in TeamViewController
. On this screen there will also be two buttons - the save button (Save) of the new command and the Cancel button (Cancel). In TeamViewController
should be handler methods for pressing these buttons.TeamViewController.h
#import <UIKit/UIKit.h> @class MasterViewController; @interface TeamViewController : UIViewController { IBOutlet UITextField *name; IBOutlet UITextField *uniformColor; NSManagedObject *team; MasterVIewController *masterController; } @property (nonatomic, retain) UITextField *name; @property (nonatomic, retain) UITextField *uniformColor; @property (nonatomic, retain) NSManagedObject *team; @property (nonatomic, retain) MasterViewController *masterController; - (IBAction)save:(id)sender; - (IBAction)cancel:(id)sender; - (id)initWithMasterController:(MasterViewController *)aMasterController team:(NSManagedObject *)aTeam; @end
TeamViewController.m
, import MasterViewController.h
, delete the initWithNibName:
, add synthesize for name
, team
and masterController
. Add this initialization method: - (id)initWithMasterController:(MasterController *)aMasterController team:(NSManagedObject *)aTeam { if((self = [super init])){ self.masterController = aMasterController; self.team = aTeam; } return self; }
TeamViewController.m
controller will be responsible for creating a new NSManagedObject
object. In the case, if the user selects one of the existing commands for editing, it is up to the controller to fill in the text fields on the screen with relevant data from the command object (the name of the command and the color of the form). We will add the last functionality to the viewDidLoad
method: - (void)viewDidLoad{ [super viewDidLoad]; if(team != nil){ name.text = [team valueForKey:@"name"]; uniformColor.text = [team valueForKey:@"uniformColor"]; } }
- (IBAction)save:(id)sender { if(masterController != nil){ if(team != nil){ [team setValue:name.text forKey:@"name"]; [team setValue:uniformColor.text forKey:@"uniformColor"]; [masterController saveContext]; } else { [masterController insertNewTeamWithName:name.text uniformColor:uniformColor.text]; } } [self dismissModalViewControllerAnimated:YES]; }
cancel:
method cancel:
simply removes the command edit / add window.TeamViewController.m
#import "TeamViewController.h" #import "MasterViewController.h" @implementation TeamViewController @synthesize name; @synthesize uniformColor; @synthesize team; @synthesize masterController; - (id)initWithMasterController:(MasterController *)aMasterController team:(NSManagedObejct *)aTeam { if((self = [super init])){ self.masterController = aMasterController; self.team = aTeam; } return self; } - (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; } #pragma mark - View lifecycle - (void)viewDidLoad{ [super viewDidLoad]; if(team != nil){ name.text = [team valueForKey:@"name"]; uniformColor.text = [team valueForKey:@"uniformColor"]; } } - (void)viewDidUnload{ [super viewDidUnload]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return (interfaceOrientation == UIInterfaceOrientationPortrait); } #pragma mark - Button handlers - (IBAction)save:(id)sender{ if(masterController != nil){ if(team != nil){ [team setValue:name.text forKey:@"name"]; [team setValue:uniformColor.text forKey:@"uniformColor"]; [masterController saveContext]; } else { [masterController insertTeamWithName:name.text uniformColor:uniformColor.text]; } } [self dismissModalViewControllerAnimated:YES]; } - (IBAction)cancel:(id)sender{ [self dismissModalViewControllerAnimated:YES]; } @end
TeamViewController.xib
, it should be empty from the beginning. We install there two inscriptions, two text entry fields and two buttons, we connect the actions of the buttons with the corresponding handler methods. The final view is something like this:MasterViewController
and add the code to display a screen with information about the command. We should display the edit command screen in two cases: 1) the user clicked on + 2) the user clicked on the command from the list. Let's start by pressing the + button. Declare a new method in MasterViewController.h
: - (void)showTeamView;
MasterViewController.m
, import TeamViewController.h
and implement the above method as follows: - (void)showTeamView{ TeamViewController *teamViewController = [[TeamViewController alloc] initWithMasterController:self team:nil]; [self presentModalViewController:teamViewController animated:YES]; }
viewDidLoad
method and replace this code here: UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject)];
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(showTeamView)];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath)indexPath{ NSManagedObject *team = [[self fetchedResultsController] objectAtIndexPath:indexPath]; TeamViewController *teamViewController = [[TeamViewController alloc] initWithMasterController:self team:team]; [self presentModalViewController:teamViewController animated:YES]; }
NSFetchedResultsController
and the rest of the code for working with Core Data, instead they delegate interaction with Core Data MasterViewController
.PlayerListViewController
controller, set its parent class UITableViewController
and uncheck the “With XIB for user interface” option. Open the file PlayerListViewController.h
. This class is responsible for displaying the list of players on the team, which means that a reference to the team object is necessary in this class. Also, given the fact that this class delegates interaction with Core Data to the MasterViewController
controller, a reference to the controller itself is also needed. #import <UIKit/UIKit.h> @class MasterViewController; @interface PlayerListViewController : UITableVIewController { NSManagedObject *team; MasterViewController *masterViewController; } @property (nonatomic, retain) NSManagedObject *team; @property (nonatomic, retain) MasterViewController *masterController; - (id)initWithMasterController:(MasterViewController *)aMasterController team:(NSManagedObject *)aTeam; - (void)showPlayerView; - (NSArray *)sortPlayers; @end
PlayerListViewController.m
file and import MasterVIewController.h
, synthesize the team
and masterController
. Change the generated method initWithStyle:
to initWithMasterController:
which takes two properties and saves as follows: - (id)initWithMasterController:(MasterVIewController *)aMasterVIewController team:(NSManagedObject *)aTeam { if((self = [super init])){ self.masterController = aMasterController; self.team = aTeam; } return self; }
viewDidLoad
method is viewDidLoad
as follows: - (void)viewDidLoad{ [super viewDidLoad]; self.title = @"Player"; UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(showPlayerView)]; self.navigationItem.rightBarButtonItem = addButton; }
showPlayerView
’ll leave the showPlayerView
method empty for now: - (void)showPlayerView{ }
- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [self.tableView reloadData]; }
valueForKey:@"players"
method of the team object, which will return NSSet*
players to us. Below is the code to customize the table display: - (NSUInteger)numberOfSectionInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableVIew *)tableView numberOfRowsInSection:(NSInteger)section { return [(NSSet *)[team valueForKey:@"players"] count]; } - (UITableViewCell *)tableView:(UITableVIew *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"PlayerCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if(cell == nil){ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier]; } NSManagedObject *player = [[self sortPlayers] objectAtIndex:indexPath.row]; cell.textLabel.text = [NSString stringWithFormat:@"%@ %@", [[player valueForKey:@"firstName"] description], [[player valueForKey:@"lastName"] description]]; cell.detailTextLabel.text = [[player valueForKey:@"email"] description]; return cell; }
sortPlayers
method, which returns a sorted array of players: - (NSArray *)sortPlayers{ NSSortDescriptor *sortLastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastName" ascending:YES]; NSArray *sortDescriptors = [NSArray arrayWithObjects:sortLastNameDescriptor, nil]; return [[(NSSet *)[team valueForKey:@"players"] allObjects] sortedArrayUsingDescriptors:sortDescriptors]; }
- (void)tableView:(UITableVIew *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{ NSManagedObject *team = [self.fetchedResultsController objectAtIndexPath:indexPath]; PlayerListViewController *playerListViewController = [[PlayerListViewController alloc] initWithMasterController:self team:team]; [self.navigationController pushViewController:playerListViewController animated:YES]; }
PlayerListViewController.h
to MasterViewController.m
. Now build and run the application. In the list of teams, we see previously created teams, when clicking on the accessory button, a list of players of the selected team opens (while the lists of players are empty, because we have not implemented the addition of a new player).UIViewController
) with the corresponding XIB and call it PlayerViewController
. It will be similar to TeamViewController
, but will contain three fields: lastName
, firstName
and email
. The controller also contains a link to MasterViewController
in order to be able to use the previously written methods for working with Core Data. There will also be two more properties: the teams in which the player plays and the player himself. If the player's object is nil
, then PlayerViewController
knows that it is necessary to create a new player, otherwise - edit the player's data. On the screen we will have three buttons: save, cancel and delete. When requesting the removal of a player, we will request confirmation from the user in the form of a UIActionSheeta
mapping, so it is imperative that PlayerViewController
implement the UIActionSheetDelegate
protocol UIActionSheetDelegate
. #import <UIKit/UIKit.h> @class MasterViewController; @interface PlayerViewController : UIViewController <UIActionSheetDelegate> { IBOutlet UITextField *firstName; IBOutlet UITextField *lastName; IBOutlet UITextField *email; NSManagedObject *team; NSManagedObject *player; MasterViewController *masterViewController; } @property (nonatomic, retain) UITextField *firstName; @property (nonatomic, retain) UITextField *lastName; @property (nonatomic, retain) UITextField *email; @property (nonatomic, retain) NSManagedObject *team; @property (nonatomic, retain) NSManagedObject *player; @property (nonatomic, retain) MasterViewController *masterController; - (IBAction)save:(id)sender; - (IBAction)cancel:(id)sender; - (IBAction)confirmDelete:(id)sender; - (id)initWithMasterController:(MasterViewController *)aMasterController team:(NSManagedObject *)aTeam player:(NSManagedObject *)aPlayer; @end
PlayerViewController.m
, import MasterViewController.h
and add @synthesize
for all properties from the interface. Add an initialization method to PlayerVIewController.m
that will receive an instance of the class MasterViewController
, a team and possibly a player object. - (id)initWithMasterController:(MasterViewController *)aMasterController team:(NSManagedObject *)aTeam player:(NSManagedObject *)aPlayer{ if((self = [super init])){ self.masterController = aMasterController; self.team = team; self.player = player; } return self; }
viewDidLoad
add the code to fill in the text fields with the player's data, if it is not equal nil
. - (void)viewDidLoad { [super viewDidLoad]; if(player != nil){ firstName.text = [player valueForKey:@"firstName"]; lastName.text = [player valueForKey:@"lastName"]; email.text = [player valueForKey:@"email"]; } }
- (IBAction)save:(id)sender{ if(masterController != nil){ if(player != nil){ [player setValue:firstName.text forKey:@"firstName"]; [player setValue:lastName.text forKey:@"lastName"]; [player setValue:email.text forKey:@"email"]; } else { [masterController insertPlayerWithTeam:team firstName:firstName.text lastName:lastName.text email:email.text]; } } [self dismissModelViewControllerAnimated:YES]; } - (IBAction)cancel:(id)sender{ [self dismissModalViewControllerAnimated:YES]; }
insertPlayerWithTeam:firstName:lastName:email:
in the controller MasterViewController
yet, but we will write it in just a couple of minutes. First, we implement the confirmDelete:
method that is called when you click on the “Delete” button. This method will not immediately remove the player, but he will ask the user for confirmation to perform this action (this is done in order to avoid accidental clicks and deletions of the players). Here is what the method will look like confirmDelete:
: - (IBAction)confirmDelete:(id)sender{ if(player != nil){ UIActionSheet *confirm = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete Player" otherButtonTitles:nil]; confirm.actionSheetStyle = UIActionSheetStyleBlackTranslucent; [confirm showInView:self.view]; } }
UIActionSheet
will be the current class. When you click on the button, the UIActionSheet
method will be called clickedButtonAtIndex:
, which means you need to implement it. In the method there will be a check on which button was pressed and, if the button is Delete, then the method (which we will later implement) of the player’s removal will be called: - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ if(buttonIndex == 0 && masterController != nil){ [masterController deletePlayer:player]; [self dismissModalViewControllerAnimated:YES]; } }
MasterViewController.h
and declare two methods that we have not yet implemented, but have already used: - (void)insertPlayerWithTeam:(NSManagedObject *)team firstName:(NSString *)firstName lastName:(NSString *)lastName email:(NSString *)email; - (void)deletePlayer:(NSManagedObject *)player;
MasterViewController.m
and implement the methods: - (void)insertPlayerWithTeam:(NSManagedObject *)team firstName:(NSString *)firstName lastName:(NSString *)lastName email:(NSString *)email{ NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext]; NSManagedObject *player = [NSEntityDescription insertNewObjectForEntityForName:@"Player" inManagedObjectContext:context]; [player setValue:firstName forKey:@"firstName"]; [player setValue:lastName forKey:@"lastName"]; [player setValue:email forKey:@"email"]; [player setValue:team forKey:@"team"]; [self saveContext]; } - (void)deletePlayer:(NSManagedObject *)player{ NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext]; [context deleteObject:player]; [self saveContext]; }
PlayerViewController.xib
, bring it to the look shown in the image below and connect all the Actions with the corresponding buttons.showPlayerView:
that we previously used. Import to PlayerListViewController.m
file PlayerViewController.h
. - (void)showPlayerVIew{ PlayerVIewController *playerViewController = [[PlayerVIewController alloc] initWithMasterController:masterController team:team player:nil]; [self presentModalViewController:playerVIewController animated:YES]; }
PlayerListViewController.m
automatically generated method didSelectRowAtIndexPath:
and bring it to the following form: - (void)tableView:(UITableVIew *)tableVIew didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ NSManagedObject *player = [[self sortPlayers] objectAtIndex:indexPath.row]; PlayerViewController *playerViewController = [[PlayerVIewController alloc] initWithMasterController:masterController team:team player:player]; [self presentModalVIewController:playerViewController animated:YES]; }
sqlite3
to study the structure of the database that Core Data generates. At the end of the section on working with SQLite storages, we will search our database League_Manager.sqlite3
and start sqlite3
passing the name of our database as an input parameter. sqlite3 ./5.0/Applications/CE79C20B-4CBF-47C3–9E7C- 9EC24FA22488/Documents/League_Manager.sqlite
sqlite3
them and them, and see what happens. sqlite> .tables ZPLAYER ZTEAM Z_METADATA Z_PRIMARYKEY
ZPLAYER
stores entity data Player
; ZTEAM
- stores entity data Team
. sqlite> select * from ZTEAM; 1|2|3|Crew|Blue 2|2|1|Fire|Red 3|2|1|Revolution|Green
sqlite> select * from ZPLAYER;
sqlite3
displaying all players in the application: sqlite> select * from ZPLAYER; 1|1|1|1|Jordan|Gordan|jgordon@example.com 2|1|1|1|Pat|Sprat|psprat@example.com 3|1|1|1|Bailey|Staley|bstaley@example.com
sqlite> select ZTEAM.ZNAME, ZPLAYER.ZFIRSTNAME, ZPLAYER.ZLASTNAME from ZTEAM, ZPLAYER where ZTEAM.Z_PK = ZPLAYER.ZTEAM; Crew|Jordan|Gordon Crew|Pat|Sprat Crew|Bailey|Staley Fire|Terry|Gary
sqlite> select ZTEAM.ZNAME, ZPLAYER.ZFIRSTNAME, ZPLAYER.ZLASTNAME from ZTEAM, ZPLAYER where ZTEAM.Z_PK = ZPLAYER.ZTEAM; Crew|Jordan|Gordon Crew|Bailey|Staley Fire|Terry|Gary
sqlite> select ZTEAM.ZNAME, ZPLAYER.ZFIRSTNAME, ZPLAYER.ZLASTNAME from ZTEAM, ZPLAYER where ZTEAM.Z_PK = ZPLAYER.ZTEAM; Crew|Jordan|Gordon Crew|Bailey|Staley
NSPersistentStoreCoordinator
specify a different type when creating it NSSQLiteStoreType
.persistentStoreCoordinator:
in the League_ManagerAppDelegate.m
file: - (NSPersistentStoreCoordinator *)persistentStoreCoordinator { if(_persistentStoreCoordinator != nil) return _persistentStoreCoordinator; // NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"League_Manager.sqlite"]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; if(![_persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error]){ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _persistentStoreCoordinator; }
Starting with iOS 4 and the introduction of multitasking, switching to another application does not necessarily lead to the termination of your application. Instead of completing, our application continues to work in the background, and therefore the data in the memory continues to remain.
NSAtomicStore
class (subclass NSPersistentStore
) that provides the capabilities (methods) necessary to work with the data. To better understand how this works, imagine the two inner layers inside the Core Data Framework, as shown in the image below:NSManagedObject
and NSManagedObjectContext
. The second layer directly stores the data and contains the data stores and data warehouse coordinator. In the case of custom storage types, the data storage layer also contains NSAtomicStoreCacheNode
, which stores objects containing the data itself. Attitude NSAtomicStoreCacheNode
towards NSAtomicStore
the same as NSManagedObject
k NSManagedObjectContext
.CustomStore
. Add a new class to the League Manager that will inherit from the NSAtomicStore. #import <Foundation/Foundation.h> @interface CustomStore : NSAtomicStore { } @end
#import "CustomStore.h" @implementation CustomStore #pragma mark - NSPersistentStore - (NSString *)type { return [[self metadata] objectForKey:NSStoreTypeKey]; } - (NSString *)identifier { return [[self metadata] objectForKey:NSStoreUUIDKey]; } - (id)initWithPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator configurationName:(NSString *)configurationName URL:(NSURL *)url options:(NSDictionary *)options { self = [super initWithPersistentStoreCoordinator:coordinator configurationName:configurationName URL:url options:options]; return self; } + (NSDictionary *)metadataForPersistentStoreWithURL:(NSURL *)url error:(NSError **)error { return nil; } #pragma mark - NSAtomicStore - (BOOL)load:(NSError **)error { return YES; } - (id)newReferenceObjectForManagedObject:(NSManagedObject *)managedObject { return nil; } - (NSAtomicStoreCacheNode *)newCacheNodeForManagedObject:(NSManagedObject *)managedObject { return nil; } - (BOOL)save:(NSError **)error { return YES; } - (void)updateCacheNode:(NSAtomicStoreCacheNode *)node fromManagedObject:(NSManagedObject *)managedObject { } @end
NSPersistentStoreCoordinator'
manage different types of vaults. The NSPersistentStore
metadata is presented in the form of a dictionary NSDictionary
. The values of the two keys are of particular interest: NSStoreTypeKey
and NSStoreUUIDKey
. The value for the key NSStoreTypeKey
must be a unique string identifying the type of storage, and NSStoreUUIDKey
the storage itself. + (NSString *)makeUUID { CFUUIDRef uuidRef = CFUUIDCreate(NULL); CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef); CFRelease(uuidRef); NSString *uuid = [NSString stringWithString:(__bridge NSString *)uuidStringRef]; CFRelease(uuidStriingRef); return uuid; }
metadataForPersistentStoreWithURL:error:
. + (void)writeMetadata:(NSDictionary *)metadata toURL:(NSURL *)url { NSString *path = [[url relativePath] stringByAppendingString:@".plist"]; [metadata writeToFile:path atomically:YES]; }
NSStoreTypeKey
, NSStoreUUIDKey
. Find the method metadataForPersistentStoreWithURL:error:
and change its body so that it checks for the presence of the metadata file and, if there is no such file, it was created (with the specified keys) along with an empty storage file (text file). + (NSDictionary *)metadataForPersistentStoreWithURL:(NSURL *)url error:(NSError **)error { // determine the filename for metadata file NSString *path = [[url relativePath] stringByAppendingString:@".plist"]; if(![[NSFileManager defaultManager] fileExistsAtPath:path]) { // create a dictionary and store the store type key (CustomStore) // and the UUID key NSMutableDictionary *metadata = [NSMutableDictionary dictionary]; [metadata setValue:@"CustomStore" forKey:NSStoreTypeKey]; [metadata setValue:[CustomStore makeUUID] forKey:NSStoreUUIDKey]; // write the metadata to the .plist file [CustomStore writeMetadata:metadata toURl:url]; // write an empty data file [@"" writeToURL:url atomically:YES encoding:[NSString defaultCStringEncoding] error:nil]; NSLog(@"Created new store at %@", path); } return [NSDictionary dictionaryWithContentsOfFile:path]; }
- (id)initWithPersistentStoreCoordinator:(NSPersistentStoreCoordinator *)coordinator configurationName:(NSString *)configurationName URL:(NSURL *)url options:(NSDictionary *)options { self = [super initWithPersistentStoreCoordinator:coordinator configurationName:configurationName URL:url options:options]; NSDictionary *metadata = [CustomStore metadataForPersistentStoreWithURL:[self URL] error:nil]; [self setMetadata:metadata]; return self; }
NSManagedObject'
. The referencing objects are unique identifiers for each NSAtomicStoreCacheNode'
(like the primary key, the relationship is the same as NSObjectID
for NSManagedObject
). Since a custom repository is responsible for translating between NSManagedObject'
to NSAtomicStoreCacheNode
, it must be able to create a referring object for those just created NSManagedObject'
. To do this, we use the UUID again: - (id)newReferenceObjectForManagedObject:(NSManagedObject *)managedObject { NSString *uuid = [CustomStore makeUUID]; return uuid; }
NSAtomicStoreCacheNode
class for the corresponding NSManagedObject
object. When a new one NSManagedObject
is created and the framework needs to save it, the method is called newReferenceObjectForManagedObject:
. NSAtomicCache
keeps track of relationships between NSObjectID
and referencing objects. When Core Data saves NSManagedObjects to local storage, a method is invoked newCacheNodeForManagedObject:
that, as can be seen from its name, creates a new instance NSAtomicStoreCacheNode
that serves as an analogue of NSManagedObject. - (NSAtomicStoreCacheNode *)newCacheNodeForManagedObject:(NSManagedObject *)managedObject { NSManagedObjectID *oid = [managedObject objectID]; id referenceID = [self referenceObjectForObjectID:oid]; NSAtomicStoreCacheNode* node = [self nodeForReferenceObject:referenceID andObjectID:oid]; [self updateCacheNode:node fromManagedObject:managedObject]; return node; }
newCacheNodeForManagedObject:
searches for a referencing object that was created for the corresponding NSManagedObject object and creates a new NSAtomicStoreCacheNode with copying all the fields from the NSManagedObject instance.Source: https://habr.com/ru/post/192960/
All Articles