Use Realm data to fill the UITableView (table)? - objective-c

My solution which doesn't work - I get data with realm store it as array and reuse this array in cellForRowAtIndexPath:.
The problem is I should get data in the background thread but fill the table in main thread.
Note: I have already read that if I get the realm data in current thread then I can use them in this thread only. Otherwise I should create ThreadSafeReference for each realm object I need and reuse it in main thread. But I can't understand how to do it - the example is written with such way that they create an object before dispatch_async and use it inside this object (like passing a variable into the code block).
In my case I have a separate array which I should store and reuse in cellForRowAtIndexPath (which is not code block) and called multiple times. I also can't call dispatch_async with a thread I need inside it because the cell may become invalid.
How to solve this issue? Should I cache Realm objects manually? I accept both Swift and Objective-C languages

The following code should work for you:
// ViewController.h
#import <UIKit/UIKit.h>
#import "MyObject.h" // subclass of RLMObject
#interface MyViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UITableView *myTableView;
#property RLMResults<MyObject *> *objectsList;
#end
And the implementation:
// ViewController.m
#import "MyViewController.h"
#implementation MyViewController {
- (void)viewDidLoad {
_objectsList = [MyObject allObjects]; // optionally sort, query, etc.
}
- (void)viewDidAppear {
[super viewDidAppear];
// Do any other setup
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _objectsList.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Setup the cell with the object's information. It's up to you how
// to do it. Register a custom subclass of UITableViewCell, for example.
MyObject *thisCellObject = [objectsList objectAtIndex:indexPath.row];
UITableViewCell *cell = [self.myTableView dequeueReusableCellWithIdentifier:#"customIdentifier"];
// Title should be a property on your MyObject class
cell.textLabel.text = thisCellObject.title;
}
#pragma mark - UITableViewDelegate
// Implement the Delegate methods as you want it...
}
No need to invoke the Realm Objects from other threads of using Dispatch. The only case you'll get those "incorrect thread" errors would be if you pass a reference from you view controller to other class, like a background networking client or something similar.
Anyway those cases are avoidable too. If your objects got an ID, let's say from an API, you pass the ID to the other class, maybe the background sync client, and from that client perform a query for the object, like [MyObject objectsWhere:#"objectID == %#", idString];. The object is being called from the background process that will use it and therefore it's not going to fail.

Related

Can anyone please help me out? iOS How Datasource methods are implemented? and How do they work?

I am trying to implements my own Datasource methods.Not getting how can we implements.
Datasources and delegates are both implemented using protocols. The difference between the two is that the delegate controls user interaction and the datasource controls data.
This means that your typical delegate method will be called from something like a button click and your datasource method will be called during initialization or refresh of the control. It also means that delegates are more likely to have void methods and data source methods will most likely define a return type.
If you take an example that has a view controller and your custom control, then you would do something like this to setup your datasource protocol.
CustomControl.h:
#protocol CustomControlDatasource <NSObject>
- (NSInteger)NumberOfThingsInControl;
#end
#interface CustomControl : NSObject
#property (nonatomic, weak) id <CustomControlDatasource> delegate;
#end
You could call datasource methods from your init or refresh method and use the result to configure the control however you'd like (don't forget to synthesize the delegate property).
CustomControl.m:
#synthesize datasource = _datasource;
...
-(void)refresh {
NSInteger numOfThings = [self.datasource NumberOfThings];
// Use the result to update the control
...
}
The class that implements the datasource needs to conform to it and implement the datasource method.
MyViewController.h:
#interface MyViewController : UIViewController <CustomControlDelegate> {
CustomControl *coolControl;
}
#end
MyViewController.h:
- (void)viewDidLoad {
[super viewDidLoad];
coolControl = [[CustomControl alloc] init];
[coolControl setDatasource:self];
[coolControl refresh];
}
- (NSInteger)NumberOfThingsInControl {
return 3;
}
I hope this clears things up a bit. protocols are an awesome way to have objects communicate.
You can read more in the documentation:
Working with Protocols
Delegates and Data Sources

How to initialize main window objects from custom class during program start?

I have a main window with a couple of popupbuttons. I want to clear them, then load the lists from a method in a custom class. I've got my view controller working and I know the method in the custom class (newRequest) is working because I added a NSLog command to print "Test" when the method executes. In AppDelegate I'm calling the method via:
[polyAppRequest newRequest];.
As I said, I know the method is executing. Why can't I removeallitems from the popupbutton from this custom class method?
Thanks
Keith
I read that you should use an NSWindowController to manage a window. See here:
Windows and window controllers
Adding views or windows to MainWindow
Then if your window gets complicated enough, the NSWindowController can employ various NSViewControllers to manage parts of the window.
In any case, I used an NSWindowController in my answer.
The image below shows the outlet's for File's Owner, which is my MainWindowController:
I created MainWindowController .h/.m in Xcode6.2 by:
Selecting File>New>File>OS X - Source - Cocoa Class
Selecting NSWindowController for Subclass of:
Checking also create .xib file for user interface
Then I deleted the window--not the menu--in the default MainMenu.xib, and I changed the name of MainWindowController.xib, created by the steps above, to MainWindow.xib.
The following code works for me (but I'm a Cocoa beginner!):
//
// AppDelegate.m
// PopUpButtons
#import "AppDelegate.h"
#import "MainWindowController.h"
#interface AppDelegate ()
#property(strong) MainWindowController* mainWindowCtrl;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self setMainWindowCtrl:[[MainWindowController alloc] init]];
[[self mainWindowCtrl] showWindow:nil];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
#end
...
//
// MainWindowController.m
// PopUpButtons
//
#import "MainWindowController.h"
#import "MyData.h"
#interface MainWindowController ()
#property(strong) MyData* data;
#property(weak) IBOutlet NSPopUpButton* namePopUp;
#property(weak) IBOutlet NSPopUpButton* agePopUp;
#end
#implementation MainWindowController
-(id)init {
if (self = [super initWithWindowNibName:#"MainWindow"]) {
_data = [[MyData alloc] init]; //Get data for popups
}
return self;
}
- (void)windowDidLoad {
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
[[self namePopUp] removeAllItems];
[[self namePopUp] addItemsWithTitles:[[self data] drinks]];
[[self agePopUp] removeAllItems];
[[self agePopUp] addItemsWithTitles:[[self data] extras]];
}
#end
...
//
// MyData.h
// PopUpButtons
//
#import <Foundation/Foundation.h>
#interface MyData : NSObject
#property NSArray* drinks;
#property NSArray* extras;
#end
...
//
// MyData.m
// PopUpButtons
//
#import "MyData.h"
#implementation MyData
- (id)init {
if (self = [super init]) {
_drinks = #[#"coffee", #"tea"];
_extras = #[#"milk", #"sugar", #"honey"];
}
return self;
}
#end
I hope that helps. If you need any more screenshots, let me know.
Edit1:
I think I see what you are asking about. Although I don't think it is a very good approach, if I change my code to this:
//
// MyData.h
// PopUpButtons
//
#import <Cocoa/Cocoa.h>
#interface MyData : NSObject
#property (copy) NSArray* drinks;
#property (copy) NSArray* extras;
-(void)newRequest;
#end
...
//
// MyData.m
// PopUpButtons
//
#import "MyData.h"
#interface MyData()
#property (weak) IBOutlet NSPopUpButton* drinksPopUp;
#property (weak) IBOutlet NSPopUpButton* extrasPopUp;
#end
#implementation MyData
- (id)init {
if (self = [super init]) {
_drinks = #[#"coffee", #"tea"];
_extras = #[#"milk", #"sugar", #"honey"];
}
return self;
}
-(void)newRequest {
[[self drinksPopUp] removeAllItems];
[[self drinksPopUp] addItemsWithTitles:[self drinks]];
[[self extrasPopUp] removeAllItems];
[[self extrasPopUp] addItemsWithTitles:[self extras]];
}
#end
I am unable to populate the NSPopUpButtons. This is what I did:
I dragged an Object from the Object Library to the dock in IB, and in the Identity Inspector, I changed the Object's class to MyData.
Then I clicked on the Connections Inspector, and the two instance variables in MyData, drinksPopUp and extrasPopUp, were listed in the Outlets.
I dragged from the outlets to the respective NSPopUpButtons.
I guess I assumed, like you, that when my program ran, the NSPopUpButtons would be assigned to the instance variables drinksPopUp and extrasPopUp--but that doesn't seem to be the case. According to the Apple docs, you should be able to do that:
An application typically sets outlet connections between its custom
controller objects and objects on the user interface, but they can be
made between any objects that can be represented as instances in
Interface Builder,...
Edit2:
I am able to pass the NSPopUpButtons from my MainWindowController to the newRequest method, and I can use the NSPopUpButtons inside newRequest to successfully populate the data.
Edit3:
I know the method in the custom class (newRequest) is working because
I added a NSLog command to print "Test" when the method executes.
But what happens when you log the variables that point to the NSPopUpButtons? With my code in Edit1, I get NULL for the variables, which means the NSPopUpButtons never got assigned to the variables.
Edit4:
If I add an awakeFromNib method to MyData, and inside awakeFromNib I log the NSPopUpButton variables for the code in Edit1, I get non NULL values. That tells me that the MainWindowController's windowDidLoad method is executing before MyData's awakeFromNib method, and therefore you cannot call newRequest inside MainWindowController's windowDidLoad method because MyData has not been fully initialized.
Edit5:
Okay, I got the code in Edit1 to work. The Apple docs say this:
About the Top-Level Objects
When your program loads a nib file, Cocoa recreates the entire graph
of objects you created in Xcode. This object graph includes all of the
windows, views, controls, cells, menus, and custom objects found in
the nib file. The top-level objects are the subset of these objects
that do not have a parent object [in IB]. The top-level objects typically
include only the windows, menubars, and custom controller objects that
you add to the nib file [like the MyData Object]. (Objects such as File’s Owner, First
Responder, and Application are placeholder objects and not considered
top-level objects.)
Typically, you use outlets in the File’s Owner object to store
references to the top-level objects of a nib file. If you do not use
outlets, however, you can retrieve the top-level objects from the
nib-loading routines directly. You should always keep a pointer to
these objects somewhere because your application is responsible for
releasing them when it is done using them. For more information about
the nib object behavior at load time, see Managing the Lifetimes of
Objects from Nib Files.
In accordance with the bolded line above, I changed this declaration in MainWindowController.m:
#interface MainWindowController ()
#property(strong) MyData* data;
...
#end
to this:
#interface MainWindowController ()
#property(strong) IBOutlet MyData* data;
...
#end
Then, in IB I dragged a connection from the MainWindowController data outlet to the MyData Object(the Object I had previously dragged out of the Object Library and onto the doc).
I guess that causes MyData to unarchive from the .xib file and initialize before MainWindowController.

Class Cluster as a Singleton?

SORRY FOR THE LENGTH OF THIS POST; IT IS MEANT TO DOCUMENT MY JOURNEY WITH THIS PROBLEM.
I have a question about a shared object in a Cocoa app that needs to change from time to time and how best to store it so that it's accessible from a few different places. Bear with me.
Class Implementation
The shared object is implemented as a Class Cluster (i.e., https://stackoverflow.com/a/2459385/327179) that looks like the following (note that Document is merely a class name; it is not necessarily indicative of what my actual class does):
In Document.h:
typedef enum {
DocumentTypeA,
DocumentTypeB
} DocumentType;
#interface Document : NSObject {}
- (Document *) initWithDocumentType:(NSUInteger)documentType;
- (void) methodA;
- (void) methodB;
#end
In Document.m:
#interface DocumentA : Document
- (void) methodA;
- (void) methodB;
#end
#interface DocumentB : Document
- (void) methodA;
- (void) methodB;
#end
#implementation Document
- (Document *)initWithDocumentType:(NSUInteger)documentType;
{
id instance = nil;
switch (documentType) {
case DocumentTypeA:
instance = [[DocumentA alloc] init];
break;
case DocumentTypeB:
instance = [[DocumentB alloc] init];
break;
default:
break;
}
return instance;
}
- (void) methodA
{
return nil;
}
- (void) methodB
{
return nil;
}
#end
#implementation DocumentA
- (void) methodA
{
// ...
}
- (void) methodB
{
// ...
}
#end
#implementation DocumentB
- (void) methodA
{
// ...
}
- (void) methodB
{
// ...
}
#end
How The User Interacts with a Document
Via a menu item, the user can switch between DocumentA and DocumentB at will.
What Happens When A "Switch" Occurs
When the user switches from, say, DocumentA to DocumentB, I need two things to happen:
My primary NSViewController (MainViewController) needs to be able to use the new object.
My AppDelegate needs to update an NSTextField that happens to be located in the content border of the main window. (FWIW, I can only seem to assign an outlet for the NSTextField in the AppDelegate)
The Question(s)
I've seen singletons mentioned quite a bit as a way to have a global reference without cluttering up one's AppDelegate (primarily here and here). That said, I've not seen much info on overwriting such a singleton (in our case, when a user switches from DocumentA to DocumentB [or vice versa], this global reference would need to hold the new object). I'm not an expert on design patterns, but I do remember hearing that singletons are not meant to be destroyed and recreated...
So, given all this, here are my questions:
How would you store my Class Cluster (such that MainViewController and AppDelegate can access it appropriately)?
Am I mixing concerns by having both MainViewController (who uses Document heavily) and AppDelegate (who manages the primary window [and thus, my NSTextField]) have knowledge of Document?
Feel free to let me know if I'm thinking about this problem incorrectly; I want this implementation to be as orthogonal and correct as possible.
Thanks!
Status Update #1
Thanks to advice from #JackyBoy, here's the route I've taken:
Document is the one that, upon "switching", "notifies" AppDelegate and MainViewController by passing them the newly created instance.
Both AppDelegate and MainViewController can update the Document object via the Singleton instance as necessary.
Here are my new files (dumbed down so that y'all can see the crux of the matter):
In Document.h:
#import <Foundation/Foundation.h>
#class AppDelegate;
#class MainViewController;
typedef enum {
DocumentTypeA,
DocumentTypeB
} DocumentType;
#interface Document : NSObject
#property (weak, nonatomic) MainViewController *mainViewControllerRef;
#property (weak, nonatomic) AppDelegate *appDelegateRef;
+ (Document *)sharedInstance;
- (id)initWithParser:(NSUInteger)parserType;
#end
In Document.m:
#import "AppDelegate.h"
#import "Document.h"
#import "MainViewController.h"
#interface DocumentA : Document
// ...
#end
#interface DocumentB : Document
// ...
#end
#implementation Document
#synthesize appDelegateRef;
#synthesize mainViewControllerRef;
+ (Document *)sharedInstance
{
static XParser *globalInstance;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
// By default, I return a DocumentA object (for no particular reason).
globalInstance = [[self alloc] initWithDocumentType:DocumentA];
});
return globalInstance;
}
- (id)initWithDocumentType:(NSUInteger)documentType
{
Document *instance = nil;
switch (parserType) {
case DocumentTypeA:
instance = [[DocumentA alloc] init];
break;
case DocumentTypeB:
instance = [[DocumentB alloc] init];
break;
default:
break;
}
// QUESTION: Is this right? Do I have to store these references
// every time a new document type is initialized?
self.appDelegateRef = (AppDelegate *)[NSApp delegate];
self.mainViewControllerRef = self.appDelegateRef.mainViewController;
[self.appDelegateRef parserSwitchedWithParser:instance];
[self.mainViewControllerRef parserSwitchedWithParser:instance];
return instance;
}
#end
#implementation Xparser_NSXML
// ...
#end
#implementation DocumentA
// ...
#end
Should I be bothered by the fact that Document has knowledge of the existence of AppDelegate and MainViewController? Additionally, should I be bothered by the fact that when the Document object updates, it re-notifies both AppDelegate and MainViewController (even though one of those initiated the update)?
As always, I appreciate everyone's eyeballs on this as my quest for the ideal implementation continues. :)
Status Update #2
A comment from #Caleb helped me understand that an NSNotification-based setup would be a lot less unwieldy for this particular problem.
Thanks, all!
I don't see he need for a shared object here, much less a singleton. Do you really need to find the current Document at arbitrary times from many different objects? Seems more like you just have two objects (app delegate and view controller) that both need to know about the current Document. Notifications provide an easy way to manage that: whenever a switch happens, you can post a NSNotification that includes the new Document. Any objects that need to know about the current Document will have registered for the "document switch" notification, and when the notification arrives they can stash a pointer to the Document in an instance variable or property.
I do remember hearing that singletons are not meant to be destroyed
and recreated...
Well, you can have references inside of it, so you are not actually "destroying" the singleton, but the objects he points to. I tend to leave the App Delegate without application logic, so I normally put it somewhere else. In your case, since you need to access something from different places, it makes sense to have one. About the cluster, you can still have it, you just ask the singleton to access it and return the appropriate object like so:
Document *myDocument = [[MySingleton defaultManager] createObjectWithType:aType];
You gain some things out of this:
you can access your cluster from any place in your app
you decouple things, only one entity knows about your cluster.
Inside the Singleton you can have a reference to you AppDelegate and interact with it.
Inside the Singleton you can have a reference to the objects that are being used (Document A, Document B)
One more thing, I would advise putting the cluster access method as a class method (instead of an instance one).

Objective-C iOS 6 delegate NSString query

I'm trying to use a delegate to pass a value from one VC to another. I think I'm am misunderstanding the way it is supposed to work.
In my main ViewController.h I have this:
#protocol defaultLocationChoice <NSObject>
- (NSString *) locChoice;
#end
In both my PreferencesViewController.h and ChooseServerViewController.h I have defaultLocationChoice declared in the #interface section and the property assinged like so:
#property (nonatomic, assign) id <defaultLocationChoice> locationDelegate;
Both are synthesized also.
When the user segues from PreferencesViewController to ChooseServerViewController the prepare for segue code is:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toServerChoice"]) {
ChooseServerViewController *viewController = (ChooseServerViewController *)segue.destinationViewController;
viewController.locationDelegate = self;
}
}
When a cell choice is made in ChooseServerViewController I call:
[self locChoice];
Which is:
- (NSString *) locChoice {
NSLog(#"Cell Vale Loc choice %#",cellValue);
return cellValue;
}
The NSLog verifies the correct value is returned.
Now, as I think I understand it, the value of LocChoice in the delegate is now the value returned, no?
When the user goes back (NavController) the PreferencesViewController has:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
defaultLocation = [locationDelegate locChoice];
[self.tableView reloadData];
}
I was expecting the value of defaultLocation to now equal the value passed to locChoice. However when the table reloads the cell in question is still blank, implying what I exepct to happen isn't happening.
Any ideas?
If I followed your code properly, you do not need to adopt the mentioned protocol in your ChooseServerViewController, only PreferencesViewController.
The reasoning is you want to send data back to the previous view controller. Try:
#protocol defaultLocationChoice <NSObject>
- (void) locChoice:(NSString*)choice;
#end
Have your PreferencesViewController implement that method so it receives the selection. You will have to store that in an appropriate instance variable.
// in PreferencesViewController.m
-(void)locChoice:(NSString*)choice {
self.choice = choice; // this just my example
}
When the choice is made (in ChooseServerViewController) to send the choice back, call:
// this is in 'ChooseServerViewController.m' some where appropriate
[self.delegate locChoice:cellValue];
Your implementation is simply doing nothing with cell value (not even storing it, just logging it). When you return to PreferencesViewController, you will now have the selected value and that view controller can what it wants with it.
Protocols are somewhat analgous to interfaces in Java or C#, but more flexible.
Some more concepts about delegation.
Working with Protocols.
UPDATE:
The declaration for ChooseServerViewController should look like:
#import "FileWithProtocolDecalration.h"
#interface ChooseServerViewController
.
.
.
#property ( nonatomic,assign) id<defaultLocationChoice> delegate;
.
.
.
#end
I think you do have some misunderstanding there
protocol and delegates are something that is passed around. in other words somebody need to be receiver and somebody need to be the sender. in your case.
update your protocol to this
#protocol defaultLocationChoice <NSObject>
- (void)locChoice:(NSString *)updateString; // the method from delegate and implementer must be exact
#end
and set implement the protocol to ViewController as a receiver
#interface VCReceiver : UIViewController <defaultLocationChoice>
then later in VCReceiver
- (void)viewDidLoad {
ChooseServerViewController *vcSender = [[ChooseServerViewController alloc] init];
[vcSender setLocationDelegate:self]; // this is like saying. i have implemented method from protocol in this file (self)
}
- (void)locChoice:(NSString *)updateString {
// update the VCReceiver here
// or access vcSender value
// or use the updateString value
}
then in ChooseServerViewController locChoice: method (the one from your example) replace with this one and call [self updateChoice] instead:
- (void)updateChoice {
if ([self.locationDelegate respondsToSelector:#selector(locChoice:)]) {
[self.locationDelegate locChoice:aStringToUpdate]; // this will call VCReceiver locChoice
}
it does not have to return anything because it is actually calling the VCReceiver method to tell it that ChooseServerViewController got the value ready to be read.

How to generate a generic table view controller?

I've created a custom TablePickerViewController which is a subclass of UITableViewController. I'm using this class to display a list of object of a custom type TablePickerItem.
I'm using TablePickerViewController multiple times in my iOS application to show different kinds of lists where the user has to pick an item -- and then another view controller MainViewController should react on this selection and do something.
I've created this protocol and created a delegate property in the TablePickerViewController:
#protocol TablePickerViewControllerDelegate <NSObject>
- (void)tablePickerViewController:(TablePickerViewController *)controller
didSelectItem:(TablePickerItem*)item;
#end
When I setup a new TablePickerViewController in MainViewController it is also set as delegate -- than it will be notified when the user taps an cell in the table view.
The problem is that my MainViewController will setup multiple TablePickerViewController with different data (TablePickerItem). How should I setup my MainViewController to handle these multiple TablePickerViewController? Events from each of them will results in calling to the same protocol-method in my MainViewController.
Further I need to get the element which the TablePickerItem represents, as I need to know for instance the elements ID when acting in the tablePickerViewController:didSelectItem method. Should I just handle this by adding something like #property (nonatomic) id element to the TablePickerItem and set the original object into this property then creating it?
Maybe someone can give me an example on how to create an generic table view controller, if my solutions seems being done in the wrong way.
I'm not entirely sure of your set up, but if you have multiple pickers that feedback to the main controller then you could just have a reference to the picker e.g.
// MainViewController.m
#interface MainViewController ()
#property (nonatomic, strong) TablePickerViewController *picker1;
#property (nonatomic, strong) TablePickerViewController *picker2;
// ... and so on. Obviously you know your problem domain so you can change
// the terrible naming above to something appropriate
#end
#implementation MainViewController
// ...
- (void)theMethodWhereYouSetUpYourPickers;
{
TablePickerViewController *picker1 = [[TablePickerViewController alloc] init];
picker1.delegate = self;
self.picker1 = picker1;
// ...
}
- (void)tablePickerViewController:(TablePickerViewController *)controller
didSelectItem:(TablePickerItem*)item;
{
if (controller == self.picker1) {
NSLog(#"Something was picked in picker 1 %#", item);
} else if (controller == self.picker2) {
NSLog(#"Something was picked in picker 2 %#", item);
}
}
// ...
#end