How to reuse an API key specified in `AppDelegate`? - objective-c

I have started Objective C a few hours ago, apologies if my question is already answered, I could not find anything similar.
I have my API key specified in the AppDelegate as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[GMSServices provideAPIKey = "..."]
}
As per google documentation this key need to be provided when I query for the nearby locations. Ideally, I would like to have my key in one file, no copy-paste.
Is there way to get the reference to the key from AppDelegate and use it in another file? Maybe it should be kept in yet another file and both AppDelegate file and the file that calls Locations API should query that another file? Thanks.

You don't need to reference this key in any other file. The API key is paired with your application's bundle ID to identify it. You should call '+ (BOOL) provideAPIKey:(NSString *)APIKey' exactly once in your application.
Therefore, if you called it in application: didFinishLaunchingWithOptions:, you should be able to use google maps services in any other class.
Take a look at https://developers.google.com/maps/documentation/ios/ if you want to see some example codes.
However, if you want, to any reason, be able to reuse any object between your class, you should use Singleton.
The API key, for example is a NSString. Thus, you would declare a class in your .h file like:
#interface APIKey : NSObject
+ (APIKey*)sharedInstance;
#property NSString * apiValue;
#end
And a .m:
#implementation APIKey
+ (APIKey *)sharedInstance{
static APIKey * sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
#end
Then you would be able to access 'apiValue' in any class (after import .h file) just calling
[[APIKey sharedInstance] apiValue];
Or set new values like this:
[[APIKey sharedInstance] setApiValue:#"someThingHere"];
But, as I said, you don't need this to Google API Key.
Hope this helps.

Related

Transfer NSMutableArray between classes [duplicate]

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 7 years ago.
I've looked at other posts and none of them make any sense. I have an AppController (for a NSCollectionView), and I have a table in it called filePaths. File paths is an #property. MY object file (for the collection view), needs those paths. How do I transfer the filePaths from AppController to my file class? Singletons and other things are confusing to me, even though I've read a lot about them. If you have an answer involving singletons or something similar, please explain it, because I have no idea what is going on.
Simply assign it to a property.
If you're using storyboards, you'll want to use the "prepare for segue" method typically found at the bottom of your view controllers.
You'll create a property in the destination view controller like this in your header:
#property (nonatomic) NSMutableArray *recievedArray;
and in your origin view controller's m file (the m file where the mutable array originally is) you'll import the destination view controller at the top like so
#import "DestinationViewController.h"
Now we'll populate "prepareForSegue" :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"segueToDestinationViewController"]) {
DestinationViewController *vc = [segue destinationViewController];
[vc setRecievedArray:theMutableArray];
}
}
Where "segueToDestinationViewController is what you set in storyboard like this (I reused this image from something else but you want to click on the segue arrow and fill in "Identifier" with "segueToDestinationViewController")
Now you're all set and the property will be properly assigned upon segue!
Edit: You can also set up singletons easily by creating your singleton class and adding this method to it:
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
Then you will invoke the single instance of the singleton using the class method like this
MyManager *mySingleton = [MyManager sharedManager];
Going through the GCD like this is pretty fool proof so you should be fine. Now all you have to do is get the properties from the singleton instance.

Create and access "debugMode" variable across different classes

I am trying to create a Boolean variable, debugMode, and access it across several different classes. This way I can set it's value once in my ViewController, and will be able to access it in my different classes (subclasses of SKScene) to show framerate, log physics values, etc.
I have read that I need to create an instance of my class? I don't see how that applies in this program.
I am new to objective-c and would greatly appreciate any help! Thank you!
The default solution is a preprocessor define, this is set by default in xcode projects.
So, in the source you can put
#ifdef DEBUG
// code that should only run in Debug Configuration
#endif
So if I get you right you want an instance of a given class that you can use across the whole of your application without losing the state of the class but this should only exist in the DEBUG version of your code?
Ok we can do this using a Singleton Pattern mixed with the #ifdef DEBUG to determine whether in debug mode or not.
DebugManager.h
// Our Debug Class that we have just made up.
// Singleton class
#interface DebugManager : NSObject
// Some properties that we want on the class
#property (nonatomic, strong) NSString *debugName;
#property (nonatomic, readonly) NSDate *instanceCreatedOn;
// a method for us to get the shared instance of our class
+ (id)sharedDebugManager;
#end
DebugManager.m
#import "DebugManager.h"
#implementation DebugManager
// Create a shared instance of our class unless one exists already
+ (id)sharedDebugManager
{
static DebugManager *sharedDebugManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedDebugManager = [[self alloc] init];
});
return sharedDebugManager;
}
- (id)init
{
if (self = [super init]) {
debugName = #"Debug Instance";
instanceCreatedOn = [NSDate new];
}
return self;
}
#end
Now that we have a Singleton class setup we can add the below line to our *-Prefix.pch which will give us an instance of our DebugManager class that we can use throughout our app.
#ifdef DEBUG
DebugManager *manager = [DebugManager sharedDebugManager];
#endif
Just remember that when you want to use your manager instance you will need to wrap it in #ifdef DEBUG because when running in production this will not see the instance of manager anymore. So make sure you do:
#ifdef DEBUG
NSLog(#"The DebugManagers instance name is %#", [manager debugName]);
#endif
Don't forget to add your preprocessor macro in xcode under your Build Settings follow this answer to find out how to do that
If you have any questions just ask below.

One NSDictionary visible everywhere in application

Now I am developing an iOS application which works like this:
User scans QR code,
App searches for a specific key - > value,
it gives out a value to the user.
Currently I have two ViewControllers - the main and "value" ViewController, which is inherited from main. The problem is that if I create NSDictionary in main VC it is not visible in "value" VC. Main VC gives only the string (QR code, the key) through the segue. So, the value VC has to search for key and display the value.
What I ask is some kind of global variable or one DataSource visible across the whole app. Of course, I can implement NSDictionary initialisation inside value ViewDidLoad method and it will work, but this is not the point. New modules are to be added there and the variable has to be global. I googled a lot and got the idea that singleton pattern can be helpful here. I tried to implement it, but no idea how to do. Do I need it, or it is too complex for this kind of DataSource?
Thank you!
The basic idea is, you will still need to #include the header file of the place where this dictionary will be. The solution that Naveen proposes means that you will be including the header for the app delegate wherever you want to access it. Whether to use the app delegate for this purpose or not is kinda grayish. Some people often do this, some say its a bad use of it.
The singleton approach means that you will create a class, that will always contain the same information since the init method will return object that was previously created.
For the singleton aproach, imagine I have a database manager class. So in the header of this class (the DatabaseManagerSingleton.h) ill have this:
#interface DatabaseManager : NSObject
+ (DatabaseManager*)sharedInstance;
// Your dictionary
#property (nonatomic,strong) NSMutableDictionary* someDictionary;
The implementation will look like this: (check how "sharedInstance" initializes the object)
#implementation DatabaseManager
#pragma mark - Singleton Methods
+ (DatabaseManager*)sharedInstance {
static DatabaseManager *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init
{
self = [super init];
if (self != nil)
{
// Custom initialization
_someDictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
Now, a VERY important thing is that, any place you want to use this object should first include the header:
EDIT: To use it in your code:
1) add the header
#import "DatabaseManager.h"
2) initialize the object
DatabaseManager *databaseManager = [DatabaseManager sharedInstance];
3) do whatever you need
// Initialize the dictionary
databaseManager.someDictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"OBJECT",#"someKey", nil]; // In this case the object is just a NSString.
// Access
[databaseManager.someDictionary objectForKey:#"someKey"];
Put as a property on Appdelegate
#property (nonatomic,strong) NSDictionary * sharedData;
Access anywhere like
NSDictionary *sharedData= ((APPDelegate *) [UIApplication sharedApplication].delegate).sharedData;

Global variable in iOS TabBar Application

I am creating ios app in xcode 4.2.
I have outside file with database. I dont wanna download data in every view. How should i create one global variable for tabbar application? And when should i upload this database before closing of application?
In iOS applications the model data is often kept in a singleton, rather than in a global variable. Here is an article briefly describing singletons in Objective-C.
You can load your data in the class method that initializes your shared singleton. Uploading the data back is a bit trickier, because the singleton itself does not know when to do it. Therefore you should make an instance method -(void)uploadData in your singleton class, and call that method when your application is about to close. applicationWillResignActive: method of your application delegate is a good place to initiate the upload.
I use singletones like this: in class DataBase with some arrays of data i implement share method:
+(id)share
{
static id share = nil;
if (share == nil) {
share = [[self alloc] init];
}
return share;
}
and then in some classes: self.dataBase = [DataBase share];
You can create global variables by doing this
extern NSString *someString;
#interface ......
#property (strong, nonatomic) NSString *someString;
#end
#implementation ......
#systhesize someString;
NSString *someString;
#end

Singleton - Reading from a Data Store

I'm trying to do the right thing by not using global variables in my Xcode project. I've successfully created a singleton. I have a simple data store which I named "myContactsStore" that contains an array, with each object in the array having only 3 instance variables (name, phoneNum, and eMail). I have no problem creating, modifying, saving etc. the data in the array when I'm executing the view controller that created the array.
My problem is trying to access the data store from another view controller. I'm halfway there, as proven by my ability to print the contents of the entire test array from another view controller by using the following code in a for loop:
NSLog(#"%#", myContactsStore.description);
Here's the output:
"Mary, 0938420839, PaulDoe#Mac.com",
"John, 9932097372, PaulDoe#Mac.com",
"Mary, 0726756893, RedCat#iwon.com",
"Mary, 8556327199, xxxbct#mac.com",
"John, 0640848317, xxxbct#mac.com"
How do I access just one instance variables? For example, I want to create a read-only array in another view controller that contains just the email addresses of every contact in the "myContactsStore" array. I've tried several things, but I'm new at this and I must be missing something very basic.
Thanks for you help and any code example you might have the time to include.
While Singletons are an easy way to share data across classes they cause cohesion problems in your overall design where your classes start to "import the world". The issue is coming up with exactly the right dependency for a given class and this is very easily missed when you try to design from classes instead of designing from use cases. You want to ask yourself, "what data does this class use?" You then create a protocol (abstraction) that gives the class the view of the data it wants. In your case one class writes to the data store. It doesn't need to create the data store nor does either class need to know that the data is maintained in an array. Follow these steps exactly in order and follow directly or you'll miss my point. Try a protocol in a separate .h file like the following:
#protocol MyDataStore <NSObject> {
}
Import this header in your 1st class that creates the contacts and declare a property of the protocol's type.
#import "MyDataStore.h"
#interface MyContactCreator : UIViewController
#property (nonatomic, retain) id<MyDataStore> dataStore;
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataStore:(id<MyDataStore>)aDataStore;
#end
I threw in a custom init method in case you are currently instantiating your view controllers programmatically instead of via InterfaceBuilder. In your implementation you would do something like this:
#implementation MyContactCreator
//other methods...
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataStore:(id<MyDataStore>)aDataStore
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.dataStore = aDataStore;
}
return self;
}
//other methods...
#end
If you are using Interface Builder to create your view controller you can drag a custom object into play and call it something like "MyDataStoreImpl". The idea here is that you are giving the datastore to the view controller instead of it creating it directly and knowing about it. Also you want to defer worrying about how the data store works until you really need to. Later in your view controller where you create the contacts you would use the data store to create them. Assuming the info comes from standard screen elements you would write code like this:
-(void) addContactTapped:(id)sender
{
[self.datastore createContactWithName:txtNameField.text phoneNumber:txtPhoneField.text email:txtEmailField.text];
}
Your editor would scream at you (with little red marks like what your 2nd grade teacher would use on your spelling homework) because the datastore doesn't respond to the message you are sending. You go back and add that method to the protocol:
#protocol MyDataStore <NSObject> {
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
}
In your other view controller class that wants the list of email addresses you would import the same data store protocol. You would also declare a datasource property identical as what we did above using a complimentary custom init method or Interface Builder to pass the datasource in. This view controller (assuming it's a table view controller) would probably have some methods like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.datasource numberOfContacts];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *email = [self.datasource emailForContactNumber:indexPath.row];
UITableViewCell *cell = //create tableview cell with the email string
return cell;
}
Your editor will start screaming with the little red lines and all. This is where you go and add more methods to the protocol.
#protocol MyDataStore <NSObject> {
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
-(NSInteger) numberOfContacts;
-(NSString*) emailForContactNumber:(NSInteger)index;
}
The little red lines go away and finally you can begin thinking about how the contacts are stored and retrieved. Create a separate class called MyDataStoreImpl which extends NSObject and imports and follows the "MyDataStore" protocol. Fill out implementations of all of the methods and you should be up and running. It could be as simple as storing NSDisctionary objects containing the contact info in an internal NSMutableArray property.
- (id)init {
self = [super init];
if (self) {
self.allContacts = [NSMutableArray array];
}
return self;
}
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
{
NSDictionary *newContact = [NSDictionary dictionaryWithObjectsAndKeys:
aName, #"name", aPhoneNumber, #"phone", anEmail, #"email",
nil];
[self.allConstacts addObject:newContact];
}
-(NSInteger) numberOfContacts;
{
return [self.allContacts count];
}
-(NSString*) emailForContactNumber:(NSInteger)index;
{
[[self.allContacts objectAtIndex:index] valueForKey:#"email"];
}
The advantages here are many. You can later re-implement the datasource to read/write from a plist file, network server, or database without touching any of your controllers. Also, your app will be easier to optimize for performance because you can design the read write methods to pull directly from a source instead of naively copying data from one array to another as you would if you were worrying about how it is managed too early. All of the above thrown together without testing and likely has errors but given to illustrate a point of how to properly share data between controllers without Singletons while maintaining a testable and easily maintainable codebase.
You can stick this to any class to make it a singleton:
+(MySingleton *)singleton {
static dispatch_once_t pred;
static MySingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingleton alloc] init];
shared.someVar = someValue;
});
return shared;
}
-(void) dealloc {
abort();
[someVar release];
[super dealloc];
}
Better yet, you can add the class as an ivar of the application delegate, which is a singleton you already have. You can get a reference to it like this:
// AppDelegate or whatever name
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Then you access the datastore as an ivar on that delegate, and optionally implement one of the several persistence technologies available on iOS, basically plist files through direct file access or NSCoding or even NSUserDefaults (which you shouldn't but it's handy for small tasks), Core Data, or SQLite.
If you are using this data store class only to pass data between controllers, you can do so directly instead. Example:
CitySelectionVC *citySel = [[CitySelectionVC alloc] initWithCities:self.cities];
[self.navigationController pushViewController:citySel animated:TRUE];
[citySel release];
I always use SynthesizeSingleton.h by Mike Gallagher. He has a really informative article relating to this topic here. You should really check it out. It makes the creation of Singleton classes really easy.
Assuming your myContactsStore object has a method contactsArray which returns the internal NSArray object, you can do this:
NSArray *emails = [myContactsStore.contentsArray valueForKey:#"email"];
NSLog(#"output: %#", emails);
Which should output:
output: (
"PaulDoe#Mac.com",
"PaulDoe#Mac.com",
"RedCat#iwon.com",
"xxxbct#mac.com",
"xxxbct#mac.com"
)
You simply have to enumerate through the array and retrieve the data you want. Like so:
NSMutableArray *emails = [[NSMutableArray alloc] initWithCapacity:myContactsStore.count];
for (ContactClassName *contact in myContactsStore) {
NSString *email = contact.eMail; // I'm assuming your ivar eMail is also a property
if (email) [emails addObject:email];
}
You now have an NSMutableArray containing just the list of emails. If you want to make this list immutable simply do:
NSArray *emailList = [emails copy];