415 lines
16 KiB
Swift
415 lines
16 KiB
Swift
//
|
|
// StringExtensions.swift
|
|
// EZSwiftExtensions
|
|
//
|
|
// Created by Goktug Yilmaz on 15/07/15.
|
|
// Copyright (c) 2015 Goktug Yilmaz. All rights reserved.
|
|
//
|
|
// swiftlint:disable line_length
|
|
// swiftlint:disable trailing_whitespace
|
|
|
|
#if os(OSX)
|
|
import AppKit
|
|
#else
|
|
import UIKit
|
|
#endif
|
|
|
|
extension String {
|
|
/// EZSE: Init string with a base64 encoded string
|
|
init ? (base64: String) {
|
|
let pad = String(count: base64.length % 4, repeatedValue: Character("="))
|
|
let base64Padded = base64 + pad
|
|
if let decodedData = NSData(base64EncodedString: base64Padded, options: NSDataBase64DecodingOptions(rawValue: 0)), decodedString = NSString(data: decodedData, encoding: NSUTF8StringEncoding) {
|
|
self.init(decodedString)
|
|
return
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/// EZSE: base64 encoded of string
|
|
var base64: String {
|
|
get {
|
|
let plainData = (self as NSString).dataUsingEncoding(NSUTF8StringEncoding)
|
|
let base64String = plainData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
|
|
return base64String
|
|
}
|
|
}
|
|
|
|
/// EZSE: Cut string from integerIndex to the end
|
|
public subscript(integerIndex: Int) -> Character {
|
|
let index = startIndex.advancedBy(integerIndex)
|
|
return self[index]
|
|
}
|
|
|
|
/// EZSE: Cut string from range
|
|
public subscript(integerRange: Range<Int>) -> String {
|
|
let start = startIndex.advancedBy(integerRange.startIndex)
|
|
let end = startIndex.advancedBy(integerRange.endIndex)
|
|
let range = start..<end
|
|
return self[range]
|
|
}
|
|
|
|
/// EZSE: Character count
|
|
public var length: Int {
|
|
return self.characters.count
|
|
}
|
|
|
|
/// EZSE: Counts number of instances of the input inside String
|
|
public func count(substring: String) -> Int {
|
|
return componentsSeparatedByString(substring).count - 1
|
|
}
|
|
|
|
/// EZSE: Capitalizes first character of String
|
|
public mutating func capitalizeFirst() {
|
|
guard characters.count > 0 else { return }
|
|
self.replaceRange(startIndex...startIndex, with: String(self[startIndex]).capitalizedString)
|
|
}
|
|
|
|
/// EZSE: Capitalizes first character of String, returns a new string
|
|
public func capitalizedFirst() -> String {
|
|
guard characters.count > 0 else { return self }
|
|
var result = self
|
|
|
|
result.replaceRange(startIndex...startIndex, with: String(self[startIndex]).capitalizedString)
|
|
return result
|
|
}
|
|
|
|
/// EZSE: Uppercases first 'count' characters of String
|
|
public mutating func uppercasePrefix(count: Int) {
|
|
guard characters.count > 0 && count > 0 else { return }
|
|
self.replaceRange(startIndex..<startIndex.advancedBy(min(count, length)),
|
|
with: String(self[startIndex..<startIndex.advancedBy(min(count, length))]).uppercaseString)
|
|
}
|
|
|
|
/// EZSE: Uppercases first 'count' characters of String, returns a new string
|
|
public func uppercasedPrefix(count: Int) -> String {
|
|
guard characters.count > 0 && count > 0 else { return self }
|
|
var result = self
|
|
result.replaceRange(startIndex..<startIndex.advancedBy(min(count, length)),
|
|
with: String(self[startIndex..<startIndex.advancedBy(min(count, length))]).uppercaseString)
|
|
return result
|
|
}
|
|
|
|
/// EZSE: Uppercases last 'count' characters of String
|
|
public mutating func uppercaseSuffix(count: Int) {
|
|
guard characters.count > 0 && count > 0 else { return }
|
|
self.replaceRange(endIndex.advancedBy(-min(count, length))..<endIndex,
|
|
with: String(self[endIndex.advancedBy(-min(count, length))..<endIndex]).uppercaseString)
|
|
}
|
|
|
|
/// EZSE: Uppercases last 'count' characters of String, returns a new string
|
|
public func uppercasedSuffix(count: Int) -> String {
|
|
guard characters.count > 0 && count > 0 else { return self }
|
|
var result = self
|
|
result.replaceRange(endIndex.advancedBy(-min(count, length))..<endIndex,
|
|
with: String(self[endIndex.advancedBy(-min(count, length))..<endIndex]).uppercaseString)
|
|
return result
|
|
}
|
|
|
|
/// EZSE: Uppercases string in range 'range' (from range.startIndex to range.endIndex)
|
|
public mutating func uppercase(range range: Range<Int>) {
|
|
let from = max(range.startIndex, 0), to = min(range.endIndex, length)
|
|
guard characters.count > 0 && (0..<length).contains(from) else { return }
|
|
self.replaceRange(startIndex.advancedBy(from)..<startIndex.advancedBy(to),
|
|
with: String(self[startIndex.advancedBy(from)..<startIndex.advancedBy(to)]).uppercaseString)
|
|
}
|
|
|
|
/// EZSE: Uppercases string in range 'range' (from range.startIndex to range.endIndex), returns new string
|
|
public func uppercased(range range: Range<Int>) -> String {
|
|
let from = max(range.startIndex, 0), to = min(range.endIndex, length)
|
|
guard characters.count > 0 && (0..<length).contains(from) else { return self }
|
|
var result = self
|
|
result.replaceRange(startIndex.advancedBy(from)..<startIndex.advancedBy(to),
|
|
with: String(self[startIndex.advancedBy(from)..<startIndex.advancedBy(to)]).uppercaseString)
|
|
return result
|
|
}
|
|
|
|
|
|
/// EZSE: Counts whitespace & new lines
|
|
@available(*, deprecated=1.6, renamed="isBlank")
|
|
public func isOnlyEmptySpacesAndNewLineCharacters() -> Bool {
|
|
let characterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet()
|
|
let newText = self.stringByTrimmingCharactersInSet(characterSet)
|
|
return newText.isEmpty
|
|
}
|
|
|
|
/// EZSE: Checks if string is empty or consists only of whitespace and newline characters
|
|
public var isBlank: Bool {
|
|
get {
|
|
let trimmed = stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet())
|
|
return trimmed.isEmpty
|
|
}
|
|
}
|
|
|
|
/// EZSE: Trims white space and new line characters
|
|
public mutating func trim() {
|
|
self = self.trimmed()
|
|
}
|
|
|
|
/// EZSE: Trims white space and new line characters, returns a new string
|
|
public func trimmed() -> String {
|
|
return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
|
|
}
|
|
|
|
/// EZSE: Position of begining character of substing
|
|
public func positionOfSubstring(subString: String, caseInsensitive: Bool = false, fromEnd: Bool = false) -> Int {
|
|
if subString.isEmpty {
|
|
return -1
|
|
}
|
|
var searchOption = fromEnd ? NSStringCompareOptions.AnchoredSearch : NSStringCompareOptions.BackwardsSearch
|
|
if caseInsensitive {
|
|
searchOption.insert(NSStringCompareOptions.CaseInsensitiveSearch)
|
|
}
|
|
if let range = self.rangeOfString(subString, options: searchOption) where !range.isEmpty {
|
|
return self.startIndex.distanceTo(range.startIndex)
|
|
}
|
|
return -1
|
|
}
|
|
|
|
/// EZSE: split string using a spearator string, returns an array of string
|
|
public func split(separator: String) -> [String] {
|
|
return self.componentsSeparatedByString(separator).filter {
|
|
!$0.trimmed().isEmpty
|
|
}
|
|
}
|
|
|
|
/// EZSE: split string with delimiters, returns an array of string
|
|
public func split(characters: NSCharacterSet) -> [String] {
|
|
return self.componentsSeparatedByCharactersInSet(characters).filter {
|
|
!$0.trimmed().isEmpty
|
|
}
|
|
}
|
|
|
|
/// EZSE : Returns count of words in string
|
|
public var countofWords: Int {
|
|
let regex = try? NSRegularExpression(pattern: "\\w+", options: NSRegularExpressionOptions())
|
|
return regex?.numberOfMatchesInString(self, options: NSMatchingOptions(), range: NSRange(location: 0, length: self.length)) ?? 0
|
|
}
|
|
|
|
/// EZSE : Returns count of paragraphs in string
|
|
public var countofParagraphs: Int {
|
|
let regex = try? NSRegularExpression(pattern: "\\n", options: NSRegularExpressionOptions())
|
|
let str = self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
|
|
return (regex?.numberOfMatchesInString(str, options: NSMatchingOptions(), range: NSRange(location:0, length: str.length)) ?? -1) + 1
|
|
}
|
|
|
|
internal func rangeFromNSRange(nsRange: NSRange) -> Range<String.Index>? {
|
|
let from16 = utf16.startIndex.advancedBy(nsRange.location, limit: utf16.endIndex)
|
|
let to16 = from16.advancedBy(nsRange.length, limit: utf16.endIndex)
|
|
if let from = String.Index(from16, within: self),
|
|
to = String.Index(to16, within: self) {
|
|
return from ..< to
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/// EZSE: Find matches of regular expression in string
|
|
public func matchesForRegexInText(regex: String!) -> [String] {
|
|
let regex = try? NSRegularExpression(pattern: regex, options: [])
|
|
let results = regex?.matchesInString(self, options: [], range: NSRange(location: 0, length: self.length)) ?? []
|
|
return results.map { self.substringWithRange(self.rangeFromNSRange($0.range)!) }
|
|
}
|
|
|
|
/// EZSE: Checks if String contains Email
|
|
public var isEmail: Bool {
|
|
let dataDetector = try? NSDataDetector(types: NSTextCheckingType.Link.rawValue)
|
|
let firstMatch = dataDetector?.firstMatchInString(self, options: NSMatchingOptions.ReportCompletion, range: NSRange(location: 0, length: length))
|
|
return (firstMatch?.range.location != NSNotFound && firstMatch?.URL?.scheme == "mailto")
|
|
}
|
|
|
|
/// EZSE: Returns if String is a number
|
|
public func isNumber() -> Bool {
|
|
if let _ = NSNumberFormatter().numberFromString(self) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
/// EZSE: Extracts URLS from String
|
|
public var extractURLs: [NSURL] {
|
|
var urls: [NSURL] = []
|
|
let detector: NSDataDetector?
|
|
do {
|
|
detector = try NSDataDetector(types: NSTextCheckingType.Link.rawValue)
|
|
} catch _ as NSError {
|
|
detector = nil
|
|
}
|
|
|
|
let text = self
|
|
|
|
if let detector = detector {
|
|
detector.enumerateMatchesInString(text, options: [], range: NSRange(location: 0, length: text.characters.count), usingBlock: {
|
|
(result: NSTextCheckingResult?, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
|
|
if let result = result, url = result.URL {
|
|
urls.append(url)
|
|
}
|
|
})
|
|
}
|
|
|
|
return urls
|
|
}
|
|
|
|
/// EZSE: Checking if String contains input
|
|
public func contains(find: String) -> Bool {
|
|
return self.rangeOfString(find) != nil
|
|
}
|
|
|
|
/// EZSE: Checking if String contains input with comparing options
|
|
public func contains(find: String, compareOption: NSStringCompareOptions) -> Bool {
|
|
return self.rangeOfString(find, options: compareOption) != nil
|
|
}
|
|
|
|
/// EZSE: Converts String to Int
|
|
public func toInt() -> Int? {
|
|
if let num = NSNumberFormatter().numberFromString(self) {
|
|
return num.integerValue
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// EZSE: Converts String to Double
|
|
public func toDouble() -> Double? {
|
|
if let num = NSNumberFormatter().numberFromString(self) {
|
|
return num.doubleValue
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// EZSE: Converts String to Float
|
|
public func toFloat() -> Float? {
|
|
if let num = NSNumberFormatter().numberFromString(self) {
|
|
return num.floatValue
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// EZSE: Converts String to Bool
|
|
public func toBool() -> Bool? {
|
|
let trimmed = self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).lowercaseString
|
|
if trimmed == "true" || trimmed == "false" {
|
|
return (trimmed as NSString).boolValue
|
|
}
|
|
return nil
|
|
}
|
|
|
|
///EZSE: Returns the first index of the occurency of the character in String
|
|
public func getIndexOf(char: Character) -> Int? {
|
|
for (index, c) in characters.enumerate() {
|
|
if c == char {
|
|
return index
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/// EZSE: Converts String to NSString
|
|
public var toNSString: NSString { get { return self as NSString } }
|
|
|
|
#if os(iOS)
|
|
|
|
///EZSE: Returns bold NSAttributedString
|
|
public func bold() -> NSAttributedString {
|
|
let boldString = NSMutableAttributedString(string: self, attributes: [NSFontAttributeName: UIFont.boldSystemFontOfSize(UIFont.systemFontSize())])
|
|
return boldString
|
|
}
|
|
|
|
#endif
|
|
|
|
///EZSE: Returns underlined NSAttributedString
|
|
public func underline() -> NSAttributedString {
|
|
let underlineString = NSAttributedString(string: self, attributes: [NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue])
|
|
return underlineString
|
|
}
|
|
|
|
#if os(iOS)
|
|
|
|
///EZSE: Returns italic NSAttributedString
|
|
public func italic() -> NSAttributedString {
|
|
let italicString = NSMutableAttributedString(string: self, attributes: [NSFontAttributeName: UIFont.italicSystemFontOfSize(UIFont.systemFontSize())])
|
|
return italicString
|
|
}
|
|
|
|
#endif
|
|
|
|
#if os(iOS)
|
|
|
|
///EZSE: Returns hight of rendered string
|
|
func height(width: CGFloat, font: UIFont, lineBreakMode: NSLineBreakMode?) -> CGFloat {
|
|
var attrib: [String: AnyObject] = [NSFontAttributeName: font]
|
|
if lineBreakMode != nil {
|
|
let paragraphStyle = NSMutableParagraphStyle()
|
|
paragraphStyle.lineBreakMode = lineBreakMode!
|
|
attrib.updateValue(paragraphStyle, forKey: NSParagraphStyleAttributeName)
|
|
}
|
|
let size = CGSize(width: width, height: CGFloat(DBL_MAX))
|
|
return ceil((self as NSString).boundingRectWithSize(size, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes:attrib, context: nil).height)
|
|
}
|
|
|
|
#endif
|
|
|
|
///EZSE: Returns NSAttributedString
|
|
public func color(color: UIColor) -> NSAttributedString {
|
|
let colorString = NSMutableAttributedString(string: self, attributes: [NSForegroundColorAttributeName: color])
|
|
return colorString
|
|
}
|
|
|
|
///EZSE: Returns NSAttributedString
|
|
public func colorSubString(subString: String, color: UIColor) -> NSMutableAttributedString {
|
|
var start = 0
|
|
var ranges: [NSRange] = []
|
|
while true {
|
|
let range = (self as NSString).rangeOfString(subString, options: NSStringCompareOptions.LiteralSearch, range: NSRange(location: start, length: (self as NSString).length - start))
|
|
if range.location == NSNotFound {
|
|
break
|
|
} else {
|
|
ranges.append(range)
|
|
start = range.location + range.length
|
|
}
|
|
}
|
|
let attrText = NSMutableAttributedString(string: self)
|
|
for range in ranges {
|
|
attrText.addAttribute(NSForegroundColorAttributeName, value: color, range: range)
|
|
}
|
|
return attrText
|
|
}
|
|
|
|
/// EZSE: Checks if String contains Emoji
|
|
public func includesEmoji() -> Bool {
|
|
for i in 0...length {
|
|
let c: unichar = (self as NSString).characterAtIndex(i)
|
|
if (0xD800 <= c && c <= 0xDBFF) || (0xDC00 <= c && c <= 0xDFFF) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
/// EZSE: copy string to pasteboard
|
|
func addToPasteboard() {
|
|
let pasteboard = UIPasteboard.generalPasteboard()
|
|
pasteboard.string = self
|
|
}
|
|
}
|
|
|
|
/// EZSE: Pattern matching of strings via defined functions
|
|
public func ~=<T> (pattern: (T -> Bool), value: T) -> Bool {
|
|
return pattern(value)
|
|
}
|
|
|
|
/// EZSE: Can be used in switch-case
|
|
public func hasPrefix(prefix: String) -> (value: String) -> Bool {
|
|
return { (value: String) -> Bool in
|
|
value.hasPrefix(prefix)
|
|
}
|
|
}
|
|
|
|
/// EZSE: Can be used in switch-case
|
|
public func hasSuffix(suffix: String) -> (value: String) -> Bool {
|
|
return { (value: String) -> Bool in
|
|
value.hasSuffix(suffix)
|
|
}
|
|
}
|