Noob needs help designing data model & controller - objective-c

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

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.

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...

communicating between classes with pointers

Hi all,
I'm having a hard time understanding how to use pointers
(my first time using them) hoping someone can point me in the right
direction (no pun intended) :P
(it controls the sound,pagenumber,bg,audio int, and text to be displayed)
(NSString *)textFromPlist pointer which points to the plist containing the text for #"Page01Text"
I want to take #"Page01Text" and use it in the HighLighter class
I am able to log the output for the sound,pagenumber,bg,audio int, and text etc in the hello world layer (its logging the TextManagerWithpageNum) so I know that the plist is reading,
but for some reason I cant link textFromPlist I'd like to be able to use the textFromPlist from the TextManagerWithpageNum class and let the hilighter class work with it (this class will hilight the text and work with the audio intervals supplied by the plist)
this piece of code it returns (NULL)
#interface HighLighter.h
TextManagerWithpageNum *myClassAccess;
//declaring the TextManagerWithpageNum class so I can access textFromPlist
#implementation HighLighter.m
-(NSString *)Sentance01
{
NSString *textFromTextManager = myClassAccess.textFromPlist;
NSString *storyText = [NSString stringWithFormat:#"%#",textFromTextManager];
return [[[NSString alloc]initWithString:StoryText]autorelease];
//doesn't crash but doesn't load text
NSLog(#"text form plist #%",storyText);
}
What is it that I am doing wrong? (I have an int array I also wish to link, but thought I'd start with the NSString as I thought this would be simpler.)
any help would be wonderful, kind regards,
Natalie.
#interface TextManagerWithpageNum : CCLayer
NSString* textFromPlist_Pntr;
#property(nonatomic, readonly) NSString* textFromPlist;
//NSString* textFromPlist is an item on the plist #"page01" which contains text for the page
#property(nonatomic, readonly) NSString* audioInterval;
//NSString* AudioInterval is an array on the plist #"AudioTimings_PG01"
#implementation TextManagerWithpageNum
#synthesize textFromPlist = textFromPlist_Pntr;
#synthesize audioInterval = _audioInterval;
There are a couple of options when communicating any data between classes. I will show both, using an example SomeData object that is being sent from ObjectA to ObjectB. You can also use class level methods to, for example, send or receive data in a pointer to a singleton object. I won't show you how to do that because it doesn't sound like that fits your problem.
Option 1
Define a receiving message in ObjectB.
#interface ObjectA
{
SomeData *someDataPointer;
ObjectB *someObjectB;
}
#end
#implementation ObjectA
-(void)someMethod
{
someDataPointer = [[SomeData alloc] init]; // Or whatever way you want to create and set someDataPointer
[someObjectB receiveData:someDataPointer];
// Note: if you will be calling receiveData on someObjectB much later than the
// time of creation of someDataPointer, you should retain someDataPointer to
// hold onto it until you need to pass it on.
// ie)
// [someDataPointer retain];
}
#end
#interface ObjectB
-(void)receiveData:(SomeData *)dataPtr;
#end
#implementation ObjectB
-(void)receiveData(SomeData *)dataPtr
{
// Here the data from someDataPointer will also be pointed to by dataPtr
// Use dataPtr now
[self doSomethingWithData:dataPtr];
}
#end
Option 2
Define a sending message in ObjectA.
#interface ObjectA
{
SomeData *someDataPointer;
}
#property (nonatomic, retain) SomeData *someDataPointer;
#end
#implementation ObjectA
#synthesize someDataPointer = someDataPointer_;
#end
#interface ObjectB
{
ObjectA *someObjectA;
}
#end
#implementation ObjectB
-(void)someMethod
{
SomeData *dataPtr = [someObjectA getSomeDataPointer];
// Now do something with dataPtr
}
#end
I hope that helps. I wanted to show you these two techniques generally so that you can learn to apply it to your case specifically. Let me know if you have questions.

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.