Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Sample Applications/CoreRecipesApp/MainApplication/Sources/Application/RecipeUtilities.m
/* |
File: RecipeUtilities.m |
Abstract: Generic utilities for working with recipes. This provides |
class method for working with RTF data and generating URLs. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Computer, Inc. ("Apple") in consideration of your agreement to the |
following terms, and your use, installation, modification or |
redistribution of this Apple software constitutes acceptance of these |
terms. If you do not agree with these terms, please do not use, |
install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Computer, |
Inc. may be used to endorse or promote products derived from the Apple |
Software without specific prior written permission from Apple. Except |
as expressly stated in this notice, no other rights or licenses, express |
or implied, are granted by Apple herein, including but not limited to |
any patent rights that may be infringed by your derivative works or by |
other works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright © 2005 Apple Computer, Inc., All Rights Reserved |
*/ |
#import "RecipeUtilities.h" |
#import "CookingWithFractionsTransformer.h" |
#import "Recipe.h" |
#import "Ingredient.h" |
#import "Measure.h" |
#import "Chef.h" |
#import "RecipeMedia.h" |
static NSString *APPLICATION_URL_SCHEME = @"corerecipes"; |
static NSString *COREDATA_URL_SCHEME = @"x-coredata"; |
@implementation RecipeUtilities |
#define MakeAttributed(s) [[[NSAttributedString alloc] initWithString: s] autorelease] |
/** |
Returns an attributed string formatted to be used as a title in a document. |
This string is formatted using the Helvetica font, set to 24 points, and |
bolded. |
*/ |
NSMutableAttributedString * MakeTitle( NSString *string ) { |
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string]; |
NSRange range = NSMakeRange( 0, [attributedString length] ); |
[attributedString beginEditing]; |
[attributedString addAttribute:NSFontAttributeName value:[NSFont fontWithName: @"Helvetica-Bold" size:24.0] range:range]; |
[attributedString endEditing]; |
return [attributedString autorelease]; |
} |
/** |
Returns an attributed string set as a link, with the specified string as the |
text and the passed in URL for the link. This string is formatted as a |
standard link, with blue text and underlining. |
*/ |
NSMutableAttributedString * MakeLink( NSString *string, NSURL *url ) { |
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string]; |
NSRange range = NSMakeRange( 0, [attributedString length] ); |
[attributedString beginEditing]; |
[attributedString addAttribute:NSLinkAttributeName value:[url absoluteString] range:range]; |
[attributedString addAttribute:NSForegroundColorAttributeName value:[NSColor blueColor] range:range]; |
[attributedString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:range]; |
[attributedString endEditing]; |
return [attributedString autorelease]; |
} |
/** |
Returns a bolded attributed string version of the passed in string. The |
only attribute formatting applied to this string is the bolding of the |
default font (Helvetica, 12 point). |
*/ |
NSMutableAttributedString * MakeBold( NSString *string ) { |
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string]; |
NSRange range = NSMakeRange( 0, [attributedString length] ); |
[attributedString beginEditing]; |
[attributedString addAttribute:NSFontAttributeName value:[NSFont fontWithName: @"Helvetica-Bold" size:12.0] range:range]; |
[attributedString endEditing]; |
return [attributedString autorelease]; |
} |
/** |
Returns a attributed string version of the passed in string, set with |
obliqueness (to immitate italics). The obliqueness is set at .35 for |
the string, but no other attributes are set. |
*/ |
NSMutableAttributedString * MakeItalic( NSString *string ) { |
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: string]; |
NSRange range = NSMakeRange( 0, [attributedString length] ); |
[attributedString beginEditing]; |
[attributedString addAttribute:NSObliquenessAttributeName value:[NSNumber numberWithFloat: 0.35] range:range]; |
[attributedString endEditing]; |
return [attributedString autorelease]; |
} |
/** |
Takes the specified recipe, generates an RTF version (showing description, |
ingredients, and directions), and writes the contents to the specified URL. |
No attempt is made to see if the file already exists. |
*/ |
+ (BOOL) writeRTFDataForRecipe:(Recipe *)recipe toFileURL:(NSURL *)fileURL { |
// get the data |
NSData *data = [self RTFDataForRecipe: recipe includesHyperlink: YES]; |
if ( data != nil ) { |
// write to the file |
return [data writeToURL: fileURL atomically:YES]; |
} |
// no save happened |
return NO; |
} |
/** |
Takes the specified recipe and generates an RTF version of the content. |
This content includes the name, chef name, description, |
ingredients, and directions), and writes the contents to the specified URL. |
No attempt is made to see if the file already exists. |
*/ |
+ (NSData *) RTFDataForRecipe:(Recipe *)recipe includesHyperlink:(BOOL)includesHyperlink { |
// sanity check |
if ( recipe != nil ) { |
// create an autorelease pool |
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; |
// create a string for the content |
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init]; |
// create some helper strings |
NSMutableAttributedString *paragraph = [[NSMutableAttributedString alloc] initWithString: @"\n"]; |
NSMutableAttributedString *doubleParagraph = [[NSMutableAttributedString alloc] initWithString: @"\n\n"]; |
NSMutableAttributedString *space = [[NSMutableAttributedString alloc] initWithString: @" "]; |
NSString *workerString; |
// put in the recipe title |
[string appendAttributedString: MakeTitle( [recipe name] )]; |
[string appendAttributedString: paragraph]; |
// add in the chef name |
workerString = [[recipe chef] fullName]; |
if ( workerString && [workerString length] > 0 ) { |
[string appendAttributedString: MakeItalic( @"By " )]; |
[string appendAttributedString: MakeItalic( workerString )]; |
[string appendAttributedString: doubleParagraph]; |
} |
// add cooking information |
workerString = [NSString stringWithFormat: @"%i", [recipe numberOfServings]]; |
[string appendAttributedString: MakeAttributed( @"Serves: " )]; |
[string appendAttributedString: MakeAttributed( workerString )]; |
[string appendAttributedString: paragraph]; |
workerString = [NSString stringWithFormat: @"%.2d", [recipe cookingTime]]; |
[string appendAttributedString: MakeAttributed( @"Cooking time: ")]; |
[string appendAttributedString: MakeAttributed( workerString )]; |
[string appendAttributedString: MakeAttributed( @" minutes")]; |
[string appendAttributedString: paragraph]; |
workerString = [NSString stringWithFormat: @"%.2d", [recipe preparationTime]]; |
[string appendAttributedString: MakeAttributed( @"Preparation time: ")]; |
[string appendAttributedString: MakeAttributed( workerString )]; |
[string appendAttributedString: MakeAttributed( @" minutes")]; |
[string appendAttributedString: doubleParagraph]; |
// add the recipe description |
workerString = [recipe detailDescription]; |
if ( workerString && [workerString length] > 0 ) { |
[string appendAttributedString: MakeBold( @"Description\n" )]; |
[string appendAttributedString: MakeAttributed( workerString )]; |
[string appendAttributedString: doubleParagraph]; |
} |
// add the ingredient list |
[string appendAttributedString: MakeBold( @"Ingredients\n" )]; |
NSSet *ingredientSet = (NSSet *)[recipe valueForKey: @"ingredients"]; |
int count = [ingredientSet count]; |
if ( count > 0 ) { |
// get the list as an array |
NSSortDescriptor *desc = [[NSSortDescriptor alloc] initWithKey:@"displayOrder" ascending:YES]; |
NSArray *ingredientArray = [[ingredientSet allObjects] sortedArrayUsingDescriptors: [NSArray arrayWithObject:desc]]; |
[desc release]; |
// iterators |
NSString *summaryString; |
Ingredient *ingredient; |
id convertedAmount; |
int i; |
// create a transformer |
CookingWithFractionsTransformer* transformer = [[CookingWithFractionsTransformer alloc] init]; |
[transformer setUsesAttributedStrings: YES]; |
// loop through the ingredients |
for ( i = 0; i<count; i++ ){ |
// get the ingredient |
ingredient = [ingredientArray objectAtIndex: i]; |
// append the converted amount |
convertedAmount = [transformer transformedValue: [NSNumber numberWithDouble: [ingredient amount]]]; |
[string appendAttributedString: convertedAmount]; |
// append the rest of the information |
summaryString = [NSString stringWithFormat: @" %@ %@ %@", |
([ingredient measure] ? [[ingredient measure] name] : @""), |
[ingredient name],( [ingredient optional] ? @"(optional)" : @"" )]; |
[string appendAttributedString: MakeAttributed( summaryString )]; |
[string appendAttributedString: paragraph]; |
} |
// clean up |
[transformer release]; |
} |
// spacer |
[string appendAttributedString: paragraph]; |
// see if there are directions |
NSData *directions = [recipe valueForKey: @"directions"]; |
if ( directions && [directions length] > 0 ) { |
// append the directions |
[string appendAttributedString: MakeBold( @"Directions\n" )]; |
NSAttributedString *directionsString = [[NSAttributedString alloc] initWithRTF:directions documentAttributes:nil]; |
[string appendAttributedString: directionsString]; |
[directionsString release]; |
[string appendAttributedString: doubleParagraph]; |
} |
// put in a link to get back to the application, if we've saved the recipe |
NSManagedObjectID *objectID = [recipe objectID]; |
if ( includesHyperlink && ![objectID isTemporaryID] ) { |
// get the URI and write it |
[string appendAttributedString: MakeLink( @"[Open in the CoreRecipesApp]", [self applicationURLForRecipe: recipe] )]; |
[string appendAttributedString: doubleParagraph]; |
} |
// clean up |
[space release]; |
[doubleParagraph release]; |
[paragraph release]; |
[pool release]; |
// convert to data and return |
NSData *data = [string RTFFromRange:NSMakeRange(0,[string length]) documentAttributes:nil]; |
[string release]; |
return data; |
} |
// return nothing |
return nil; |
} |
/** |
Returns an application URL to locate the specified recipe in the this |
application. The URL generated use a predefined URL scheme (set up in the |
CFBundleURLTypes in the Info.plist file of the project) and simply places |
the URI representation of the object as the host and path. |
*/ |
+ (NSURL *) applicationURLForRecipe: (Recipe *)recipe { |
// URL to return |
NSURL *objectURL = nil; |
// get the managed object ID information |
NSManagedObjectID *objectID = [recipe objectID]; |
if ( ![objectID isTemporaryID] ) { |
// get the URI and write it |
NSURL *objectURI = [objectID URIRepresentation]; |
objectURL = [[[NSURL alloc] initWithScheme:APPLICATION_URL_SCHEME host:[objectURI host] path:[objectURI path]] autorelease]; |
} |
// return |
return objectURL; |
} |
/** |
Returns the Recipe for the specified application URL in the specified |
context. The URL passed in is checked to be formatted correctly (using the |
scheme defined in the CFBundleTypesArray for the project), and then we |
re-create the URI representation of the object from it. We return an |
object if found in the context, otherwise we return nil. |
*/ |
+ (Recipe *) recipeForApplicationURL:(NSURL *)url inContext:(NSManagedObjectContext *)context { |
// Recipe to return |
Recipe *recipe = nil; |
// ensure we have a URL and context |
if ( url && context ) { |
// catch anything awkward |
@try { |
// ensure the URL is interesting to us |
if ( [[url scheme] isEqualToString: APPLICATION_URL_SCHEME] ) { |
// create the object URI representation, and create a managed object ID |
NSURL *objectURI = [[NSURL alloc] initWithScheme:COREDATA_URL_SCHEME host:[url host] path:[url path]]; |
NSManagedObjectID *objectID = [[context persistentStoreCoordinator] managedObjectIDForURIRepresentation:objectURI]; |
[objectURI release]; |
// if we get one |
if ( objectID ) { |
// first get the object into the context |
Recipe *recipeFault = (Recipe *)[context objectWithID:objectID]; |
// this only creates a fault, which may NOT resolve to an object (for example, if the ID is for |
// an objec that has been deleted already): create a fetch request to get the object for real |
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease]; |
[request setEntity: [NSEntityDescription entityForName:@"Recipe" inManagedObjectContext:context]]; |
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"(self == %@)", recipeFault]; |
[request setPredicate: predicate]; |
// attempt to get it |
NSArray *results = [context executeFetchRequest:request error:nil]; |
if ( [results count] > 0 ) { |
recipe = [results objectAtIndex: 0]; |
} |
} |
} |
} |
// log |
@catch ( NSException *e ) { |
NSLog( @"An error occurred trying to get the recipe from the application url '%@': %@", url, e ); |
} |
} |
// return the recipe |
return recipe; |
} |
@end |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-06-01