How to save child value in Firebase using Objective-C - ios

I'm trying to use Firebase to save a small amount of user data in an app I'm working on. I'd like to save the users zip code (which they'll enter) and then let them choose to follow certain topics.
I currently have the zip code being saved, but I can't get the child data to save correctly.
Can anyone help point out what I need to add next to allow child data to be saved off of a users zip code?
#import "ZipCodeVC.h"
#import <FirebaseStorage/FirebaseStorage.h>
#import <FirebaseDatabase/FirebaseDatabase.h>
#interface ZipCodeVC ()
#property FIRDatabaseReference *ref;
#end
#implementation ZipCodeVC
- (void)viewDidLoad {
[super viewDidLoad];
self.ref=[[FIRDatabase database]reference];
[[self.ref child:#"Zip Code"] setValue:#"61354"];
}
#end

Figured it out!
#import "ZipCodeVC.h"
#import <FirebaseStorage/FirebaseStorage.h>
#import <FirebaseDatabase/FirebaseDatabase.h>
#import Firebase;
#interface ZipCodeVC ()
#property NSString *uid;
#property (strong, nonatomic) IBOutlet UITextField *zipcodeTextField;
#property FIRDatabaseReference *ref;
#end
#implementation ZipCodeVC
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)tappedSubmit:(id)sender
{
[[FIRAuth auth]
signInAnonymouslyWithCompletion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
if (!error) {
self.uid = user.uid;
self.ref=[[FIRDatabase database]reference];
//[[self.ref child:#"User ID"] setValue:uid]; old code that worked for creating main value
[self writeNewPost:self.uid zipcode:self.zipcodeTextField.text title:#"Test Title" body:#"Body"];
}
}];
}
- (void)writeNewPost:(NSString *)userID zipcode:(NSString *)zipcode title:(NSString *)title body:(NSString *)body
{
NSString *key = self.uid;
NSDictionary *post = #{#"uid": userID,
#"zip code": zipcode,
#"title": title,
#"body": body};
NSDictionary *childUpdates = #{[NSString stringWithFormat:#"/user/%#", key]: post};
[_ref updateChildValues:childUpdates];
}
#end

Related

Objective c Parsing XML data between view controllers

I'm pretty new to objective-c and need some tips for my challenge.
I have 2 view controllers and need to show xml data retrieved from FirstViewController to the TermsViewController.
I'm successful getting user input and retrieve xml objects I need.
But don't know how to show the user name in the TermsViewController.m
Since data is downloaded async, can't figure out how to implement this for IOS 6.
Thanks in advance.
FirstViewController.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIButton *accessButton;
#property (weak, nonatomic) IBOutlet UITextField *codeField;
#property (weak, nonatomic) NSString *codeUser;
#property (strong, nonatomic) NSString *nameUser;
#property (strong, nonatomic) NSDictionary *xmlDictionary;
#end
TermsViewController.h
#import <UIKit/UIKit.h>
#interface TermsViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#property (strong, nonatomic) NSString *nameUserTerms;
#end
FirstViewController.m
#import "FirstViewController.h"
#import "TermsViewController.h"
#import "XMLReader.h"
#interface FirstViewController ()
#property (nonatomic, strong) NSMutableURLRequest *postRequest;
#property NSUInteger responseStatusCode;
#property (nonatomic, strong) NSString *theXML;
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)accessButton:(UIButton *)sender {
self.codeUser = self.codeField.text;
NSString *xmlCode = [NSString stringWithFormat:
#"<?xml version='1.0' encoding='utf-8'?>\n"
"<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
"<soap:Body>\n"
"<GetInterview xmlns='http://www.url.com/url.JCV'>\n"
"<Codigo>"
"%#"
"</Codigo>\n"
"</GetInterview>\n"
"</soap:Body>\n"
"</soap:Envelope>", self.codeUser];
NSLog(#"User code is: %#", self.codeUser);
NSLog(#"XML is: %#", xmlCode);
self.postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.url.com/url.JCV/web.url.asmx"]];
[self.postRequest setValue:#"text/xml" forHTTPHeaderField:#"Content-Type"];
[self.postRequest setHTTPMethod:#"POST"];
[self.postRequest setHTTPBody:[NSMutableData dataWithBytes:[xmlCode UTF8String] length:strlen([xmlCode UTF8String])]];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:self.postRequest delegate:self];
if (conn) {
NSLog(#"Connected to: %#", conn);
} else {
NSLog(#"Connection Error");
}
[self.codeField resignFirstResponder];
}
FirstViewController.m connectionDidFinishLoading method
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
if (self.responseStatusCode == 200) {
NSLog(#"Succeeded! Received %lu bytes of data",[self.theXML length]);
// Parse the XML into a dictionary
NSError *parseError = nil;
self.xmlDictionary = [XMLReader dictionaryForXMLString:self.theXML options:XMLReaderOptionsProcessNamespaces error:&parseError];
NSLog(#"%#", self.xmlDictionary);
//name of the candidate
self.nameUser = [[[[[[[self.xmlDictionary objectForKey:#"Envelope"] objectForKey:#"Body"] objectForKey:#"GetInterviewResponse"] objectForKey:#"GetInterviewResult"] objectForKey:#"Obj"] objectForKey:#"ProfissionalName"] objectForKey:#"text"];
NSLog(#"User name is: %#", self.nameUser);
TermsViewController *nv = [[TermsViewController alloc] init];
nv.nameUserTerms = self.nameUser;
//check
NSLog(#"User name stored: %#", nv.nameUserTerms);
[self performSegueWithIdentifier:#"goToTerms" sender:self];
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error!"
message:#"bla bla."
delegate:self
cancelButtonTitle:#"Try again"
otherButtonTitles:nil];
[alert show];
}
TermsViewController.m
#import "TermsViewController.h"
#interface TermsViewController ()
#end
#implementation TermsViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.nameLabel.text = self.nameUserTerms;
//this check is returning NULL
NSLog(#"User name: %#", self.nameUserTerms);
}
#end
You should use prepareForSegue to exchange data between controllers.
Remove these lines from your code:
TermsViewController *nv = [[TermsViewController alloc] init];
nv.nameUserTerms = self.nameUser;
And put them in a method like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"goToTerms"]) {
TermsViewController *nv = segue.destinationViewController;
nv.nameUserTerms = self.nameUser;
}
}

iOS should model objects be able to access network with NSURLSession

I am in the middle of an app re-design and am refactoring and extending my model.
One aspect of my apps model is that the app retrieves data from a web service and populates the model.
My question is: Should my model objects have the capability to implement NSURLSession or should I rely on the VC to provide the connection?
I'm asking from a best practices standpoint. What's the best way to think about this? Should the model be totally on its own or should it have network access?
One consideration is that these model objects are essentially useless without data from the network, meaning data from the Internet is a fundamental aspect of their existence.
If we take SOLID — especially the S for Single Responsible Principle — in account, it becomes obvious, that neither the VC nor the model should do the networking:
a VC's single responsible would be to handle views
the model's purpose would be to hold data
networking should be done by a third class, a networking controller.
This three points will fulfill SOLID, but how do you get data from the network into model objects show on a view?
Well, this depends on your overall architectural design on the app, but a common approach would be to use callback — either a delegate protocol or a block — with your network controller.
You create a network controller in the app delegate and pass it from view controller to view controller via properties to any place in the app were newly fetched data is needed. I wouldn't use a singleton here, as that violates O, I & D of SOLID.
Add a class method to your model +(NSArray *)modelObjectsFromDictionaries:(NSArray *) or similar.
In the view controller you can now do
-(void)viewDidLoad
{
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
[self.networkController fetchModels:^(NSArray *modelDictionaries, NSError *error){
typeof(weakSelf) self = weakSelf;
if(self) {
if(!error){
[self.dataSource addOrUpdateData:[Model modelObjectsFromDictionaries:modelDictionaries]];
} else {
// error handling
}
}
}];
}
This is just a starting point. For more complicated APIs it might be useful to use an api controller that itself uses the networking controller and maybe a persistence controller.
Although instead of a Model class method you might want to use some sort of mapping and abstract factory pattern… But all this things would require more information about your app and are out of the scope for this question.
Update:
I created a sample project to demonstrate this.
It is slightly different than what I say above:
As it uses a table view, I am using a data source class to populate it. Instead of the view controller the data source will tell the network controller to fetch new data.
I am using OFAPopulator for this, a library written by me to populate table views and collection views in a SOLID-conform fashion, or to «Keep view controllers clean and MVC smart».
#import "AppDelegate.h"
#import "VSNetworkController.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self.window.rootViewController setValue:[[VSNetworkController alloc] initWithBaseURL:[NSURL URLWithString:#"http://api.goeuro.com/api/v2/"]]
forKey:#"networkController"];
return YES;
}
#end
// VSNetworkController.h
#import <Foundation/Foundation.h>
#interface VSNetworkController : NSObject
-(instancetype)initWithBaseURL:(NSURL *) baseURL;
-(void)suggestionsForString:(NSString *)suggestionString
responseHandler:(void(^)(id responseObj, NSError *error))responseHandler;
#end
// VSNetworkController.m
#import "VSNetworkController.h"
#interface VSNetworkController ()
#property (nonatomic, strong) NSURL *baseURL;
#end
#implementation VSNetworkController
-(instancetype)initWithBaseURL:(NSURL *)baseURL
{
self = [super init];
if (self) {
_baseURL = baseURL;
}
return self;
}
-(void)suggestionsForString:(NSString *)suggestionString
responseHandler:(void(^)(id responseObj, NSError *error))responseHandler
{
NSURL *url = [self.baseURL URLByAppendingPathComponent:[NSString stringWithFormat:#"position/suggest/en/%#", suggestionString]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *connectionError) {
responseHandler([NSJSONSerialization JSONObjectWithData:data options:0 error:nil], connectionError);
}];
}
#end
// VSLocationSuggestion.h
#import <Foundation/Foundation.h>
#import CoreLocation;
#interface VSLocationSuggestion : NSObject
#property (nonatomic, copy, readonly) NSString *name;
#property (nonatomic, copy, readonly) NSString *country;
#property (nonatomic, strong, readonly) CLLocation *position;
+(NSArray *)suggestionsFromDictionaries:(NSArray *)dictionaries;
#end
// VSLocationSuggestion.m
#import "VSLocationSuggestion.h"
#interface VSLocationSuggestion ()
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *country;
#property (nonatomic, strong) CLLocation *position;
#end
#implementation VSLocationSuggestion
+(NSArray *)suggestionsFromDictionaries:(NSArray *)dictionaries
{
NSMutableArray *array = [#[] mutableCopy];
[dictionaries enumerateObjectsUsingBlock:^(NSDictionary *suggestionDict, NSUInteger idx, BOOL *stop) {
[array addObject:[[self alloc] initWithDictionary:suggestionDict]];
}];
return [array copy];
}
-(instancetype)initWithDictionary:(NSDictionary *)dict
{
self = [super init];
if (self) {
_name = dict[#"name"];
_country = dict[#"country"];
CLLocationDegrees latitude = [dict[#"geo_position"][#"latitude"] doubleValue];
CLLocationDegrees longitude =[dict[#"geo_position"][#"longitude"] doubleValue];
_position = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
}
return self;
}
#end
// VSSuggestionDataSource.h
#import <Foundation/Foundation.h>
#import <OFADataProvider.h>
#class VSNetworkController;
#interface VSSuggestionDataSource : NSObject <OFADataProvider>
-(instancetype)initWithNetworkController:(VSNetworkController *)networkController;
-(void)setNewSuggestions:(NSArray *)suggetsions;
-(void)enteredStringForSuggestions:(NSString *)suggestionString;
#end
// VSSuggestionDataSource.m
#import "VSSuggestionDataSource.h"
#import "VSNetworkController.h"
#import "VSLocationSuggestion.h"
#interface VSSuggestionDataSource ()
#property (nonatomic, copy) void (^available)(void);
#property (nonatomic, strong) VSNetworkController *networkController;
#end
#implementation VSSuggestionDataSource
#synthesize sectionObjects;
-(instancetype)initWithNetworkController:(VSNetworkController *)networkController
{
self = [super init];
if (self) {
_networkController = networkController;
}
return self;
}
-(void)dataAvailable:(void (^)(void))available
{
_available = available;
}
-(void)setNewSuggestions:(NSArray *)suggetsions
{
self.sectionObjects = suggetsions;
self.available();
}
-(void)enteredStringForSuggestions:(NSString *)s
{
__weak typeof(self) weakSelf = self;
[self.networkController suggestionsForString:s responseHandler:^(NSArray *responseObj, NSError *error) {
typeof(weakSelf) self = weakSelf;
if (self) {
if (!error && responseObj) {
NSArray *suggestion = [VSLocationSuggestion suggestionsFromDictionaries:responseObj];
[self setNewSuggestions:suggestion];
}
}
}];
}
#end
// ViewController.h
#import <UIKit/UIKit.h>
#class VSNetworkController;
#interface ViewController : UIViewController
#property (nonatomic, strong) VSNetworkController *networkController;
#end
// ViewController.m
#import "ViewController.h"
#import "VSLocationSuggestion.h"
#import <OFAViewPopulator.h>
#import <OFASectionPopulator.h>
#import "VSSuggestionDataSource.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (strong, nonatomic) OFAViewPopulator *viewPopultor;
#property (strong, nonatomic) VSSuggestionDataSource *dataSource;
- (IBAction)textChanged:(UITextField *)sender;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = [[VSSuggestionDataSource alloc] initWithNetworkController:self.networkController];
OFASectionPopulator *sectionPopulator = [[OFASectionPopulator alloc] initWithParentView:self.tableView
dataProvider:self.dataSource
cellIdentifier:^NSString *(id obj, NSIndexPath *indexPath) {
return #"Cell";
} cellConfigurator:^(VSLocationSuggestion *obj, UITableViewCell *cell, NSIndexPath *indexPath) {
cell.textLabel.text = obj.name;
}];
sectionPopulator.objectOnCellSelected = ^(VSLocationSuggestion *suggestion, UIView *cell, NSIndexPath *indexPath ){
NSString * string =[NSString stringWithFormat:#"%#, %# (%f %f)", suggestion.name, suggestion.country, suggestion.position.coordinate.latitude, suggestion.position.coordinate.longitude];
UIAlertController *avc = [UIAlertController alertControllerWithTitle:#"Selected" message:string preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(#"Cancel", #"Cancel action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
;
}];
[avc addAction:cancelAction];
[self presentViewController:avc animated:YES completion:NULL];
};
self.viewPopultor = [[OFAViewPopulator alloc] initWithSectionPopulators:#[sectionPopulator]];
}
- (IBAction)textChanged:(UITextField *)sender
{
NSString *s = sender.text;
if ([s length]) {
[self.dataSource enteredStringForSuggestions:s];
}
}
#end;
I made this code available on github: https://github.com/vikingosegundo/LocationSugesstion

use of undeclared identifier objective-c

I am getting the use of undeclared identifier 'data' in the didreceiveresponse method. I have imported the Model header file which has the array and data declared. I found that if I declare them inside the viewcontroller.h file the error goes away. What is the cause of this problem?
#import "ViewController.h"
#import "DetailViewController.h"
//#import "Model.h"
#import "Model.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURL *url = [NSURL URLWithString:#"https://s3.amazonaws.com/jon-hancock-phunware/nflapi-static.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
data = [[NSMutableData alloc]init];
}
This is the model header file.
#import <UIKit/UIKit.h>
#interface Model : UIViewController
//NSArray *bars;
//NSMutableData *data;
#property (nonatomic, retain) NSArray *bars;
#property (nonatomic, retain) NSMutableData *data;
#end
This fixes the error defining the variables inside the viewcontroller.h file.
import
#interface ViewController : UIViewController {
IBOutlet UITableView *mainTableView;
NSArray *bars;
NSMutableData *data;
}
#end
It's Objective-C not Swift. You access properties with self.data or _data. It doesn't know well enough to look to your global variables unless you're explicit about it. Also, I would lean towards self.data over directly accessing _data so the setter gets called. In this case, it's almost certainly not going to be an issue but setting ivars directly with an underscore can mess up key value observation and might have unexpected consequences that are difficult to debug.

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?

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

Resources