How to pass contextual objects to NSValueTransformer - objective-c

NSValueTransformer is used to transform a value. But what if the transformation needs more contextual information? For example how can a NSValueTransformer access other objects in the nib, or access the view controller? Is this just not what NSValueTransformer is meant to be used?
As an example I want to bind a text view's attributed string to an employee's name, using a value transformer to format the name based on the employee's department.
My solution so far has been to just implement a new employeeNameForDisplay property or so, skipping value transformers altogether.
If that's truly the best solution, I have a hard time understand the value of value transformers. Are they really this limited?

Doc quote ...
NSValueTransformer is an abstract class that is used by the Cocoa Bindings technology to transform values from one representation to another.
... it's a very simple class, which transforms values between different representations. Sometimes I do use it with Cocoa Bindings, but I heavily do use it with CoreData where I need better (smaller, faster, ...) representation for underlying sqlite store. So, this class has the value for us even if you can't achieve your goal with it.
Also I would solve like you do. New property along with ...
+ (NSSet *)keyPathsForValuesAffectingEmployeeNameForDisplay:(NSString *)key {
return [NSSet setWithArray:#[ #"employeeName", #"employeeDepartment" ]];
}

Related

iOS: is there a way to use a single class to hold common variables and not break Object Oriented principles?

I want to separate the data from the implementation in several classes, for many reasons.
One reason, for example, is that I have a few different menu screens that display text. I want to have one class that lists all the text for all the menus in one place, and then have the different menu objects read from that class when they initialize.
That way, whenever I want to make changes, I know exactly where the text variables are, and if I want to, I can change a bunch of them all at once.
I want to use the same principle in a lot of different ways, for example, setting the color and alpha values of various UIViews; having them all in one place would enable me to coordinate their settings and make small adjustments very easily.
Added to these reasons is that I'm working with a small team of other developers, and if we all know we're storing this kind of information in one place it's easier to understand each other's code.
So basically I want one big UberData class that every other class can read from and write to.
As far as I can figure, the only way to do this is make each of the needed variables a property, so I'll basically have a big methodless class with a heck of a lot of properties. But to my understanding, that's kind of bending the OO rules, because as much as possible classes should hide their innards. Not to mention the whole things seems really kludgey.
So the question is: is there a better way to do this than having the class with a million properties, and is it even proper to do it, from an OO perspective, at all?
One big UberData class (and really, if you are thinking properties, you mean one instance of that class) is the wrong approach.
What do menu strings and view colors have to do with each other? Nothing. Therefore they don't belong in the same class.
Strings
For your menu strings, look into the NSLocalizedString macro and creating a strings file. You could create a CommonStrings class that wraps all of your calls to NSLocalizedString:
#interface CommonStrings : NSObject
+ (NSString *)open;
+ (NSString *)save;
// etc.
#end
#implementation CommonStrings
+ (NSString *)open {
return NSLocalizedString(#"open", #"menu item title for opening a file");
}
+ (NSString *)save {
return NSLocalizedString(#"save", #"menu item title for saving a file");
}
// etc.
#end
This approach means you only write #"open" in one place, and then you refer to [CommonStrings open] when you need the (localized) string. The compiler checks that you've spelled [CommonStrings open] correctly, which is nice.
However, it's still probably better to break this into multiple helpers (one for each independent part of your app), rather than one giant helper for your entire app. If you use one giant catch-all class, then compiling your app takes longer because so much has to be recompiled every time you add or remove a method in this class.
UIView colors
First, watch the appearance customization videos from WWDC 2012 and WWDC 2013 and read up on UIAppearance. Maybe you can just use that to customize your app's colors.
If that doesn't suffice, create a category on UIColor for your app's colors:
#interface UIColor (MyApp)
+ (UIColor *)MyApp_menuBackgroundColor;
+ (UIColor *)MyApp_menuTextColor;
// etc.
#end
#implementation UIColor (MyApp)
+ (UIColor *)MyApp_menuBackgroundColor {
return [self colorWithPatternImage:[UIImage imageNamed:#"menuBackgroundPattern"]];
}
+ (UIColor *)MyApp_menuTextColor {
return [self colorWithWhite:0.0 alpha:1.0];
}
// etc.
#end
Again, it may be better to have multiple helper categories for different parts of your app, so you don't have to recompile as much when you add or remove a category method.
First off, forget "propriety". What is proper is what works, works reliably and efficiently, is easy to understand, and easy to maintain. Adhering to "object-oriented principles", while to some degree a worthwhile goal, should not cause you to do awkward, error-prone, and inefficient things. Just about any "programming rule" can be followed too literally.
Having dozens (or hundreds) of properties is clumsy and hard to maintain from several standpoints, so that's basically a non-starter.
More appropriate is having a few queryable interfaces that return the values you want. The "right" scheme often depends on the characteristics of the data, but one can, eg, have a method with a simple switch statement or "if ladder" that returns value Y given value X (where X is, eg, an enumeration value).
One can also have what is either actually or conceptually an NSDictionary which you query with a character "key" value.
A variation of that is a property list, which allows you to describe the data in a data file, vs having to code it into source. You can also put the data in a JSON file, if that suits your design and habits better.
And there are several other schemes that I've used in the past that aren't occurring to me just now.
Added:
OK, I'll give an example of something that is basically impossible with "macros" and "extern variables". In several cases, in some of the code I work on, there are objects that contain information about specific events. There may be 50 different categories of events, each with a different set of properties (sub-category, display color, text for various conditions, etc).
One could, of course, have several hundred declared constants, of the style "kXyzCategoryAbcSubCategory", "kXyzCategoryAbcColorMode", "kXyzCategoryAbcTitleText", etc, but maintaining is a PITA, and typos in use are routine. Plus it's not really usable, since you can't take "category" from the object and "index" the attributes.
Instead, I use one of two schemes:
A set of callable interfaces where you pass in the constant "kXyzCategoryAbc" (or the category value dynamically extracted from the object) and call one of several methods -- xyzSubCategory, xyzColorMode, xyzTitleText -- and the method returns the required data.
A callable interface where you pass in the category value and it returns an NSArray (with keys, eg, of "subCategory", "colorMode", and "titleText").
Both of these techniques work pretty well, though one or the other may be preferred depending on circumstances. Both allow you to maintain the data as a table of some sort, and allow you to use the same constant to fetch multiple values, vs having to introduce 50 new constants when you add one new attribute to one of your category objects.
You shouldn't use a class for this at all. Use macros for code internal constants, and extern variables for values that can change.
Note: User visible strings should be done through localization files -- see the other answers.
When it comes to constants, I usually have a CPConstants.h (CP being my class prefix) file that looks something like this:
#define kCLConstant1 42
#define kCLColorConstant [UIColor blackColor]
And so on.
If you need values to be changeable, first create a CLConstants.m file, like this:
#import "CLConstants.h"
int some_global_var = 42;
UIColor* some_global_changable_color = [UIColor blackColor];
And so on. Then, in CLConstants.h, add a line like this for every variable you declared in CLConstants.m:
extern type varname;
Now all files that include/import CLConstants.h can use and change those variables, and changes will be visible to all other files in the project.
Yes, the common response to a single, shared configuration object is the Singleton pattern. Briefly, a class method knows how to make or return an instance of the class and the instance knows how to configure the needed things. I think you will find many results from stack overflow by searching although What should my Objective-C singleton look like? is one easy example.
Beyond that, I would encourage you to look into how localization works in iOS - the problem you initially reference of having a source for menu titles and things is one that the localization library has solved for you, and in an extensible way.
You may also want to look at property lists (aka, plists) which is a structured data file that can be easily read in iOS applications.
You basically have two choices in sharing data across several controllers:
1) Use a singleton
In this instance, a singleton data model sounds appropriate. (A singleton data model is a single, shared instance of a data model used by several controllers.)
As you mentioned, however, you may indeed wind up with a class that has lots of properties by doing this... there's nothing wrong per se about this, however. Just be choosy in the properties that you have on the singleton... if it's not shared by several controllers, it probably shouldn't be there.
See Apple's documentation on how to create such here.
Here's another SO post about Singletons in iOS.
2) Use a header (.h) file in which you define all your shared variables, then import this wherever you need them (or put into the .pch file to be automatically imported everywhere in your project).
Typically, this is done using static constant variables. I'd imagine you could possibly do this via just static variables so that you can edit them, but at least the last I checked, the compiler may give incorrect unused variable warnings.

Is there a way to change default values of a core data attribute

I would like to change the default value of some of the attributes in my core data model dynamically.
For instance, my app deals with real estate investment, and I have an attribute pertaining to interest rate (the type Float). If the user enters an interest rate of 3.5% (float value would be 3.5) for a particular property they are analyzing, I would like the value for the next property they analyze to automatically populate with 3.5.
Is there a way to accomplish this without subclassing NSManagedObject?
Good question - by default, managed objects are initialized with the default values given in the managed object model. But like you say, sometimes you might want a dynamic default value (the example Apple use in their own documentation is using the current date/time as a default value).
Unfortunately I don't believe there's a way to do this without subclassing NSManagedObject. There's an Apple recommended way to do this - rather than overriding the init method (not recommended), you instead use the awakeFromInsert method, which is called when the object in question is first inserted into the managed object context.
Here's what Apple say from their own docs:
awakeFromInsert:
You typically use this method to initialize special default property values. This method is invoked only once in the object's lifetime.
If you want to set attribute values in an implementation of this method, you should typically use primitive accessor methods (either setPrimitiveValue:forKey: or—better—the appropriate custom primitive accessors). This ensures that the new values are treated as baseline values rather than being recorded as undoable changes for the properties in question.
So to answer your original question - I can't think of a way to do this without subclassing NSManagedObject, and subclassing is the officially recommended approach for handling dynamic default values.

Whats the most effective way to use Jastor to create object and managedobject to store in iOS?

given objective c doesn't do multiple inheritance, what would be the best way to implement class(es) using Jastor to convert and json data source into object and also use managedobject (core data) as storage?
The whole reason for choosing Jastor was that I want to avoid laboriously code each property/field and then to have to copy the whole lot property by property into a managedobject seems dire.
But whats the most elegant way of achieving this?
Thanks
Any NSObject subclass including NSManagedObject does just what you want out of box by means of NSKeyValueCoding protocol. See example here.

How do I use Core Data with the Cocoa Text Input system?

Hobbyist Cocoa programmer here. Have been looking around all the usual places, but this seems relatively under-explained:
I am writing something a little out of the ordinary. It is much simpler than, but similar to, a desktop publishing app. I want editable text boxes on a canvas, arbitrarily placed.
This is document-based and I’d really like to use Core Data.
Now, The cocoa text-handling system seems to deal with a four-class structure: NSTextStorage, NSLayoutManager, NSTextContainer and finally NSTextView. I have looked into these and know how to use them, sort of. Have been making some prototypes and it works for simple apps.
The problem arrives when I get into persistency. I don't know how to, by way of Cocoa Bindings or something else, store the contents of NSTextStorage (= the actual text) in my managed object context.
I have considered overriding methods pairs like -words, -setWords: in these objects. This would let me link the words to a String, which I know how to store in Core Data. However, I’d have to override any method that affects the text - and that seems a little much.
Thankful for any insights.
NSTextStorage is just a subclass of NSMutableAtrributedString which support the NSCoding protocol so you can save it in Core Data as a transformable attribute using the default NSKeyedUnArchiveFromData transform.
I'm pretty sure that holds true for all the other classes you might want to save with the possible exception of the views. (I've never tried to store them in Core Data buts its theoretically possible to do so.)
Any class that implements/inherits NSCoding or has a initWithCoder: method can be stored in code data as a transformable attribute.
I suggest binding the value binding of a text view to a string attribute of one of your model entities, or the attributedString binding to a transformable attribute. This hooks up the view to the model without you having to pass text back and forth yourself.

Instance variables of custom classes in NSManagedObject subclass

I have written a class Foo, and would like to include a Foo object as a property for an NSManagedObject subclass. Is this possible? Can Core Data store information other than strings, booleans, integers, etc.?
One way is to write an NSValueTransformer that transforms your Foo object to NSData. Make the attribute "Transformable" and give the name of the transformer in the attribute settings.
For more details or information or other options, see the Core Data Programming Guide, in particular the section "Non-Standard Persistent Attributes".