Parsing JSON with AFHTTPClient - objective-c

I'm using the AFNetworking library to parse json using the AFHTTPClient. I can verify that the json is being parsed within the client block and send that data to my json model. However, when I try to access the json model from outside the block I get no data. How can I pass the parsed json data to the json model then access that model data elsewhere in the app?
the AFHTTPClient subclass / singleton:
#import <Foundation/Foundation.h>
#import "AFHTTPClient.h"
#interface JsonClient : AFHTTPClient
+ (JsonClient *)sharedClient;
#end
#import "JsonClient.h"
#import "AFJSONRequestOperation.h"
static NSString *const kJsonBaseURLString = #"https://alpha-api.app.net/";
#implementation JsonClient
+ (JsonClient *)sharedClient {
static JsonClient *_sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedClient = [[JsonClient alloc] initWithBaseURL:[NSURL URLWithString:kJsonBaseURLString]];
});
return _sharedClient;
}
- (id)initWithBaseURL:(NSURL *)url {
self = [super initWithBaseURL:url];
if (!self) {
return nil;
}
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setDefaultHeader:#"Accept" value:#"application/json"];
return self;
}
#end
the JSON model data:
#import <Foundation/Foundation.h>
#interface TheJson : NSObject
#property (nonatomic, copy) NSString *createdAt;
#property (nonatomic, copy) NSString *userText;
- (id)initWithDictionary:(NSDictionary *)dict;
#end
#import "TheJson.h"
#implementation TheJson
- (id)initWithDictionary:(NSDictionary *)dict {
self = [super init];
if (self) {
self.createdAt = [dict objectForKey:#"created_at"];
self.userText = [dict objectForKey:#"text"];
}
return self;
}
#end
the ViewController to update the user interface:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
#import "ViewController.h"
#import "JsonClient.h"
#import "TheJson.h"
#interface ViewController ()
#property (weak) IBOutlet UILabel *createdLabel;
#property (weak) IBOutlet UILabel *textLabel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (IBAction)fetchJsonData:(id)sender {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[[JsonClient sharedClient] getPath:#"stream/0/posts/stream/global" parameters:nil
success:^(AFHTTPRequestOperation *operation, id JSON) {
NSArray *postsFromResponse = [JSON valueForKeyPath:#"data"];
NSDictionary *dictFromArray = postsFromResponse[0];
TheJson *jsonObject = [[TheJson alloc] initWithDictionary:dictFromArray];
NSLog(#"createdAt is %#", jsonObject.createdAt);
NSLog(#"text from user is %#", jsonObject.userText);
[self updateInterface];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error is %#", [error description]);
}
];
}
- (void)updateInterface {
TheJson *thejson;
[_createdLabel setText:thejson.createdAt];
[_textLabel setText:thejson.userText];
}
#end

You haven't passed the new jsonObject out of the block nor stored it anywhere. A short term answer is to declare updateInterface to take the jsonObject as a parameter.
So your updateInterface becomes updateInterface: something like this:
- (void)updateInterface:(TheJson*)thejson {
[_createdLabel setText:thejson.createdAt];
[_textLabel setText:thejson.userText];
}
...and then within your block, you call this method like this:
[self updateInterface:jsonObject];
Longer term, if your app has many of these objects and/or needs to hold onto them for any amount of time, you probably want to think about how you will store and organize these as you download them.

Related

Objective C protocol method is not called

I have created singleton class for AVAudioPlayer. I am able to call the methods in the class and everything works fine. When the song finishes,the method (void)audioPlayerDidFinishPlaying is called which in turn suppose to call the method ' processSuccessful' in my downloadPlay.m class. But, it is not calling the method 'processSuccessful'
My codes as follows
PlayerManager.h
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#protocol ProcessDataDelegate <NSObject>
#required
- (void) processSuccessful;
#end
#interface PlayerManager : NSObject<AVAudioPlayerDelegate,AVAudioSessionDelegate>
{
id <ProcessDataDelegate> delegate;
}
+ (PlayerManager *)sharedAudioPlayer;
#property (nonatomic,assign) id <ProcessDataDelegate>delegate;
#property (nonatomic, strong) AVAudioPlayer* player;
-(void)preparesong:(NSURL *)url;
-(void)stopsong;
-(void)pause;
-(void)playsong;
-(void)prepareToPlay;
-(BOOL)isPlaying;
-(BOOL)isPlayerExist;
#end
PlayerManager.m
#import "PlayerManager.h"
#interface PlayerManager()
#end
#implementation PlayerManager
#synthesize player;
#synthesize delegate;
static PlayerManager *sharedAudioPlayer = nil;
+ (PlayerManager *)sharedAudioPlayer {
static PlayerManager *sharedAudioPlayer = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedAudioPlayer = [[self alloc] init];
});
return sharedAudioPlayer ;
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
{
if (flags & AVAudioSessionInterruptionOptionShouldResume)
{
[self.player play];
}
}
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
}
#pragma mark - AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[[self delegate] processSuccessful];
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
}
-(void)preparesong:(NSURL *)url
{
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
NSError *error;
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if(!self.player)
{
NSLog(#"Error creating player: %#", error);
}
self.player.delegate = self;
[self.player prepareToPlay];
}
-(BOOL)isPlayerExist
{
if (player)
return YES;
return NO;
}
-(BOOL)isPlaying
{
if (player && player.playing)
return YES;
return NO;
}
-(void)prepareToPlay
{
if (player)
[self.player prepareToPlay];
}
-(void)playsong
{
if (player)
[self.player play];
}
-(void)pause
{
if (player.playing)
[self.player pause];
}
-(void)stopsong
{
if (player)
[self.player stop];
}
#end
downloadPlay.h
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import "PlayerManager.h"
#interface downloadPlay: UIViewController <UITableViewDelegate,AVAudioPlayerDelegate,ProcessDataDelegate>
{
PlayerManager *protocolPlay;
}
#property (retain, nonatomic) IBOutlet UITableView *tblFiles;
......
- (void)startPlay:(id)sender;
........
#end
downloadPlay.m
import "downloadPlay.h"
#import "PlayerManager.h"
#interface downloadPlay ()
#end
#implementation downloadPlay
.....
- (void)processSuccessful
{
NSLog(#"This method suppose to be called from the method audioPlayerDidFinishPlaying - from PlayerManager");
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
protocolPlay = [[PlayerManager alloc]init];
[protocolPlay setDelegate:self];
}
- (void)startPlay
{
............
.........
NSURL *destinationURL = [self.docDirectoryURL URLByAppendingPathComponent:filename];
NSError* error = nil;
[[PlayerManager sharedAudioPlayer]stopsong];
[[PlayerManager sharedAudioPlayer ] preparesong:destinationURL ];
[[PlayerManager sharedAudioPlayer]playsong];
}
#end
In viewDidLoad method you are creating a different object by using
protocolPlay = [[PlayerManager alloc]init];
line and set the delegate of this object while you have to set the delegate of shared object.
Solution is:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[PlayerManager sharedAudioPlayer] setDelegate:self];
}

Null variable in Objective C

I'm developing an iOS application and I'm having some problems when I try to save data.
My code is:
TuplaUsuario:
It is a class where I save user data. In that case, I use mensaje variable, so here is its code:
//TuplaUsuario.h:
#interface TuplaUsuario : NSObject
{
NSMutableString* mensaje;
}
#property NSMutableString* mensaje;
#end
//TuplaUsuario.m:
#import "TuplaUsuario.h"
#implementation TuplaUsuario
#synthesize mensaje;
- (id)initWithString:(NSString *)identifier {
if ( self = [super init] ) {
mensaje = [[NSMutableString alloc] initWithString:identifier];
}
return self;
}
#end
WebService:
It is a class where I communicate with a Web Service.
//WebService.h:
#import "TuplaUsuario.h"
#interface WebService : NSObject {
// Some other data
NSMutableString* message;
TuplaUsuario* usuario;
}
//Declaration of methods
#end
//WebService.m:
#import "WebService.h"
#import "AppDelegate.h"
#implementation WebService
- (id)init:(NSString *)identifier {
self = [super init];
usuario = [[TuplaUsuario alloc] initWithString:#""];
return self;
}
- (void) processComplete: (BOOL)success {
[[self delegate] processSuccessful:success];
AppDelegate* myAppDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[myAppDelegate setUsuarioActual:usuario];
}
- (void)login:(NSString *)username password:(NSString *)password
{
//Connection with Web Service
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData {
NSDictionary* jsonArray=[NSJSONSerialization
JSONObjectWithData:theData
options:0
error:nil];
message = [jsonArray objectForKey:#"message"];
[usuario setMensaje:message];
}
AppDelegate:
//AppDelegate.h:
#import <UIKit/UIKit.h>
#import "TuplaUsuario.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
Boolean rememberMe;
TuplaUsuario* usuarioActual;
}
#property (strong, nonatomic) UIWindow *window;
#property (retain) TuplaUsuario* usuarioActual;
#end
//AppDelegate.m:
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize usuarioActual;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
usuarioActual = [[TuplaUsuario alloc] initWithString:#""];
return YES;
}
PROBLEM
In WebService, method connection didReceiveData, if I print message variable, it has the correct value, but if I print [usuario mensaje] it prints (null).
Where's my error?
SOLVED
The problem was in my ViewController and init method of WebService. I called init instead of initWithString:
ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
wSProtocol = [[WebService alloc] initWithString:#""]; //Variable of type WebService
}
WebService
- (id)initWithString:(NSString *)identifier {
self = [super init];
usuario = [[TuplaUsuario alloc] initWithString:#""];
return self;
}
Maybe because this method:
- (id)init:(NSString *)identifier {
if ( self = [super init] ) {
mensaje = [[NSMutableString alloc] init];
}
return self;
}
is never called, since you use:
usuario = [[TuplaUsuario alloc] init];
but is still wonder why you pass identifier into the method and never use it. I think you should make it like this:
mensaje = [[NSMutableString alloc] initWithString:identifier];

Pass NSArrays from ViewController to NSObject class

I have a single view application where the ViewController contains a UIView of custom class CPTGraphHostingView so that it holds a CorePlot graph. The content of that UIView is managed by an NSObject sub-class called SimpleScatterPlot. This SimpleScatterPlot object holds all the parameters needed to configure the graph (axis ranges, labels, number of points, etc.)
ViewController.h
#import <UIKit/UIKit.h>
#import "CorePlot-CocoaTouch.h"
#import "SimpleScatterPlot.h"
#interface ViewController : UIViewController
{
IBOutlet CPTGraphHostingView *_graphHostingView;
SimpleScatterPlot *_scatterPlot;
NSMutableData *dataConexion;
}
#property (nonatomic,strong) NSArray *valor;
#property (nonatomic,strong) NSArray *strAnoMes;
#property (nonatomic,strong) NSArray *indicador;
#property (nonatomic, retain) SimpleScatterPlot *scatterPlot;
#end
In ViewController.m I have some methods to initialise a few arrays from some JSON data:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize indicador,valor,strAnoMes;
- (void)viewDidLoad
{
[super viewDidLoad];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSString *urlString = #"http://xxxx/ios/datosgrafica.php";
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
(void)[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
dataConexion = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
[dataConexion appendData:theData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
indicador = [NSJSONSerialization JSONObjectWithData:dataConexion options:nil error:nil];
// ARRAYS OF INTEREST
strAnoMes = [indicador valueForKey:#"StrAnoMes"];
valor = [indicador valueForKey:#"Valor"];
self.scatterPlot = [[SimpleScatterPlot alloc] initWithHostingView:_graphHostingView andData:pointArray];
[self.scatterPlot initialisePlot];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
UIAlertView *errorView = [[UIAlertView alloc] initWithTitle:#"Error" message:#"The download could not complete - please make sure you're connected to either 3G or Wi-Fi." delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[errorView show];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
In the SimpleScatterPlot.m I would like to use the array "valor" to configure axis ranges, and I would like to use the array "strAnoMes" to draw custom labels in the xAxis.
What do I have to do so that I can pass those arrays defined in ViewController to SimpleScatterPlot?
I have tried to #import "ViewController.h" but it gives me errors. Any other ideas? What is the best way to approach this situation?
You can take two properties in SimpleScatterPlot.
SimpleScatterPlot.h
#property (nonatomic, retain) NSArray * strAnoMes;
#property (nonatomic, retain) NSArray * valor;
SimpleScatterPlot.m
#synthesize strAnoMes;
#synthesize valor;
In ViewController.m, After creating scatterPlot in connectionDidFinishLoading:, assign the values to above properties as below.
self.scatterPlot.strAnoMes = strAnoMes;
self.scatterPlot.valor = valor;
One way to do this is to create a simple data object and pass it directly into your SimpleScatterPlot object:
Data Object:
#interface SimpleScatterPlotData : NSObject
#property (...) NSArray *valor;
#property (...) NSArray *strAnoMes;
#end
#implementaton SimpleScatterPlotData
#synthesize valar;
#synthesize strAnoMes;
-(void)dealloc
{
...
...
[super dealloc];
}
#end
Implement a load method in your SimpleScatterPlot class:
#interface SimpleScatterPlot
-(void)loadData:(SimpleScatterPlotData *)data;
#end
#implementation SimpleScatterPlot
-(void)loadData:(SimpleScatterPlotData *)data
{
NSArray *valor = data.valor;
NSArray *strAnoMes = data.strAnoMes;
/*
Do something
*/
}
#end
Then in your ViewController class:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
indicador = [NSJSONSerialization JSONObjectWithData:dataConexion options:nil error:nil];
// ARRAYS OF INTEREST
strAnoMes = [indicador valueForKey:#"StrAnoMes"];
valor = [indicador valueForKey:#"Valor"];
SimpleScatterPlotData *data = [[[SimpleScatterPlotData alloc] init] autorelease];
data.valor = valor;'
data.strAnoMes = strAnoMes;
self.scatterPlot = [[SimpleScatterPlot alloc] initWithHostingView:_graphHostingView andData:pointArray];
[self.scatterPlot loadData:data];
}
You could also change your current initializer or add a new initializer to your SimpleScatterPlot class so that you pass those arrays in when you alloc the object. So your the call to your initializer would look something like:
self.scatterPlot = [[SimpleScatterPlot alloc] initWithHostingView:_graphHostingView andData:pointArray andValor:valor andstrAnoMes:strAnoMes];
Then in your initializer you can set the object properties to those passed in values.

obj-c delegate method is null

My FetchVenuesView preceeds the VenuesIDController. VenuesIDController is the second tabbar item in a tabbarcontroller. FetchVenuesView is not part of the tabbar.
The first item in the tabbar is a tableview in which i can call a delegate without issue.
However when I try and call the delegate in VenuesIDController it always shows up in the log as null.
What do I do here? Do i connect the delegate in the storyboard? How?
I have a FetchVenuesViewController.h
#import "VenueTableViewController.h"
#import "VenueIDController.h"
#interface FetchVenuesViewController : UIViewController< VenueTableViewControllerDelegate, VenueIDControllerDelegate>{
NSDictionary* venueJSON;
NSDictionary* idJSON;
};
#property (strong) NSDictionary* idJSON;
- (void)VenueFetch;
- (void)IDFetch;
#end
In FetchVenuesViewController.m
#synthesize idJSON;
- (void)IDFetch {
//request some webservice
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
//save the response
if (data) {
id IDJSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (!IDJSON) {
//handle error
}
else {
//do something
}
} else {
// fetch failed
}
activityIndicator.hidden = NO;
}
-(NSDictionary *)getID{
[self IDfetch];
NSLog(#"json%#",idJSON);
return idJSON;
}
In VenueIDController.h
#protocol VenueIDControllerDelegate;
#interface VenueIDController : UIViewController{
}
#property (assign) id <VenueIDControllerDelegate> delegate;
-(IBAction)getIDData:(id)sender;
#end
#protocol VenueIDControllerDelegate <NSObject>
-(NSDictionary *)getID;
#end
and in VenueIDController.m
#interface VenueIDController (){
NSMutableArray* IDData;
UIImage* IDBarcode;
}
-(void) displayIDData:(NSDictionary*)data;
#end
#implementation VenueIDController
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
VenueIDController *vid = [[VenueIDController alloc] init];
vid.delegate = self;
NSLog(#"%#",vid);
}
return self;
}
-(void) displayIDData:(NSDictionary*)data{
[delegate getID];
NSDictionary* idJSON = data;
}
Your init on VenueIDController appears wrong. you're already in an init, so you don't need to create another one. instead you should have self.delegate = self. The vid object you are creating there isn't going to be retained.

Why is Xcode saying my class implementation is incomplete?

I have created a singleton for my MusicBackground. And I receive a line code of imcomplete implementation of this line #implementation MyBgMusic. Can anyone tell me why ? Below is the code:
#import "MyBgMusic.h"
static MyBgMusic *sharedMyManager = nil;
#implementation MyBgMusic
#synthesize player,playBgMusic;
#pragma mark -
#pragma mark Singleton Methods
+ (MyBgMusic*)sharedInstance {
static MyBgMusic *_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;
}
#if (!__has_feature(objc_arc))
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; //denotes an object that cannot be released
}
- (id)autorelease {
return self;
}
- (void)dealloc
{
[MyBgMusic release];
[playBgMusic release];
[player release];
[super dealloc];
}
#endif
#pragma mark -
#pragma mark Custom Methods
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"music" ofType:#"mp3"];
self.player=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
player.delegate = self;
[player play];
player.numberOfLoops = -1;
[super viewDidLoad];
}
#end
For the M file, below is the code:
#import <Foundation/Foundation.h>
#import <AVFoundation/AVAudioPlayer.h>
#interface MyBgMusic : UIViewController <AVAudioPlayerDelegate> {
AVAudioPlayer *player;
UIButton *playBgMusic;
}
#property (nonatomic, retain) IBOutlet AVAudioPlayer *player;
#property (nonatomic, retain) IBOutlet UIButton *playBgMusic;
+ (id)sharedManager;
-(IBAction) toggleMusic;
#end
And how do I reference to my toggle button: Below is the code :
- (IBAction)toggleMusic {
if ([self.player isPlaying] == YES) {
[self.player stop];
} else {
[self.player play];
}
self.playBgMusic.enabled = YES;
}
It means that your MyBgMusic class isn't doing everything it promised to do in its header file, which includes being a UIViewController and implementing the AVAudioPlayerDelegate protocol. I'm not familiar with exactly what the AVAudioPlayerDelegate is, but it's quite possible that your class doesn't implement all of the required methods.
Also, you're declaring methods +(id)sharedManager and -(IBAction)toggleMusic, but I don't see them anywhere in the implementation file. That would be a case of promising something in the header and not implementing it in the class.
It would help if you posted the actual error message.
That error means your #implementation section does not contain everything described in the #interface section.
I can see two problems.
First you need to place this code:
- (IBAction)toggleMusic {
...
}
Somewhere in between #implementation and #end.
And you also need to rename the line + (MyBgMusic*)sharedInstance to + (id)sharedManager.
EDIT:
To access the toggle music method elsewhere in your code, you would do:
[[MyBgMusic sharedManager] toggleMusic];
Your +(id)sharedManagerimplementation is called +(id)sharedInstance. Just guessing, but it seems they are supposed to do the same.