"Global" model in NSDocument-based application - objective-c

I have a NSDocument based application, which allows the user to send messages, after he has logged in.
These should be the user's steps:
The user starts the App and logs in (credentials are stored in the user model)
The user user sends the first message
The user hits CMD + N and sends another message (without having to log in again)
The message model is placed in the MyDocument.xib. However the user model should be place in a global place (I would say MainMenu.xib).
My question is now:
Does it make sense to put the model in the MainMenu.xib and how can I access it from MyDocument.m
Is there maybe a better way to do this properly without having to make a singleton class?

I would create an NSApplication delegate and then add you functionality in you delegate or an property of you delegate, you can then use [[NSApplicaton sharedApplicaton] delegate] to get you delegate. You application delegate is the place to associate model data that is common to you application.

I finally got it working with my NSApplication subclass (some say this is not the way to go). For this I had to change the principal class from NSApplication to my new custom NSApplication subclass.

Related

What are the correct connections for App Delegate and Application

Unannounced, the 2 notices in the image below were discovered while searching for why -applicationShouldTerminate: is not being called in AppDelegate.m on quit (Cmd+Q). It worked some time ago.
Hovering over the two yellow triangles reveals:
NSObject does not have an outlet named delegate.
The action 'terminate:' is not defined on 'NSObject'.
Xcode is not showing errors or warnings and the app builds.
-applicationShouldTerminateAfterLastWindowClosed: IS called within AppDelegate.m when the red dot of the window is clicked.
My experience with Cocoa is thin (learning). I've compared the connections for File's Owner, Application and App Delegate in 2 other projects, and think a missing window outlet might be the cause. The notices above point to something else.
I would like to make sure the connections are correct as a first step. How do I repair this?
Edit: Add image of Main Window Controller connections
Edit 2: Add image of File's Owner connections
In the main NIB of an application, the two placeholders, File's Owner and Application, both end up referring to the same object. That object is the application object, the sole instance of NSApplication or a custom subclass of it. The Application placeholder is always holding the place of the application object because that's its purpose. The File's Owner placeholder is holding the place of whatever object is specified as the NIB's owner when it is loaded at run time. When Cocoa loads the main NIB at application startup, it specifies the application object as the owner. For other NIBs, File's Owner will likely be some other object.
However, Interface Builder doesn't know that a given NIB is the main one. So, it treats those two placeholders independently.
I don't know why Interface Builder has taken to setting the class of the Application placeholder to NSObject. It should really be NSApplication. That's why Interface Builder doesn't realize that the application object has a delegate outlet and an action method -terminate:.
As it happens, the class of File's Owner is properly set to NSApplication.
So, there are two ways to fix this:
Set the class of the Application placeholder to NSApplication or, in the rare case that you're using a subclass, that subclass.
Disconnect those connection from the Application placeholder and connect them to File's Owner instead. This is the way that the standard project templates do it.
For any given main NIB, you should probably standardize on using one or the other but not both. That is, one or the other should have no connections. That way you avoid conflicting or redundant connections.

Define a controller for NSDocument for document-based application

I'm not very sure how Document-Based Applications works.
I've created some actions for NSObject in the Mainmenu.xib. One of this is called when the user click on "File>new":
-(IBAction) newDocument:(id)sender{
Document* newDoc =[[Document alloc] init];
[[NSDocumentController sharedDocumentController]addDocument:newDoc];
[newDoc addWindowController: [[NSWindowController alloc] initWithWindowNibName:[newDoc windowNibName] owner:newDoc]];
[newDoc showWindows];
}
I've also this code inside the openDocument:(id) sender action that does the same but of course loading data to define the application workspace.
If I run the application it show a blank document without to call newDocument action. I don't know how to stop default blank document and to set newDocument: to be called.
Then if i do openDocument: too (so I've two documents, one blank and one not) and I do some operation on the second document it also replicate in the first blank one.
I've double check delegates, file owners, and also if the - (void)windowDidBecomeMain:(NSNotification *)notification return different pointers and all seem to be ok.
Probably I've not understood document based application work flow but I've read the Apple guide and other istructions. What do I miss?
An IBAction method is called, when the user did something. So this is not called from the system at app launch.
You can customize the behavior at app launch with -applicationShouldOpenUntitledFile: (NSApplicationDelegate) and – this is probably your next question – -applicationShouldHandleReopen:hasVisibleWindows: (NSApplicationDelegate). Changing the behavior in both cases is not recommended.
Looking to your action method, I see no reason, why you want to customize it.
A instance of your document class is created automatically.
You can create a window controller for it in your document subclass. This is documented.
Just let NSDocumentController do the work for you. What is the problem of the default behavior?
No. I thought to be confused instead the only problem was about releasing observer notification. When you call the close message for a NSDocument notification observers still persist. Working in ARC I miss this point.
So This is the solution at my issue. Thank you anyway.

Is this a valid use of NSNotificationCenter

I come from a .NET web application background and have just started iOS development. The initial design of my app focuses around the NSNotificationCenter. I was reasonably happy with this until I saw various posts explaining how reaching for the NSNotificationCentre was a common newbie mistake.
Here is a simplified version of the problem I am trying to address:
My application is trying to show a list of messages that are populated using web service calls, think Facebook messaging.
When the app is first loaded it pulls a collection of messages from the server and displays them in a table to the user. The user can add new messages (which get sent back over the API) and the app can receive Push Notifications about new messages which are added to the feed.
The messages are never persisted to disk so I'm just using POCOs for the model to keep things simple.
I have a MessageFeedController which is responsible for populating the message feed view (in a storyboard). I also have a message feed model, which stores the currently retrieved values and has various methods:
(void) loadFromServer;
(void) createMessage: (DCMMessage*) message;
(void) addMessage: (DCMMessage*) message;
(NSArray*) messages;
(int) unreadMessages;
The current implementation I have is this:
Use case 1 : Initial Load
When the view first appears the "loadFromServer" method is called. This populates the messages collection and raises an NSNotificationCenter event.
The controller observes this event, and when received it populates the tableview
Use Case 2: New Message
When a user clicks the "add" button a new view appears, they enter their message, hit send and then the view is dismissed.
This calls the createMessage method on the model, which calls the API
Once we have a response the model raises the NSNotificationCenter event
Once again the MessageFeedController listens for this event and re-populates the table
Use Case 3: Push Message
A push notification is received while the app is open, with the new message details
The AppDelegate (or some other class) will call the addMessage method on the model, to add it to the collection
Once again, assuming the MessageFeed view is open, it re-populates
In all three cases the MessageFeed view is updated. In addition to this a BadgeManager also listens to these events which has the responsibility of setting the app icon badge and the tabbar badge, in both cases the badge number relates to the number of unread messages.
It's also possible that another view is open and is listening to these events, this view holds a summary of messages so needs to know when the collection changes.
Right, thanks for sticking with me, my question is: Does this seem like a valid use of NSNotificationCentre, or have I misused it?
One concern I have is that I'm not 100% sure what will happen if the messages collection changes half-way through re-populating the message table. The only time I could see this happening is if a push notification was received about a new message. In this case would the population of the table have to finish before acting upon the NSNotification anyway?
Thanks for your help
Dan.
In other words, you're posting a notification whenever the message list is updated. That's a perfectly valid use of NSNotificationCenter.
Another option is to use Key-Value Observing.
Your controller (and anyone else) can register as an observer to the "messages" property, and will be notified whenever that property changes. On the model side, you get KVO for free; simply calling a method named setMessages: will trigger the KVO change notification. You can also trigger the notification manually, and, if so desired, the KVO notification can include which indexes of the array have been added, removed, or changed.
KVO is a standardized way to do these kinds of change notifications. It's particularly important when implementing an OS X app using Cocoa Data Binding.
NSNotificationCenter is more flexible in that you can bundle any additional info with each notification.
It's important to ensure that your messages list is only updated on the main thread, and that the messages list is never modified without also posting a corresponding change notification. Your controller should also take care to ignore these notifications whenever it is not the top-most view controller or not on screen. It's not uncommon to register for change notifications in viewWillAppear: and unregister in viewWillDisappear:.
In my opinion using a delegate protocol pattern would be a much better fit for this scenario. Consider the scenario where your "api layer" needs used across many view controllers in an application. If another developer were to be introduced to your code, they would have to hunt around for notificationcenter subscriptions instead of just following a clean 'interface' like protocol.
That being said, your code will work just fine and this is a valid use of notification center. It is just my personal preference for 'cleaner' code to use a protocol based approach. Take a look around in the iOS SDK itself and you will see scenarios where Apple themselves use protocols and use notifications. I feel it is much more easy to comprehend and use the protocols than having to dig around and determine what I must listen to for a notification.
NSNotifications run the receivers code synchronously as soon as they are posted, so a new message during repopulation would join the back of that execution queue. On the whole it seems valid to me, and it keeps a reasonable degree of separation between The view controllers and the model.
Depending on the number of classes that are likely to want to listen for the same information arriving, you may want to use a delegate pattern, maybe keeping an dictionary of delegate objects, but I personally don't feel as though this scales so well, and you also have to take care of nil-ing out delegates if a page is dealloced to avoid crashes. To sum up, your approach seems good to me.

iOS – Subclassing MFMessageComposeViewController to add extra functionality

I have subclassed RMMessageComposeViewController : MFMessageComposeViewController. The extra functionality that I'm aiming for is for the MFMessageComposeViewController to be able by itself to present a new message compose controller (over itself).
So I should from one RMMessageComposeViewController instance present a new one. The message result from the new instance should be sent to the parent (or "old" one). So I suppose I need to set the parent message compose controller as the delegate when I'm creating the child ("new" one).
Could someone please help me think this out, what instance variables I need to add (parents, children?) How to setup the child message compose controller?
From the docs:
The message composition interface itself is not customizable and must not be modified by your application. In addition, after presenting the interface, your application is unable to make further changes to the SMS content.
What you're trying to do there is explicitly not supported because of security concerns: It would make it easy for an application to forge messages. While you can probably push a view on top of it, I suspect your app would get rejected from the App Store for doing it.
I wouldn't be surprised if MFMessageComposeViewController prevents an application from creating more than one instance at a time, though I haven't confirmed this.

Objective C: Communicating between classes

I'm writing an iPhone program that has a login view controller that allows the user to login. I have a method I use within that controller that checks the authentication of the username and password. I'm using the keychain to store the username and password, but I'm wondering how I go about communicating outside of the class that the username and password is authentic without duplicating code.
This is probably very simple, but it's getting late and my brain is completely fried...
Good question, and Simon's approach is certainly reasonable.
Another idea would be to create a new class, modeled as a Singleton, called Security and place your authentication method there. Then any object who wishes to validate the user could simply call into Security.
You can read more on the Singleton pattern here: http://developer.apple.com/iphone/library/documentation/cocoa/conceptual/cocoaFundamentals/CocoaDesignPatterns/CocoaDesignPatterns.html#//apple_ref/doc/uid/TP40002974-CH6-SW6
Hope this helps.
Andrew
One way would be to move the code for handling storing and checking username/password into your AppDelegate class, then in your view controller classes you can do something like:
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
// Store password
[appDelegate storePassword:foo forUsername:bar];
// Then later, or in another class...
if ([appDelegate userIsAuthenticated]) {
// Stuff for authenticated users goes here
}
(You'll need to add #import "MyAppDelegate.h" to the top of the view controller's .m file.)
To better match the MVC paradigm, you should probably move all your password checking and storing code outside of the view controller, and into a state modeling class.
The password model object doesn't particularly need to be a singleton (some app might need multiple passwords, etc.).
When creating the view controller and any other objects than needs to touch the password credentials, pass it a pointer to the new password model object to message for store, verify, etc. That way all your password modeling code will be encapsulated and hidden in one place.