Retrieving simple data in iOS Issue - ios

I am getting an error in my viewDidLoad. It is saying there is a undeclared identifier 'object'.
I'm new to this method of saving and retrieving data and I am not sure what to do.
//Viewcontroller.m
-(void)saveString:(UIImage*)myString
{
[[NSUserDefaults standardUserDefaults] setObject:myString forKey:#"image"];
}
- (IBAction)back:(id)sender {
[self saveString:object.image];
}
//ViewController2.m
-(UIImage*)retrieveString
{
UIImage* recoveredString = [[NSUserDefaults standardUserDefaults] objectForKey:#"image"];
return recoveredString;
}
- (void)viewDidLoad
{
object.image = [self retrieveString];
[super viewDidLoad];
}

You can't store an image in NSUserDefault, but you can store the path to it. So is not right to access NSUserDefault and pass the obeject for key image or viceversa. You can save it into your app sandbox and save the path to it. Then you can use class method [UIimage imageWithContentsOfFile:path];
By the way you can save it as a string of data char, but I don't think it worth it and you need a proper encoding and decoding. You can also try to save it as an NSData object after a proper conversion, NSUserDefault supports it.

Related

NSUserDefaults saves / loads on simulator but not on device

I am doing an iphone game and saving data using NSUserDefaults. Here is how I save a user:
-(void) saveUser:(User*)user forPosition:(int) position{
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:user];
NSUserDefaults*savedData=[NSUserDefaults standardUserDefaults];
[savedData setValue:myEncodedObject forKey:[NSString stringWithFormat:#"user%i",position]];
[savedData synchronize];
self.currentUser=user;}
And here is how I load it:
-(BOOL) loadUser:(int) position{
if([savedData objectForKey:[NSString stringWithFormat:#"user%i",position]]){
NSData *encodedObject = [savedData objectForKey:[NSString stringWithFormat:#"user%i",position]];
User *user = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
self.currentUser=user;
return YES;
}
else return NO;}
As you can see, I am saving a custom class on NSUserDefaults. To do that I used guidance from How to store custom objects in NSUserDefaults (in case anyone is interested).
When I use this in the simulator it works perfectly. The problem is that if I run this on my iphone (iphone 6 on ios 8.1) it doesn't load anything.
Any ideas of what it could be? I've already visited this question NSUserdefaults works on Simulator but not on Device but nothing worked.
Thanks!
I guess there could be some problem saving it.
Check what exactly is being saved.
You can print the complete NSUserDefaults using this
Have you tried setObject instead of setValue ?
[savedData setObject:myEncodedObject forKey:[NSString stringWithFormat:#"user%i",position]];

Saving a user/account to be stored and passed around app

I am using AFNetworking 2.0 and Mantle in order to connect to an API and return a user account.
My plan is to call the function that gets the user data in the application:didFinishLaunchingWithOptions: method. I will then encode the data and save the user into NSUserDefaults
Is this the best way to approach this task? What alternatives are there? (I'd like to stay away from creating singletons)
UPDATE
Some code to maybe help show what I am thinking in my head:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *encodedUserData = [defaults objectForKey:#"currentUser"];
if (encodedUserData) {
self.currentUser = [NSKeyedUnarchiver unarchiveObjectWithData:encodedUserData];
} else {
NSLog(#"No current user");
// Show login ViewController
}
If you are going to get the user account every time user launches the app, then you don't need to store it in NSUserDefaults. Just use a singleton or static object to store it as your model object type.
static Account *_userAccount = nil;
+ (Account*)userAccount {
return _userAccount;
}
+ (void)setUserAccount:(Account*)account {
_userAccount = account;
}
You can use NSUserDefaults for this purpose and can access it through out application.
// to save data in userdefaults
[[NSUserDefaults standardUserDefaults] setObject:#"your object" forKey:#"your key"];
[[NSUserDefaults standardUserDefaults] synchronize];
// for getting saved data from User defaults
NSData *encodedUserData = [[NSUserDefaults standardUserDefaults] objectForKey:#"your key"];
if (encodedUserData) {
self.currentUser = [NSKeyedUnarchiver unarchiveObjectWithData:encodedUserData];
} else {
NSLog(#"No current user");
// Show login ViewController
}

CKFetchRecordChangesOperation - moreComing

changesOperation.fetchRecordChangesCompletionBlock = ^(CKServerChangeToken *serverChangeToken, NSData *clientChangeTokenData, NSError *operationError){
//encode and save token
NSData *encodedServerChangeToken = [NSKeyedArchiver archivedDataWithRootObject:serverChangeToken];
[[NSUserDefaults standardUserDefaults] setObject:encodedServerChangeToken forKey:fetchToken];
[[NSUserDefaults standardUserDefaults] synchronize];
//handle more - **this causes a retain cycle**
if(changesOperation.moreComing){
}
};
Hi just wondering in the fetchRecordChangesCompletionBlock, the docs say:
If the server is unable to deliver all of the changed results with this operation object, it sets this property to YES before executing the block in the fetchRecordChangesCompletionBlock property. To fetch the remaining changes, create a new CKFetchRecordChangesOperation object using the change token returned by the server.
In the code above this causes a retain cycle so how should this be handled and when recreating the operation is it possible to use the same completion blocks alreay created?
You should define a weak changesoperation like this
__weak CKFetchNotificationChangesOperation *weakChangesOperation = changesOperation;
changesOperation.fetchRecordChangesCompletionBlock = ^(CKServerChangeToken *serverChangeToken, NSData *clientChangeTokenData, NSError *operationError){
...
if(weakChangesOperation.moreComing){
}

Save and Load Data on Today Extensions (iOS 8)

Is it possible to save and load data on Today Extension using NSUserDefaults?
After closing the Notification Center, the widget behaves like an app which is terminated, so any data results lost. How could I solve this issue?
This is my code:
NSUserDefaults *defaults;
- (void)viewDidLoad {
[super viewDidLoad];
defaults = [NSUserDefaults standardUserDefaults];
NSArray *loadStrings = [defaults stringArrayForKey:#"savedStrings"];
if ([loadStrings objectAtIndex:0] != nil) {
[display setText:[NSString stringWithFormat:#"%#", [loadStrings objectAtIndex:0]]];
}
if ([loadStrings objectAtIndex:1] != nil) {
calculatorMemory = [NSString stringWithFormat:#"%#", [loadStrings objectAtIndex:1]].doubleValue;
}
}
- (IBAction)saveData:(id)sender {
NSString *displayString;
NSString *memoryString;
NSArray *saveStrings = [[NSArray alloc] initWithObjects: displayString, memoryString, nil];
defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:saveStrings forKey:#"savedStrings"];
[defaults synchronize];
}
You need to use app group identifier instead of com.*
For instance:
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.company.appgroup"];
Don't forget to synchronise when you store data
[shared synchronize];
You need to add the App Group stuff detailed under here and then if it actually worked (pretty iffy under beta) it should allow you to share NSUserDefault data like normal between the host and widget.
Edit: Normal NSUserDefaults does not work. Apple has implemented a new method. To use, simply redefine your NSUserDefaults instance like this:
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"com.you.app.container"];
For anyone wondering how in the world do you save and get values then look at this code.
In your regular app add this to save whatever you like in your *.m file.
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.yourcompanyname.TodayExtensionSharingDefaults"];
//save dic
[shared setObject:dictionary2 forKey:#"dicForTodayWidget"];
//save array
[shared setObject:tempArray2 forKey:#"arrayForTodayWidget"];
//save some value
[shared setObject:#"1234" forKey:#"myValForTodayWidget"];
[shared synchronize];
In your today widget under TodayViewController.m in viewDidLoad add this.
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.yourcompanyname.TodayExtensionSharingDefaults"];
//get dic
NSMutableDictionary *dictionary = [shared objectForKey:#"dicForTodayWidget"];
You first need the App Groups set up for both targets (application and the extension).
Then, use the
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.company.myapp"];
to obtain the defaults object which you can read from/write to as usual.
If you want to be notified of changes to the defaults, use the NSUserDefaultsDidChangeNotification in your widget (or app).
For a step-by-step tutorial explaining all this, take a look at this blog post.
#edukulele
Today Extension and Main app run on two processes. Today Extension can't receive NSUserDefaultsDidChangeNotifications. I tried use MMWormhole. It is very good.

How to save data in iOS

I'm making a game and when I close the app (close at multitask manager), all my data is gone! So, My question is very simple: How do I save the data?
Let's say you want to save score and level, which are both properties of an object called dataHolder.
DataHolder can be created as a singleton, so you don't have to worry too much about from where you access it (its sharedInstance actually):
It's code would look a bit like this:
DataHolder.h
#import <Foundation/Foundation.h>
#interface DataHolder : NSObject
+ (DataHolder *)sharedInstance;
#property (assign) int level;
#property (assign) int score;
-(void) saveData;
-(void) loadData;
#end
DataHolder.m
NSString * const kLevel = #"kLevel";
NSString * const kScore = #"kScore";
#implementation DataHolder
- (id) init
{
self = [super init];
if (self)
{
_level = 0;
_score = 0;
}
return self;
}
+ (DataHolder *)sharedInstance
{
static MDataHolder *_sharedInstance = nil;
static dispatch_once_t onceSecurePredicate;
dispatch_once(&onceSecurePredicate,^
{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
//in this example you are saving data to NSUserDefault's
//you could save it also to a file or to some more complex
//data structure: depends on what you need, really
-(void)saveData
{
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInt:self.score] forKey:kScore];
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInt:self.level] forKey:kLevel];
[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void)loadData
{
if ([[NSUserDefaults standardUserDefaults] objectForKey:kScore])
{
self.score = [(NSNumber *)[[NSUserDefaults standardUserDefaults]
objectForKey:kScore] intValue];
self.level = [(NSNumber *)[[NSUserDefaults standardUserDefaults]
objectForKey:kLevel] intValue];
}
else
{
self.level = 0;
self.score = 0;
}
}
#end
Don't forget to #import "DataHolder.h" where you need it, or simply put it in ...-Prefix.pch.
You could perform actual loading and saving in appDelegate methods:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[DataHolder sharedInstance] saveData];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[DataHolder sharedInstance] loadData];
}
You can access your score and level data from anywhere with [DataHolder sharedInstance].score and [DataHolder sharedInstance].level.
This might seem like an overkill for a simple task but it sure helps to keep things tidy and it can help you to avoid keeping all the data in appDelegate (which is usually the quick & dirty path to solution).
[NSUserDefaults standardUserDefaults] is good for small amounts of data like user settings and preferences. Typically you use this to enable users to save various bits of data that define global values such as character preferences, weapons preferences, whatever, etc.
For larger amounts of data like game level details or achievements or weapons inventory, etc. You will want to use something like Core Data. This is a more formal database that can be easily migrated as your data schema changes. See the docs here:
Core Data and Core Data Programming Guide
You can save data in CoreData, SqlLite or NSUserDefaults
Update
Realm is also an option and very easy to implement.
There are few ways to save data in ios.
UserDefaults - great way to save a small amount of data.
Keychain - safe location to safe high sensible data like login data and passwords.
Sqlite Database - If your application have a huge amount of structured data
CoreData - based on an object graph that describes the objects that should be saved
Saving Files - Of course you can also directly save all types of files to the file system. However, you can just access the file system within the app container due to security reasons.
Using Realm database - best alternative to Core Data and SQLite db
NSKeyedArchiver and NSKeyedUnarchiver - better way for small amount of objects. // i think so
ref - http://www.thomashanning.com/data-persistence-ios/
Swift 3
Use UserDefaults
UserDefaults.standard.set(„some value”, „key”)
let value = UserDefaults.standard.string(„key”)
You can even persist array using this
I recommend using Realm for more generalized solutions.

Resources