Introduction

iOS provides many forms of local storage: file system (NSFileManager), relational database (SQLite), object-oriented storage system (Core Data) and information area sensitive encrypted (Keychain). In this section we will analyze the main functionalities of each of them, we will compare SQLite with Core Data and finally we will discuss how we can add the powerful capacity iCloud to a project developed with Core Data. For example, Adding iCloud to a notebook project allows us to keep our notebooks synchronized. notes in real time between our different Apple devices.

5.1 File system: Bundle and document area

Bundle: It is the area that contains the resources added at compile time. It is a read-only area. It can contain any type of document: images, xml files, videos, databases SQLite, etc.
It is important to ensure that within the Build Phases, in Copy Bundle Resources are the resources that we want to package with our application.

FaseBuildCopy.png

In this case we add, among others, an image banana.png and a base file data database.sqlite3 which we will use in the next point.

On many occasions we need to access the path of one of these resources. In this example we access the path of the image Banana.png

Swift:

Document area: The documents area of our application. It is an area read/write. To access the base path we use:

Swift:

In this area we can perform all the functions related to a file system traditional, both on files and directories: copy, move, delete, ask for existence, etc.

The base path to the document area may not be created. It is important to use the following code to make sure it exists before using it to save documents:

Swift:

When we use a local database we usually add to the bundle a file database that contains the definition of the tables and the initial data that we want it to contain the application. When the application is executed we see if that file exists in the documents area, if it does not exist we copy it from the Bundle. We made this copy because the Bundle It is a read-only space.

Swift:

If we want to write a text file in the document area, it is important to take into account the encoding of the text. Usually working with UTF-8. what we do is:

  • Access the base path.
  • Convert the NSString to an NSData by encoding it in UTF-8.
  • (NSData* content = [data dataUsingEncoding:NSUTF8StringEncoding] and
  • Finally use the writeToFile method of NSData to write the file.

writeToFile has a parameter called atomically which indicates If you should first write the information to a temporary file and then rename it. This is an extreme security measure used to ensure that there is space, that there will be no battery problems, etc.

5.2 Local relational databases (SQLite)

Apple provides database access SQLite ( https://www.sqlite.org/ SQLite is a library that implements a relational database transactional model based in a single file. Its code is in the public domain and can be used for any use private or commercial. We must have knowledge of relational database modeling: define entities, relationships, primary keys, foreign keys, indexes, etc.

There are many free tools that allow us to model our database before add it to our project as a resource. There is another option which consists of entering within the application code the SQL scripts for the creation and initialization of the tables. In this section we are going to opt for the first option. We will use the application free for mac SQLiteBrowser ( https://sqlitebrowser.org/ to model our database.

rel_1_N.png

BriwserSQLite.png

We create the tables User and City. We model the relationship 1:N as an attribute within the table User which points to the attribute city_id (your primary key) of the table City. The file database.sqlite3 generated by SQLiteBrowser can be added directly to our project.

As we have already commented in the file system section, in the method viewDidLoad of our ViewController we check if it exists our database file in our documents area. If it does not exist, it will be the first time run the application, we copy from the bundle our resource to the area of documents. We made this copy because the bundle It is a reading area and we need access to the database in read/write mode.

Swift:

Now we can open the database from the documents area.

Swift:

We have declared the variable g_database in a way static. If usually done this way or using a pattern Singleton to prevent performance issues, erroneous readings, etc. This does not ensure its proper functioning in concurrent applications. A good solution is to create a single thread that is responsible for all operations with the database or even perform them in the main thread.

The use of SQLite in our application it involves the construction of sentences in language SQL inside the code.

To insert rows we will build sentences SQL,insert.

Swift:

- The parties parameterizable of the sentence are marked on the string SQL with the symbol ? and are instantiated using:
Swift:

- We execute the sentence with:
Swift:

In our example we will first insert rows from the table City for later to insert table rows User be able to relate them to that table using the foreign key user_city_id

Swift:

We carry out the queries by constructing type statements select. In the construction of the select we will have to use joins for combine two or more tables correctly using their primary keys and indexes.

We iterate over the query results using sqlite3_step. We access the values of each attribute selected using the methods sqlite3_column_intandsqlite3_column_text depending of the type of the returned attribute.

Swift:

Update operations (update) and erased (delete) They consist of generating your SQL statements.

Update:
Swift:

Deletion:
Swift:

5.3 Core Data, object-oriented storage system

Core Data provides an object storage layer. This layer can use different storage media: SQLite, binary, etc. In our explanation we will use storage of objects SQLite.

Our object structure will be this:

rel_1_N_objects.png

When we create our project we indicate that we will use Core Data, marking the check “Use Core Data".

check_coredata.png

This will generate a file CoreData1.xcdatamodeld, by clicking on it We will access the data model editor. This editor allows us to model objects and their relationships.

The editor has two views: graphical and tabular.

EditorModeloVisual.png

EditorModeloTabla.png

We distinguish two types of structure elements:

Entity: Similar to the entities of the relational model (click on the button Add Entity). These entities have attributes (click on the button Add Attribute).
Relationship: Similar to the relationships in the relational model (press and hold the button Add Attribute so that a menu appears that allows us create relationships, Add Relationship).

Although we have commented that the previous types of objects have analogies with their counterparts in a relational database system, there are important differences. we don't have have to define primary keys or indexes. The system will automatically manage the object identifiers that you will then use to materialize relationships.

Once the model is created we will generate the classes.

- We click on New File on the file tree and select iOS/Core Data/ NSManagedObject subclasses

crear_clases1.png

- We select a model, we can have several in the same project.

crear_clases2.png

- We select the entities of the model that we want to generate. We can mark them all.

crear_clases3.png

- The result is the generation of classes that we can use directly in our project.
These classes derive from NSManagedObject. This class is the one that allows the recording and reading objects using Core Data.

create_classes3.png

Important: Some model modifications using the visual editor make necessary the deletion of the generated classes and a new generation.

We start using the model created in the code.

- We add an NSManagedObjectContext property to our ViewController.

@property (nonatomic, strong) NSManagedObjectContext* m_managedObjectContext;

This class represents a set of objects loaded and managed by Core Data.

- To start using our model we must perform three basic operations: determine the concurrency mode, indicate its definition and select a recording format.

In the method viewDidLoad of our ViewController we perform these operations.

We create our property NSManagedObjectContext indicating that he will manage the model from main thread NSMainQueueConcurrencyType ( Concurrent programming section ).

This should only be done if we know that our objects will be small in size. we can indicate that our model be managed from another thread using NSPrivateQueueConcurrencyType

self.m_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// Other threads
// self.m_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

- We indicate where the file that contains the definition information is located (entities, relationships, type of attributes, etc.).

NSBundle*bundle = [ NSBundlebundleWithIdentifier:@"com.TheOrganization.CoreData1"];
NSURL*url = [bundle URLForResource:@"CoreData1"withExtension: @"momd"];

NSManagedObjectModel*model = [[ NSManagedObjectModel alloc] initWithContentsOfURL:url];

- We indicate the format in which we want it to be recorded. We create a NSPersistentStoreCoordinator and we add a type support SQLiteaddPersistentStoreWithType:NSSQLiteStoreType. By adding such support We will indicate the name of the file. This file should not exist, it will create it and will manage Core Data. We create a Persistent Store linked to database database.sqlite with the following options:
- NSMigratePersistentStoresAutomaticallyOption: Tells the NSPersistentStoreCoordinator that you should use the information from the object model to create a physical format. In this case the physical format will be tables of SQLite.
- NSInferMappingModelAutomaticallyOption: Tells the NSPersistentStoreCoordinator that if the physical format already exists, file sqlite For example, you should check that the mapping between the object model and the physical format is correct. If it is not, use automatic rules to add or remove entities, relationships and attributes.

NSPersistentStoreCoordinator*coordinator = [[ NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:model];

NSDictionary*options = [ NSDictionarydictionaryWithObjectsAndKeys:
[ NSNumbernumberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[ NSNumbernumberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];


NSArray*searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString*documentPath = [searchPaths objectAtIndex:0];

NSURL*storeUrl = [ NSURLfileURLWithPath: [documentPath stringByAppendingPathComponent: @"database.sqlite"]];


NSError*error;
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration: nilURL:storeUrl options:options error:&error];


- Finally we indicate the model that will be managed by the NSPersistentStoreCoordinator that we just created.

self.m_managedObjectContext.persistentStoreCoordinator = coordinator;

We can add more than one NSPersistentStore to NSPersistentStoreCoordinator. The NSPersistentStore can record in different formats: SQLite, Binary, etc.

The NSPersistentStoreCoordinator is not safe in concurrent environments, you must use locking mechanisms ( Concurrent programming section ).

Once our NSManagedObjectContext we can now start work.

Creation:

- We create the objects using the constructor NSEntityDescription indicating the object NSManagedObjectContext who will take care of its management.

NSManagedObjectContext*context = [ self m_managedObjectContext];

DBCity *City = [NSEntityDescription
insertNewObjectForEntityForName:@"City"
inManagedObjectContext:context];
NSManagedObjectContext*context = [ self m_managedObjectContext];

DBCity *City = [NSEntityDescription
insertNewObjectForEntityForName:@"City"
inManagedObjectContext:context];

- We assign properties simply using the syntax of Objective-C:

City.city_name =@"London";

- Finally we call the method save of the NSManagedObjectContext:

NSError*error;
if (![context save: &error]) {
NSLog(@"Error save: %@", [error localizedDescription]);
}

The class NSManagedObjectContext performs recording intelligently. Only save changes to the model: creation, modification or deletion.

We can accumulate changes and make a single call to save. In the example Next we create three users.

DBUser *user1 = [NSEntityDescription
insertNewObjectForEntityForName:@"User"
inManagedObjectContext:context];
user1.user_name =@"John";
user1.user_phone =@"999 888 777";
user1.city_rel = City;

DBUser *user2 = [NSEntityDescription
insertNewObjectForEntityForName:@"User"
inManagedObjectContext:context];
user2.user_name =@"Steve";
user2.user_phone =@"999 888 777";
user2.city_rel = City2;


DBUser *user3 = [NSEntityDescription
insertNewObjectForEntityForName:@"User"
inManagedObjectContext:context];
user3.user_name =@"Peter";
user3.user_phone =@"999 888 777";
user3.city_rel = City;


if (![context save: &error]) {
NSLog(@"Error save: %@", [error localizedDescription]);
}

We can see that the relationships are implemented as a standard object assignment in Objective-C: user1.city_rel = City;

Selection:

The simplest selection is to load all objects of a specific class. In this case we don't use our DBUser class, we use the parent class NSManagedObject and we access the properties using the method
valueForKey.

NSError*error;
NSManagedObjectContext*context = [ self m_managedObjectContext];
NSFetchRequest*fetchRequest = [[ NSFetchRequest alloc] init];
NSEntityDescription*entity = [ NSEntityDescription
entityForName:@"User"inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray*fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject*info in fetchedObjects) {
NSLog(@"Name: %@", [info valueForKey: @"user_name"]);
}

On many occasions we only want to load the objects of a class that meet a certain condition. To perform this type of query we use the class NSPredicate. In the example we want the model to be loaded only with the users who are related to London.

Let's notice that not only the objects are loaded, all those related to them are also loaded. them. This time we will not use the generic parent class NSManagedObject, we will use our classes DBUser and DBCity. In our example we load the objects DBUser and this will cause the objects to load DBCity with which some object is related DBUser

To access the city name we use the usual way in Objective-C, user.city_rel.city_name.

NSError*error;
NSManagedObjectContext*context = [ self m_managedObjectContext];
NSFetchRequest*fetchRequest = [[ NSFetchRequest alloc] init];
NSEntityDescription*entity = [ NSEntityDescription
entityForName:@"User"inManagedObjectContext:context];
[fetchRequest setEntity:entity];

[fetchRequest setPredicate:[NSPredicatepredicateWithFormat:@"city_rel.city_name == 'London'"]];


NSArray*fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (DBUser *user in fetchedObjects) {
NSLog(@"User name: %@   City name: %@", user.user_name, user.city_rel.city_name);
}

We initialize the NSPredicate from a string where we indicate to the condition that must be fulfilled:

[NSPredicatepredicateWithFormat:@"city_rel.city_name == 'London'"]
[NSPredicatepredicateWithFormat:@"city_rel.city_name == 'London'"]

Modification:
When you change the value of any property of an object loaded in the model, it is checked to be recorded when the next method call is made save of NSManagedObjectContext.

We can modify the previous selection to modify the users' phone number connected with London.

- After loading we modify the property user_phone   user.user_phone = @"902 888 888";

- At the end of the loop that runs through them we call the method save of the NSManagedObjectContext

NSError*error;
NSManagedObjectContext*context = [ self m_managedObjectContext];
NSFetchRequest*fetchRequest = [[ NSFetchRequest alloc] init];
NSEntityDescription*entity = [ NSEntityDescription
entityForName:@"User"inManagedObjectContext:context];
[fetchRequest setEntity:entity];

[fetchRequest setPredicate:[NSPredicatepredicateWithFormat:@"city_rel.city_name == 'London'"]];


NSArray*fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (DBUser *user in fetchedObjects) {
NSLog(@"User name: %@   City name: %@", user.user_name, user.city_rel.city_name);
user.user_phone =@"902 888 888";
}

if (![context save: &error]) {
NSLog(@"Error save: %@", [error localizedDescription]);
}

Deletion:
To delete an element or set of elements we use the method deleteObject of NSManagedObjectContext.

In the example we will load into memory all the users related to the city of New York and then we will proceed to delete it.

NSError*error;
NSManagedObjectContext*context = [ self m_managedObjectContext];
NSFetchRequest*fetchRequest = [[ NSFetchRequest alloc] init];
NSEntityDescription*entity = [ NSEntityDescription
entityForName:@"User"inManagedObjectContext:context];
[fetchRequest setEntity:entity];

[fetchRequest setPredicate:[NSPredicatepredicateWithFormat:@"city_rel.city_name == 'New York'"]];

NSArray*fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (DBUser *user in fetchedObjects) {
NSLog(@"User name: %@   City name: %@", user.user_name, user.city_rel.city_name);
[context deleteObject:user];

}

if (![context save: &error]) {
NSLog(@"Error save: %@", [error localizedDescription]);
}

Important: We must execute the save method to apply the deletion to the physical storage; If we don't do it we will be deleting only the object from memory.

since iOS9Is there a way to delete all associated objects from a class without loading them into memory. We create a NSBatchDeleteRequest from of a NSFetchRequest which indicates the class to which the objects that belong we want to delete.

NSManagedObjectContext*context = [ self m_managedObjectContext];
NSFetchRequest*request = [[ NSFetchRequest alloc] initWithEntityName:@"User"];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];

NSError*deleteError =nil;

[context executeRequest:delete error: &deleteError];


NSError*error;
if (![context save: &error]) {
NSLog(@"Error save: %@", [error localizedDescription]);
}

5.4 SQLite vs. Core Data

Best SQLite in:

- Memory usage. Core Data uses a lot more memory. When we load an object We load all its attributes and all objects related to it automatically. Core Data loads object hierarchies. With SQLite we can access specific attributes of a entity and read the foreign keys of the relationships without loading the entire object related.

- Disk space. Core Data uses much more disk space, up to four times more. Adds meta information (internal information about the structure of the model), object identifiers and even duplicate information to increase the speed of load.

- Multiplatform. SQLite is a cross-platform environment. SQL commands and physical file we can use it directly in an application for another system (Android or Windows Phone).

Best in Core Data:
- Better access time. Once the initial loading has been done, let's think that a large part of the model is already in memory and therefore access is very fast.

- There is no SQL management within the code. The developers work directly with objects. We never see SQL statements, we don't have to create a model relational (definition of primary keys, foreign keys, indexes, etc.), we do not worry about type conversion to perform the recording.

- Integration with iCloud. This is one of the strong points of Core Data. It is Fully integrated with iCloud. It is very spectacular to see how our data synchronize in real time between our application running on an iPhone and it running on our iPad. We are going to cover integration with iCloud in the next point.

5.5 iCloud + Core Data

Apple has bet heavily on cloud storage. All Apple users have 5 GB of free storage.

First of all we have to configure the capabilities iCloud of our application.

iCloudConfig.png

- We access the properties of the target of our application and in capabilities we put to“on” iCloud.

Although by default they are not selected, we must select all the services: Key-value storage, iCloud Documents and CloudKit.

- We access the application settings in Apple and activate iCloud. The first time the indicator will be yellow because we have to finish the configuration.

iCloudApple.png

We edit the application definition in Apple to finish the iCloud configuration.

iCloudApple2.png

We select CloudKit, which is the current mode of use of iCloud. We click on Edit to create the containers. A container is similar to a folder of the file system. We create a container, we have to give it a name and an identifier unique using reverse domain nomenclature.

iCloudApple3.png

Once the configuration is finished we can make small changes to the code of our application example of CoreData for you to use iCloud instead of just local storage.

- At initialization Core Data, when we create the NSPersistentStore We indicate that now it will have to be synchronized automatically with a copy that will be in iCloud. To do this we indicate in which container there will be the copy.

NSDictionary*options = [ NSDictionarydictionaryWithObjectsAndKeys:
[ NSNumbernumberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[ NSNumbernumberWithBool:YES], NSInferMappingModelAutomaticallyOption,

@"iCloud.com.TheOrganization.CoreData2",NSPersistentStoreUbiquitousContainerIdentifierKey, @"CoreData1",NSPersistentStoreUbiquitousContentNameKey,nil];

[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration: nilURL:storeUrl options:options error:&error];

Now when we start the application we should get these two messages in the window Output. It is important to carry out tests with the device, since with the emulator they do not work correctly.

2016-03-25 13:32:54.091 CoreData2[3826:1802455] -[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](898): CoreData: Ubiquity:  mobileD4406686-F8CA-414A-9EDF-8222AACB3382:CoreData1
Using local storage: 1 for new NSFileManager current token <e97487ef c61cd4cb 4f80bf40 3739bdf3 bdd657b1>

After a second this will appear:

2016-03-25 13:32:59.970 CoreData2[3826:1802496] -[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](898): CoreData: Ubiquity:  mobileD4406686-F8CA-414A-9EDF-8222AACB3382:CoreData1
Using local storage: 0 for new NSFileManager current token <e97487ef c61cd4cb 4f80bf40 3739bdf3 bdd657b1>

The second message indicates that synchronization has already started with iCloud

Notifications:
We must add ourselves as notification observers NSPersistentStoreDidImportUbiquitousContentChangesNotification in the NSNotificationCenter default of our application so that changes made from another device are displayed in real time on ours.
Let's remember that each application has a NSNotificationCenter. This notification is generated locally since it is received after the information on the local physical medium, in this case SQLite, with the information received from the iCloud. What we do is update the model that we have in memory using mergeChangesFromContextDidSaveNotification

[[NSNotificationCenter defaultCenter]
addObserverForName:NSPersistentStoreDidImportUbiquitousContentChangesNotification
objects:self.m_managedObjectContext.persistentStoreCoordinator
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification*note) {
[self.m_managedObjectContext performBlock: ^{
[self.m_managedObjectContext mergeChangesFromContextDidSaveNotification:note];
}];
}];

If we use NSBatchDeleteRequest errors occur in the synchronization of iCloud. We will have to use deletion by loading the objects into memory.

5.6 Encrypted sensitive information, keychain

The services keychain provide secure local storage of information such as usernames, passwords, etc. keychain allows you to share these Secure data between different applications from the same manufacturer. This is the most characteristic important of keychain, since since iOS9 all the files are They are encrypted.

The operations on the keychain receive a dictionary NSMutableDictionary as a parameter. In this dictionary we introduce the parameters necessary to carry out each operation.

You can find a list of the constants used to define these parameters in: https://developer.apple.com/documentation/security/keychain-services

Creation:

NSString*key =@"password";
NSString*value =@"hello";

OSStatus error;
NSMutableDictionary*query = [ NSMutableDictionary dictionary];

- We specify the type of content (kSecClass) that we are going to save in the keychain service. In this case a password (kSecClassGenericPassword)

[query setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)CFBridgingRelease(kSecClass)];

- Now we indicate the key (kSecAttrAccount)

[query setObject:key forKey:(id)CFBridgingRelease(kSecAttrAccount)];

- Then we indicate when the value will be accessible (kSecAttrAccessible). You We indicate that only when the device is unlocked (kSecAttrAccessibleWhenUnlocked)

[query setObject:(id)CFBridgingRelease(kSecAttrAccessibleWhenUnlocked) forKey:(id)CFBridgingRelease(kSecAttrAccessible)];

- We indicate the value (kSecValueData)

[query setObject:[value dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)CFBridgingRelease(kSecValueData)];

- Finally we call the function SecItemAdd


error = SecItemAdd((CFDictionaryRef)CFBridgingRetain(query), NULL);

if(error != errSecSuccess){
NSLog(@"Error %d",error);
}

We use intensively CFBridgingRelease and CFBridgingRetain. This is necessary because the memory management of the security library and in general of Core Foundation It is not the same as our memory management application written in Objective-C. What we do is indicate how the ARC (Automatic Reference Counting) should behave with pointers not ARC ( https://developer.apple.com/library/ios/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html).

Consultation:

NSMutableDictionary*query = [ NSMutableDictionary dictionary];
NSString*key =@"password";
NSString*value =@"";


- We specify the type of content (kSecClass) that we want to consult in the keychain service. In this case a password (kSecClassGenericPassword)

[query setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)CFBridgingRelease(kSecClass)];

- Now we indicate the key (kSecAttrAccount):

[query setObject:key forKey:( id)CFBridgingRelease(kSecAttrAccount)];

- We indicate that we want to obtain the result as a copy of the information (kSecReturnData).  We don't want a pointer.

[query setObject:(id)kCFBooleanTrue forKey:(id)CFBridgingRelease(kSecReturnData)];

- Run the query.


CFDataRef attributes;
OSStatus error = SecItemCopyMatching((CFDictionaryRef)CFBridgingRetain(query), (CFTypeRef *)&attributes);


- Performs pointer type conversion between Objective-C pointers and non-Objective pointers. Objective-C (from the security library).

NSData*dataFromKeychain = ( __bridge_transferNSData*)attributes;

NSString*stringToReturn =nil;
if (error == errSecSuccess) {
value = [[NSString alloc] initWithData:dataFromKeychain encoding:NSUTF8StringEncoding];
}

Modify password:

NSMutableDictionary*query = [ NSMutableDictionary dictionary];

NSString*key =@"password";
NSString*value =@"1234";

[query setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)CFBridgingRelease(kSecClass)];
[query setObject: key forKey:( id)CFBridgingRelease(kSecAttrAccount)];
[query setObject:(id)CFBridgingRelease(kSecAttrAccessibleWhenUnlocked) forKey:(id)CFBridgingRelease(kSecAttrAccessible)];


- We indicate the set of attributes that we want to modify in a NSDictionary that we pass as a parameter to the function SecItemUpdate. In this case we just want to modify the value.


NSDictionary*attributesToUpdate = [ NSDictionarydictionaryWithObject:[value dataUsingEncoding:NSUTF8StringEncoding]
forKey:( id)CFBridgingRelease(kSecValueData)];


error = SecItemUpdate((CFDictionaryRef)CFBridgingRetain(query), (CFDictionaryRef)CFBridgingRetain(attributesToUpdate));

Delete key:
- We indicate the password that we want to delete

NSString*key =@"password";
NSMutableDictionary*query = [ NSMutableDictionary dictionary];
[query setObject:(id)CFBridgingRelease(kSecClassGenericPassword) forKey:(id)CFBridgingRelease(kSecClass)];
[query setObject:key forKey:( id)CFBridgingRelease(kSecAttrAccount)];

- We execute the function SecItemDelete

OSStatus status = SecItemDelete((CFDictionaryRef)CFBridgingRetain(query));

Subsequent queries will return an error.