NSMutableArray is not instantiated as expected - objective-c

I am developing a class in Objective C which contains two arrays audioFiles and files. Files array contains all the names of the audio files and audioFiles is an array to hold AVAudioFile generated from the items in files array.
I want to instantiate both the array when I create a class instance.
AudioPlayer *player = [[AudioPlayer alloc] init];
But I am unable to instantiate the audioFiles array as its showing nothing (nil). I put both the arrays in the implementation because I don't want to expose these two arrays outside of my class.
Could you please help me to fix the problem?
#import <AVFoundation/AVFoundation.h>
#import "AudioPlayer.h"
#implementation AudioPlayer
{
NSMutableArray *audioFiles;
NSArray *files;
}
- (instancetype)init
{
self = [super init];
if (self) {
files = [NSArray arrayWithObjects:#"one", #"two", #"three",#"four", nil];
[self createAudioFiles];
}
return self;
}
-(void) createAudioFiles{
for (int i = 0; i < [files count]; i++) {
NSString *audioPath = [[NSBundle mainBundle] files[i] ofType:#"wav"];
NSURL *url = [NSURL fileURLWithPath:audioPath];
AVAudioFile *file = [[AVAudioFile alloc] initForReading:url error:nil];
[audioFiles addObject:file];
}
}
#end

You are not initializing the audioFiles at all, you need to add:
audioFiles = [NSMutableArray array];
to your init or at the beginning of createAudioFiles method.
If you don't plan to add more objects after initialization, there is no need to keep the array mutable though, just declare it as NSArray *audioFiles; and change your method to:
-(void) createAudioFiles{
NSMutableArray *buffer = [NSMutableArray array];
for (int i = 0; i < [files count]; i++) {
NSString *audioPath = [[NSBundle mainBundle] files[i] ofType:#"wav"];
NSURL *url = [NSURL fileURLWithPath:audioPath];
AVAudioFile *file = [[AVAudioFile alloc] initForReading:url error:nil];
[buffer addObject:file];
}
audioFiles = [buffer copy]; // make it immutable
}

Related

Objective C - NSString working but plist string doesn't work

I have a piece of code that finds a random word in a plist and then assigns it to the NSString 'correctWord'. For some reason the code works fine in one instance but doesn't work in another.
This code works:
- (void) viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:
#"small" ofType:#"plist"];
NSArray *plistArray = [[NSArray alloc] initWithContentsOfFile:path];
NSInteger randV = arc4random_uniform(plistArray.count); // randV is from 0 to number of strings -1 in array
[super viewDidLoad];
self.correctWord = #"Hello"; //<--- Code works fine here..
[self setupHangmanWord:self.correctWord];
}
This code doesn't work. (there are no errors in the compiler though)
- (void) viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:
#"small" ofType:#"plist"];
NSArray *plistArray = [[NSArray alloc] initWithContentsOfFile:path];
NSInteger randV = arc4random_uniform(plistArray.count); // randV is from 0 to number of strings -1 in array
[super viewDidLoad];
self.correctWord = [plistArray objectAtIndex:randV]; //<--- Doesn't work anymore.
[self setupHangmanWord:self.correctWord];
}
use:
NSString *path = [[NSBundle mainBundle] pathForResource:#"small" ofType:#"plist"];
NSDictionary *plistDict = [NSDictionary dictionaryWithContentsOfFile:path];

coordinates called from JSON not appearing

I'm trying to display pins from my JSON file and they are not appearing any help would be greatly appreciated: also buttons will be calling separate JSON pages.
my code for mapview.m:
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"https://w1281595.users.ecs.westminster.ac.uk/shops.php"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSArray *shoparray = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
if (error != nil)
{
}
CLLocationCoordinate2D location;
NSMutableArray *newAnnotations = [NSMutableArray array];
MKPointAnnotation *newAnnotation;
for (NSDictionary *dictionary in shoparray)
{
location.longitude = [dictionary[#"Longitude"] doubleValue];
location.latitude = [dictionary[#"Latitude"] doubleValue];
newAnnotation = [[MKPointAnnotation alloc] init];
newAnnotation.title = dictionary[#"Name"];
newAnnotation.coordinate = location;
}
[self.mapview addAnnotations:newAnnotations];
Your loop creates a newAnnotation each time through. And then you add an array called newAnnotations to the map. But you've forgotten to put anything into the newAnnotations array.

Saving to plist file issue

I can't save my new data to plist file for some reason. This is code I have been using for saving data:
-(void)saveData:(NSMutableDictionary *)dictionaryData toFile:(NSString *)filename {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
NSString *path = [docDir stringByAppendingPathComponent:filename];
NSMutableArray *data = [[NSMutableArray alloc] initWithContentsOfFile:path];
[data addObject:dictionaryData];
[data writeToFile:filename atomically:YES];
}
this is code I used to copy file from bundle to app directory in case if it is not there :
-(NSMutableArray *)loadFromFile:(NSString *)filename {
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
NSString *path = [docDir stringByAppendingPathComponent:filename];
NSFileManager *fileMgr = [NSFileManager defaultManager];
if(![fileMgr fileExistsAtPath:path]) {
NSArray *fileArray = [filename componentsSeparatedByString:#"."];
NSString *name = [fileArray objectAtIndex:0];
NSString *ext = [fileArray objectAtIndex:1];
NSString *bundle = [[NSBundle mainBundle] pathForResource:name ofType:ext];
[fileMgr copyItemAtPath:bundle toPath:path error:&error];
}
NSMutableArray *data = [[NSMutableArray alloc] initWithContentsOfFile:path];
return data;
}
For some reason I can't save new data to plist file. When I try to add new NSMutableDictionary object to my plist file (with saveData:toFile: method) and than reload my array variable with the plist file data - new object is not there. Am I doing something wrong?
this is how I load plist file :
#property (nonatomic, strong) NSMutableArray *modules;
#property (nonatomic, strong) NSMutableDictionary *module;
- (void)viewDidLoad {
[super viewDidLoad];
self.modules = [self loadFromFile:#"ModulesList.plist"];
self.module = [self.modules objectAtIndex:0];
for (int i = 0; i < self.modules.count; i++ ) {
NSLog(#"Modules array from plist file, module at index %i : %#",i, [self.modules objectAtIndex:i]);
}
than for testing purpose I have this code to add new module object:
- (IBAction)leftButton:(id)sender {
NSString *mytitle = [[NSString alloc] initWithFormat:#"my title"];
NSString *myauthor = [[NSString alloc] initWithFormat:#"my author2"];
NSUInteger myean = 22023423;
NSString *mytitle2 = [[NSString alloc] initWithFormat:#"my title 2"];
NSString *myauthor2 = [[NSString alloc] initWithFormat:#"my author2"];
NSUInteger myean2 = 29032432;
NSString *mytitle3 = [[NSString alloc] initWithFormat:#"my title 3"];
NSString *myauthor3 = [[NSString alloc] initWithFormat:#"my author 3"];
NSUInteger myean3 = 21023423;
NSMutableDictionary *mybook = [[NSMutableDictionary alloc] init];
NSMutableDictionary *mybook2 = [[NSMutableDictionary alloc] init];
NSMutableDictionary *mybook3 = [[NSMutableDictionary alloc] init];
[mybook setObject:mytitle forKey:#"title"];
[mybook setObject:myauthor forKey:#"author"];
[mybook setObject:[NSNumber numberWithInteger:myean] forKey:#"ean"];
[mybook2 setObject:mytitle2 forKey:#"title"];
[mybook2 setObject:myauthor2 forKey:#"author"];
[mybook2 setObject:[NSNumber numberWithInteger:myean2] forKey:#"ean"];
[mybook3 setObject:mytitle3 forKey:#"title"];
[mybook3 setObject:myauthor3 forKey:#"author"];
[mybook3 setObject:[NSNumber numberWithInteger:myean3] forKey:#"ean"];
NSMutableArray *mybooks = [[NSMutableArray alloc] init];
[mybooks addObject:mybook];
[mybooks addObject:mybook2];
[mybooks addObject:mybook3];
[self.module setObject:mybooks forKey:#"books"];
[self.modules addObject:self.module];
for (int i = 0; i < self.modules.count; i++ ) {
NSLog(#"Modules array after add operation, module at index: %i: %#",i, [self.modules objectAtIndex:i]);
}
[self saveData:self.module toFile:#"ModulesList.plist"];
}
than when I will reload my self.modules array from plist with button action, my new data is not there:
- (IBAction)reload:(id)sender {
self.modules = [self loadFromFile:#"ModulesList.plist"];
for (int i = 0; i < self.modules.count; i++ ) {
NSLog(#"RELOAD: Modules array from plist file, module at index %i : %#",i,[self.modules objectAtIndex:i]);
}
}
this is screenshot of my plist file : http://dl.dropbox.com/u/49076351/Screen%20Shot%202013-02-27%20at%2016.27.45.png

loading AVAudioPlayer file URLs from a Plist

I'm working with about 50 AVAudioPlayers that each load a separate audio file. The files are fixed and within the app bundle. They are triggered by events later on in the app. Currently I'm hardcoding the creation of each player instance, like so:
//No01
NSURL *no01URL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:#"audio1" ofType:#"aiff"]];
self.no01Player = [[AVAudioPlayer alloc] initWithContentsOfURL:no01URL error:nil];
no01Player.numberOfLoops = 0;
no01Player.volume = 0;
no01Player.delegate = self;
//No02
NSURL *no02URL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:#"audio2" ofType:#"aiff"]];
self.no02Player = [[AVAudioPlayer alloc] initWithContentsOfURL:no02URL error:nil];
no02Player.numberOfLoops = 0;
no02Player.volume = 0;
no02Player.delegate = self;
//No03 and so on...
Obviously this is labourious and bad coding practice. I'd like to instead have the list of files in a Plist and load these into variables that populate just one example of the above code. I'd like to learn how to be DRY with this but have limited experience of loading data from Plists, arrays, dictionaries, etc.
Any help is appreciated, even if it is to point me in the direction of a relevant tutorial.
Thank you.
You can simply place the audio file names in text file using 'new line' separator. And read the file and store the names in an array. To create the URL take the file names dynamically from array as given below
// Read description from text file
NSBundle *audioBundle = [NSBundle mainBundle];
NSString *audioPath = [audioBundle pathForResource:audioFileNames ofType:#"txt"];
NSString *audioContent = [NSString stringWithContentsOfFile:audioPath encoding:NSUTF8StringEncoding error:nil];
// ===========Make an array using audioContent=============
NSArray *audioFiles = [audioContent componentsSeparatedByString:#"\n"];
Use the array to get file names :
NSURL *no01URL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%#",[audioFiles objectAtIndex:audioCount]] ofType:#"aiff"]];
If your file names are like audio1,audio2.. no need of using all these. Just change in URL like :
NSURL *no01URL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"audio%d",audioCount] ofType:#"aiff"]];
OK for anyone else needing to do this, here's what I ended up with:
//Load location data from Plist file into an Array
NSString *path = [[NSBundle mainBundle] pathForResource:#"Audionodes" ofType:#"plist"];
locationArray = [[NSArray alloc] initWithContentsOfFile:path];
// Create AVAudioPlayers for this view
self.audioPlayerArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [locationArray count]; i++) {
NSString *filename = [[locationArray objectAtIndex:i] valueForKey:#"filename"];
NSString *filetype = [[locationArray objectAtIndex:i] valueForKey:#"filetype"];
int loops = [[[locationArray objectAtIndex:i] valueForKey:#"loops"] intValue];
NSURL *url = [[NSURL alloc]initFileURLWithPath:[[NSBundle mainBundle] pathForResource:filename ofType:filetype]];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
player.delegate = self;
player.numberOfLoops = loops;
player.volume = 0;
[self.audioPlayerArray addObject:player];
[url release];
[player release];
}
Putting the generated players in audioPlayerArray means I can get at them later.

Load and save NSMutableArray won't work

first of all, I have a class which mamages an NSMutableArray.
When the class is instantiated, it should look if there is already a saved array and load it, or otherwise made a new one.
everytime an item is added it should be saved. but: nothing happens.
#import "NEList.h"
#interface NEList()
#end
#implementation NEList
#synthesize theInternArray;
-(id) initWithArray {
self = [super init];
if(self != nil) {
NSLog(#"build");
theInternArray = [[NSMutableArray alloc] initWithCapacity:20];
return self;
}
return nil;
}
-(id) initWithContent {
self = [super init];
if(self != nil) {
NSLog(#"load");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"theFiles"];
theInternArray = [NSMutableArray arrayWithContentsOfFile:filePath];
if(theInternArray == nil) {
theInternArray = [[NSMutableArray alloc] initWithCapacity:200];
}
NSLog(#"second %#", theInternArray);
return self;
}
return nil;
}
-(BOOL) save {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"theFiles"];
return [self.theInternArray writeToFile:filePath atomically:NO];
}
-(BOOL) addObject:(NEListItem *)theItem {
[theInternArray addObject:theItem]; [self save];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
localNotif.fireDate = theItem.theBestBeforeDate;
localNotif.timeZone = [NSTimeZone defaultTimeZone];
localNotif.alertBody = [theItem.theName stringByAppendingString:#" expires!"];
localNotif.alertAction = NSLocalizedString(#"View Details", nil);
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
return true;
}
-(NEListItem *) getElementAtIndex:(NSUInteger)theIndex {
return [theInternArray objectAtIndex:theIndex];
}
-(BOOL)removeObjectByName:(NSString *)theName {
for (NEListItem *tempItem in theInternArray) {
if([tempItem.theName isEqualToString:theName]) {
[theInternArray removeObject:tempItem];
break;
return true;
}
}
NSLog(#"Nicht gefunden");
return false;
}
#end
the save method is called in another class which owns an instance of NEList
Ok, all I changed was the "initWithContent" Method and the save method. I've commented out your code and under it made the changes to use NSUserDefaults. NSUserDefaults should save a binary file of your Array and later get it back and convert it back to a NSArray. If this doesn't work just let me know what error your getting.
-(id) initWithContent {
self = [super init];
if(self != nil) {
NSLog(#"load");
/*
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"theFiles"];
theInternArray = [NSMutableArray arrayWithContentsOfFile:filePath];
if(theInternArray == nil) {
theInternArray = [[NSMutableArray alloc] initWithCapacity:200];
}
NSLog(#"second %#", theInternArray);*/
NSUserDefaults *uDefaults = [NSUserDefaults standardUserDefaults];
theInternArray = [uDefaults objectForKey:#"internArray"];
if (!theInternArray)
{
theInternArray = [[NSMutableArray alloc] initWithCapacity:200];
}
NSLog(#"%#", theInternArray);
return self;
}
return nil;
}
-(BOOL) save {
/*NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"theFiles"];*/
NSUserDefaults *uDefaults = [NSUserDefaults standardUserDefaults];
[uDefaults setObject:theInternArray forKey:#"internArray"];
//return [self.theInternArray writeToFile:filePath atomically:NO];
return [uDefaults objectForKey:#"internArray"];
}
Oh, and the reason why your way didn't work is because it's not that simple to save a file then get it back. You would need to print out your data from the array (like "item1,item2,item3, etc." to the file, then parse it and create a NSArray with that data later.
So, this is the solution worked for me.
First of all, this is my custom class which is stored in the arrays:
#import <Foundation/Foundation.h>
#interface NEListItem : NSObject <NSCoding>
#property (nonatomic, strong) NSString *theName;
#property (nonatomic, strong) NSString *theBestBeforeDate;
#property (nonatomic, strong) NSDate *theInternDate;
-(id) initWithName:(NSString *) theInitName;
#end
and the implementation
#import "NEListItem.h"
#implementation NEListItem
#synthesize theName = _theName;
#synthesize theBestBeforeDate = _theBestBeforeDate;
#synthesize theInternDate = _theInternDate;
-(id)initWithName:(NSString *)theInitName {
self = [super init];
if(self != nil) {
self.theName = theInitName;
return self;
}
return nil;
}
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:self.theName forKey:#"theName"];
[coder encodeObject:self.theBestBeforeDate forKey:#"theBBDate"];
[coder encodeObject:self.theInternDate forKey:#"theInternDate"];
}
- (id)initWithCoder:(NSCoder *)coder;
{
self = [[NEListItem alloc] init];
if (self != nil)
{
self.theName = [coder decodeObjectForKey:#"theName"];
self.theInternDate = [coder decodeObjectForKey:#"theInternDate"];
self.theBestBeforeDate = [coder decodeObjectForKey:#"theBBDate"];
}
return self;
}
#end
so, this is needed for saving custom values.
the save method:
-(BOOL) save {
NSLog(#"save called");
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:theInternArray] forKey:#"savedArray"];
return true;
}
and the loading which is part of a init method
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"savedArray"];
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil) {
theInternArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
return self;
}
else
theInternArray = [[NSMutableArray alloc] initWithCapacity:200];
return self;
}
many thanks to ManOx which helped me a lot