2
\$\begingroup\$

I have a form that I'm turning into a string value for an email, but the string value that I turn it into seems very complicated, plus I believe this way takes up a lot of memory and is somewhat slow as well. Is there any other way to make a NSString with many arguments besides using [NSString stringWithFormat:]?

Here is what I'm doing now:

strMessage = [NSString stringWithFormat:@"%@\n\n%@\n\n%@\n%@\n%@, Il\n%@\nPhone: %@\nEmail: %@", detailsText.text, nameText.text, addrLn1Text.text, addrLn2Text.text, cityAddrText.text, zipAddrText.text, phoneNumberText.text, emailText.text];
\$\endgroup\$
2
\$\begingroup\$

It's straightforward to build a named substitution system that can be easily applied to strings loaded either in code or in files. The following NSString category allows you to specify named substitutions in a dictionary, such that any occurrence of the string ${toSubstitute} are replaced by the object corresponding to toSubstitue's object in a dictionary.

NSString+CVTemplateAdditions.h:

#import <Foundation/Foundation.h>

@interface NSString (CVTemplateAdditions)

- (NSString *)cvStringByApplyingSubstitutions:(NSDictionary *)substitutions;

@end

NSString+CVTemplateAdditions.m:

#import "NSString+CVTemplateAdditions.h"

@implementation NSString (CVTemplateAdditions)

- (NSString *)cvStringByApplyingSubstitutions:(NSDictionary *)substitutions
{
    NSMutableString *toReturn = [self mutableCopy];
    [substitutions enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
        NSString *stringToSub = [NSString stringWithFormat:@"${%@}",key];
        [toReturn replaceOccurrencesOfString:stringToSub withString:obj options:NSLiteralSearch range:NSMakeRange(0, [toReturn length])];
    }];
    return [toReturn copy];
}

@end

We could load your text into a file, like so:

Template.txt:

${detailsText}

${nameText}

${addressLineOneText}
${addressLineTwoText}
${cityAddressText}, Il
${zipAddressText}
Phone: ${phoneText}
Email: ${emailText}

And then apply substitutions like:

NSDictionary *substitutions = @{
                                @"detailsText": @"details text here",
                                @"nameText": @"name text here",
                                @"addressLineOneText": @"address line one text here",
                                @"addressLineTwoText": @"address line two text here",
                                @"cityAddressText": @"city address text here",
                                @"zipAddressText": @"zip address text here",
                                @"phoneText": @"phone text here",
                                @"emailText": @"email text here"
                                };
NSString *actual = [input cvStringByApplyingSubstitutions:substitutions];

In the above code, actual evaluates to the following string:

details text here

name text here

address line one text here
address line two text here
city address text here, Il
zip address text here
Phone: phone text here
Email: email text here

The following SenTestCase subclass and input files demonstrate the functionality further:

TemplatingEngineTests.h:

#import <SenTestingKit/SenTestingKit.h>

@interface TemplatingEngineTests : SenTestCase

@end

TemplatingEngineTests.m:

#import "TemplatingEngineTests.h"
#import "NSString+CVTemplateAdditions.h"

@implementation TemplatingEngineTests

- (void)setUp
{
    [super setUp];

    // Set-up code here.
}

- (void)tearDown
{
    // Tear-down code here.

    [super tearDown];
}

- (void)testApplyingSubstitutionsResultsInExpectedString
{
    NSString *expected = [self expectedString];
    NSString *input = [self templateString];
    NSDictionary *substitutions = @{
                                    @"detailsText": @"details text here",
                                    @"nameText": @"name text here",
                                    @"addressLineOneText": @"address line one text here",
                                    @"addressLineTwoText": @"address line two text here",
                                    @"cityAddressText": @"city address text here",
                                    @"zipAddressText": @"zip address text here",
                                    @"phoneText": @"phone text here",
                                    @"emailText": @"email text here"
                                    };
    NSString *actual = [input cvStringByApplyingSubstitutions:substitutions];
    STAssertEqualObjects(actual, expected, nil);
}

- (void)testApplyingNilSubstitutionsReturnsStringEqualToItself
{
    NSString *testString = [self templateString];
    NSString *result = [testString cvStringByApplyingSubstitutions:nil];
    STAssertEqualObjects(result, testString, nil);
}

- (void)testNonTemplatedStringReturnsStringEqualToItself
{
    NSString *testString = [self nonTemplateString];
    NSString *result = [testString cvStringByApplyingSubstitutions:@{@"key": @"sub"}];
    STAssertEqualObjects(result, testString, nil);
}

- (NSString *)nonTemplateString
{
    return [self stringFromFile:@"NoTemplate"];
}

- (NSString *)templateString
{
    return [self stringFromFile:@"Template"];
}

- (NSString *)expectedString
{
    return [self stringFromFile:@"TemplateExpected"];
}

- (NSString *)stringFromFile:(NSString *)name
{
    NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:name ofType:@"txt"];
    NSString *testString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    return testString;
}

@end

Template.txt:

${detailsText}

${nameText}

${addressLineOneText}
${addressLineTwoText}
${cityAddressText}, Il
${zipAddressText}
Phone: ${phoneText}
Email: ${emailText}

TemplateExpected.txt:

details text here

name text here

address line one text here
address line two text here
city address text here, Il
zip address text here
Phone: phone text here
Email: email text here

NoTemplate.txt:

This is an email with no template parameters.

It's quite boring.

Sigh.


TELL ALL YOUR FRIENDS HOW AWESOME OUR APP IS!!!

Hopefully you agree that it becomes much more manageable and readable when you can load complex formats from text files and refer to them by name.

\$\endgroup\$
4
+50
\$\begingroup\$

You could use NSMutableString. It has appendString method which you could use to create string by appending other strings. For example:

NSMutableString *mutableString = [NSMutableString stringWithFormat:@"%@", someString];
[mutableString appendString:otherString];
// and so on...

NSMutableString also has the stringByAppendingString method, but appendString method is more memory friendly.

You can also do this:

NSString *exampleString = @"Multiple " @"string " @"example";

NSString can even be built using NSArray:

NSArray *stringArray = [NSArray arrayWithObjects:@"Another", @"string", @"example", nil];
NSLog(@"%@",[pathArray componentsJoinedByString:@"\n"]);
// Log wold look like
// Another
// string
// example
\$\endgroup\$
  • \$\begingroup\$ I'm a bit confused on the second example. What's the difference between that and concatenation? \$\endgroup\$ – cory ginsberg Aug 11 '13 at 15:47
  • 1
    \$\begingroup\$ Second example just a technique to break up long literal strings within your code. This way your code could look cleaner and more readable but using this technique you can only concatenate string literals and cannot use string variables and methods like stringWithFormat:. \$\endgroup\$ – JPetric Aug 12 '13 at 7:36
0
\$\begingroup\$

Since you have a concept of line, an array with one entry per line would be a clear representation:

NSArray *lines = @[detailsText.text,
                   @"",
                   nameText.text,
                   @"",
                   addrLn1Text.text,
                   addrLn2Text.text,
                   [cityAddrText.text stringByAppendingString:@", Il"],
                   zipAddrText.text,
                   [@"Phone: " stringByAppendingString:phoneNumberText.text],
                   [@"Email: " stringByAppendingString:emailText.text]];

NSString *strMessage = [lines componentsJoinedByString:@"\n"];

I wouldn't care about optimizing run time when you're sending an email.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.