Can't call variable from AppDelegate - ios

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..

Related

No visible #interface for 'RNCAsyncStorage' declares the selector

I'm working on a brownfield integration on react-native-asyncstorage.
In the pod file, RNCAsyncStorage.h has the following:
#import <React/RCTBridgeModule.h>
#import <React/RCTInvalidating.h>
#import "RNCAsyncStorageDelegate.h"
...
#interface RNCAsyncStorage : NSObject <RCTBridgeModule,RCTInvalidating>
...
- (void)multiGet:(NSArray<NSString *> *)keys callback:(RCTResponseSenderBlock)callback;
And in my AppDelegate.m I have
#implementation AppDelegate {
__weak RCTBridge *_bridge;
}
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
...
}
And in my Stuff.m I have this in my method:
#import <RNCAsyncStorage/RNCAsyncStorage.h>
....
RNCAsyncStorage *asyncStorage = [self.bridge moduleForClass:[RNCAsyncStorage class]];
[asyncStorage multiGet:#[#"playerQueue"]] callback:^(NSArray *response){
NSObject *count = response[0];
if ([count isKindOfClass:[NSError class]]) {
// Failed to get count
return;
}
// Use count here
}];
But I kept getting the error saying No visible #interface for 'RNCAsyncStorage' declares the selector 'multiGet:'.
There's a multiGet selector being declared in the header file as well as in the .m file.
I should say that RNCAsyncStorage is imported from Pods, but I did try to pull those into my project and still getting the same error. Anything I should do to address this? Thanks!
Just a mistake in syntax. There is an extra ], the correct call is:
[asyncStorage multiGet:#[#"playerQueue"] callback:^(NSArray *response){
NSObject *count = response[0];
if ([count isKindOfClass:[NSError class]]) {
// Failed to get count
return;
}
// Use count here
}];

How to pass database from AppDelegate to ViewController

I don't know why this code error.
Please help.
I read some articles and I think the problem is about context.
What Should I do?
This program is about shows data in coredata to label in viewcontroller.
AppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "Test.h"
#import "ViewController.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
NSManagedObjectContext *context = [self managedObjectContext];
Test *t = [NSEntityDescription insertNewObjectForEntityForName:#"Test"
inManagedObjectContext:context];
t.name = #"please";
return YES;}
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *label;
#property (nonatomic,strong) NSArray *temp;
#property (nonatomic,strong) NSManagedObjectContext* managedObjectContext;
#end
ViewController.m
#import "ViewController.h"
#import "Test.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize managedObjectContext;
#synthesize temp;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Test"
inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
self.temp = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
for(Test *info in temp){
_label.text = info.name;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Test.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface Test : NSManagedObject
#property (nonatomic, retain) NSString * name;
#end
Test.m
#import "Test.h"
#implementation Test
#dynamic name;
#end
I don't know why this code error.
Please help.
I read some articles and I think the problem is about context.
What Should I do?
In your view controller, replace the line:
#synthesize managedObjectContext;
With this:
- (NSManagedObjectContext *) managedObjectContext {
return ((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;
}
Instead of storing another, different object context in your view controller, this property will return the object context that you set up in the app delegate.
There are other ways to do this, such as creating a Core Data helper class following the Singleton pattern (as #NewYork167 suggests), but this should at least solve your current problem.
For any future reference, you can also try subclassing the NSManagedObjectContext like this:
#interface MyManagedObjectContext : NSManagedObjectContext
+ (MyManagedObjectContext *)mainThreadContext;
#end
#implementation MyManagedObjectContext
+ (MyManagedObjectContext *)mainThreadContext;
{
static MyManagedObjectContext *moc;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
moc = [[self alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// Setup persistent store coordinator here
});
return moc;
}
#end
Reference: Best practices for passing NSManagedObjectContext around to UITabBarController child view controllers?

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.

No visible #interface for class declares the selector iPhone issue

So I have this code in my app delegate.m -
#import "AppDelegate.h"
#import "SearchViewController.h"
- (NSOperationQueue *) getOperationQueue
{
if(queue == nil){
queue = [[NSOperationQueue alloc] init];
}
return queue;
}
this is my app delegate.h-
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property NSOperationQueue *queue;
- (NSOperationQueue*) getOperationQueue;
#end
in another file I have something like this-
#import "AppDelegate.h"
#import "AJAXUtils.h"
+ (void)getAsyncJsonWithUrl:(NSURL *)url callback:(void (^)(NSDictionary*))callbackFunction
{
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
#try{
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:[appDelegate getOperationQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *error){
//more code
}
//more code
}
I'm getting an error - "No visible #interface for AppDelegate declares the selector 'getOperationQueue'
Not really sure why - can anyone tell me?
I think, typecasting should work:
appDelegate = (AppDelegate *)[[UIApplication sharedApplication]
delegate];

Access an instance of AppDelegate but an error:EXC_BAD_ACCESS

Pls help me with this:
The AppDelegate has an argument called "user":
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
User *user;
}
#property (nonatomic, retain) User *user;
and I init the user instance in the frist viewController:
User *userInfo = [[User alloc] initWithRealName:realName UserId:userId];
and set the user to AppDelegate's:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.user = userInfo;
In the second viewController, I can get the use's realName,there is no problem:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *realName = appDelegate.user.realName;
But when I push to another viewController,and I want to get the user's realName like just now,
but there is an error:EXC_BAD_ACCESS:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
User *user = appDelegate.user;
NSLog(#"I am in noticeDetailViewController:%#",user.realName);***//ERROR***
And I want to know why? and how to fix this error.
Thanks!
User.h & User.m
#interface User : NSObject
{
NSString *realName;
NSString *userId;
}
#property (nonatomic, retain)NSString *realName;
#property (nonatomic, retain)NSString *userId;
- (id)initWithRealName :(NSString *)realNameArgument UserId :(NSString *)userIdArgument;
#end
#implementation User
#synthesize realName,userId;
- (id)initWithRealName :(NSString *)realNameArgument UserId :(NSString *)userIdArgument
{
self = [super init];
if (self)
{
realName = realNameArgument;
userId = userIdArgument;
}
return self;
}
- (void)dealloc
{
[super dealloc];
[realName release];
[userId release];
}
#end
AppDelegate does not retain user, so it gets deallocated before you try to access it in:
NSLog(#"I am in noticeDetailViewController:%#",user.realName);***//ERROR***
So Change AppDelegate by creating a retained property:
#interface AppDelegate : UIResponder <UIApplicationDelegate> {
// ...
#property (retain, nonatomic) User*user;
// ..
And don't forget to synthesize it.
Also change the init method of user to:
- (id)initWithRealName:(NSString *)realNameArgument UserId:(NSString *)userIdArgument
{
self = [super init];
if (self) {
self.realName = realNameArgument;
self.userId = userIdArgument;
}
return self;
}

Resources