Search code examples
iosnsstringnsrangensscanner

NSScanner based on last occurrence of substring "@"


I have a user name system similar to twitters using the @ symbol.

I have it set up to message users by typing @username. This is basically identical to mentions in Twitter.

So far I have been able to detect the text following the @ symbol and to stop detecting when it hits white space. This has been done perfectly with NSScanner.

My problem is if I want to message multiple @usernames, the NSScanner will only detect the first occurrence. I need it to detect the latest occurrence. I have found this as a useful tool:

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];

I can't seem to figure out how to properly use this in conjunction with NSScanner. Here are 2 examples of my code attempts.

Example 1:

NSString *user = nil;
NSString *text = textView.text;

NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];

if (lastSymbol.location != NSNotFound) {

    NSScanner *userScanner = [NSScanner scannerWithString:text];
    [userScanner scanUpToString:@"@" intoString:nil];

    NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@"@"];

    if (![userScanner isAtEnd]) {

        [userScanner scanUpToCharactersFromSet:charset intoString:nil];
        [userScanner scanCharactersFromSet:charset intoString:nil]
        [userScanner scanUpToCharactersFromSet:charset intoString:&user];


    }
}


if (user.length > 0) {

    NSRange whiteSpaceRange = [user rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
    if (whiteSpaceRange.location != NSNotFound) {

        // White space, username has ended

    } else {

        // Still getting username
        NSLog(@"%@", user);

    }

}

As you can see, I am just not quite sure how to make these work together to produce the results I am looking for. Any ideas? Thanks!

**************************UPDATE****************************

To be more specific, when a user types the @ symbol and starts typing a username, I want to display a list of their friends based on what they type. So naturally, I want to pull the string following the @ symbol and as the user keeps typing, keep updating the list of their friends. Once the user types a space, we assume they are done typing the user name (usernames cannot contain spaces) so I then stop displaying the table view.

The above code so far is simply detecting and pulling the string following the @ symbol. Here is the output right now:

Textfield Text: Hello @username

enter image description here

This works perfectly, as you can see I can keep checking this text against an array of friends.

Let's say the user wants to message another user in the same text like so:

Hello @username, this is my friend @goodfriend

This time it still displays the first @username, but ignore the second @goodfriend.

I figure the best way to do this is somehow tell the NSScanner to reference the LAST occurrence of the @ symbol, because what I have now is only referencing the first @ symbol.

This might also have to do with the way I am detecting the white space to know the user is done typing the username, and does not need any more suggestions.

Hope this helps, thanks!


Solution

  • For working with the live input like this, I would recommend using a completely different approach. Instead of looking for the last "@", look at just the last word, and see if it starts with "@".

    NSString *user = nil;
    NSString *text = textView.text;
    // Find the last whitespace character
    NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
    NSRange lastWordRange = [text rangeOfCharacterFromSet:whitespace options:NSBackwardsSearch];
    // If found, get the index after it. Otherwise, the entire string is one word.
    if(lastWordRange.location != NSNotFound)
        lastWordRange.location += lastWordRange.length;
    else lastWordRange.location = 0;
    // If the index is not at the end, look for a '@' right after it
    if(lastWordRange.location < [text length] &&
       [text characterAtIndex:lastWordRange.location] == '@') {
        // Found a '@', use the rest of the string as the username
        user = [text substringFromIndex:lastWordRange.location+1];
    }
    if(user) NSLog(@"%@", user); // The user is still typing a name
    

    If you really want to use a scanner, you can use the same substringFromIndex: method to get a substring to scan.

    NSRange lastSymbol = [text rangeOfString:@"@" options:NSBackwardsSearch];
    if (lastSymbol.location != NSNotFound) {
        NSString *substring = [text substringFromIndex:lastSymbol.location];
        NSScanner *userScanner = [NSScanner scannerWithString:substring];
        ...