Skip to content

Instantly share code, notes, and snippets.

@torinnguyen
Created October 13, 2020 13:55
Show Gist options
  • Save torinnguyen/4ffae0b328b391fa2dd2bad87b823434 to your computer and use it in GitHub Desktop.
Save torinnguyen/4ffae0b328b391fa2dd2bad87b823434 to your computer and use it in GitHub Desktop.
//
// NSString+Additions.m
//
// Created by Daud Abas on 24/2/12.
// Copyright (c) 2012 MyCompany. All rights reserved.
//
#import <time.h>
#import "NSString+Additions.h"
#import <CommonCrypto/CommonDigest.h>
#import "TPLISO8601DateFormatter.h"
@implementation NSString (Additions)
- (BOOL)isEmpty
{
return [[self trim] length] <= 0;
}
- (BOOL)isNotEmpty
{
return [[self trim] length] > 0;
}
- (NSUInteger)wordCount {
NSScanner *scanner = [NSScanner scannerWithString: self];
NSCharacterSet *whiteSpace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSUInteger count = 0;
while ([scanner scanUpToCharactersFromSet: whiteSpace intoString: nil])
count++;
return count;
}
- (BOOL)contains:(NSString*)needle {
if (needle == nil || [needle length] <= 0)
return NO;
NSRange range = [self rangeOfString:needle options: NSCaseInsensitiveSearch];
return (range.length == needle.length && range.location != NSNotFound);
}
- (BOOL)containsCaseInsesitive:(NSString*)needle {
if (needle == nil || [needle length] <= 0)
return NO;
NSRange range = [[self lowercaseString] rangeOfString:[needle lowercaseString] options: NSCaseInsensitiveSearch];
return (range.length == needle.length && range.location != NSNotFound);
}
- (BOOL)startsWith:(NSString*)needle {
return [self hasPrefix:needle];
}
- (BOOL)endsWith:(NSString*)needle {
return [self hasSuffix:needle];
}
- (BOOL)isNotEqualToString:(NSString*)anotherString {
return [self isEqualToString:anotherString] == NO;
}
- (BOOL)isEqualToStringIgnoreCase:(NSString *)anotherString {
return [[self uppercaseString] isEqualToString:[anotherString uppercaseString]];
}
- (NSUInteger)lastIndexOf:(NSString *)needle
{
NSRange range = [self rangeOfString:needle options:NSBackwardsSearch];
return range.location;
}
- (NSInteger)integerValueTrim
{
return [[self trimCommaSeparator] integerValue];
}
- (CGFloat)floatValueTrim
{
return [[self trimCommaSeparator] floatValue];
}
- (NSString *)trim
{
return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
- (NSString *)trimHead
{
//Leading only
NSRange range = [self rangeOfString:@"^\\s*" options:NSRegularExpressionSearch];
return [self stringByReplacingCharactersInRange:range withString:@""];
}
- (NSString *)trimTail
{
//Trailing only
NSRange range = [self rangeOfString:@"\\s*$" options:NSRegularExpressionSearch];
return [self stringByReplacingCharactersInRange:range withString:@""];
}
- (NSString *)stringValue
{
return self;
}
- (NSString *)firstCharacter
{
if ([self length] <= 1)
return self;
return [self substringToIndex:1];
}
- (NSString *)firstNCharacter:(NSUInteger)n
{
if (n <= 0)
return nil;
if ([self length] <= n)
return self;
return [self substringToIndex:n];
}
- (NSString *)lastCharacter
{
if ([self length] <= 1)
return self;
return [self substringFromIndex:self.length-1];
}
- (NSString *)lastNCharacter:(NSUInteger)n
{
if (n <= 0)
return nil;
if ([self length] <= n)
return self;
return [self substringFromIndex:self.length-n];
}
- (NSString *)trimCommaSeparator
{
return [self stringByReplacingOccurrencesOfString:@"," withString:@""];
}
- (NSString *)substringToString:(NSString *)needle
{
NSRange range = [self rangeOfString:needle];
if (range.location == NSNotFound)
return self;
return [self substringToIndex:range.location];
}
- (NSString *)substringToLastString:(NSString *)needle
{
NSInteger index = [self lastIndexOf:needle];
if (index == NSNotFound)
return self;
return [self substringToIndex:index];
}
- (NSString *)lowerCaseFirstCharacter
{
if ([self length] <= 1)
return [self lowercaseString];
NSString *firstCapChar = [[self substringToIndex:1] lowercaseString];
NSString *cappedString = [self stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:firstCapChar];
return cappedString;
}
- (NSString *)upperCaseFirstCharacter
{
if ([self length] <= 1)
return [self uppercaseString];
NSString *firstCapChar = [[self substringToIndex:1] uppercaseString];
NSString *cappedString = [self stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:firstCapChar];
return cappedString;
}
- (NSString *)subSentenceUpToCharacters:(NSUInteger)numChars
{
if ([self length] <= numChars)
return self;
NSArray * sentences = [self componentsSeparatedByString:@". "];
if ([sentences count] <= 1)
return [[self substringToIndex:numChars] stringByAppendingString:@"..."];
NSString * outputString = @"";
for (NSString * string in sentences)
{
if ([outputString length] <= 0) {
outputString = string;
continue;
}
if ([outputString length] + [string length] > numChars)
break;
outputString = [outputString stringByAppendingFormat:@". %@", string];
}
return [outputString stringByAppendingFormat:@"..."];
}
- (NSData *)dataWithUTF8Encoding
{
return [self dataUsingEncoding:NSUTF8StringEncoding];
}
- (NSString *)URLEncodedString
{
__autoreleasing NSString *encodedString;
NSString *originalString = (NSString *)self;
encodedString = (__bridge_transfer NSString * )
CFURLCreateStringByAddingPercentEscapes(NULL,
(__bridge CFStringRef)originalString,
(CFStringRef)@"$-_.+!*'(),&+/:;=?@#",
NULL,
kCFStringEncodingUTF8);
encodedString = [encodedString stringByReplacingOccurrencesOfString:@"%25" withString:@"\%"]; //revert double escape
return encodedString;
}
- (NSString *)URLEncodeEverything
{
__autoreleasing NSString *encodedString;
NSString *originalString = (NSString *)self;
encodedString = (__bridge_transfer NSString * )
CFURLCreateStringByAddingPercentEscapes(NULL,
(__bridge CFStringRef)originalString,
NULL,
(CFStringRef)@"$-_.+!*'(),&+/:;=?@#",
kCFStringEncodingUTF8);
encodedString = [encodedString stringByReplacingOccurrencesOfString:@"%25" withString:@"\%"]; //revert double escape
return encodedString;
}
- (NSString *)URLDecodeString
{
NSString * result = [self stringByReplacingOccurrencesOfString:@"+" withString:@" "];
result = [result stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return result;
}
- (NSMutableDictionary *)convertToKeyValueDictionary
{
NSArray * keyValuePairs = [self componentsSeparatedByString:@"&"];
NSMutableDictionary * keyValueDictionary = [NSMutableDictionary dictionary];
for (NSString * keyValuePairString in keyValuePairs)
{
NSArray *kv = [keyValuePairString componentsSeparatedByString:@"="];
if ([kv count] < 2)
continue;
NSString * key = [kv objectAtIndex:0];
NSString * value = [kv objectAtIndex:1];
[keyValueDictionary setValidObject:value forKey:key];
}
if ([keyValueDictionary count] <= 0)
return nil;
return keyValueDictionary;
}
- (NSString *)sha1 {
const char *cStr = [self UTF8String];
unsigned char result[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(cStr, (CC_LONG)strlen(cStr), result);
NSString *s = [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3], result[4],
result[5], result[6], result[7],
result[8], result[9], result[10], result[11], result[12],
result[13], result[14], result[15],
result[16], result[17], result[18], result[19]
];
return s;
}
- (NSString *)sha256
{
const char * s = [self cStringUsingEncoding:NSASCIIStringEncoding];
NSData * keyData=[NSData dataWithBytes:s length:strlen(s)];
uint8_t digest[CC_SHA256_DIGEST_LENGTH] = {0};
CC_SHA256(keyData.bytes, (CC_LONG)keyData.length, digest);
NSData * out = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
NSString * hash = [out hexString];
hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];
return hash;
}
- (NSString *)hashm
{
const char * cStr = [self UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
NSString * s = [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3], result[4],
result[5], result[6], result[7],
result[8], result[9], result[10], result[11], result[12],
result[13], result[14], result[15]
];
return s;
}
- (NSDate*)dateFromString
{
static TPLISO8601DateFormatter *dateFormatter = nil;
if (dateFormatter == nil)
dateFormatter = [[TPLISO8601DateFormatter alloc] init];
NSDate *theDate = [dateFormatter dateFromString:self];
return theDate;
}
- (NSDate*)dateFromFacebookBirthdayFormat
{
//"07/31/1985"
//Creating a NSDateFormater is expensive, we cache it
static NSDateFormatter * myFormatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
myFormatter = [[NSDateFormatter alloc] init];
[myFormatter setDateFormat:@"MM/dd/yyyy"];
});
NSDate * myDate = [myFormatter dateFromString:self];
return myDate;
}
+ (NSString *)formatFloat:(CGFloat)num maxDecimalPlaces:(NSUInteger)numDecPlaces
{
if (numDecPlaces <= 0)
return [NSString stringWithFormat:@"%f", roundf(num)];
//Round the number up to N number of decimal places
num = roundf(num * pow(10,numDecPlaces)) / (int)pow(10,numDecPlaces);
return [@(num) stringValue];
}
#pragma mark - Substring
- (BOOL)matchRegexPattern:(NSString *)pattern
{
NSPredicate *regExPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
return [regExPredicate evaluateWithObject:self];
}
- (NSString *)replaceRegexPattern:(NSString *)pattern withString:(NSString *)newString
{
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive
error:&error];
if (error != nil) {
DLog(@"%@", error);
return self;
}
NSString *modifiedString = [regex stringByReplacingMatchesInString:self
options:0
range:NSMakeRange(0, [self length])
withTemplate:newString];
return modifiedString;
}
- (NSArray *)getAllRangesOfOccurrencesOfString:(NSString *)substring
{
NSMutableArray * array = [NSMutableArray array];
NSUInteger count = 0, length = [self length];
NSRange range = NSMakeRange(0, length);
while (range.location != NSNotFound)
{
range = [self rangeOfString:substring options:0 range:range];
if (range.location != NSNotFound)
{
[array addObject:[NSValue valueWithRange:range]];
range = NSMakeRange(range.location + range.length, length - (range.location + range.length));
count++;
}
}
return array;
}
- (NSInteger)countOccurencesOfString:(NSString *)searchString
{
NSInteger strCount = [self length] - [[self stringByReplacingOccurrencesOfString:searchString withString:@""] length];
return strCount / [searchString length];
}
- (BOOL)safeIsEqualToNumber:(id)stringOrNumber
{
NSString * anotherString = [NSString stringWithFormat:@"%@", stringOrNumber];
return [self integerValue] == [anotherString integerValue];
}
- (NSArray *)arrayOfCaptureComponentsMatchedByRegex:(NSString *)regex
{
NSError *error = NULL;
NSRegularExpression *regExpression = [NSRegularExpression regularExpressionWithPattern:regex
options:NSRegularExpressionCaseInsensitive
error:&error];
NSMutableArray *test = [NSMutableArray array];
NSArray *matches = [regExpression matchesInString:self options:NSMatchingReportProgress range:NSMakeRange(0, self.length)];
for(NSTextCheckingResult *match in matches) {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:match.numberOfRanges];
for(NSInteger i=0; i<match.numberOfRanges; i++) {
NSRange matchRange = [match rangeAtIndex:i];
NSString *matchStr = nil;
if(matchRange.location != NSNotFound) {
matchStr = [self substringWithRange:matchRange];
} else {
matchStr = @"";
}
[result addObject:matchStr];
}
[test addObject:result];
}
return test;
}
- (NSString *)extractContentBetweenHtmlTag:(NSString *)htmlTag
{
NSString * regExp = [NSString stringWithFormat:@"(?i)([\\s\\.,>'-])(%@)([\\s\\.,;!\\?\\)<])", htmlTag];
NSArray * regexResult = [self arrayOfCaptureComponentsMatchedByRegex:regExp];
NSString * result = self;
if ([regexResult count] > 0)
{
for (NSArray * match in regexResult)
{
NSString * all = [match objectAtIndex:0];
NSString * before = [match objectAtIndex:1];
NSString * matched = [match objectAtIndex:2];
NSString * after = [match objectAtIndex:3];
result = [result stringByReplacingOccurrencesOfString:all
withString:[NSString stringWithFormat:@"%@<SPAN style=\"BACKGROUND-COLOR: #FF0000\">%@</SPAN>%@",before, matched, after]
options:NSCaseInsensitiveSearch
range: [result rangeOfString:all]];
}
}
return result;
}
- (NSString *)stringByReplacingOpenTag:(NSString *)openTag closeTag:(NSString *)closeTag replaceOpenTag:(NSString *)replaceOpenTag replaceCloseTag:(NSString *)replaceCloseTag
{
//Stop condition for recursive
NSRange fullRange = NSMakeRange(0, [self length]);
NSRange startRange = [self rangeOfString:openTag options:NSCaseInsensitiveSearch range:fullRange];
if (startRange.location == NSNotFound)
return self;
//Stop condition for recursive
NSRange endSearchRange = NSMakeRange(startRange.location+startRange.length, fullRange.length-startRange.location-startRange.length);
NSRange endRange = [self rangeOfString:closeTag options:NSCaseInsensitiveSearch range:endSearchRange];
if (endSearchRange.location == NSNotFound)
return self;
NSRange centerRange = NSMakeRange(startRange.location + startRange.length, endRange.location - startRange.location - startRange.length);
NSString * centerString = [self substringWithRange:centerRange];
NSString * frontString = [self substringToIndex:startRange.location];
NSString * endString = [self substringFromIndex:endRange.location+endRange.length];
NSString * combinedString = [NSString stringWithFormat:@"%@%@%@%@%@", frontString, replaceOpenTag, centerString, replaceCloseTag, endString];
//Recursive
return [combinedString stringByReplacingOpenTag:openTag closeTag:closeTag replaceOpenTag:replaceOpenTag replaceCloseTag:replaceCloseTag];
}
- (NSString *)stringByReplacingOpenTag:(NSString *)openTag closeTag:(NSString *)closeTag withTag:(NSString *)replaceTag
{
NSString * replaceOpenTag = @"";
NSString * replaceCloseTag = @"";
if ([replaceTag length] > 0) {
replaceOpenTag = [NSString stringWithFormat:@"<%@>", replaceTag];
replaceCloseTag = [NSString stringWithFormat:@"</%@>", replaceTag];
}
return [self stringByReplacingOpenTag:openTag closeTag:closeTag replaceOpenTag:replaceOpenTag replaceCloseTag:replaceCloseTag];
}
- (NSString *)stringByStrippingEmptyTag:(NSString *)stripTagtag
{
NSString * openTag = [NSString stringWithFormat:@"<%@>", tag];
NSString * closeTag = [NSString stringWithFormat:@"</%@>", tag];
return [self stringByReplacingOpenTag:openTag closeTag:closeTag withTag:nil];
}
- (NSString *)stringByReplacingWithBlockQuote
{
//<p style="margin-left:24.99999px;">some quoted text</p>
return [self stringByReplacingOpenTag:@"<p style=\"margin-left:24.99999px;\">" closeTag:@"</p>" withTag:@"blockquote"];
}
- (NSString *)stringByRemovingHtmlTag:(NSString *)tag
{
NSString *regexStr = [NSString stringWithFormat:@"<%@ ([^>]+)>([^>]+)</%@>", tag, tag];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:NULL];
return [regex stringByReplacingMatchesInString:self options:0 range:NSMakeRange(0, [self length]) withTemplate:@"$2"];
}
- (NSString *)extractFirstImageUrl
{
NSString *string = self;
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray *matches = [linkDetector matchesInString:string options:0 range:NSMakeRange(0, [string length])];
for (NSTextCheckingResult *match in matches)
if ([match resultType] == NSTextCheckingTypeLink) {
NSString * string = [[match URL] absoluteString];
NSString * lowerUrl = [string lowercaseString];
if ([lowerUrl contains:@".jpg"] || [lowerUrl contains:@"jpeg"] || [lowerUrl contains:@"png"] || [lowerUrl contains:@"gif"])
return string;
}
return nil;
}
- (NSString *)extractFirstUrlWithSuffix:(NSString *)suffix
{
NSString *string = self;
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray *matches = [linkDetector matchesInString:string options:0 range:NSMakeRange(0, [string length])];
for (NSTextCheckingResult *match in matches)
if ([match resultType] == NSTextCheckingTypeLink)
if ([[[match URL] absoluteString] hasSuffix:suffix])
return [[match URL] absoluteString];
return nil;
}
- (NSArray *)extractArrayOfUrls
{
NSMutableArray * arrayOfURLs = [NSMutableArray array];
NSString *string = self;
NSDataDetector *linkDetector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray *matches = [linkDetector matchesInString:string options:0 range:NSMakeRange(0, [string length])];
for (NSTextCheckingResult *match in matches)
if ([match resultType] == NSTextCheckingTypeLink) {
NSString * string = [[match URL] absoluteString];
if ([string isHttpUrl])
[arrayOfURLs addStringWithoutDuplication:[[match URL] absoluteString] caseSensitive:NO];
}
//Fallback to regex
NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:@"(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))" options:NSRegularExpressionCaseInsensitive error:NULL];
NSArray *regexMatches = [expression matchesInString:self options:NSMatchingReportCompletion range:NSMakeRange(0, [self length])];
for (NSTextCheckingResult *result in regexMatches) {
NSString *url = [self substringWithRange:result.range];
[arrayOfURLs addStringWithoutDuplication:url caseSensitive:NO];
}
return arrayOfURLs;
}
- (void)drawWithBasePoint:(CGPoint)basePoint
andAngle:(CGFloat)angle
andFont:(UIFont *)font{
CGSize textSize = [self sizeWithFont:font];
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform t = CGAffineTransformMakeTranslation(basePoint.x, basePoint.y);
CGAffineTransform r = CGAffineTransformMakeRotation(angle);
CGContextConcatCTM(context, t);
CGContextConcatCTM(context, r);
[self drawAtPoint:CGPointMake(-1 * textSize.width / 2, -1 * textSize.height / 2)
withFont:font];
CGContextConcatCTM(context, CGAffineTransformInvert(r));
CGContextConcatCTM(context, CGAffineTransformInvert(t));
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment