Ad

Our DNA is written in Swift
Jump

Splitting a String into Paragraphs … with Blocks

I found myself in need of splitting an NSString into paragraphs. Or more precisely to analyze a string and find the NSRange for each such paragraph. At first I wrote a C-style function that looked for the ‘\n’ in the NSString’s utf8String, but it turns out that this approach has problems with multi-character UTF8 sequences.

For the longest time I shirked from using Blocks, which became part of iOS with iteration 4.0. But since this project will have a minimum deployment target of higher than this, I gained the ability to use a block-based enumeration function to achieve my goal with a record minimum of code.

Serendipity helped me find that with this new substring enumeration function you can enumerate over a string’s substrings by words, lines, paragraphs or even sentences. And if you feel so inclined even reverse.

Here’s my  code:

- (void)buildParagraphRanges
{
    // get naked NSString
    NSString *string = [self.attributedString string];
 
    // entire string
    NSRange range = NSMakeRange(0, [string length]);
 
    NSMutableArray *tmpArray = [NSMutableArray array];
 
    [string enumerateSubstringsInRange:range options:NSStringEnumerationByParagraphs usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop)
     {
         NSValue *value = [NSValue valueWithRange:substringRange];
         [tmpArray addObject:value];
     }
     ];
 
    self.paragraphRanges = tmpArray;
}

As you can see there is an attributed string involved so at the very top I’ll have to get the plain NSString from this. In the middle I’ll do the enumeration and by converting the substringRange into an NSValue I can add it to an NSArray. This way I have the index location and length of each paragraph handy when I need it.

We don’t know what goes on under the hood, but not having to do any kind of calculating to get the ranges can only be a good thing.

Blocks work such that all local variables currently in scope when the method is called are still available inside the block. Additionally the parameters inside the ^() are also filled in for you to do with as you please. The block is sort of a function pointer on steroids. Since it is a parameter in this method you could even set up the block elsewhere and then just pass the block variable to it.

But let’s not go overboard here. This is only the second time I get to use a method with a block and I’m lovin’ it. 🙂


Categories: Recipes

Leave a Comment