How to share data between number of ViewControllers? - ios

I know one way to share data is segue. But in my application I have multiple tabs which contain number of VCs. For instance userName and address. I want to show in some of the VCs these infos.
Every time I query the cloud is not right way. I am following this answer first part: answer. But as a newbie I am not sure how MyDataModel is defined. Is it a NSObject class? I appreciate if anyone can define this class as example with two NSString fields. And how to access these fields in VC and AppDelegate.
Inside AppDelegate
#interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate>
{
MyDataModel *model;
AViewController *aViewController;
BViewController *bViewController;
...
}
#property (retain) IBOutlet AViewController *aViewController;
#property (retain) IBOutlet BViewController *aViewController;
#end
#implementation MyAppDelegate
...
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
...
aViewController.model = model;
bViewController.model = model;
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
}
Inside VC:
#interface AViewController : UIViewController {
MyDataModel *model;
}
#property (nonatomic, retain) MyDataModel *model;
#end
#interface BViewController : UIViewController {
MyDataModel *model;
}
#property (nonatomic, retain) MyDataModel *model;
#end
The only thing I need is where to define MyDataMode and how to access its fields?

You can use singleton class for that,
----------
SharedManages.h
----------
#import <Foundation/Foundation.h>
#import "Reachability.h"
#import "Reachability.h"
#interface SharedManager : NSObject
{
}
+(SharedManager *)sharedInstance;
// Create property of your object which you want to access from whole over project.
#property (retain, nonatomic) User *loginUser;
#property (assign, readwrite) BOOL isNetAvailable;
#end
----------
----------
SharedManages.m
----------
#import "SharedManager.h"
static SharedManager *objSharedManager;
#implementation SharedManager
#synthesize
isNetAvailable = _isNetAvailable,
loginUser = _ loginUser;
+(SharedManager *)sharedInstance
{
if(objSharedManager == nil)
{
objSharedManager = [[SharedManager alloc] init];
objSharedManager. loginUser = [User alloc]] init];
Reachability *r = [Reachability reachabilityForInternetConnection];
NetworkStatus internetStatus = [r currentReachabilityStatus];
// Bool
if(internetStatus == NotReachable)
{
NSLog(#"Internet Disconnected");
objSharedManager.isNetAvailable = NO; // Internet not Connected
}
else if (internetStatus == ReachableViaWiFi)
{
NSLog(#"Connected via WIFI");
objSharedManager.isNetAvailable = YES; // Connected via WIFI
}
else if (internetStatus == ReachableViaWWAN)
{
NSLog(#"Connected via WWAN");
objSharedManager.isNetAvailable = YES; // Connected via WWAN
}
}
return objSharedManager;
}
#end
Access from other Class...
[SharedManager sharedInstance].isNetAvailable ;
[SharedManager sharedInstance].loginUser ;
Hope, This will help you..

I don't have "local" copies of the values. I set them in the delegate and fetch them from there. That way you don't have to hard code it for all UIViewController's in the delegate.
Assigning values is best done on the first view, or with default values. I personally use viewDidLoad for those kind of things. Since it is only called once on the first view once and pertains until the app is terminated.
Then I get the delegate from inside the VC, call the instance and from there the values.
Swift
Inside AppDelegate:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var globals : GlobalValueClass?
First VC:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
delegate.globals = GlobalValueClass()
delegate.globals!.numbers = [1,2,3]
}
}
Other VC's:
class ViewControllerTwo: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
print(delegate.globals!.numbers)
// Do any additional setup after loading the view.
}
}
Objective C ( don't have the full method in obj-c, but easy to find)
MainClass *appDelegate = (MainClass *)[[UIApplication sharedApplication] delegate];
how to get the delegate in obj-c

Easiest way i could think of is using NSUserDefaults. Save your name and address string in NSUserDefaults like
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:YourNameString forKey:#"NameString"];
[defaults setValue:YourAddressString forKey:#"AddressString"];
[defaults synchronize];
and access it in any ViewController as
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [plistContent valueForKey:#"NameString"];
NSString *address= [plistContent valueForKey:#"AddressString"];
Hope this helps.

You can create a class and use it in your tab controller.
#import <Foundation/Foundation.h>
#interface UserModel : NSObject <NSCoding>
#property (nonatomic, strong) NSString *lastName;
#property (nonatomic, strong) NSString *firstName;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
#end
implementation file
#import "UserModel.h"
NSString *const kUserModelLastName = #"LastName";
NSString *const kUserModelFirstName = #"FirstName";
#interface UserModel ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
#end
#implementation UserModel
#synthesize lastName = _lastName;
#synthesize firstName = _firstName;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict
{
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict
{
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if(self && [dict isKindOfClass:[NSDictionary class]]) {
self.lastName = [self objectOrNilForKey:kUserModelLastName fromDictionary:dict];
self.firstName = [self objectOrNilForKey:kUserModelFirstName fromDictionary:dict];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation
{
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:self.lastName forKey:kUserModelLastName];
[mutableDict setValue:self.firstName forKey:kUserModelFirstName];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description
{
return [NSString stringWithFormat:#"%#", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict
{
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
self.lastName = [aDecoder decodeObjectForKey:kUserModelLastName];
self.firstName = [aDecoder decodeObjectForKey:kUserModelFirstName];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_lastName forKey:kUserModelLastName];
[aCoder encodeObject:_firstName forKey:kUserModelFirstName];
}
Import this class where you are setting or initializing data or values. and do below code.
NSDictionary *dicUserModel = [[NSDictionary alloc]initWithObjectsAndKeys:#"Moure",#"LastName",#"Jackson",#"FirstName", nil];
UserModel *userModel = [[UserModel alloc]initWithDictionary:dicUserModel];
//NSUserDefault save your class with all property. and you can simply retrieve your UserModel from NSUserDefault.
//Below code save this model into nsuserdefault.
[[NSUserDefaults standardUserDefaults]setObject:[NSKeyedArchiver archivedDataWithRootObject:userModel] forKey:#"UserModel"];
[[NSUserDefaults standardUserDefaults]synchronize];
You can retrieve you class object using below code.
UserModel *savedUserModel = (UserModel *)[NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults]objectForKey:#"UserModel"]];
NSLog(#"%#",savedUserModel.firstName);
NSLog(#"%#",savedUserModel.lastName);

Related

Objective-C Defining a Global Array for use by several ViewControllers

I've been trying to implement a global NSMutableArray from what I think to be a singleton class that I've implemented.
I can enter ViewController # 2, add and remove objects to the array.
However, when I leave ViewController #2 and come back, the data does not persist, and I have an array with 0 objects.
What do you think I'm doing wrong?
.h
// GlobalArray.h
#interface GlobalArray : NSObject{
NSMutableArray* globalArray;
}
+(void)initialize;
.m
#import "GlobalArray.h"
#implementation GlobalArray
static GlobalArray* sharedGlobalArray;
NSMutableArray* globalArray;
+(void)initialize{
static BOOL initalized = NO;
if(!initalized){
initalized = YES;
sharedGlobalArray = [[GlobalArray alloc] init];
}
}
- (id)init{
if (self = [super init]) {
if (!globalArray) {
globalArray = [[NSMutableArray alloc] init];
}
}
return self;
}
View Controller #2
GlobalArray* myGlobalArray;
myGlobalArray = [[GlobalArray alloc] init];
//Various add and remove code
Thank you for your input.
Following is best approach to share data Globally at Application level. Singleton Class is a key. Singleton is only initialised once, rest of times shared data is returned.
#interface Singleton : NSObject
#property (nonatomic, retain) NSMutableArray * globalArray;
+(Singleton*)singleton;
#end
#implementation Singleton
#synthesize globalArray;
+(Singleton *)singleton {
static dispatch_once_t pred;
static Singleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[Singleton alloc] init];
shared.globalArray = [[NSMutableArray alloc]init];
});
return shared;
}
#end
Following is the way to access/use shared data.
NSMutableArray * sharedData = [Singleton singleton].globalArray;
You create separate instance of GlobalArray in your ViewController#2 with this code:
GlobalArray* myGlobalArray;
myGlobalArray = [[GlobalArray alloc] init];
Instead, you should create accessor method to return your shared instance, something like this:
// GlobalArray.h
#interface GlobalArray : NSObject{
NSMutableArray* globalArray;
}
+(void)initialize;
+(GlobalArray*)sharedInstance;
with implementation:
// GlobalArray.m
// ... your existing code
// accessor method
+(GlobalArray*)sharedInstance
{
return sharedGlobalArray;
}
and then call it from your ViewController#2:
GlobalArray* myGlobalArray = [GlobalArray sharedInstance];
However, using global variables to transfer data between view controllers is bad practice; I suggest you to use more safe methods, create a delegate, for example.
To create a shared global array, if that's really what you want, just put this in the header file:
extern NSMutableArray *myGlobalArray;
and this in your main source file:
NSMutableArray *myGlobalArray;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
myGlobalArray = [NSMutableArray new];
}
Use this code for set and get the array views, for adding and removing do it separate in controller itself.
// GlobalArray.h
#interface GlobalArray : NSObject
#property (nonatomic, strong) NSMutableArray* globalArray;
+ (id)sharedManager;
-(NSMutableArray *) getGlobalArray;
-(void) setGlobalArray:(NSMutableArray *)array;
#end
/*-----------------------------------------*/
#import "GlobalArray.h"
#implementation GlobalArray
+ (id)sharedManager {
static GlobalArray *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init{
if (self = [super init]) {
if (!globalArray) {
globalArray = [[NSMutableArray alloc] init];
}
}
return self;
}
-(NSMutableArray *) getGlobalArray{
return self.globalArray;
}
-(void) setGlobalArray:(NSMutableArray *)array{
_globalArray = globalArray;
}
#end
-------------------------
//get array
NSArray * array = [[GlobalArray sharedManager] getGlobalArray];
//set array
[[GlobalArray sharedManager] setGlobalArray:array]
-------------------------

How to call Objective-C instancetype method in Swift?

I have an Objective-C class which looks like this:
#interface CustomObjectHavingData : NSObject
#property (nonatomic, strong) NSData *objWithData;
- (instancetype)initWithObjHavingData;
#end
and implementation like this
#implementation CustomObjectHavingData
- (instancetype)initWithObjHavingData{
if (self = [super init]) {
NSString *str = #"This is simple string.";
self.objWithData = [str dataUsingEncoding:NSUTF8StringEncoding];
}
return self;
}
#end
Now I want to call this initWithObjHavingData in Swift
var objData = CustomObjectHavingData()
This returns nil to me. Please help how I can call the above init method here.
You are not supposed to write initializer like that in Objective C. Either you should have it just init or then if you are passing argument in constructor then only you can name it otherwise.
Here is how you can do it,
#interface CustomObjectHavingData : NSObject
#property (nonatomic, strong) NSData *objWithData;
- (instancetype)initWithObjHavingData:(NSData *)data;
#end
#implementation CustomObjectHavingData
- (instancetype)initWithObjHavingData:(NSData *)data
{
if (self = [super init]) {
_objWithData = data;
}
return self;
}
#end
In Swift, you can simply call it like this,
let myCustomObject = CustomObjectHavingData(objHavingData: someData)
The name is quite inappropriate though.
If you want to call the init method without any parameter with the requirements I posted in the question, we have to write the init method like this:
#interface CustomObjectHavingData : NSObject
#property (nonatomic, strong) NSData *objWithData;
- (id)init;
#end
And implement it like this
#implementation CustomObjectHavingData
- (instancetype)initWithObjHavingData{
if (self = [super init]) {
NSString *str = #"This is simple string.";
self.objWithData = [str dataUsingEncoding:NSUTF8StringEncoding];
}
return self;
}
#end
#implementation CustomObjectHavingData
- (instancetype)initWithObjHavingData:(NSData *)data
{
if (self = [super init]) {
_objWithData = data;
}
return self;
}
#end
Then, you can call this from swift like this:
var objData = CustomObjectHavingData()
It will by default initialize all the objects.
You can use this :
+ (Common *)sharedInstance
{
static Common *sharedInstance_ = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance_ = [[Common alloc] init];
});
return sharedInstance_;
}
After that for calling
var com_obj : Common!
com_obj = Common.sharedInstance()
com_obj.anyfunc(..)

Trouble setting an NSMutableDictionary inside of another NSMutableDictionary

I need to take information submitted by a user, store that information in an NSMutableDictionary, then store that NSMutableDictionary inside another NSMutableDictionary which is then encoded inside another class. For whatever reason, I can't seem to store the first NSMutableDictionary inside of the other.
I had to slim down the code that's in here due to work rules, so sorry if it seems to be missing anything. I only posted the parts that I'm having trouble with.
UserInfo.h:
#import <Foundation/Foundation.h>
#interface MyPlanInfo : NSObject <NSCoding>
#property (nonatomic, strong) NSMutableDictionary *emergencyDictionary;
#end
UserInfo.m:
#import <Foundation/Foundation.h>
#import "MyPlanInfo.h"
static NSString *emergencyDictionaryKey = #"emergencyDictionaryKey";
#implementation MyPlanInfo
#synthesize emergencyDictionary;
- (id) initWithCoder:(NSCoder *)coder
{
self = [super init];
self.emergencyDictionary = [coder decodeObjectForKey:emergencyDictionaryKey];
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.emergencyDictionary forKey:emergencyDictionaryKey];
}
#end
infoView.h
#import <UIKit/UIKit.h>
#import "MyPlanInfo.h"
#interface infoView : UIViewController <NSCoding>
{
NSMutableDictionary *emergencyContactInfo;
NSArray *userInfo;
NSArray *userKeys;
NSMutableArray *tempArray;
}
#property (nonatomic, strong) MyPlanInfo *myPlanInfoObject;
-(void)saveUserInfo;
-(void)loadUserInfo;
#end
infoView.m:
#import "infoView.h"
#interface infoView ()
#end
#implementation infoView
static NSString *userInfoKey = #"userInfoKey";
static NSString *userName;
-(void)viewDidLoad
{
[super viewDidLoad];
if(!self.myPlanInfoObject)
{
self.myPlanInfoObject = [[MyPlanInfo alloc] init];
}
[self loadUserInfo];
}
-(void)addToDictionary
{
emergencyContactInfo = [NSMutableDictionary dictionaryWithObjects:userInfo forKeys:userKeys];
if([userInfo count] != 0 || userInfo == nil)
{
self.myPlanInfoObject.emergencyDictionary = [NSMutableDictionary dictionaryWithObject:emergencyContactInfo forKey:userName];
}
[self saveUserInfo];
}
- (void)saveUserInfo
{
NSData *userInfoData = [NSKeyedArchiver archivedDataWithRootObject:self.myPlanInfoObject];
[[NSUserDefaults standardUserDefaults] setObject:userInfoData forKey:userInfoKey];
}
- (void)loadUserInfo
{
NSData *userInfoData = [[NSUserDefaults standardUserDefaults] objectForKey:userInfoKey];
if(userInfoData)
{
self.myPlanInfoObject = [NSKeyedUnarchiver unarchiveObjectWithData:userInfoData];
}
}
#end
In infoView.m, in the addToDictionary method, userInfo is an array of user inputted information, and userKey's is an array of key's. The emergencyContactInfo NSMutableDictionary works just fine, everything is in it, but when I try to set that as an object in a new NSMutableDictionary, for a key, it doesn't work. Everything is nil.
Anyone have any ideas on how what I'm doing wrong?
Edit: If you down vote, please leave a reason as to why so that I can avoid doing whatever I did wrong in the future.
In the following line you’re creating an instance of MyPlanInfo using plain alloc/init:
self.myPlanInfoObject = [[MyPlanInfo alloc] init];
However, at least in the code provided, you haven’t overridden init in MyPlanInfo, but instead, initWithCoder::
- (id) initWithCoder:(NSCoder *)coder
{
self = [super init];
self.emergencyDictionary = [coder decodeObjectForKey:emergencyDictionaryKey];
return self;
}
When you use just plain init, the MyPlanInfo’s emergencyDictionary instance variable will be nil. You should likely add something like the following to MyPlanInfo to override init:
- (id) init
{
if ((self = [super init])) {
emergencyDictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
That will assure that the newly created MyPlanInfo instance has a proper NSMutableDictionary that can be manipulated from other classes.

How to create an array outside of a function, and be able to add to it in other functions

It seems if I do something like:
NSMutableArray *randomSelection = [[NSMutableArray alloc] init];
Then this needs to be in a function, and I can't modify it later using a different function.
I tried just instantiating it in the .h file,
#interface ViewController:
{
NSMutableArray *Values;
}
But then when I try to append to it during runtime, nothing happens. I try to append to it with this:
int intRSSI = [RSSI intValue];
NSString* myRSSI = [#(intRSSI) stringValue];
[Values addObject:myRSSI];
But the array remains empty when I do this.
How can I fix this?
The recommended way is to create a property;
// ViewController.h
#interface ViewController : UIViewController
{
}
#property (nonatomic, strong) NSMutableArray *values;
#end
Then override the getter for that property, to lazy-initialize it, i.e. the array will be allocated and initialized on first call of the NSMutableArray property's getter:
// ViewController.m
#interface ViewController ()
#end
#implementation ViewController
- (NSMutableArray *)values
{
if (!_values) {
_values = [[NSMutableArray alloc] init];
}
return _values;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//int intRSSI = [RSSI intValue];
//NSString *myRSSI = [#(intRSSI) stringValue];
//[self.values addObject:myRSSI];
// Keep it simple:
[self.values addObject:RSSI];
}
#end

iOS: Singleton returning null

I have a singleton and I pass data to it but it returns null can you please help me in my situation. Thanks in advance :)
Here's my code
Card.h
#property (weak,nonatomic) NSString *email;
#property (weak,nonatomic) NSString *fName;
#property (weak,nonatomic) NSString *lName;
#property (weak,nonatomic) NSString *category;
+(Card *)getCard;
Card.m
#synthesize email;
#synthesize fName;
#synthesize lName;
#synthesize category;
static csCard *instance;
+(Card *) getCard
{
#synchronized (self)
{
if(instance == nil)
{
instance = [[Card alloc]init];
}
}
return instance;
}
- (id) init{
self.email = [[NSUserDefaults standardUserDefaults]stringForKey:#"email"];
self.fName = [[NSUserDefaults standardUserDefaults]stringForKey:#"firstName"];
self.lName = [[NSUserDefaults standardUserDefaults]stringForKey:#"lastName"];
self.category = #"TestCategory";
return self;
}
and here's my test code to see if it's working
Test.m
Card *card = [Card getCard];
[card setEmail:self.emailField.text];
NSLog(#"%#",card.email);
but this code give me (null)
Modify your class like this.
Card.h
#property (strong,nonatomic) NSString *email; //Let the modal be strong property
#property (strong,nonatomic) NSString *fName;
#property (strong,nonatomic) NSString *lName;
#property (strong,nonatomic) NSString *category;
+(Card *)getCard;
Card.m
static Card *instance;
+(Card *) getCard
{
#synchronized (self)
{
if(instance == nil)
{
instance = [[Card alloc]init];
}
}
return instance;
}
- (NSString)email{
return [[NSUserDefaults standardUserDefaults]stringForKey:#"email"];
}
- (void)setEmail:(NSString)email{
[[NSUserDefaults standardUserDefaults] setString:email forkey:#"email"];
}
No need of overriding init
in your test class
Card *card = [Card getCard];
[card setEmail:self.emailField.text];
NSLog(#"%#",card.email);
static csCard *instance;
+(csCard *) getCard
{
#synchronized (self)
{
if(instance == nil)
{
instance = [[csCard alloc]init];
}
}
return instance;
}
Replace it with this code
static Card *instance;
+(Card *) getCard
{
#synchronized (self)
{
if(instance == nil)
{
instance = [[Card alloc]init];
}
}
return instance;
}
The Class name Of the instance Object was wrong and In singleton method,the return datatype was also wrong. I think u will understand what I am saying.
+ (Card *)instance {
static Card *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[Card alloc] init];
});
return sharedInstance;
}
It should be work
With the help of what βhargavḯ sujjested u can modify your code as below because
in the line static csCard *instance; u are using csCard i think it is typo so better u can do like this,
#import "Card.h"
static dispatch_once_t onceDispach;
#implementation Card
#synthesize email = _email;
#synthesize fName;
#synthesize lName;
#synthesize category;
static Card *instance = nil; //change this to Card because this instance which is of type Card
+(Card *)getCard
{
dispatch_once(&onceDispach, ^{
instance = [[self alloc] init];//careate Card shared object named instance
});
return instance;
}
- (id) init
{
self.email = [[NSUserDefaults standardUserDefaults]stringForKey:#"email"];
self.fName = [[NSUserDefaults standardUserDefaults]stringForKey:#"firstName"];
self.lName = [[NSUserDefaults standardUserDefaults]stringForKey:#"lastName"];
self.category = #"TestCategory";
return self;
}
#end
- (NSString *)email
{
return _email;
}
- (void)setEmail:(NSString *)email
{
_email = email;
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setObject:email forKey:#"email"];
}
in the class where u are using this shared instance use like below
- (void)actionMathodCalled
{
Card *card = [Card getCard];
NSLog(#"before saving to defaults->%#",card.email);
[card setEmail:#"happyCoding#ymail.com"];
NSLog(#"after savng to defaults->%#",card.email);
}

Resources