Use core data entity in a countdown timer - loops

I have 2 entities in core data to create countdown timers. Timer has an attribute called timerName and entity Blinds(changed from 'Times') has an attribute called duration.
Entities called
Timer <---->> Blind
and attributes called
timerName <---->> duration
with relationships called
blinds <---->>timer
I need to place the various durations into a countdown timer one at a time. When the first duration reaches 0 the next duration is fetched from core data and that is counted down to zero etc.
I am very new to Objective-C and core data but I know I need a loop and fetch request but don't know where to start. Any code examples would be appreciated. Thanks
EDIT
I have setup a fetchrequest in my model.m
- (NSFetchedResultsController *)frc_newTimer
{
if (_frc_newTimer) return _frc_newTimer;
// Otherwise, create a new frc, and set it as the property (and return it below)
_frc_newTimer = [_cdStack frcWithEntityNamed:#"Timer"
withPredicateFormat:nil
predicateObject:nil
sortDescriptors:#"timerName,YES"
andSectionNameKeyPath:nil];
return _frc_newTimer;
}
Then in my view controller.h
#import <UIKit/UIKit.h>
#import "Timer.h"
#import "Blind.h"
#interface BlindTimerViewController : UIViewController <NSFetchedResultsControllerDelegate>
{
IBOutlet UILabel *lblCountDown;
NSTimer *countdownTimer;
int secondsCount;
}
- (IBAction)StartTimer:(id)sender;
- (IBAction)ResetTimer:(id)sender;
#property (assign, nonatomic) NSInteger currentTimeIndex;
#property (nonatomic, strong) Model *model;
#property (nonatomic, strong) Timer *myTimer;
#end
then in view controller.m
#interface BlindTimerViewController ()
#end
#implementation BlindTimerViewController
#synthesize model = _model;
and
-(void) timerRun
{
secondsCount = secondsCount -1;
int minutes = secondsCount / 60;
int seconds = secondsCount - (minutes * 60);
NSString *timerOutput = [NSString stringWithFormat:#"%2d:%.2d", minutes, seconds];
lblCountDown.text = timerOutput;
//need to add a label for the next blind in the coredata list and update it while in a loop......
if (secondsCount == 0) {
[countdownTimer invalidate];
countdownTimer = nil;
}
}
-(void) setTimer{
// Configure and load the fetched results controller
self.model.frc_newTimer.delegate = self;
self.model.frc_newTimer.fetchRequest.predicate = [NSPredicate predicateWithFormat:#"timerName LIKE %#", #"Sample Timer"];
//add code to get the first coredata item in the blinds list
secondsCount = 240; // i need to insert the CoreData Blinds HERE
countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerRun) userInfo:nil repeats:YES];
}
and buttons (yet to be fully sorted) to start actions
- (IBAction)StartTimer:(id)sender
{
[self setTimer];
}
- (IBAction)ResetTimer:(id)sender {
[countdownTimer invalidate];
countdownTimer = nil;
secondsCount = 0;
lblCountDown.text = #"00:00";
}

I'm assuming that you're running the countdown for a known Timer. In this case you don't need a fetch request as you have a relationship from the Timer to its set of Times, we can access it directly:
NSSet *times = self.myTimer.times;
We want to sort it so you can run the durations in some order:
(you might also want to check that the count of times > 0)
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"duration" ascending:YES];
NSArray *orderedTimes = [times sortedArrayUsingDescriptors:#[ sortDescriptor ]];
Next, we're going to need an instance variable to track where we are:
#property (assign, nonatomic) NSInteger currentTimeIndex;
With these parts, you can manage the process, and use an NSTimer to actually do the work. When the timer fires you go back to the time, get and sort the times, increment the index we're using, check the index is in range, get the duration and start the timer.
I'm going to be cheeky and say that if the expiring timer is nil, that means we're starting the process from scratch (it would be better to take the first case out into a specific method):
- (void)timerFired:(NSTimer *)expiringTimer
{
[expiringTimer invalidate];
NSInteger index = (expiringTimer != nil ? (self.currentTimeIndex + 1) : 0);
NSSet *times = self.myTimer.times;
if (times.count < index) {
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"duration" ascending:YES];
NSArray *orderedTimes = [times sortedArrayUsingDescriptors:#[ sortDescriptor ]];
double duration = [[[orderedTimes objectAtIndex:index] duration] doubleValue];
[NSTimer scheduledTimerWithTimeInterval:duration target:self selector:#selector(timerFired:) userInfo:nil repeats:NO];
} else {
// deal with the error
}
}
Now you can start the countdown with [self timerFired:nil];
You haven't said what you're doing while the timers are running, that could change things quite a bit (like you want to display an update of the time on screen each second)...
If you need to fetch the timer from your Core Data DB, that's where the fetch request comes in:
NSManagedObjectContext *context = <#Managed object context#>;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Timer"];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"timerName LIKE %#", #"Sample Timer"]];
NSArray *timers = [context executeFetchRequest:fetchRequest error:nil]; // should really add the error...
Timer *myTimer = nil;
if (timers.count == 1) {
myTimer = [timers lastObject];
} else {
// we didn't find the timer, agh!
}

Related

Accessing a variable in multiple methods

Still a bit new and I am having some issues I was hoping someone could help with. I am trying to load a JSON string coming from my server into a collectionview in iOS6
I can pull in the data using a fetchedData method called from the viewDidLoad method and that part works fine. In the fetchedData method, I break out the JSON data and place it in NSDictionaries and NSArrays and can dump the correct data to the log to see it.
The problem is when I try and use any of the information elsewhere in my code such as get the amount of elements in any of hte arrays to use as a counter to fill the collectionview.
It may be that I am tired but I can't seem to get my head around this part. The declaration of many of the main variables was in the fetchedData method and I thought since the were declared there it could be the reason I could not see them elsewhere so I moved the declaration of the variables to the interface section and was hoping this would make the variables GLOBAL and the fetchedData method continues to work just fine, but nowhere else.
When I put in breaks in the cell definition area I can see in the debugger window the variables come up as empty.
I am not sure what sections of the code you may want to see so let me know and I can post them but maybe someone could give an example of how arrays and dictionary items can be accessed in multiple methods.
To avoid confusion and to expose my hodgepodge of code at this point anyway here is the .m file or at least most of it Please don't rip to hard on the coding style I have been trying anything I could think of and tore it up pretty hard myself and it was late.
#import "ICBCollectionViewController.h"
#import "ICBCollectionViewCell.h"
#import "ICBDetailViewController.h"
#interface ICBCollectionViewController () {
NSDictionary* json;
NSDictionary* title;
NSDictionary* shortDescrip;
NSDictionary* longDescrip;
NSDictionary* price;
NSDictionary* path;
NSDictionary* sKU;
NSDictionary* audiotrack;
NSDictionary* audiotracksize;
NSArray* titles;
NSArray* shortDescription;
NSArray* longDescription;
NSArray* prices;
// NSArray* paths;
NSArray* SKUs;
NSArray* audiotracks;
NSArray* audiotracksizes;
}
#end
/*
#interface NSDictionary(JSONCategories)
+(NSDictionary*)dictionaryWithContentsOfJSONURLString:(NSString*)urlAddress;
-(NSData*)toJSON;
#end
#implementation NSDictionary(JSONCategories)
+(NSDictionary*)dictionaryWithContentsOfJSONURLString:(NSString*)urlAddress
{
NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString: urlAddress] ];
__autoreleasing NSError* error = nil;
id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error != nil) return nil;
return result;
}
-(NSData*)toJSON
{
NSError* error = nil;
id result = [NSJSONSerialization dataWithJSONObject:self options:kNilOptions error:&error];
if (error != nil) return nil;
return result;
}
#end
*/
#implementation ICBCollectionViewController
#synthesize paths;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: imobURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
// Do any additional setup after loading the view.
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
//parse out the json data
json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
titles = [json objectForKey:#"title"]; //2
shortDescription = [json objectForKey:#"shortD"];
longDescription = [json objectForKey:#"longD"];
prices = [json objectForKey:#"price"];
self.paths = [json objectForKey:#"path"];
SKUs = [json objectForKey:#"SKU"];
audiotracks = [json objectForKey:#"audiotrack"];
audiotracksizes = [json objectForKey:#"audiotracksize"];
NSLog(#"paths: %#", paths); //3
// NSLog(#"shortDescrip: %#", shortDescription);
NSInteger t=7;
// 1) Get the latest loan
title = [titles objectAtIndex:t];
shortDescrip = [shortDescription objectAtIndex:t];
longDescrip = [longDescription objectAtIndex:t];
price = [prices objectAtIndex:t];
path = [paths objectAtIndex:t];
sKU = [SKUs objectAtIndex:t];
audiotrack = [audiotracks objectAtIndex:t];
audiotracksize = [audiotracksizes objectAtIndex:t];
//NSLog(title.count text);
//NSLog(title.allValues);
// 2) Get the data
NSString* Title = [title objectForKey:#"title"];
NSString* ShortDescrip = [shortDescrip objectForKey:#"shortD"];
NSString* LongDescrip = [longDescrip objectForKey:#"longD"];
NSNumber* Price = [price objectForKey:#"price"];
NSString* Path = [path objectForKey:#"path"];
NSString* SKU = [sKU objectForKey:#"SKU"];
NSString* AudioTrack = [audiotrack objectForKey:#"audiotrack"];
NSNumber* AudioTrackSize = [audiotracksize objectForKey:#"audiotracksize"];
/*************************HERE THE DATA EXISTS*******************************/
/******** Path = "XYXYXYXYXYXY" for example ********************************/
// 3) Set the label appropriately
NSLog([NSString stringWithFormat:#"Here is some data: Title: %# Path %# SKU: %# Price: %# Track %# Size %#",Title, Path, SKU, Price, LongDescrip, AudioTrackSize]);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
//DetailSegue
if ([segue.identifier isEqualToString:#"DetailSegue"]) {
ICBCollectionViewCell *cell = (ICBCollectionViewCell *)sender;
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
ICBDetailViewController *dvc = (ICBDetailViewController *)[segue destinationViewController];
dvc.img = [UIImage imageNamed:#"MusicPlayerGraphic.png"];
}
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
NSLog(#"paths qty = %d",[paths count]);
return 20;
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier=#"Cell";
ICBCollectionViewCell *cell = (ICBCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
// paths = [json objectForKey:#"path"];
NSDictionary* path = [paths objectAtIndex:indexPath.row];
NSString* Path = [path objectForKey:#"path"];
// NSString* Path = [paths objectAtIndex:indexPath.row];
NSLog(#"%d",indexPath.row);
/***********************HERE IT DOES NOT**************************/
/******** Path = "" **********************************************/
NSLog(#"xxx");
NSLog(path);
NSLog(paths);
NSLog(Path);
NSLog(#"ZZZ");
Path=#"deepsleep";
NSLog(#"xxx");
NSLog(Path);
NSLog(#"ZZZ");
// paths = [json objectForKey:#"path"];
// NSString* Path = [path objectForKey:#"path"];
NSString *imagefile = [NSString stringWithFormat:#"https://imobilize.s3.amazonaws.com/glennharrold/data/%#/mid.png", Path];
NSLog(imagefile);
NSURL *url1=[NSURL URLWithString:imagefile];
dispatch_async(kBgQueue, ^{
NSData *data1 = [NSData dataWithContentsOfURL:url1];
cell.imageView.image =[[UIImage alloc]initWithData:data1];
});
return cell;
}
#end
Try breaking out the JSON data and sorting it in the appDelegate. If you declare public variables there #property (nonatomic, strong) NSDictionary *myDict etc., then you can access those variables by importing your appDelegate and using the following code:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSDictionary *newDict = appDelegate.myDict;
Otherwise, you can store the information in a singleton, or in the root view controller. The key is to store your variables in a class that won't be deallocated. Most often, it is a bad idea to use a viewController for that purpose-- they have a tendency to be navigated away from, which deallocates the memory and gets rid of your variables. Google "model-view-controller" for more info.
I found out what the main issue was it the ViewDidLoad method I was using a background activity to get the JSON data from my server and as that process was running the foreground was also being processed and since the rest of the code was based on a value returned when the background process finished the data was actually null so all data based on that single piece were also null and it looked as if it was not available. Once I made the process run in the foreground all the variable started having values.
Thanks for your assistance with this

Zooming UIImageView inside UIScrollView with autolayout

I have a UIImageView embedded inside a UIScrollView, prior to iOS 6 and autolayout I used the following snippet inside the controller's viedDidLoad method to display a scrollable and zoomable image.
self.scrollView.contentSize = self.imageView.image.size;
self.imageView.frame = CGRectMake(0, 0, self.imageView.image.size.width, self.imageView.image.size.height);
But now the constraints set in the storyboard are used instead. I've found this question Embed ImageView in ScrollView with Auto Layout on iOS 6 and some others here in SO stating that constraints are loaded/enforced after viewDidLoad, and that moving my previous snippet to viewDidAppear would fix this issue but zooming does not work properly and it seems that the sizes of the scrollView and the imageView are reset to the storyboard's constraint after a pinch-to-zoom gesture.
I'm just guessing, but I think maybe if there's some way to override the scrollView's and imageView's vertical and horizontal space constraints in code that might work.
Anyone else having this issues?
The best solution was proposed by Zsolt in the comments:
http://github.com/evgenyneu/ios-imagescroll check this out. Works perfectly for me.
The solution proposed in this repository is to adjust the minimum and current zoom level before displaying the image:
#interface MyViewController () <UIScrollViewDelegate>
#property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#end
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.delegate = self;
[self initZoom];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[self initZoom];
}
// Zoom to show as much image as possible
- (void) initZoom {
float minZoom = MIN(self.view.bounds.size.width / self.imageView.image.size.width,
self.view.bounds.size.height / self.imageView.image.size.height);
if (minZoom > 1) return;
self.scrollView.minimumZoomScale = minZoom;
self.scrollView.zoomScale = minZoom;
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
However sample conatins issue with zooming out. Image is not getting centered. This can be easily fixed by using the custom scroll view class with the following code in it:
#interface MyScrollView : UIScrollView
#end
#implementation MyScrollView
-(void)layoutSubviews
{
[super layoutSubviews];
UIView* v = [self.delegate viewForZoomingInScrollView:self];
CGFloat svw = self.bounds.size.width;
CGFloat svh = self.bounds.size.height;
CGFloat vw = v.frame.size.width;
CGFloat vh = v.frame.size.height;
CGRect f = v.frame;
if (vw < svw)
f.origin.x = (svw - vw) / 2.0;
else
f.origin.x = 0;
if (vh < svh)
f.origin.y = (svh - vh) / 2.0;
else
f.origin.y = 0;
v.frame = f;
}
#end
Solved my problem using the following code sample. The github repository corresponds to the book Programming iOS by Matt Neuburg.
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/11c6c57743b04e6e722b635b87be69fa41a5abaf/ch20p573scrollViewAutoLayout/ch20p573scrollViewAutoLayout/ViewController.m
I also agree with Zsolt's suggestion and link.
But I update the width/height constraints to allow it to handle any size image:
- (void) initZoom
{
for (NSLayoutConstraint *constraint in self.photoImageView.constraints)
{
if (constraint.firstAttribute == NSLayoutAttributeWidth)
constraint.constant = self.photoImageView.image.size.width;
else if (constraint.firstAttribute == NSLayoutAttributeHeight)
constraint.constant = self.photoImageView.image.size.height;
}
float minZoom = MIN(self.scrollView.bounds.size.width / self.photoImageView.image.size.width,
self.scrollView.bounds.size.height / self.photoImageView.image.size.height);
if (minZoom > 1) return;
self.scrollView.minimumZoomScale = minZoom;
self.scrollView.zoomScale = minZoom;
}

UICollectionView Setup Help for Newbie

First, apologies if the answer is quite obvious but I'm brand new to iOS development (like this is the first app I'm trying to program even though it's just for me to play with :P) so the probability that my problem is rather minor is quite high.
Code (zip file containing the xcode project):
http://www.mediafire.com/?p55xw0q2dwwwwvm
I promise there's nothing else in there that's harmful :).
Problem:
I'm following:
http://www.techotopia.com/index.php/An_iPhone_iOS_6_Storyboard-based_Collection_View_Tutorial
I'm attempting to make a UICollectionView to play around with. I got down to the "Testing the Application" section. I put my own spin on a few parts from the guide:
instead of using different pictures, I just use 1 picture with a label at the bottom that just has a number in it. The number is dynamically set. Yes, I could implement it without the array I'm using, but having that array there will be helpful if I extend this project's scope in the future.
I added code in viewDidLoad to set the view layout to a flow layout. This was done again for futureproofing since once I get this working at a basic form I want to play around with formatting which will require me to subclass flowlayout.
The code compiles without errors but nothing shows up on the screen. I've checked the code to the best of my ability for about an hour or two, but nothing I did made any difference. My controller class is a bare bones attempt to just get the collection view to show up on screen, and my cell class is a bare bones cell with just an imageview and label in it.
Thanks for any help that can be given to get this working!
tl;dr
Just look at the bolded stuff. Code provided below for convenience:
MyCollectionViewController.m
#import "MyCollectionViewController.h"
#interface MyCollectionViewController ()
#property (nonatomic, strong) NSMutableArray *dataArray;
#end
#implementation MyCollectionViewController
#synthesize dataArray = _dataArray;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Create data to display
for(int i = 0; i < 50; i++){
[self.dataArray addObject:[NSString stringWithFormat:#"%d", i]];
}
// Configure Layout
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[self.collectionView setCollectionViewLayout:flowLayout];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark -
#pragma mark UICollectionViewDataSource
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section
{
return [self.dataArray count];
}
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCollectionViewCell *myCell = [collectionView dequeueReusableCellWithReuseIdentifier:#"MyCell" forIndexPath:indexPath];
int row = [indexPath row];
UIImage* image = [UIImage imageNamed:#"200px-AND_ANSI"];
myCell.cellTitle = [self.dataArray objectAtIndex:row];
myCell.cellImageView.image = image;
myCell.backgroundColor = UIColor.whiteColor;
return myCell;
}
MyCollectionViewcontroller.h
#import <UIKit/UIKit.h>
#import "MyCollectionViewCell.h"
#interface MyCollectionViewController : UICollectionViewController <UICollectionViewDataSource, UICollectionViewDelegate>
#end
MyCollectionViewCell.h
#import <UIKit/UIKit.h>
#interface MyCollectionViewCell : UICollectionViewCell
#property (strong, nonatomic) IBOutlet UIImageView *cellImageView;
#property (strong, nonatomic) IBOutlet UILabel *cellTitle;
#end
MyCollectionViewCell.m
#import "MyCollectionViewCell.h"
#implementation MyCollectionViewCell
#synthesize cellImageView = _cellimageView;
#synthesize cellTitle = _cellTitle;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
self.dataArray is nowhere allocated/initialized in your code and therefore is equal to nil. Sending messages to nil is allowed, but has no effect, therefore even after
for(int i = 0; i < 50; i++){
[self.dataArray addObject:[NSString stringWithFormat:#"%d", i]];
}
self.dataArray is still nil, and [self.dataArray count] returns 0.
You have to allocate and initialize the array with
self.dataArray = [[NSMutableArray alloc] init];
A proper place is some initXXX method of the view controller. But initWithNibName: is not called if the view controller is instantiated from a storyboard file, you have to use initWithCoder: instead:
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
self.dataArray = [NSMutableArray array];
}
return self;
}

ivar is releasing under ARC - how do I retain it for use in another method?

I have been struggling with something for weeks and it has brought a real halt to my progress. I have asked a question a few times on SO, people have been helpful but no-one has cracked what I am doing wrong. It seems a fairly simple thing so hopefully someone out there will have a lightbulb moment and solve this. I am implementing a TWRequest, the result is coming back in a dictionary, I am looping through the results to extract a part of the tweet and creating an array of these 'text' components. Straight adter the loop through I am peinting the log of the array - _twitterText and it prints fine. Staright after this method is complete it seems as though _twitterText is being dumped. I have created it in my .h file as a strong property and created an ivar in viewdidload. Still no joy. how do I retain this array to use in another Method?
Here is my .h file....
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import "CustomCell.h"
#import "AppDelegate.h"
#import <Twitter/Twitter.h>
#interface MyViewController : UITableViewController <CLLocationManagerDelegate>
{
CLLocationManager *here;
}
#property(strong) NSDictionary *dict;
#property(strong) CLLocationManager *here;
#property (strong, nonatomic) NSMutableArray *twitterText;
- (void)fetchTweets;
#end </p>
Here is my .m implementation file......
#import "MyViewController.h"
#interface MyViewController ()
#end
#implementation MyViewController
#synthesize dict;
#synthesize twitterText = _twitterText;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_twitterText = [[NSMutableArray alloc] init];
here = [[CLLocationManager alloc] init];
here.delegate = self;
[here startUpdatingLocation];
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
NSLog(#"phrase carried over is %#", delegate.a);
[self fetchTweets];
}
- (void)fetchTweets
{
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:
#"http://search.twitter.com/search.json?q=%40wimbledon"]
parameters:nil requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if ([urlResponse statusCode] == 200)
{
// The response from Twitter is in JSON format
// Move the response into a dictionary and print
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
//NSLog(#"Twitter response: %#", dict);
NSArray *results = [dict objectForKey:#"results"];
//Loop through the results
for (NSDictionary *tweet in results) {
// Get the tweet
NSString *twittext = [tweet objectForKey:#"text"];
// Save the tweet to the twitterText array
[_twitterText addObject:twittext];
}
NSLog(#"MY ************************TWITTERTEXT************** %#", _twitterText);
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
// Return the number of rows in the section.
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
//cell.venueDetails.text = [_twitterText objectAtIndex:indexPath.row];
NSLog(#"MY ************************OTHER BIT THAT WONT PRINT************** %#", _twitterText);
return cell;
}
So the issue here is that your completion handler that you pass to -[TWTweet performRequestWithHandler:] isn't going to (can't) fire until the network connection is complete and the server responds to your request. That could take hundreds of milliseconds or even seconds to complete. (Or it may never happen).
Meanwhile while that is happening, the UITableView wants to draw itself and so will ask you how many sections/rows you have and then will ask you for a cell for each row. So when the table view asks, you should return the actual number of rows you have to draw at that time:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.twitterText count]; // the actual number of rows we have right now
}
So then the next step you need is to reload the table when your data is actually in from the server. That will prompt your table view to ask again for the number of sections and rows, and then ask for cells for each section and row. So somewhere in your completion block after you've processed all your data you will need to do this:
dispatch_async(dispatch_get_main_queue(), ^{
// you'll need an outlet to the UITableView
// here I assume you call that 'tableView'
// then just ask it to reload on the main thread
[self.tableView reloadData];
});
I hope that helps?

CoreData leaks with fetched results arrays

I'm using CoreData to store objects like cars, trips, data recorded from GPS, etc.
When I fetch what I want to show a list of trips, some stats for a trip, or add a new car in my settings view controller, I use pretty much this kind of request:
- (void)getDataTrip
{
// Fetched data trips.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DataTrip" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
// Set predicate and sort orderings...
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timestamp" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"idTrip = %#", self.idTrip];
[fetchRequest setPredicate:predicate];
// Execute the fetch -- create a mutable copy of the result.
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
NSLog(#"failed with error: %#", error);
}
// Set the array.
[self setDataTripArray:mutableFetchResults];
// Memory management.
[fetchRequest release];
[mutableFetchResults release];
}
Sometimes, I have leaks when I do the [self setDataTripArray:mutableFetchResults]; and sometimes not. In this case, when I get the data for a trip, it leaks all the time when I use the navigation controller to come back to the root view controller (displaying a list of trips), and/or when I change tab.
Anyway, it just leaks and it's all the time coming from fetching data from CoreData, and give this array to my local array var.
Please let me know if you see how to fix this! It made the app crash after a while.
Thanks!
SOLUTION
I found that I do a retain on my object dataTripArray object when creating another UIViewController that I use to create graphs for my scroll view.
- (void)loadScrollViewWithPage:(int)page
{
if (page < 0)
return;
if (page >= kNumberOfPages)
return;
// Replace the placeholder if necessary.
GraphController *controller = [self.graphControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null])
{
controller = [[GraphController alloc] initWithPageNumber:page data:[self.dataTripArray retain]];
[self.graphControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
// Add the controller's view to the scroll view.
if (controller.view.superview == nil)
{
CGRect frame = _scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[self.scrollView addSubview:controller.view];
}
}
I just removed the retain and the leak is no longer coming up. Fixed!

Resources