I am trying to save NSMutableDictionary in iOS keychain using KeychainItemWrapper classes. But I am not able to save it. I am getting error
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Couldn't add the Keychain Item.'
Here is my data to be saved
{
country = USA;
id = 3;
name = "Test User";
photo = "http://www.mydomain.com/images/user1.jpg";
result = true;
"country" = 1;
}
Here is my code
// Call to save
[self storeLoggedInUserInfoInKeychainWithDictionary:dict];
-(void)storeLoggedInUserInfoInKeychainWithDictionary:(NSMutableDictionary*)dict
{
// Save Login Credentials
KeychainItemWrapper* loginUserkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_SERVICE accessGroup:nil];
NSString *error;
[loginUserkeychain setObject:(__bridge id)(kSecAttrAccessibleWhenUnlocked) forKey:(__bridge id)(kSecAttrAccessible)];
NSData *dictionaryRep = [NSPropertyListSerialization dataFromPropertyList:dict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
[loginUserkeychain setObject:dictionaryRep forKey:(__bridge id)(kSecValueData)];
}
-(NSMutableDictionary*)fetchLoggedInUserInfoFromKeychain
{
KeychainItemWrapper* loginUserkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_SERVICE accessGroup:nil];
NSString *error;
//When the NSData object object is retrieved from the Keychain, you convert it back to NSDictionary type
NSData *dictionaryRep = [loginUserkeychain objectForKey:(__bridge id)(kSecValueData)];
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:dictionaryRep mutabilityOption:NSPropertyListImmutable format:nil errorDescription:&error];
if (error) {
NSLog(#"%#", error);
}
return [NSMutableDictionary dictionaryWithDictionary:dictionary];
}
-(void)resetLoggedInUserInfoFromKeychain
{
KeychainItemWrapper* loginUserkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_SERVICE accessGroup:nil];
[loginUserkeychain resetKeychainItem];
}
Can anybody tell me whats wrong in above code ?
Thanks in advance.
After few attempts & research using below code I am able to save the data in keychain. If any one is interested can have a look at below code
-(void)storeLoggedInUserInfoInKeychainWithDictionary:(NSMutableDictionary*)dict
{
// Create encoded data
NSData *encodedData= [NSKeyedArchiver archivedDataWithRootObject:dict];
// Create encoded string from data
NSString *encodedString= [encodedData base64EncodedString];
// Save Login Credentials
KeychainItemWrapper* tranxkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_KEYCHAIN accessGroup:nil];
[tranxkeychain setObject:(__bridge id)(kSecAttrAccessibleWhenUnlocked) forKey:(__bridge id)(kSecAttrAccessible)];
[tranxkeychain setObject:LOGIN_USER_SERVICE forKey: (__bridge id)kSecAttrService];
[tranxkeychain setObject:LOGIN_USER_INFO forKey:(__bridge id)(kSecAttrAccount)];
[tranxkeychain setObject:encodedString forKey:(__bridge id)(kSecValueData)];
}
-(NSDictionary*)fetchLoggedInUserInfoFromKeychain
{
KeychainItemWrapper* tranxkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_KEYCHAIN accessGroup:nil];
[tranxkeychain setObject:(__bridge id)(kSecAttrAccessibleWhenUnlocked) forKey:(__bridge id)(kSecAttrAccessible)];
[tranxkeychain setObject:LOGIN_USER_SERVICE forKey: (__bridge id)kSecAttrService];
// Get decoded string
NSString *decodedString=[tranxkeychain objectForKey:(__bridge id)(kSecValueData)];
// Get decoded data
NSData *decodedData= [NSData dataFromBase64String:decodedString];
NSDictionary *dict =[NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
return dict;
}
-(void)resetLoggedInUserInfoFromKeychain
{
KeychainItemWrapper* tranxkeychain = [[KeychainItemWrapper alloc] initWithIdentifier:LOGIN_USER_KEYCHAIN accessGroup:nil];
[tranxkeychain resetKeychainItem];
}
Related
I have an iOS app where Users have to Register before using the app. I have created the UI in Storyboard and I am reading the Users details from the UITextfields. I then send the details to the Register API that sends back a JSON response. I am using NSURLConnection for the communication.
Here is the response I am receiving from the test URL - for testing purposes only:
{"username":"Hans","password":"Hans"}
However, when I try to read the password to make sure that the user doesn't already exists (again, just for testing purposes), I get nil returned for password value returned.
In my .h file I declare the Data and Connection:
#interface RegisterViewController : UIViewController <NSURLConnectionDataDelegate>
{
// Conform to the NSURLConnectionDelegate protocol and declare an instance variable to hold the response data
NSMutableData *buffer;
NSURLConnection *myNSURLConnection;
}
In my .m file, when someone clicks the REGISTER button, I create the request and start the connection as below. I am giving a dummy URL in example but the response I am receiving is:
{"username":"Hans","password":"Hans"}
- (IBAction)registerButtonClicked:(id)sender
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://myDummyURL/Login.php"]];
// Construct the JSON Data
NSDictionary *stringDataDictionary = #{#"firstname": firstname, #"lastname": lastname, #"email": email, #"password" : password, #"telephone" : telephone};
NSError *error;
NSData *requestBodyData = [NSJSONSerialization dataWithJSONObject:stringDataDictionary options:0 error:&error];
// Specify that it will be a POST request
[request setHTTPMethod:#"POST"];
// Set header fields
[request setValue:#"text/plain" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
//NSData *requestBodyData = [stringData dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:requestBodyData];
myNSURLConnection = [NSURLConnection connectionWithRequest:request delegate:self];
// Ensure the connection was created
if (myNSURLConnection)
{
// Initialize the buffer
buffer = [NSMutableData data];
// Start the request
[myNSURLConnection start];
}
}
The connection is created with no problem here.
In my .m file I implement the delegate methods and in connectionDidFinishLoading() I try and read the returned JSON. Below is the code I am using for this.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Dispatch off the main queue for JSON processing
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSString *jsonString = [[NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error] description];
// Dispatch back to the main queue for UI
dispatch_async(dispatch_get_main_queue(), ^{
// Check for a JSON error
if (!error)
{
NSError *error = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&error];
NSDictionary *dictionary = [jsonArray objectAtIndex:0];
NSString *test = [dictionary objectForKey:#"password"];
NSLog(#"Test is: %#", test);
}
else
{
NSLog(#"JSON Error: %#", [error localizedDescription]);
}
// Stop animating the Progress HUD
});
});
}
From the log screen grab below you can see the jsonString returned has values but the jsonArray is always nil. The error reads: error NSError * domain: #"NSCocoaErrorDomain" - code: 3840 0x00007ff158498be0
Thanks in advance.
Your jsonString is in fact the NSDictionary object - created by NSJSONSerialization - you're looking for, there is no array. In a JSON string curly braces {} represent a dictionary and square brackets [] represent an array
Try this
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Dispatch off the main queue for JSON processing
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error];
// Dispatch back to the main queue for UI
dispatch_async(dispatch_get_main_queue(), ^{
// Check for a JSON error
if (!error)
{
NSString *test = [dictionary objectForKey:#"password"];
NSLog(#"Test is: %#", test);
}
else
{
NSLog(#"JSON Error: %#", [error localizedDescription]);
}
// Stop animating the Progress HUD
});
});
}
Edit: I overlooked the description method at the end of the NSJSONSerialization line. Of course that must be deleted.
Your code has 2 issues:
You are converting JSON server response to NSString.
Your JSON data is indeed a NSDictionary.
This must fix your issue:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Dispatch off the main queue for JSON processing
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error];
// Dispatch back to the main queue for UI
dispatch_async(dispatch_get_main_queue(), ^{
// Check for a JSON error
if (!error)
{
NSString *test = [dictionary objectForKey:#"password"];
NSLog(#"Test is: %#", test);
}
else
{
NSLog(#"JSON Error: %#", [error localizedDescription]);
}
// Stop animating the Progress HUD
});
});
}
I'm developing a OS X app with sqlite3 and I want to copy the database from the application's bundle to Library/Application support/ directory because I need read/write on the db, however, the sql is copied to my documents folder and I don't want that location. My code:
- (NSString *)filePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[paths objectAtIndex:0] stringByAppendingPathComponent:#"myDB.sql"];
}
- (void)openDB {
NSString *destinationPath = [self filePath];
if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"myDB.sql"];
NSError *error;
[[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destinationPath error:&error];
if (error != nil) {
//Error
}
}
A file like this belongs in the Application Support folder of the Library. So, instead of NSDocumentDirectory, use NSApplicationSupportDirectory. Also, as the File System Basics documentation says, "All content in this directory should be placed in a custom subdirectory whose name is that of your app’s bundle identifier or your company."
For example:
- (NSString *)filePath {
NSString *applicationSupport = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
NSString *applicationFolder = [applicationSupport stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]];
return [applicationFolder stringByAppendingPathComponent:#"myDB.sql"];
}
By the way, when creating this file, remember to create the folder:
- (void)openDB {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationPath = [self filePath];
if (![fileManager fileExistsAtPath:destinationPath]) {
NSError *error;
if (![fileManager createDirectoryAtPath:[destinationPath stringByDeletingLastPathComponent] withIntermediateDirectories:TRUE attributes:nil error:&error]) {
NSLog(#"%s: createDirectoryAtPath error: %#", __FUNCTION__, error);
}
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"myDB" ofType:#"sql"];
if (![fileManager copyItemAtPath:sourcePath toPath:destinationPath error:&error]) {
NSLog(#"%s: copyItemAtPath error: %#", __FUNCTION__, error);
}
}
}
I develop an application and I need to display the content using an UITextView which must have set the height dynamically and it must recognize a link.
I used code above:
self.textView.text = [NSString stringWithFormat:#"%# \n %#", self.offersObjects.body, self.offersObjects.url];
self.textView.dataDetectorTypes = UIDataDetectorTypeLink;
if (([[[UIDevice currentDevice] systemVersion] integerValue] < 7)){
CGRect frame = self.textView.frame;
frame.size.height = self.textView.contentSize.height;contentSize.height;
self.textView.frame = frame;
}else{
[self.textView sizeToFit];
[self.textView layoutIfNeeded];
}
My problem is that it doesn't recognize the link .
try with below code :
-(IBAction)txtStustes:(id)sender
{
NSError *error = nil;
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink
| NSTextCheckingTypePhoneNumber error:&error];
NSString *string = self.textView.text;
NSArray *matches = [detector matchesInString:string options:0 range:NSMakeRange(0, [string length])];
for (NSTextCheckingResult *match in matches) {
if ([match resultType] == NSTextCheckingTypeLink) {
NSURL *url = [match URL];
[[UIApplication sharedApplication] openURL:url];
}
}
}
Also add below code in your viewDidLoad method
UITapGestureRecognizer *LblProfileNameTouch=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(txtStustes:)];
[LblProfileNameTouch setNumberOfTouchesRequired:1];
[self.textView addGestureRecognizer:LblProfileNameTouch];
Unsure how to use an image loaded from the web as an asset in a GLKit skybox (like the old apple/google maps streetview) There are 2 methods for loading cubemaps with GLKTextureLoader: cubeMapWithContentsOfFile and cubeMapWithContentsOfUrl
If I grab the image locally it works fine:
NSString *path = [[NSBundle mainBundle] pathForResource:#"pano" ofType:#"jpg"];
GLKTextureInfo *skyboxCubemap = [GLKTextureLoader cubeMapWithContentsOfFile:imgPath options:options error:&error];
So is there a way to get a path from an image loaded from the web and use it here?
I ended up loading the image from the web like so:
- (void) cacheImage: (NSURL *) ImageURL : (NSString *)imageName
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex: 0];
NSString *file = [docDir stringByAppendingPathComponent: imageName];
if(![[NSFileManager defaultManager] fileExistsAtPath: file])
{
NSData *data = [[NSData alloc] initWithContentsOfURL: ImageURL];
UIImage *image = [[UIImage alloc] initWithData: data];
[UIImageJPEGRepresentation(image, 100) writeToFile: docFile atomically: YES];
}
}
caching and returning the file url via :
- (NSString *) getCachedImage : (NSString *)imageName
{
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString* cachedPath = [documentsDirectory stringByAppendingPathComponent:imageName];
NSString *path;
if([[NSFileManager defaultManager] fileExistsAtPath: cachedPath])
{
path = cachedPath;
}
return path;
}
and loading the file via
NSString *cached = [self getCachedImage:#"cacheKey"];
self.skyboxCubemap = [GLKTextureLoader cubeMapWithContentsOfFile:cached options:options error:&error];
I have a UICollectionViewController (with a navigation controller) and i want to show an image in a Cell that 'pushes' to a normal ViewController (different by every image). How do i do that?
Seem you want to build photo gallery by UICollectionView.
If use storyBoard, use segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetail"])
{
NSIndexPath *selectedIndexPath = [[self.collectionView indexPathsForSelectedItems] objectAtIndex:0];
// load the image, to prevent it from being cached we use 'initWithContentsOfFile'
NSString *imageNameToLoad = [NSString stringWithFormat:#"%d_full", selectedIndexPath.row];
NSString *pathToImage = [[NSBundle mainBundle] pathForResource:imageNameToLoad ofType:#"JPG"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:pathToImage];
DetailViewController *detailViewController = [segue destinationViewController];
detailViewController.image = image;
}
}
If use nib: inside didSelectItemAtIndexPath, use self.navigationController push.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
NSString *imageNameToLoad = [NSString stringWithFormat:#"%d_full", indexPath.row];
NSString *pathToImage = [[NSBundle mainBundle] pathForResource:imageNameToLoad ofType:#"JPG"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:pathToImage];
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
detailViewController.image = image;
[self.navigationController pushViewController:detailViewController animated:YES];
}
Sample code from Apple:
https://developer.apple.com/library/ios/#samplecode/CollectionView-Simple/Introduction/Intro.html
CollecionView tutorial: http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12