Removing and Adding same object to different classes - objective-c

I'm reading a book to learn Objective-C. I'm stuck in one of the exercises. You have to make a program that has a class song, playlist, and music collection. If you create a song, it has to automatically add to the music collection, who has a NSMutableArray for collecting songs. And if You remove an object from music collection, the song has to remove from every playlist containing that song.
Song Interface
#import <Foundation/Foundation.h>
#interface Song : NSObject{
NSString *title;
NSString *artist;
NSString *album;
}
#property (copy) NSString *title;
#property (copy) NSString *artist;
#property (copy) NSString *album;
-(Song *) initWithNames:(NSString*) title1 and: (NSString*) artist1 and: (NSString*) album1;
#end
Playlist Interface
#import <Foundation/Foundation.h>
#interface Playlist : NSObject{
NSString * title;
NSMutableArray *collecsongs;
}
#property (strong) NSString *title;
#property (strong) NSMutableArray *collecsongs;
-(Playlist *) initWithName: (NSString *) name;
#end
Music Collection Interface
#import <Foundation/Foundation.h>
#import "Playlist.h"
#interface MusicCollection : NSObject{
NSMutableArray *collecplist;
Playlist *library;
}
#property (strong) NSMutableArray *collecplist;
#property (strong) Playlist *library;
#end
So if i create a song for example song1, is there a way if i add it to a playlist, automatically add it to the mastercoleection variable "library", instead of doing this
Song *song1 = [[Song alloc] initWithNames:#"Somebody That I Used To Know" and: #"Gotye" and: #"First Album"];
Playlist *Misrolas = [[Playlist alloc] initWithName: #"Misrolas"];
MusicCollection *music = [[MusicCollection alloc] init];
[Misrolas.collecsongs addObject: song1];//adds song1 to the playlist named "Misrolas"
[music.library.collecsongs addObject: song1];//adds song1 to the music collection
So i don't know what to do, i was thinking overriding maybe addObject:, but that doesn't seem right and easy, thanks for the help =)
I do it like this, is there more efficient or better ways to add it ???
-(void) addsong: (Song *)song addtocollection: (Playlist *) library{
NSAssert([song isKindOfClass: [Song class]], #"Not the same class");
[self.collecsongs addObject:song];
[library.collecsongs addObject: song];
}

-(Song *) initWithNames:(NSString*) title1 and: (NSString*) artist1 and: (NSString*) album1;
This is a pretty bad naming. Your selector shortens to initWithNames:and:and: (which is not really descriptive). Consider using
- (id)initWithTitle:(NSString *)title artist:(NSString *)artist album:(NSString *)album;
Notice how I use return type of (id) here. It allows easier subclassing, as any descendant class can use the init... "constructor" without any type mismatch warnings.
Speaking of your issue, I'd suggest you to expose only NSArray * property accessors (so that you cannot modify the array contents) and make a method on Playlist class:
- (void)addSong:(Song *)song
{
NSAssert([song isKindOfClass:[Song class]]);
[self.collecplist addObject:song];
}
That's OOP's encapsulation in action. You don't expose private interface (the array), you provide interface for adding exactly songs (you cannot add other kind of object), finally, you do a verification, that what you add is really a song. Here you can also add the song to your music collection.

You will probably find this easier if you call a method on the Playlist object to add the song, rather than accessing its collecsongs property. In that method, it can add the song to the array and then add it to the library. (And then you can make the collecsongs property return an NSArray, rather than an NSMutableArray, which seems much cleaner to me.

Related

Spitting out objects from array in Objective-C

I'm quite new to classes and objects and I have a question:
I'm keeping track of books which can be input by textFields.
3 properties per book: Title, Author and Description.
What I'm trying to do is get all the objects of books in a NSMutableArray called: Collection.
(at the moment it's only 1 book (objectAtIndex:0)
Which is currently working but when I try to spit them back out I only get the description of the book. I'd love to get all the items (title, author, description).
What I've been wondering is: should I make a new (collection) class for example called BookCollection and make an array there? But how would I init it etc?
The code is below, help and tips are welcome!
(Started about a month ago)
Book.h
#import <Foundation/Foundation.h>
#interface Book : NSObject
#property(nonatomic,strong)NSString* title;
#property(nonatomic,strong)NSString* author;
#property(nonatomic,strong)NSString* description;
-(id)initWithTitle:(NSString*)newTitle withAuthor:(NSString*)newAuthor andDescription:(NSString*)newDesription;
Book.m
#import "Book.h"
#implementation Book
#synthesize title,author,description;
-(id)initWithTitle:(NSString*)newTitle withAuthor:(NSString*)newAuthor andDescription:(NSString*)newDesription{
self = [super init];
if (self) {
title = newTitle;
author = newAuthor;
description = newDesription;
}
return self;
}
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize lblTitle,lblAuthor,lblDescription;
#synthesize collection;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
- (IBAction)buttonClick:(id)sender {
//alloc the array that will hold the books
collection = [[NSMutableArray alloc]init];
//create a new book
Book *newBook = [[Book alloc]initWithTitle:[lblTitle stringValue] withAuthor:[lblAuthor stringValue] andDescription:[lblDescription stringValue]];
//logging the items of the book
NSLog(#"%#",newBook.description);
NSLog(#"%#",newBook.title);
NSLog(#"%#",newBook.author);
//adding the book to the collection
[collection addObject:newBook];
//logging the book items from the collection
NSLog(#"%#",[collection objectAtIndex:0]);
//problem... only logs 1 item from the object...
}
#end
AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Book.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property(nonatomic,strong)NSMutableArray *collection;
#property (assign) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSTextField *lblTitle;
#property (weak) IBOutlet NSTextField *lblAuthor;
#property (weak) IBOutlet NSTextField *lblDescription;
- (IBAction)buttonClick:(id)sender;
#end
You need to define your Book class's -description method.
When you call NSLog(#"%#", someObject), the object's -description method gets called and placed inside the %# format specifier. You'll want to override your Book class's -description method to print out all of the object's fields.
Have a look here for a good example.
To clarify, when you call:
NSLog(#"%#",newBook.description);
NSLog(#"%#",newBook.title);
NSLog(#"%#",newBook.author);
You are (quite correctly) logging each individual field. However, when you call:
NSLog(#"%#",[collection objectAtIndex:0]);
You are essentially writing:
NSLog(#"%#",newBook); // Gets an NSString from [newBook description];
And so you need to implement - (NSString *)desctiprion for the Book class to get the logging behaviour you are after.
Step 1 : remove collection = [[NSMutableArray alloc]init]; from - (IBAction)buttonClick:(id)sender and put it into applicationDidFinishLaunching method. Problem is You are initializing your array each time you add New Book to Collection Array.
To Iterate : all the objects of Array use following snippet
[collection enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop){
Book *objBook = (Book *)obj;
NSLog(#"%#",objBook.description);
NSLog(#"%#",objBook.title);
NSLog(#"%#",objBook.author);;
}];
This Description Method is amazing! I had no idea it was even possible #PLPiper! Thank you
Although it gets harder... at this very moment I'm just logging it all out (for me as a developer). But for example... if I wanted all these properties in labels (string value). So if I wanted to spit them all out through my array, how would that work?
I've seen code that uses the following example(sort of) which I like and it's an easy-read.
for (int i = 0; (i<=[collection.count]); i++) {
[titleLabel setStringValue:[dateCollection objectAtIndex:i].title]
[authorLabel setStringValue:[dateCollection objectAtIndex:i].author]
[descriptionLabel setStringValue:[dateCollection objectAtIndex:i].description]
}
In theory this should work, but in practice I'm missing something here...

Noob needs help designing data model & controller

Disclaimer:
I've been learning Objective C/Cocoa for 2 months or so, and I promised myself that I would always try and find the answer myself rather than clogging the internet with dumb noob questions. At this point I'm just confused all over and I think I would benefit at this point from asking questions. I apologize in advance.
Problem:
I'm trying to write a master-detail style app (this is just for practice) called "My Dream Garage". Basically I want to store car objects and their properties. I have a "Car" class that looks like this:
#import <Foundation/Foundation.h>
#interface Car : NSObject
#property (nonatomic, strong) NSString *brand, *model, *trimLevel;
#property (nonatomic, strong) NSNumber *modelYear, *engineSizeinL, *weight;
#property (nonatomic, strong) id image;
#end
In my main .XIB file I have an NSTableView and a bunch of text labels that will display each property (and an imagewell for the image).
My question is how do I store this data? I understand what NSArray, NSDictionary, ArrayController and DictionaryController are individually. I'm just a little confused on how to make them work together. When I add a new "car", am I supposed to instantiate a new "Car" object with it's properties? At that point do I add the new object to an array and then release the created "Car" object? Do I link the tableview and text-labels to an NSDictionary Controller? I'm not even sure what I should be asking at this point.
Perhaps I'm in a bit over my head. What other than Apple's documentation (which is very good but too verbose for an amateur) would be recommended to learn how to create apps similar to this?
Any help is greatly appreciated. Thank you.
Lots of questions here:
Are you wanting to store them somewhat permanently? If so, you need to start learning Core Data.
What does the implementation file look like for the Car class? How are you (or are you) instantiating and initializing a new object?
My class objects usually look something like this:
Interface:
#interface UserInfo : NSObject {
NSString *_networkID;
NSString *_displayName;
NSString *_userDomain;
BOOL _loggedIn;
}
#property(nonatomic,strong) NSString *networkID;
#property(nonatomic,strong) NSString *displayName;
#property(nonatomic,strong) NSString *userDomain;
#property(nonatomic) BOOL loggedIn;
-(id) initWithUserNetworkID:(NSString *)networkID
displayName:(NSString *)displayName
userDomain:(NSString *)userDomain;
#end
Implementation:
#import "UserInfo.h"
#implementation UserInfo
#synthesize networkID = _networkID;
#synthesize displayName = _displayName;
#synthesize loggedIn = _loggedIn;
#synthesize userDomain = _userDomain;
-(id) initWithUserNetworkID:(NSString *)networkID
displayName:(NSString *)displayName
userDomain:(NSString *)userDomain {
if ((self = [super init])) {
_networkID = [networkID copy];
_displayName = [displayName copy];
_userDomain = [userDomain copy];
_loggedIn = YES;
}
return self;
}
#end
And I will create a new one with code like this:
UserInfo *myUserInfo = [[UserInfo alloc]
initWithUserNetworkID:[loginDictionary objectForKey:#"NetworkID"]
displayName:[loginDictionary objectForKey:#"DisplayName"]
userDomain:[loginDictionary objectForKey:#"UserDomain"]];

Objective C fast enumeration trouble

I am trying to use fast enumeration to print all songs that are in a playlist, but it seems like I am doing it wrong. Could someone help me out?
I have defined a Song class like this :
#interface Song : NSObject
#property (nonatomic,strong) NSString *title;
#property (nonatomic,strong) NSString *artist;
#property (nonatomic,strong) NSString *album;
#property (nonatomic,strong) NSString *playtime;
-(void) displaySong;
#end
displaySong function looks like this :
-(void) displaySong {
NSLog(#"%# - %# [%#] - %#\n", artist, title, album, playtime);
}
and now, to the PlayList class :
#interface PlayList : NSObject{
NSString *name;
NSMutableArray *playlist;
}
-(void) addSongToPlaylist: (Song *) song;
-(void) displayPlaylist;
#end
#implementation PlayList
-(void) addSongToPlaylist: (Song *) nameOfSong{
[playlist addObject:nameOfSong];
}
-(void) displayPlaylist{
NSLog(#"Playlist called %#:\n", name);
for(id obj in playlist)
{
if([obj isKindOfClass:[Song class]])
[obj displaySong];
}
}
// I also tried the displayPlaylist method this way, but it wasn't working either
-(void) displayPlaylist{
NSLog(#"Playlist called %#:\n", name);
for(Song *song in playlist)
{
[song displaySong];
}
#end
Could somebody please explain to me why my code isn't working?
And what displayPlaylist method is better?
You are never creating playlist
in the Playlist class' init add a playlist = [[NSMutableArray alloc] init] ;
And consider using a property for playlist (man — the similar names are quite annoying!)
If you don't want to have a init, you also can do:
-(void) addSongToPlaylist: (Song *) nameOfSong{
if(!playlist)
playlist= [[NSMutableArray alloc] init];
[playlist addObject:nameOfSong];
}
to answer the second question: it depends
if you know, that there are only instances of Song in the array, the second version is better, as a bit faster
if you don't know, it is the first, as otherwise a object would receive a message, it can't handle -> crash
or you can do
for(id obj in playlist)
{
if([obj respondsToSelector:#selector(displaySong)])
[obj displaySong];
}
if you wants to be sure, that the message is understood, no matter what object it is.
You don't appear to be initializing your playlist array. You can do so lazily in your methods by checking for nil or by overriding the init method for your PlayList class.

Objective-C NSArray

I'm new to Obj-C and iPhone SDK. The test application I'm stock with is a color switcher containing two buttons ("Back", "Forward") and one text label. The idea is to switch between rainbow colors (background) and setting an appropriate text label in a cyclic manner.
I declared NSArray (which is to contain colors names) in RainbowViewController.h, synthesized it in RainbowViewController.h and I can't add any string into that array.
This is "h" file:
#import <UIKit/UIKit.h>
#interface RainbowViewController : UIViewController {
IBOutlet UILabel *currentColorTextLabel;
NSArray *colorsArray;
NSString *msg;
}
#property (nonatomic, retain) IBOutlet UILabel *currentColorTextLabel;
#property (nonatomic, retain) NSArray *colorsArray;
#property (nonatomic, retain) NSString *msg;
- (IBAction) pressForwardButton;
- (IBAction) pressBackButton;
#end
This is "m" file:
#import "RainbowViewController.h"
#import <Foundation/Foundation.h>
#implementation RainbowViewController
#synthesize currentColorTextLabel;
#synthesize colorsArray;
#synthesize msg;
int currentArrayIndex = 0;
colorsArray = [[NSArray alloc] init]; //here i get "Initializer element is not constant" error message
[coloursArray addObject:#"Red"]; //here I get "Expected identifier or '(' before '[' token"
[coloursArray addObject:#"Orange"];
//etc
- (IBAction) pressForwardButton {
//here I'm going to increment currentArrayIndex, set an appropriate color, and update a currentColorTextLabel based on currentArrayIndex.
}
- (IBAction) pressBackButton {
}
//auto-genereted code here
#end
I'm new to obj-c as well, but I think you need to initialize the array with objects, or use an NSMutableArray if you want to add objects after it is created.
You have the code that should go in your init method just sitting out in the middle of the file. You can't set instance variables like that.
jasongetsdown is correct. You need to instantiate the NSArray object with the objects it will contain and nil terminated.
#"Red", #"Blue", nil
If you wish to have an array that you can change you need to make it a Mutable Array.
However, you have another problem here. Your property that you are synthesizing and allocating for is an object named colorsArray and you are trying to pass a method to a coloursArray object, two different spellings.

Accessing NSMutableDictionary Across Classes

I have declared an NSMutableDictionary object in AppController.h and initialized in the corresponding AppController.m file.
AppController.h
#import <Cocoa/Cocoa.h>
#interface AppController : NSObject {
NSMutableDictionary *uberDict;
}
AppController.m
#import "AppController.h"
#implementation AppController
- (id)init
{
uberDict = [NSMutableDictionary new];
return self;
}
Now I want to use the NSMutableDictionary object in another view, flashcardView. I add/remove items in the dictionary using methods in the AppController class and want to use the same dictionary (with the current items still present in it) in the flashcardView view. The problem is that I don't know how to access the dictionary object from outside AppController. How would I do that?
from flashcardView.m
- (void)setAndCheckString
{
NSArray *keys = [uberDict allKeys];
NSString *i;
for (i in keys) {
string = i;
NSLog(#"%#", string);
}
}
Here's where the problem lies. What do I do with uberDict to make this work? Thanks!
While I encourage you to look at alternative design patterns, to do what you're looking for you simply need to add a method (or property) to access uberDict to AppController. As a property, the code would be:
#interface AppController : NSObject {
NSMutableDictionary *uberDict;
}
#property (readonly) NSMutableArray *uberDict;
#end
in the AppController.h file, and just add a one line #synthesize uberDict; inside the AppContoller implementation (before the #end). Don't worry about the (readonly) -- this means that the object getting the uberDict cannot set it to a new dictionary, but still can modify the contents of the dict and read from it.