Sicherung und Verschlüsselung von Daten unter iOS
German (Deutsch) translation by Federicco Ancie (you can also view the original English article)
Unabhängig davon, ob Sie eine mobile Anwendung oder einen Webdienst erstellen, ist es wichtig, vertrauliche Daten sicher zu halten, und Sicherheit ist zu einem wesentlichen Aspekt jedes Softwareprodukts geworden. In diesem Tutorial werde ich Ihnen zeigen, wie Benutzeranmeldeinformationen mithilfe des Schlüsselbunds der Anwendung sicher gespeichert werden, und wir werden uns mit dem Ver- und Entschlüsseln von Benutzerdaten mithilfe einer Bibliothek eines Drittanbieters befassen.
Einführung
In diesem Tutorial werde ich Ihnen zeigen, wie Sie vertrauliche Daten auf der iOS-Plattform sichern. Sensible Daten können die Anmeldeinformationen eines Benutzers oder Kreditkartendaten sein. Die Art der Daten ist nicht so wichtig. In diesem Tutorial verwenden wir den Schlüsselbund und die symmetrische Verschlüsselung von iOS, um die Daten des Benutzers sicher zu speichern. Bevor wir uns mit den Details befassen, möchte ich Ihnen einen Überblick darüber geben, was wir in diesem Tutorial tun werden.
iOS Schlüsselbund
Unter iOS und OS X ist ein Schlüsselbund ein verschlüsselter Container zum Speichern von Kennwörtern und anderen Daten, die gesichert werden müssen. Unter OS X ist es möglich, den Schlüsselbundzugriff auf bestimmte Benutzer oder Anwendungen zu beschränken. Unter iOS verfügt jedoch jede Anwendung über einen eigenen Schlüsselbund, auf den nur die Anwendung Zugriff hat. Dies stellt sicher, dass die im Schlüsselbund gespeicherten Daten sicher sind und für Dritte nicht zugänglich sind.
Beachten Sie, dass der Schlüsselbund nur zum Speichern kleiner Daten wie Kennwörter verwendet werden sollte. Mit diesem Artikel möchte ich Sie davon überzeugen, wie wichtig es ist, den Schlüsselbund unter iOS und OS X zu verwenden, anstatt beispielsweise die Standarddatenbank der Anwendung zu verwenden, in der die Daten im Klartext ohne jegliche Sicherheit gespeichert werden.
Unter iOS kann eine Anwendung den Schlüsselbund über die Keychain Services-API verwenden. Die API bietet eine Reihe von Funktionen zum Bearbeiten der im Schlüsselbund der Anwendung gespeicherten Daten. Schauen Sie sich die Funktionen an, die unter iOS verfügbar sind.
-
SecItemAddDiese Funktion wird zum Hinzufügen eines Elements zum Schlüsselbund der Anwendung verwendet. -
SecItemCopyMatchingMit dieser Funktion können Sie ein Schlüsselbundelement suchen, das der Anwendung gehört. -
SecItemDeleteWie der Name schon sagt, kann diese Funktion verwendet werden, um ein Element aus dem Schlüsselbund der Anwendung zu entfernen. -
SecItemUpdateVerwenden Sie diese Funktion, wenn Sie ein Element im Schlüsselbund der Anwendung aktualisieren müssen.
Die Keychain Services-API ist eine C-API, aber ich hoffe, das hindert Sie nicht daran, sie zu verwenden. Jede der oben genannten Funktionen akzeptiert ein Wörterbuch (CFDictionaryRef), das ein Element-Klasse-Schlüssel-Wert-Paar und optionale Attribut-Schlüssel-Wert-Paare enthält. Die genaue Bedeutung und der Zweck jedes einzelnen werden klar, sobald wir die API in einem Beispiel verwenden.
Verschlüsselung und Entschlüsselung
Wenn Sie über Verschlüsselung sprechen, hören Sie im Allgemeinen zwei Arten der Verschlüsselung, die symmetrische und die asymmetrische Verschlüsselung. Bei der symmetrischen Verschlüsselung wird einerseits ein gemeinsamer Schlüssel zum Ver- und Entschlüsseln von Daten verwendet. Bei der asymmetrischen Verschlüsselung wird dagegen ein Schlüssel zum Verschlüsseln von Daten und ein anderer separater, aber verwandter Schlüssel zum Entschlüsseln von Daten verwendet.
In diesem Tutorial nutzen wir das unter iOS verfügbare Sicherheitsframework, um Daten zu verschlüsseln und zu entschlüsseln. Dieser Prozess findet unter der Haube statt, sodass wir nicht direkt mit diesem Framework interagieren. In unserer Beispielanwendung verwenden wir symmetrische Verschlüsselung.
Das Sicherheitsframework bietet eine Reihe anderer Dienste, z. B. Randomisierungsdienste zum Generieren kryptografisch sicherer Zufallszahlen, Zertifikat-, Schlüssel- und Vertrauensdienste zum Verwalten von Zertifikaten, öffentlichen und privaten Schlüsseln sowie Vertrauensrichtlinien. Das Sicherheitsframework ist ein Low-Level-Framework, das sowohl unter iOS als auch unter OS X mit C-basierten APIs verfügbar ist.
Anwendungsübersicht
In diesem Tutorial werde ich Ihnen zeigen, wie Sie die Keychain Services-API und die symmetrische Verschlüsselung in einer iOS-Anwendung verwenden können. Wir erstellen eine kleine Anwendung, in der vom Benutzer aufgenommene Fotos sicher gespeichert werden.
In diesem Projekt verwenden wir Sam Soffes SSKeychain, einen Objective-C-Wrapper für die Interaktion mit der Keychain Services-API. Für die Ver- und Entschlüsselung verwenden wir RNCryptor, eine Verschlüsselungsbibliothek von Drittanbietern.
Daten mit RNCryptor verschlüsseln
Die RNCryptor-Bibliothek ist eine gute Wahl zum Ver- und Entschlüsseln von Daten. Das Projekt wird von vielen Entwicklern verwendet und von seinen Entwicklern aktiv gepflegt. Die Bibliothek bietet eine benutzerfreundliche Objective-C-API. Wenn Sie mit Kakao und Objective-C vertraut sind, finden Sie es einfach zu bedienen. Die Hauptfunktionen der Bibliothek sind unten aufgeführt.
- AES-256-Verschlüsselung
- CBC-Modus
- Kennwortverlängerung mit PBKDF2
- Passwort salzen
- Zufällig IV
- Encrypt-Then-Hash HMAC
Anwendungsablauf
Bevor wir mit der Erstellung der Anwendung beginnen, möchte ich Ihnen zeigen, wie der typische Ablauf der Anwendung aussehen wird.
- Wenn der Benutzer die Anwendung startet, wird ihm eine Ansicht zum Anmelden angezeigt.
- Wenn sie noch kein Konto erstellt hat, werden ihre Anmeldeinformationen zum Schlüsselbund hinzugefügt und sie ist angemeldet.
- Wenn sie ein Konto hat, aber ein falsches Passwort eingibt, wird eine Fehlermeldung angezeigt.
- Sobald sie angemeldet ist, hat sie Zugriff auf die Fotos, die sie mit der Bewerbung aufgenommen hat. Die Fotos werden von der Anwendung sicher gespeichert.
- Immer wenn sie mit der Kamera des Geräts ein Foto aufnimmt oder ein Foto aus ihrer Fotobibliothek auswählt, wird das Foto verschlüsselt und im Dokumentverzeichnis der Anwendung gespeichert.
- Immer wenn sie zu einer anderen Anwendung wechselt oder das Gerät gesperrt wird, wird sie automatisch abgemeldet.
Erstellen der Anwendung
Schritt 1: Projekteinrichtung
Starten Sie Xcode und erstellen Sie ein neues Projekt, indem Sie die Single View-Anwendungsvorlage aus der Liste der Vorlagen auswählen.



Benennen Sie das Projekt Secure Photos und stellen Sie die Gerätefamilie auf iPhone ein. Teilen Sie Xcode mit, wo Sie das Projekt speichern möchten, und klicken Sie auf Erstellen.



Schritt 2: Frameworks
Der nächste Schritt besteht darin, das Projekt mit den Frameworks für Sicherheit und Mobile Core Services zu verknüpfen. Wählen Sie das Projekt im Projektnavigator links aus, wählen Sie das erste Ziel mit dem Namen Sichere Fotos aus und öffnen Sie oben die Registerkarte Phasen erstellen. Erweitern Sie die Schublade Link Binary With Libraries und verknüpfen Sie das Projekt mit den Frameworks Security und Mobile Core Services.



Schritt 3: Abhängigkeiten
Wie bereits erwähnt, verwenden wir die SSKeychain-Bibliothek und die RNCryptor-Bibliothek. Laden Sie diese Abhängigkeiten herunter und fügen Sie sie dem Projekt hinzu. Stellen Sie sicher, dass Sie die Dateien in Ihr Projekt kopieren und dem Ziel Sichere Fotos hinzufügen (siehe Abbildung unten).



Schritt 4: Klassen erstellen
Wir werden die Fotos des Benutzers in einer Sammlungsansicht anzeigen, was bedeutet, dass wir sowohl UICollectionViewController als auch UICollectionViewCell in Unterklassen unterteilen müssen. Wählen Sie im Menü Datei die Option Neu > Datei..., erstellen Sie eine Unterklasse von UICollectionViewController und nennen Sie sie MTPhotosViewController. Wiederholen Sie diesen Schritt noch einmal für MTPhotoCollectionViewCell, eine Unterklasse von UICollectionViewCell.
Schritt 5: Erstellen der Benutzeroberfläche
Öffnen Sie das Haupt-Storyboard des Projekts und aktualisieren Sie das Storyboard wie im folgenden Screenshot gezeigt. Das Storyboard enthält zwei View Controller, eine Instanz von MTViewController, die zwei Textfelder und eine Schaltfläche enthält, und eine Instanz von MTPhotosViewController. Die MTViewController-Instanz ist in einen Navigationscontroller eingebettet.
Wir müssen auch einen Übergang von der MTViewController-Instanz zur MTPhotosViewController-Instanz erstellen. Setzen Sie die ID des Segues auf photosViewController. Die MTPhotosViewController-Instanz sollte auch ein Balkenschaltflächenelement enthalten, wie im folgenden Screenshot gezeigt.



Damit dies alles funktioniert, müssen wir die Schnittstelle von MTViewController wie unten gezeigt aktualisieren. Wir deklarieren für jedes Textfeld einen Ausgang und eine Aktion, die über die Schaltfläche ausgelöst wird. Stellen Sie die erforderlichen Verbindungen im Haupt-Storyboard des Projekts her.
1 |
#import <UIKit/UIKit.h>
|
2 |
|
3 |
@interface MTViewController : UIViewController |
4 |
|
5 |
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField; |
6 |
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField; |
7 |
|
8 |
- (IBAction)login:(id)sender; |
9 |
|
10 |
@end
|
Deklarieren Sie in der MTPhotosViewController-Klasse eine Eigenschaft mit dem Namen username zum Speichern des Benutzernamens des aktuell angemeldeten Benutzers und deklarieren Sie eine Aktion für das Balkenschaltflächenelement. Vergessen Sie nicht, die Aktion mit dem Balkenschaltflächenelement im Haupt-Storyboard zu verbinden.
1 |
#import <UIKit/UIKit.h>
|
2 |
|
3 |
@interface MTPhotosViewController : UICollectionViewController |
4 |
|
5 |
@property (copy, nonatomic) NSString *username; |
6 |
|
7 |
- (IBAction)photos:(id)sender; |
8 |
|
9 |
@end
|
Schritt 6: Implementieren von MTViewController
Fügen Sie in MTViewController.m eine Importanweisung für die MTPhotosViewController-Klasse, die SSKeychain-Klasse und die MTAppDelegate-Klasse hinzu. Wir passen die MTViewController-Klasse auch an das UIAlertViewDelegate-Protokoll an.
1 |
#import "MTViewController.h"
|
2 |
|
3 |
#import "SSKeychain.h"
|
4 |
#import "MTAppDelegate.h"
|
5 |
#import "MTPhotosViewController.h"
|
6 |
|
7 |
@interface MTViewController () <UIAlertViewDelegate> |
8 |
|
9 |
@end
|
Der nächste Schritt ist die Implementierung der zuvor deklarierten Aktion login:. Wir überprüfen zunächst, ob der Benutzer bereits ein Konto erstellt hat, indem wir das Kennwort für das Konto abrufen. In diesem Fall verwenden wir den Schlüsselbund der Anwendung, um festzustellen, ob das vom Benutzer eingegebene Kennwort mit dem im Schlüsselbund gespeicherten übereinstimmt. Die von der SSKeychain-Bibliothek bereitgestellten Methoden erleichtern das Lesen und Bearbeiten von Daten, die im Schlüsselbund der Anwendung gespeichert sind.
1 |
- (IBAction)login:(id)sender { |
2 |
if (self.usernameTextField.text.length > 0 && self.passwordTextField.text.length > 0) { |
3 |
NSString *password = [SSKeychain passwordForService:@"MyPhotos" account:self.usernameTextField.text]; |
4 |
|
5 |
if (password.length > 0) { |
6 |
if ([self.passwordTextField.text isEqualToString:password]) { |
7 |
[self performSegueWithIdentifier:@"photosViewController" sender:nil]; |
8 |
|
9 |
} else { |
10 |
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"Error Login" message:@"Invalid username/password combination." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; |
11 |
[alertView show]; |
12 |
}
|
13 |
|
14 |
} else { |
15 |
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"New Account" message:@"Do you want to create an account?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; |
16 |
[alertView show]; |
17 |
}
|
18 |
|
19 |
} else { |
20 |
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Input" message:@"Username and/or password cannot be empty." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; |
21 |
[alertView show]; |
22 |
}
|
23 |
}
|
Wir haben den Ansichts-Controller als Delegaten der Warnungsansicht festgelegt. Dies bedeutet, dass wir das UIAlertViewDelegate-Protokoll implementieren müssen. Schauen Sie sich die Implementierung von alertView:clickedButtonAtIndex: siehe unten.
1 |
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ |
2 |
switch (buttonIndex) { |
3 |
case 0: |
4 |
break; |
5 |
case 1: |
6 |
[self createAccount]; |
7 |
break; |
8 |
default:
|
9 |
break; |
10 |
}
|
11 |
}
|
In createAccount nutzen wir die SSKeychain-Klasse, um den vom Benutzer ausgewählten Benutzernamen und das Kennwort sicher zu speichern. Wir rufen dann performSegueWithIdentifier:sender: auf.
1 |
- (void)createAccount { |
2 |
BOOL result = [SSKeychain setPassword:self.passwordTextField.text forService:@"MyPhotos" account:self.usernameTextField.text]; |
3 |
|
4 |
if (result) { |
5 |
[self performSegueWithIdentifier:@"photosViewController" sender:nil]; |
6 |
}
|
7 |
}
|
In prepareForSegue:sender: erhalten wir einen Verweis auf die MTPhotosViewController-Instanz, setzen ihre username-Eigenschaft auf den Wert des usernameTextField und setzen das passwordTextField zurück.
1 |
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ |
2 |
MTPhotosViewController *photosViewController = segue.destinationViewController; |
3 |
photosViewController.username = self.usernameTextField.text; |
4 |
self.passwordTextField.text = nil; |
5 |
}
|
Schritt 7: Implementierung von MTPhotosCollectionViewCell
Öffnen Sie MTPhotosCollectionViewCell.h und deklarieren Sie eine Steckdose mit dem Namen imageView vom Typ UIImageView.
1 |
#import <UIKit/UIKit.h>
|
2 |
|
3 |
@interface MTPhotoCollectionViewCell : UICollectionViewCell |
4 |
|
5 |
@property (weak, nonatomic) IBOutlet UIImageView *imageView; |
6 |
|
7 |
@end
|
Öffnen Sie das Haupt-Storyboard und fügen Sie der Prototypzelle der MTPhotosViewController-Instanz eine UIImageView-Instanz hinzu. Wählen Sie die Prototypzelle (nicht die Bildansicht) aus und setzen Sie ihre Klasse im Identitätsinspektor rechts auf MTPhotosCollectionViewCell. Öffnen Sie bei noch ausgewählter Prototypzelle den Attributinspektor und setzen Sie die Kennung auf PhotoCell.
Schritt 8: Implementieren von MTPhotosViewController
Importieren Sie zunächst die erforderlichen Header-Dateien in MTPhotosViewController.m, wie unten gezeigt. Wir müssen außerdem zwei Eigenschaften deklarieren: photos zum Speichern des Arrays von Fotos, die in der Sammlungsansicht angezeigt werden, und filePath, um einen Verweis auf den Dateipfad beizubehalten. Möglicherweise haben Sie bemerkt, dass die MTPhotosViewController-Klasse den Protokollen UIActionSheetDelegate, UINavigationControllerDelegate und UIImagePickerControllerDelegate entspricht.
1 |
#import "MTPhotosViewController.h"
|
2 |
|
3 |
#import <MobileCoreServices/MobileCoreServices.h>
|
4 |
|
5 |
#import "RNDecryptor.h"
|
6 |
#import "RNEncryptor.h"
|
7 |
#import "MTPhotoCollectionViewCell.h"
|
8 |
|
9 |
@interface MTPhotosViewController () <UIActionSheetDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate> |
10 |
|
11 |
@property (strong, nonatomic) NSMutableArray *photos; |
12 |
@property (copy, nonatomic) NSString *filePath; |
13 |
|
14 |
@end
|
Ich habe auch eine Convenience- oder Hilfsmethode, setupUserDirectory, implementiert, um die erforderlichen Verzeichnisse zu erstellen und einzurichten, in denen die Benutzerdaten gespeichert werden. In prepareData entschlüsselt die Anwendung die Bilder, die im sicheren Verzeichnis des Benutzers gespeichert sind. Schauen Sie sich ihre Implementierungen unten an.
1 |
- (void)setupUserDirectory { |
2 |
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); |
3 |
NSString *documents = [paths objectAtIndex:0]; |
4 |
self.filePath = [documents stringByAppendingPathComponent:self.username]; |
5 |
NSFileManager *fileManager = [NSFileManager defaultManager]; |
6 |
|
7 |
if ([fileManager fileExistsAtPath:self.filePath]) { |
8 |
NSLog(@"Directory already present."); |
9 |
|
10 |
} else { |
11 |
NSError *error = nil; |
12 |
[fileManager createDirectoryAtPath:self.filePath withIntermediateDirectories:YES attributes:nil error:&error]; |
13 |
|
14 |
if (error) { |
15 |
NSLog(@"Unable to create directory for user."); |
16 |
}
|
17 |
}
|
18 |
}
|
1 |
- (void)prepareData { |
2 |
self.photos = [[NSMutableArray alloc] init]; |
3 |
NSFileManager *fileManager = [NSFileManager defaultManager]; |
4 |
|
5 |
NSError *error = nil; |
6 |
NSArray *contents = [fileManager contentsOfDirectoryAtPath:self.filePath error:&error]; |
7 |
|
8 |
if ([contents count] && !error){ |
9 |
NSLog(@"Contents of the user's directory. %@", contents); |
10 |
|
11 |
for (NSString *fileName in contents) { |
12 |
if ([fileName rangeOfString:@".securedData"].length > 0) { |
13 |
NSData *data = [NSData dataWithContentsOfFile:[self.filePath stringByAppendingPathComponent:fileName]]; |
14 |
NSData *decryptedData = [RNDecryptor decryptData:data withSettings:kRNCryptorAES256Settings password:@"A_SECRET_PASSWORD" error:nil]; |
15 |
UIImage *image = [UIImage imageWithData:decryptedData]; |
16 |
[self.photos addObject:image]; |
17 |
|
18 |
} else { |
19 |
NSLog(@"This file is not secured."); |
20 |
}
|
21 |
}
|
22 |
|
23 |
} else if (![contents count]) { |
24 |
if (error) { |
25 |
NSLog(@"Unable to read the contents of the user's directory."); |
26 |
} else { |
27 |
NSLog(@"The user's directory is empty."); |
28 |
}
|
29 |
}
|
30 |
}
|
Rufen Sie beide Methoden in der viewDidLoad-Methode des View Controllers auf, wie unten gezeigt.
1 |
- (void)viewDidLoad { |
2 |
[super viewDidLoad]; |
3 |
|
4 |
[self setupUserDirectory]; |
5 |
[self prepareData]; |
6 |
}
|
Das Balkenschaltflächenelement in der Navigationsleiste des View Controllers zeigt ein Aktionsblatt, in dem der Benutzer zwischen der Kamera des Geräts und der Fotobibliothek wählen kann.
1 |
- (IBAction)photos:(id)sender { |
2 |
UIActionSheet *actionSheet = [[UIActionSheet alloc]initWithTitle:@"Select Source" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Camera", @"Photo Library", nil]; |
3 |
[actionSheet showFromBarButtonItem:sender animated:YES]; |
4 |
}
|
Implementieren wir actionSheet:clickedButtonAtIndex: des UIActionSheetDelegate-Protokolls.
1 |
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ |
2 |
if (buttonIndex < 2) { |
3 |
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; |
4 |
imagePickerController.mediaTypes = @[(__bridge NSString *)kUTTypeImage]; |
5 |
imagePickerController.allowsEditing = YES; |
6 |
imagePickerController.delegate = self; |
7 |
|
8 |
if (buttonIndex == 0) { |
9 |
#if TARGET_IPHONE_SIMULATOR
|
10 |
|
11 |
#else
|
12 |
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; |
13 |
#endif
|
14 |
|
15 |
} else if ( buttonIndex == 1) { |
16 |
imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; |
17 |
}
|
18 |
|
19 |
[self.navigationController presentViewController:imagePickerController animated:YES completion:nil]; |
20 |
}
|
21 |
}
|
Um die Auswahl des Benutzers im Image Picker Controller zu handhaben, müssen wir imagePickerController:didFinishPickingMediaWithInfo: des UIImagePickerControllerDelegate-Protokolls wie unten gezeigt implementieren. Das Bild wird mit encryptData der RNEncryptor-Bibliothek verschlüsselt. Das Bild wird auch dem photos array hinzugefügt und die Sammlungsansicht wird neu geladen.
1 |
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ |
2 |
UIImage *image = [info objectForKey: UIImagePickerControllerEditedImage]; |
3 |
|
4 |
if (!image) { |
5 |
[info objectForKey: UIImagePickerControllerOriginalImage]; |
6 |
}
|
7 |
|
8 |
NSData *imageData = UIImagePNGRepresentation(image); |
9 |
NSString *imageName = [NSString stringWithFormat:@"image-%d.securedData", self.photos.count + 1]; |
10 |
NSData *encryptedImage = [RNEncryptor encryptData:imageData withSettings:kRNCryptorAES256Settings password:@"A_SECRET_PASSWORD" error:nil]; |
11 |
[encryptedImage writeToFile:[self.filePath stringByAppendingPathComponent:imageName] atomically:YES]; |
12 |
[self.photos addObject:image]; |
13 |
[self.collectionView reloadData]; |
14 |
[picker dismissViewControllerAnimated:YES completion:nil]; |
15 |
}
|
Bevor Sie die Anwendung erstellen und ausführen können, müssen Sie das UICollectionViewDataSource-Protokoll wie unten gezeigt implementieren.
1 |
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { |
2 |
return self.photos ? self.photos.count : 0; |
3 |
}
|
1 |
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { |
2 |
MTPhotoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"PhotoCell" forIndexPath:indexPath]; |
3 |
cell.imageView.image = [self.photos objectAtIndex:indexPath.row]; |
4 |
return cell; |
5 |
}
|
Schritt 9: Behandeln von Anwendungszuständen
Wenn die Anwendung in den Hintergrund tritt, muss der Benutzer abgemeldet werden. Dies ist aus Sicherheitsgründen wichtig. Um dies zu erreichen, muss der Anwendungsdelegierte einen Verweis auf den Navigationscontroller haben, damit er zum Root-View-Controller des Navigationsstapels wechseln kann. Deklarieren Sie zunächst eine Eigenschaft mit dem Namen navigationController in MTAppDelegate.h.
1 |
#import <UIKit/UIKit.h>
|
2 |
|
3 |
@interface MTAppDelegate : UIResponder <UIApplicationDelegate> |
4 |
|
5 |
@property (strong, nonatomic) UIWindow *window; |
6 |
@property (strong, nonatomic) UINavigationController *navigationController; |
7 |
|
8 |
@end
|
In der viewDidLoad-Methode des View Controllers legen wir die Eigenschaft navigationController des Anwendungsdelegierten wie unten gezeigt fest. Denken Sie daran, dass dies nur eine Möglichkeit ist, damit umzugehen.
Ich habe die obige Eigenschaft in der viewDidLoad-Methode von ViewController wie unten gezeigt festgelegt.
1 |
- (void)viewDidLoad { |
2 |
[super viewDidLoad]; |
3 |
|
4 |
MTAppDelegate *applicationDeleagte = (MTAppDelegate *)[[UIApplication sharedApplication] delegate]; |
5 |
[applicationDeleagte setNavigationController:self.navigationController]; |
6 |
}
|
Im Anwendungsdelegierten müssen wir applicationWillResignActive: wie unten gezeigt aktualisieren. So einfach ist das. Das Ergebnis ist, dass der Benutzer abgemeldet wird, wenn die Anwendung den Fokus verliert. Es schützt die in der Anwendung gespeicherten Bilder des Benutzers vor neugierigen Blicken. Der Nachteil ist, dass sich der Benutzer anmelden muss, wenn die Anwendung wieder aktiv wird.
1 |
- (void)applicationWillResignActive:(UIApplication *)application { |
2 |
[self.navigationController popToRootViewControllerAnimated:NO]; |
3 |
}
|
Schritt 10: Erstellen und ausführen
Erstellen Sie das Projekt und führen Sie die Anwendung aus, um sie auf Herz und Nieren zu testen.
Abschluss
In diesem Lernprogramm haben Sie gelernt, wie Sie die Keychain Services-API zum Speichern vertraulicher Daten verwenden und wie Sie Bilddaten unter iOS verschlüsseln. Hinterlassen Sie einen Kommentar in den Kommentaren unten, wenn Sie Fragen oder Feedback haben.



