How to pass data to viewController without creating its object - ios

I am creating a iOS static library in which user will pass the name of the Viewontroller and some parameters inside the push and I am getting these details in didReceiveRemoteNotification and from here I got a string suppose NSString *vcName = #"ViewController2" and parameter suppose NSString *param1= #"UserName" NSString *param2= #"email" now I want to pass these parameters to the viewController Which name's string is received from push. But I don't want to write #import ViewController2.
I am able to redirect to ViewController2 without importing it but don't know how to pass these parameters to ViewController2
I can redirect to the viewController from the following code.
NSString *vcName = #"ViewController2";
NSString *param1= #"UserName";
NSString *param2= #"user_email";
UIStoryboard * storyboard = [[[UIApplication sharedApplication] keyWindow] rootViewController].storyboard;
UIViewController *vcToOpen = [storyboard instantiateViewControllerWithIdentifier:vcName]];
vcToOpen.modalPresentationStyle =UIModalPresentationFullScreen;
[[[[UIApplication sharedApplication]keyWindow] rootViewController] presentViewController:vcToOpen animated:YES completion:nil];
Now I want to get these two parameter's value in ViewController2. Can anybody help me how to do it. without writing #import ViewController2 because app can has many ViewControllers and vcName can be any of them.

AppDelegate.h
-(NSString *)getEmail;
-(NSString *)getName;
-(void)setEmail:(NSString *)email Name:(NSString *)name;
+(AppDelegate *)sharedAppDelegate;
AppDelegate.m
#interface AppDelegate ()
{
NSString *strEmail, *strName;
}
-(NSString *)getEmail
{
return strEmail;
}
-(NSString *)getName
{
return strName;
}
-(void)setEmail:(NSString *)email Name:(NSString *)name
{
strEmail = email;
strName = name;
}
+(AppDelegate *)sharedAppDelegate
{
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
ViewController1.m
#import "AppDelegate.h"
-(void)gotoViewController2
{
[[AppDelegate sharedAppDelegate] setEmail:#"email#gmail.com" Name:#"name1234"];
[self performSegueWithIdentifier:#"segueToViewController2" sender:nil];
}
ViewController2.m
#import "AppDelegate.h"
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *name = [[AppDelegate sharedAppDelegate]getName];
NSString *email = [[AppDelegate sharedAppDelegate]getEmail];
NSLog(#"name = %# and email = %#",name, email); //name = name1234 and email = email#gmail.com
}

Storing values in your app delegate is just messy.
Each one of your UIViewControllers that could be launched from a push notification could conform to a custom 'launch' protocol.
Each UIViewController e.g. 'UIViewController2' would conform to this protocol.
You could write the protocol like this:
#protocol LaunchProtocol <NSObject>
- (void) launchParams:(NSDictionary *)params;
#end
Each UIViewController could conform to this protocol, like so:
#interface ViewController2 : UIViewController <LaunchProtocol>
#end
#implementation ViewController2
- (void) launchParams:(NSDictionary *)params {
}
#end
Your app delegate only needs to know about the protocol, it doesn't care about your UIViewControllers.
When you get a push notification you check if the view controller conforms to the launch protocol.
...
vcToOpen.modalPresentationStyle =UIModalPresentationFullScreen;
if ([vcToOpen conformsToProtocol:#protocol(LaunchProtocol)]) {
UIViewController <LaunchProtocol> *launchController = (UIViewController <LaunchProtocol> *) vcToOpen;
NSDictionary* params = #{ /* create your param dict */ };
[launchController launchParams:params];
}
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:vcToOpen animated:YES completion:nil];
...
You would include the information from the push notification in the 'params' dict, and the UIViewController would extract what information it needs from it in launchParams:
- (void) launchParams:(NSDictionary *)params {
NSLog(#"Username: %#", params[#"username"]);
}

Actually you can use Singleton design pattern to achieve this. Create one shared instance class to store the values.
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
create properties inside the manager class which needs to be saved, then access the values from the manager class.

Related

How to initialise a VC or Class iOS, ObjectiveC

When a button is clicked at FirstVC, it will pass data and trigger SecondVC using NSNotificationCenter
During initial launch of the app, because SecondVC has not been initialize yet, so data cannot be passed to SecondVC. NSNotificationCenter cannot function properly. Only after SecondVC has been initialize, NSNotificationCenter will function correctly.
So I need to initialise SecondVC somewhere. Will it be at - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions?
Or how do I programatically call the tab of SecondVC.
FirstVC
#import "Search.h"
#import "Classes.h"
#import "MyTabBarController.h"
#interface Search(){
AppDelegate *appDelegate;
CERangeSlider* _rangeSlider;
NSString *sURL, *strResult, *sRemaining, *sStartTime, *sEndTime, *sSelectedLat, *sSelectedLong;
}
#end
#implementation Search
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:sURL forKey:#"theURL"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"toClasses" object:nil userInfo:userInfo];
}
#end
Second VC
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(receiveTestNotification:)
name:#"toClasses"
object:nil];
dtDate = [[NSMutableArray alloc] init]; //=== Mutable array to store the dates generated
self.currentPageIndex = 0;
[self setupSegmentButtons];
NSDate *now = [NSDate date];
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/YYYY"];
sDtDate = [dateFormatter stringFromDate:now];
[self LoadClasses];
}
-(void)viewWillAppear:(BOOL)animated {
//--- Hide the Top Navigation Controller Bar at the current View
[[self navigationController] setNavigationBarHidden:YES animated:YES];
}
//--- Top Navigation Controller reappear on the next VC
-(void)viewDidDisappear:(BOOL)animated{
[[self navigationController] setNavigationBarHidden:NO animated:YES];
}
-(void) receiveTestNotification:(NSNotification*)notification
{
if ([notification.name isEqualToString:#"toClasses"])
{
NSDictionary* userInfo = notification.userInfo;
NSLog (#"Successfully received userInfo! %#", userInfo);
NSString* sFromSearch = [NSString stringWithFormat: #"%#", userInfo];
NSLog (#"Successfully received test notification! %#", sFromSearch);
}
}
In my opinion, you don't need to use notification or singleton on this case.
Simply, get SecondViewController from self.tabBarController and call the method.
First VC
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
UINavigationController* secondNav = (UINavigationController*)self.tabBarController.viewControllers[1];
SecondViewController* secondViewController = [secondNav.viewControllers firstObject];
[secondViewController handleString:sURL];
}
Second VC
- (void)handleString:(NSString*)string {
// Do whatever you want with string passed from First VC
}
You added observer in viewDidLoad, so it will not work even you create it before user tap on button and send notification. because observer will not be registered. I advise you not use observer to send data in this case. you can save this data elsewhere and use it when seconVC will load. for example in singleton object.
your Singleton object looks like this:
Interface:
#interface DataManager : NSObject
#property (nonatomic, strong) NSDictionary *userInfo;
+ (DataManager *) getInstance;
#end
Implementation:
#implementation DataManager
+ (DataManager *) getInstance {
static DataManager *appManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
appManager = [[DataManager alloc] init];
});
return appManager;
}
#end
Now you can access this object where you want and you can assured that only one instance is created.
here is your button click method:
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:sURL forKey:#"theURL"];
[DataManager getInstance].userInfo = userInfo;
}
and your viewDidLoad in secondVC
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *userInfo = [DataManager getInstance].userInfo;
}

Retrieve data from NSObject's property

I have class called GlobalArray which is an NSObject. It has an NSArray property called globalData.
I'm passing data into globalData inside of my ViewControllerOne.m, it works perfect, i can print the log in the console. The problem is, that i'm unable to retrieve this data in ViewControllerTwo.m.
GlobalArray.h
#import <Foundation/Foundation.h>
#interface GlobalArray : NSObject
#property (nonatomic, retain) NSArray *globalData; // why retain?
GlobalArray.m
#import "GlobalArray.h"
#implementation GlobalArray
- (id) init
{
self = [super init];
if(self)
{
self.globalData = [[NSArray alloc] init];
}
return(self);
}
ViewControllerOne.m (GlobalArray.h imported into .h)
- (void)viewWillAppear:(BOOL)animated {
[PubNub requestHistoryForChannel:my_channel from:nil to:nil limit:100 reverseHistory:NO withCompletionBlock:^(NSArray *message, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error) {
GlobalArray *fromHistory = [[GlobalArray alloc] init];
fromHistory.globalData = message;
NSLog(#"TEST LOG 1 %#", fromHistory.globalData);
}];
}
I try to retrieve it in ViewControllerTwo.m this way: (ViewController.h and GlobalArray.h is imported)
-(void) viewWillAppear:(BOOL)animated {
GlobalArray *history = [[GlobalArray alloc] init];
NSArray *sampleArr = [[NSArray alloc] init];
sampleArr = history.globalData;
NSLog(#" TEST LOG2 %#", sampleArr);
}
But TEST LOG2 is empty. I think i missed something in the ViewControllerTwo.m, but can't figure it out, for me it seems it's correct.
If you'd like to avoid the classic Singleton pattern, you can bind a session object to the app delegate and implement the methods to login / logout:
#interface XXXAppDelegate : UIResponder <UIApplicationDelegate>
+ (XXXSession *)loginWithUserName:(NSString*)userName password:(NSString*)password;
+ (void)logout;
+ (XXXSession)currentSession;
#end
Then you define the data managed in your session:
#interface XXXSession : NSObject
#property (nonatomic, retain) NSArray *globalData;
#end
Initialize the session object it in application:didiFinishLaunchingWithOptions: or where it is needed in your application:
#implementation XXXAppDelegate {
XXXSession *_currentSession;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self loginWithUserName: #"Test"];
}
#end
In your ViewControllers you can obtain the session as follow:
[XXXAppDelegate currentSession].globalData
This approach is similar to have a singleton object with the difference that the access to the instance is not offered by the singleton class itself (as stated in the definition of this Design Pattern) but it is implemented in the application delegate.
Of course you'll get empty because you are initializing a separate object of type GlobalArray in your ViewControllerTwo.
This is like you do:
Car car1 = [[Car alloc] init];
car1.name = #"BMW";
Car car2 = [[Car alloc] init];
NSLog(#"Car name = %#", car2.name); <--- this will be empty!
You need to keep the GlobalArray variable somewhere to access it later in ViewControllerTwo instead of reinitializing it, or make the GlobalArray class singleton to always return the same instance instead of creating separate instances.

Can't call variable from AppDelegate

I have variable in my AppDelegate.m called message, that i would like to use in a view controller, but it's not working. I've tried this solution:
If i import the AppDelegate.m into my ViewController.m, i get an error: clang: error: linker command failed with exit code 1 (use -v to see invocation), but if i don't import it i get this: No known class method for selector 'message' at this line: self.toSort = [AppDelegate message];. But when i import ViewController.m into AppDelegate.m i don't get the linker command error, however the other error already exists.
My AppDelegate.h
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic, strong) PNChannel *myChannel;
- (void)getMessage;
AppDelegate.m
#import "AppDelegate.h"
#import "ViewController.m"
static NSArray *_message = nil;
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// [self.window makeKeyAndVisible];
self.myChannel = [PNChannel channelWithName:currentChannel.username
shouldObservePresence:YES];
[self getMessage];
}
+ (NSArray *)message
{
if (_message)
return _message;
AppDelegate *appDelegate =(AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate getMessage];
return nil;
}
- (void)getMessage {
[PubNub requestFullHistoryForChannel:self.myChannel withCompletionBlock:^(NSArray *contentArray, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error) {
_message = contentArray;
NSLog(#"test log %#", _message);
}];
}
ViewController.m
#import "ViewController.h"
//#import "AppDelegate.h"
//#import "AppDelegate.m"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
//AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//[appDelegate getMessage];
self.toSort = [AppDelegate message];
[self getMessageList];
}
I'm sure i did some beginner mistake, but i can't figure it out. The "test log" works, so i think i have to call it in a different way.
Already tried this, but also get an error because message is not a property.
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *variableTest = appDelegate.message;
NSLog(#"TEST : %#",variableTest);
UPDATE: I've tried this, but the test log shows null, so something is still wrong.
AppDelegate.h
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic, strong) PNChannel *myChannel;
#property (strong, nonatomic) NSArray *message;
- (void)getMessage;
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// [self.window makeKeyAndVisible];
self.myChannel = [PNChannel channelWithName:currentChannel.username
shouldObservePresence:YES];
[self getMessage];
}
return YES;
}
+ (NSArray *)message
{
if (self.message)
return self.message;
AppDelegate *appDelegate =(AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate getMessage];
return nil;
}
- (void)getMessage {
[PubNub requestFullHistoryForChannel:self.myChannel withCompletionBlock:^(NSArray *contentArray, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error) {
self.message = contentArray;
NSLog(#"dev log %#", self.message);
}];
}
ViewController.m
- (void)viewWillAppear:(BOOL)animated {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *variableTest = appDelegate.message;
NSLog(#"TEST : %#",variableTest);
}
My try based on o Pi's answer:
#interface MessageHistoryData : NSObject {
NSArray *yourData;
}
#property(nonatomic,retain) NSArray *yourData;
+(MessageHistoryData *)getInstance;
#end
#import "MessageHistoryData.h"
#implementation MessageHistoryData #synthesize yourData;
static MessageHistoryData *instance =nil;
+(MessageHistoryData *)getInstance {
#synchronized(self) {
if(instance==nil) {
instance= [MessageHistoryData new];
}
}
return instance;
}
#end
in my ViewController.m (MessageHistoryData is imported into the .h)
-(void)setupArray {
[PubNub requestHistoryForChannel:my_channel from:nil to:nil limit:100 reverseHistory:NO withCompletionBlock:^(NSArray *contentArray, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error) {
MessageHistoryData *data = [MessageHistoryData getInstance];
data.yourData = contentArray;
NSLog(#"Dev log2 %#", data.yourData);
}];
}
I set up a sample project to verify that this works.
In the AppDelegate.h file, publicly declare the message property and -getMessage method:
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (readonly, nonatomic) NSString *message;
- (void)getMessage;
#end
In the AppDelegate.m file, implement your methods as you normally would (I explicitly set the property for the sake of example):
#import "AppDelegate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (void)getMessage {
self.message = #"This is a message";
}
#end
In your ViewController.m file, you should import the AppDelegate header, and you should be free to access the properties:
#import "AppDelegate.h"
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSLog(#"The delegate's message is: %#", delegate.message); // Logs "The delegate's message is: (null)"
[delegate getMessage];
NSLog(#"The delegate's message is: %#", delegate.message); // Logs "The delegate's message is: This is a message"
}
#end
If the above doesn't work, you should test your PubNub class and ensure it's behavior is predictable.
I don't recommend EVER storing information in your AppDelegate, as that makes the class responsible for doing more than just being your application's delegate with the system. Information like this should be stored in a dedicated store, or made available through a custom PubNub subclass that is accessed as a singleton (if there's no global state to be managed!) or an instance by instance basis.
Let me know if you need any clarification or if the solution above doesn't work for you.
EDIT: Singleton Suggestion
As per my comment, here is one way to handle sharing network data across view controllers
#interface NetworkClient : PubNub
#property (readonly, nonatomic) NSString *message;
/**
* Returns a shared network client to be used throughout the app
*/
+ (instancetype)sharedClient;
- (void)configureWithChannel:(PNChannel*)channel;
- (void)clearChannel;
- (void)getMessagesWithCompletionHandler:(void (^)(NSArray *, PNChannel *, PNDate *, PNDate *, PNError *))
#end
Where sharedInstance uses the technique described here to setup your instance. From there, you can access the client using [NetworkClient sharedClient] and retrieve any data through the instance methods or properties on the client.
I'm also guessing you are new to singletons or iOS in general, so I'm going to recommend you read this article about using singletons, and the blog objc.io to familiarize yourself with some best practices that will absolutely make your life easier.
First there is no need to declare the variable static since [UIApplication sharedAppliction] delegate] will always be the same instance. So just declare a property in the AppDelegate.h file and use that.
in AppDelegat.h
#property(nonatomic, strong) NSArray *message;
in AppDelegate.m use it like this:
self.message
And in your view controller import the .h and do:
AppDelegate *appDelegate =(AppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *arr = appDelegate.message;
You have to put public variables into your header file.
Your getMessage method uses an async call to get the messages. If you can't retrieve it in a sync way maybe you should call getMessage as soon as possible.
Better yet you could also use blocks to return the messages async:
+ (void)asyncMessage:(void(^)(NSArray * message))callbackBlock
{
if (_message)
{
callbackBlock(_message);
return;
}
[PubNub requestFullHistoryForChannel:self.myChannel withCompletionBlock:^(NSArray *contentArray, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error) {
_message = contentArray;
callbackBlock(_message);
NSLog(#"test log %#", _message);
}];
}
never import .m class in xcode because this throws cling: error.
if u want to NSArray defined in appdelegate to use later in your any view controller,there may be many approaches.some of them are-
initilise your array in appdelegate.h like this
#property(nonatmoic,retain)nsarray *message;
then in your appdelegate.m class in didfinishlaunchingwithOptions method allocate memory to this array-
message=[[nsarray alloc]initwithobjects:#"abc"];
then in your view controller make object of app delegate and access this property like this-
NSLog(#"array is %#",appdelegateobject.message);
In your first question you trying to return this array by class method,but in appdelegate you returning nil,so how will u get this message array,
Take the message variable and paste it into the .h then delete it from the .m
You can not import a implement file in your implement file like this.
AppDelegate.m
#import "AppDelegate.h"
#import "ViewController.m" // This is the reason causes error.
I made this errors before....
Modify like this easily fix this problem.
AppDelegate.m
#import "AppDelegate.h"
#import "ViewController.h"
you need to properties the message array to make public
AppDelegate.h
#property (strong, nonatomic) NSArray *message;
Hope it helps you..

How do I send strings to another viewcontroller

I currently have a class and ViewController with a button action to get the username and password from a textfield and put them into their own NSString. I then use the NSString to perform a post request like so.
NSString *user = _username.text;
NSString *password = _password.text;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://thesite.com/login.php"]];
[httpClient setParameterEncoding:AFFormURLParameterEncoding];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST"
path:#"http://thesite.com/login.php"
parameters:#{#"username":user, #"password":password}];
AFHTTPRequestOperation * httpOperation = [httpClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
//success code
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//error handler
}];
[httpClient enqueueHTTPRequestOperation:httpOperation];
However I have another class and Viewcontroller to perform a get request. However, in order to perform this get request I need to get the "NSString *user" from the first View Controller. How would I go about doing this? Should I declare a NSString *user in the header of the first Viewcontroller and then in the second View controller declare an instance of the first class?
You can pass strings through viewcontrollers. Make therefore a segue between the two viewcontroller and named it for example "secondVC"
the when you want to switch to other view make this call
[self performSegueWithIdentifier:#"secondVC"];
and implement this method.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"secondVC"]) {
SecondViewController *second = (SecondViewController *)[segue destinationViewController];
second.userString = self.user;
}
}
You can use NSUserDefaults to store the username and use it in other ViewControllers.
For example, save it in your current ViewController.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:username forKey:#"UserName"];
[defaults synchronize];
get username it in another ViewController.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *username = [defaults objectForKey:#"UserName"];
pass the userName to second view controller.
You can achieve it using following approaches.
try to pass the username when you are initialising your secondViewController like
SecondViewController * sVC = [SecondViewController alloc] initWithUserName: username];
for this in your SecondViewController class you have to modify the init method and add a property of userName {typeOF string}
#property (nonatomic, strong) NSString * userName
- (id) initWithUserName: (NSString *) name
{
self = [super initWithNibName: #"SecondViewController"
bundle: nil];
if (self)
{
self.userName = name;
}
return self;
}
Enjoy!!
Create object of first class in second class like this..
//First class
//Pass the values to seconClass
secondClass *appdelegate = [NSApp delegate];
[appdelegate initwithDetails:userName withPassword:password];
//Second class
//In second class declare the function and get the values
//When u create the object, init function will call first
-(id)initwithDetails:(NSString *)user withPassword:(NSString *)password
{
userName = [NSString stringWithFormat:#"%#", user];
newPass=[NSString stringWithFormat:#"%#", password];
return self;
}
If the second viewcontroller is spawned from the first you could just create a delegate protocol and corresponding delegate so that either the first is delegate of the second or the second is delegate of the first:
Here is an example of a protocol which includes one method, notice the instance variable delegate is of type id, as it will be unknown at compile time the type of class that will adopt this protocol.
#import <Foundation/Foundation.h>
#protocol ProcessDataDelegate <NSObject>
#required
- (void) processSuccessful: (BOOL)success;
#end
#interface ClassWithProtocol : NSObject
{
id <ProcessDataDelegate> delegate;
}
#property (retain) id delegate;
-(void)startSomeProcess;
#end
Inside the implementation section for the interface defined above we need to do two things at a minimum – first synthesize the delegate instance variable and second, call the method defined in the protocol as needed (more on that in a moment).
Let’s look at a bare bones implementation of the ClassWithProtocol.m:
#import "ClassWithProtocol.h"
#implementation ClassWithProtocol
#synthesize delegate;
- (void)processComplete
{
[[self delegate] processSuccessful:YES];
}
-(void)startSomeProcess
{
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self
selector:#selector(processComplete) userInfo:nil repeats:YES];
}
#end
Read more at this tutorial

viewWillAppear is not called in my view controller when the app becomes active

Disclaimer: I'm an iOS noob.
I created a view based app using Xcode's template that uploads pictures to a website. Xcode created AppDelegate.m (& .h) and ViewController.m as well as a storyboard for me automatically. The app is launched from a website using a URL schema, and I use that information in my view controller. I have two questions:
1) Is this a good way to pass query strings from my AppDelegate to the view controller?
I have a NSDictionary property in my AppDelegate called queryStrings which stores the query strings. Then in viewWillAppear in my view controller I use the following code to access it:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSDictionary *queryStrings = appDelegate.queryStrings;
wtID = [queryStrings valueForKey:#"wtID"];
2) When the app is already launched but in background and the user opens the app again via a url on the webpage (with different query string), I have implemented openURL in the AppDelegate which populates my queryStrings property in the AppDelegate. Problem is: the view controller's viewWillAppear method is never called so the above code isn't executed. How do I get the query string data to the view controller? How does the view controller know the app just became active?
NOTE: Because the project was created using the "view-based app" template in Xcode, the only property my AppDelegate has is the window property. I can't tell where in code that my view controller is ever even referenced by the AppDelegate... It's never set as the rootViewController or anything in the code anyway... I guess it's just some IB magic that I can't see.
In case it helps, here's my AppDelegate.h:
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) NSDictionary *queryStrings;
#end
and here's my didFinishLoadingWithOptions, openURL, and my helper method: parseQueryString:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[JMC sharedInstance] configureJiraConnect:#"https://cmsmech.atlassian.net/" projectKey:#"WTUPLOAD" apiKey:#"7fc060e1-a795-4135-89c6-a7e8e64c4b13"];
if ([launchOptions objectForKey:UIApplicationLaunchOptionsURLKey] != nil) {
NSURL *url = [launchOptions objectForKey: UIApplicationLaunchOptionsURLKey];
NSLog(#"url received: %#", url);
NSLog(#"query string: %#", [url query]);
NSLog(#"host: %#", [url host]);
NSLog(#"url path: %#", [url path]);
queryStrings = [self parseQueryString:[url query]];
NSLog(#"query dictionary: %#", queryStrings);
}
else {
queryStrings = [self parseQueryString:#"wtID=nil"];
}
return YES;
}
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
NSLog(#"openURL executed");
if (!url)
return NO;
NSLog(#"query string: %#", [url query]);
queryStrings = [self parseQueryString:[url query]];
mainViewController.wtID = [queryStrings valueForKey:#"wtID"];
return YES;
}
-(NSDictionary *)parseQueryString:(NSString *)query
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:6];
NSArray *pairs = [query componentsSeparatedByString:#"&"];
for (NSString *pair in pairs)
{
NSArray *elements = [pair componentsSeparatedByString:#"="];
NSString *key = [[elements objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *val = [[elements objectAtIndex:1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:val forKey:key];
}
return dictionary;
}
Thanks in advance!
So, storyboards are starting to get on my nerves, and I'm not even building your app! :)
As I have stated in your other question (of which this is a duplicate, but hey), the lack of the reference to the view controller is due to storyboards. viewDidAppear and viewWillAppear get called when the view is added to the hierarchy. Going to and from the background and foreground doesn't qualify as being added to the hierarchy. Luckily, your app sends notifications that correspond to the methods in the AppDelegate. Simply register as an observer of the UIApplicationWillEnterForegroundNotification notification. Then you can update your UI in the method that gets fired as a result of receiving that notification!
EDIT: Adding the link to the duplicated question for good measure: How do I access my viewController from my appDelegate? iOS
Hm, so why there is no UIViewController declared in your AppDelegate?
UPDATE
When you created a new project, you should have smth like this in your AppDelegate:
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UIViewController *viewController;
#end
//
#import "ViewController.h"
#implementation AppDelegate
#synthesize window, viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] init];
[self.window setRootViewController:self.viewController];
[self.window makeKeyAndVisible];
return YES;
}
//...
#end

Resources