I've been anticipating the release of Designed for Use: Create Usable Interfaces for Applications and the Web for a while now. I was a little disappointed that it was delayed by almost a week from its original release date. Nevertheless, I bought it and finished it right away.
Summary
Lukas Mathis breaks the design process into three distinct parts: research, design, and implementation. Each category is subdivided into smaller steps classified as either idea-based or skill-based. The progression is logical, but also is written in a way that steps can be skipped or read out of order according to the needs of the product.
Pros
When I first saw the book, I expected a focus strictly on what Mathis terms the "design phase" (Part 2 of 3). Design to me, before reading this book, meant wireframes, prototypes, and usability testing. The book gives much more. Mathis shows that the design process extends throughout the entire development cycle.
Designed for Use includes countless links to other resources. Mathis cites many prominent UX and Design blogs, books, videos, etc. Most are web based, so they are freely and widely available.
Many great techniques are suggested that I never thought of as part of "design." Mathis includes mock press releases, job shadowing, and feature sorting. Not only does he teach several techniques, but he also gives low-budget suggestions. He removes any excuse for not following certain steps such as usability testing.
Cons
Overall the book seems to have a bias towards desktop and web applications. There are several references to developing mobile applications, but there are few resources mentioned. I was expecting to see services such as Flurry and TestFlight included in the sections on user feedback and testing. Nevertheless, the ideas and techniques still apply.
Recommendation
In general product development I often see two areas that lack: design and documentation. Mathis hits on both topics. Every step of the design process is covered succinctly and thoroughly making it quick and easy to read yet includes many references for further indulgence. I would highly recommend this book to both young and experienced developers. Designed for Use can be purchased here on pragprog.com.
Tuesday, June 21, 2011
Tuesday, June 7, 2011
Git Repos
I've recently created a few Git repos on Github. So far I have included a bug reporting tool, a file previewer, and a load of handy categories. I will also be moving my samples to Github. Check them out at https://github.com/rob-brown.
Friday, June 3, 2011
Key-Value Validation
Along with all the other features of Key-Value Coding, there are methods in the API for validating the properties of an object. These methods are known as Key-Value Validation (KVV). The format for a validation method is -(BOOL)validate<Key>:(id*)error: (NSError**) where “Key” is the name of the property to validate. You must be sure to spell the property name properly, otherwise the validator may not be called properly. When calling a validator, you have two choices. First you can call the validator directly. Second, you can call -(BOOL)validateValue:(id*)forKey:(NSString*)error:(NSError**). The second choice will infer the name of the validator method, which is why it is important to spell the method name properly.
The following gives a simple template of how to implement a validator. This template may be copied directly into Xcode.
- (BOOL)validate<#Key#>:(id *)ioValue error:(NSError **)outError {
if (<#Value is not valid#>) {
// Try to coerce the value into a valid value.
// The coerced value must be autoreleased.
*ioValue = <#Some valid value#>;
if (<#Value cannot be coerced#>) {
if (error != NULL) {
// Create an error object.
// Merge errors if necessary.
}
return NO;
}
}
return YES;
}
Implementing validators has several gotchas to watch for. One of the things to notice is the value being passed in for validation is of type id*. This means that you can return a new value by reference. For example, if you want to ensure that a name is unique, you can use the validator to append a number to the name to coerce it to be unique.
Coercing a value has some critical memory management issues. The value passed in for validation must be autoreleased, and a coerced value, if any, must likewise be autoreleased. Furthermore, if you do return a coerced value it must be return a new value rather than modifying the old value, even if the old value is mutable.
When implementing a validator you must never call -set<Key> within the validator. The validator is responsible for validating, not setting.
Primitives cannot be validated since the validators expect an object. However, you may wrap the primitive value in an NSValue or NSNumber object.
Another gotcha to note is that validators are not called automatically. It is up to your discretion when you want your objects to be validated. When calling a validator, there is a general form to follow as shown below:
// Note that the value is autoreleased.
NSString * someName = [[[NSString alloc] initWithFormat”@“Timmy”] autorelease];
NSError * error = nil;
if ([obj validateName:&someName error:&error) {
// The setter is called since the value could have been coerced and to properly retain the value.
[obj setName:someName];
}
else {
// Report an error to the user or whatever else you want.
}
Aside from the above template, you could validate the parameters of your Objective-C setters by using NSParameterAssert([obj validateValue:&someValue forKey:@“someKey” error:NULL]). This is good for debugging since asserts can be disabled by defining NS_BLOCK_ASSERTIONS.
Core Data extends the ability of KVV. Although KVC does not automatically call validation methods, Core Data provides three points which validators can be called automatically. These methods are -(BOOL)validateForInsert:(NSError**), -(BOOL)validateForUpdate:(NSError**), and -(BOOL)validateForDelete:(NSError**). These methods are called when an NSManagedObject is inserted into a managed object context, updated, or deleted, respectively. You may override these methods in your subclasses of NSManagedObject to call your validators. Additionally, you may perform inter-property validation here. There may be instances where certain combinations of values are incorrect. For example, in a family tree, it is incorrect to set a female person as someone’s father.
When using the Core Data validation methods, you must be careful to not recursively dirty the managed object context. Dirtying the MOC can be done in numerous ways such as coercing property values on every validation request. If you do recursively dirty the MOC, then you will get a message like this: ‘Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.’ If you face this situation, you will either need to find another way to implement your validator or don’t have your validator called automatically. If you opt to not call your validator automatically, then you will probably want to call your validator anytime you try to set the associated property.
In order to avoid your validators from being called automatically you have a few options. You could avoid calling NSManagedObject’s -validateForX: methods (where ‘X’ is ‘Insert’, ‘Update’, or ‘Delete’) by not calling [super validateForX:&error]. However, this may not be wise since NSManagedObject may do more than call each of the validators. You could instead try having your object reject one or more validation messages from itself. The easiest and best option is to make a validator that is not KVC-compliant. In other words, name the validator method so it doesn’t match with a property name.
When using the Core Data validation methods, you may have multiple errors occur. You have two options to handle this. First, you can return the first error as soon as it is reached. Second, you can merge the errors into a single error with error code NSValidationMultipleErrorsError.
KVV is very useful, especially for Core Data. I’m surprised it isn’t used more frequently. However, Core Data does automatically generate some basic KVV methods from the MOM file. Hopefully, this tutorial will increase the custom usage of KVV.
The following gives a simple template of how to implement a validator. This template may be copied directly into Xcode.
- (BOOL)validate<#Key#>:(id *)ioValue error:(NSError **)outError {
if (<#Value is not valid#>) {
// Try to coerce the value into a valid value.
// The coerced value must be autoreleased.
*ioValue = <#Some valid value#>;
if (<#Value cannot be coerced#>) {
if (error != NULL) {
// Create an error object.
// Merge errors if necessary.
}
return NO;
}
}
return YES;
}
Implementing validators has several gotchas to watch for. One of the things to notice is the value being passed in for validation is of type id*. This means that you can return a new value by reference. For example, if you want to ensure that a name is unique, you can use the validator to append a number to the name to coerce it to be unique.
Coercing a value has some critical memory management issues. The value passed in for validation must be autoreleased, and a coerced value, if any, must likewise be autoreleased. Furthermore, if you do return a coerced value it must be return a new value rather than modifying the old value, even if the old value is mutable.
When implementing a validator you must never call -set<Key> within the validator. The validator is responsible for validating, not setting.
Primitives cannot be validated since the validators expect an object. However, you may wrap the primitive value in an NSValue or NSNumber object.
Another gotcha to note is that validators are not called automatically. It is up to your discretion when you want your objects to be validated. When calling a validator, there is a general form to follow as shown below:
// Note that the value is autoreleased.
NSString * someName = [[[NSString alloc] initWithFormat”@“Timmy”] autorelease];
NSError * error = nil;
if ([obj validateName:&someName error:&error) {
// The setter is called since the value could have been coerced and to properly retain the value.
[obj setName:someName];
}
else {
// Report an error to the user or whatever else you want.
}
Aside from the above template, you could validate the parameters of your Objective-C setters by using NSParameterAssert([obj validateValue:&someValue forKey:@“someKey” error:NULL]). This is good for debugging since asserts can be disabled by defining NS_BLOCK_ASSERTIONS.
Core Data extends the ability of KVV. Although KVC does not automatically call validation methods, Core Data provides three points which validators can be called automatically. These methods are -(BOOL)validateForInsert:(NSError**), -(BOOL)validateForUpdate:(NSError**), and -(BOOL)validateForDelete:(NSError**). These methods are called when an NSManagedObject is inserted into a managed object context, updated, or deleted, respectively. You may override these methods in your subclasses of NSManagedObject to call your validators. Additionally, you may perform inter-property validation here. There may be instances where certain combinations of values are incorrect. For example, in a family tree, it is incorrect to set a female person as someone’s father.
When using the Core Data validation methods, you must be careful to not recursively dirty the managed object context. Dirtying the MOC can be done in numerous ways such as coercing property values on every validation request. If you do recursively dirty the MOC, then you will get a message like this: ‘Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.’ If you face this situation, you will either need to find another way to implement your validator or don’t have your validator called automatically. If you opt to not call your validator automatically, then you will probably want to call your validator anytime you try to set the associated property.
In order to avoid your validators from being called automatically you have a few options. You could avoid calling NSManagedObject’s -validateForX: methods (where ‘X’ is ‘Insert’, ‘Update’, or ‘Delete’) by not calling [super validateForX:&error]. However, this may not be wise since NSManagedObject may do more than call each of the validators. You could instead try having your object reject one or more validation messages from itself. The easiest and best option is to make a validator that is not KVC-compliant. In other words, name the validator method so it doesn’t match with a property name.
When using the Core Data validation methods, you may have multiple errors occur. You have two options to handle this. First, you can return the first error as soon as it is reached. Second, you can merge the errors into a single error with error code NSValidationMultipleErrorsError.
KVV is very useful, especially for Core Data. I’m surprised it isn’t used more frequently. However, Core Data does automatically generate some basic KVV methods from the MOM file. Hopefully, this tutorial will increase the custom usage of KVV.
Labels:
Core Data,
iOS,
Key-Value Coding,
Key-Value Validation,
KVC,
KVV,
Objective-C
Subscribe to:
Posts (Atom)