Spitting out objects from array in Objective-C - 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...

Related

Objective-C class as NSMutableArray

Very simple question. Is it possible to create a class which is a list by it self? I mean:
I do
taskList *taskList1 = [[taskList alloc] init];
And than simply:
taskList1 addObject:[task1]
May seem stupid, but I'm totally new to O-C syntax
I'd need two methods:
-(instancetype) init;
which just initialize as an empty list
+(instancetype)taskList;
to allocate taskList instance
and last thing:
In interface i use:
#interface taskList : NSObject
or
#interface taskList : NSMuttableArray
I got stuck on something specific, didn't I? I'm sorry that I bother you with my programming level.
Alright, I gave up, just last question, because I have to finish it very soon.
I changed my approach I added
#property NSMutableArray *list;
Why does this:
taskList *TL1 =[taskList initTaskList];
task *task1 = [[task alloc] init];
task *task2 = [[task alloc] init];
TL1.list addObject:[task1];
doesn't work, I have "Expected identifier" Error
If you read the subclassing notes on NSArray / NSMutableArray you'll see that Apple recommend against subclassing them because they are a class cluster. (i.e. what you really get when you ask for one is an undocumented subclass, and the initialiser decides which undocumented subclass to return to you based on some undocumented qualifiers..
So just make an NSObject subclass which owns a (private) strong property of type NSMutableArray, and publish an api to access that array..
eg
#import "modelList.h"
//dont worry header is empty, its up to you to do that.. this is a subclass on NSObject
#interface modelList()
#property (strong, nonatomic) NSMutableArray *backingArray;
#end
#implementation modelList
#synthesize backingArray = _backingArray;
-(instancetype )init{
if (self = [super init]) {
[self setBackingArray:[[NSMutableArray alloc]init]];
}
return self;
}
//public API (the stuff that is prototyped in the header..)
-(id)objectAtIndex:(NSUInteger )index{
return [self.backingArray objectAtIndex:index];
}
-(BOOL )containsObject:(id)object{
return [self.backingArray containsObject:object];
}
-(void )addObject:(id)object{
//example application, qualifying object..
if ([object conformsToProtocol:#protocol(NSCoding)]) {
[self.backingArray addObject:object];
}
}
-(NSUInteger )count{
return [self.backingArray count];
}
//etc etc declare publicly the things you need to get the job done
#end
so far this is just a face for a mutable array obviously, but it gives you a place for whatever other model logic you need. good luck

Change selected object with multiple nsarraycontrollers

I am trying to implement example as shown on developer.apple.com
Everything works but selectedWeapon does not change.
Q1: I have no idea what I forgot so the selection would work correctly
Q2: According to Apple "the example requires no actual code to set up the user interface". I am filling objects from code. Is there any way to add objects into arrayController in XIB?
#implementation Combatant
- (instancetype)initWithName:(NSString *)aName
{
self = [super init];
if (self) {
_weapons = #[#"Dagger", #"Sword", #"Pike"];
_name = aName;
_selectedWeapon = [_weapons firstObject];
}
return self;
}
#end
#interface Combatant : NSObject
#property (nonatomic, strong) NSString *name;
#property id selectedWeapon;
#property NSArray *weapons;
- (instancetype)initWithName:(NSString *)aName;
#end
Repository: https://github.com/xhruso00/moderatelyComplexBindings
Q1: The selection index of NSPopupButton wasn't linked to arrayController. Without it the arrayController did not know which option is selected.
Q2: Impossible. Apple talks about the glue code.

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"]];

Removing and Adding same object to different classes

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.

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.