Basically, I want to make a program where if you start typing a name, the app will recognize it from the database and fill in the name for you. To do this, if a person types a comma after finishing a name, the app will start recording what the next name is. If it matches as a substring of one of the names in the database, it will fill it in for the user. The issue is that I need to get what part of the name has been filled out so far after the last occurrence of the comma character in the textField string, but I don't know how. For example:
User types: "Daniel, Joh"
And the app fills in John for you. Thanks.
If you really want the characters after the last comma, you could use a regular expression:
let string = "Hamilton, A"
let regex = try! NSRegularExpression(pattern: ",\\s*(\\S[^,]*)$")
if let match = regex.firstMatch(in: string, range: string.nsRange), let result = string[match.range(at: 1)] {
// use `result` here
}
Where, in Swift 4:
extension String {
/// An `NSRange` that represents the full range of the string.
var nsRange: NSRange {
return NSRange(startIndex ..< endIndex, in: self)
}
/// Substring from `NSRange`
///
/// - Parameter nsRange: `NSRange` within the string.
/// - Returns: `Substring` with the given `NSRange`, or `nil` if the range can't be converted.
subscript(nsRange: NSRange) -> Substring? {
return Range(nsRange, in: self)
.flatMap { self[$0] }
}
}
Many thanks to Rob. We can even extend his String extension by including the full answer to the initial question, with any Character:
extension String {
func substringAfterLastOccurenceOf(_ char: Character) -> String {
let regex = try! NSRegularExpression(pattern: "\(char)\\s*(\\S[^\(char)]*)$")
if let match = regex.firstMatch(in: self, range: self.nsRange), let result = self[match.range(at: 1)] {
return String(result)
}
return ""
}
// ... Rob's String extension
}
So we just need to call:
let subStringAfterLastComma = "Hamilton, A".substringAfterLastOccurenceOf(",")
Simple solution with range(of and options regularExpression and backwards.
It searches for a comma followed by an optional whitespace character.
extension String {
var subStringAfterLastComma : String {
guard let subrange = self.range(of: ",\\s?", options: [.regularExpression, .backwards]) else { return self }
return String(self[subrange.upperBound...])
}
}
let string1 = "Dan".subStringAfterLastComma // "Dan"
let string2 = "Daniel, Joh".subStringAfterLastComma // "Joh"
Thanks to Rob and Thierry G.
extension String {
var nsRange: NSRange {
return Foundation.NSRange(startIndex ..< endIndex, in: self)
}
subscript(nsRange: NSRange) -> Substring? {
return Range(nsRange, in: self)
.flatMap { self[$0] }
}
func substringAfterLastOccurenceOf(_ char: Character) -> String {
let regex = try! NSRegularExpression(pattern: "\(char)\\s*(\\S[^\(char)]*)$")
if let match = regex.firstMatch(in: self, range: self.nsRange), let result = self[match.range(at: 1)] {
return String(result)
}
return ""
}
}
we can use like this
let subStringAfterLastComma = "Hamilton, A".substringAfterLastOccurenceOf(",")
I have a method that detects urls in a string and returns me both the urls and the ranges where they can be found. Everything works perfectly until there are emojis on the string. For example:
"I'm gonna do this callenge as soon as I can swing again 😂😂😂\n http://youtu.be/SW_d3fGz1hk"
Because of the emojis, the url extracted from the text is http://youtu.be/SW_d3fGz1 instead of http://youtu.be/SW_d3fGz1hk. I figured that the easiest solution was to just replace the emojis on the string with whitespace characters (cause I need the range to be correct for some text styling stuff). Problem is, this is extremely hard to accomplish with Swift (most likely my abilities with the Swift String API is lacking).
I've been trying to do it like this but it seems that I cannot create a string from an array of unicode points:
var emojilessStringWithSubstitution: String {
let emojiRanges = [0x1F601...0x1F64F, 0x2702...0x27B0]
let emojiSet = Set(emojiRanges.flatten())
let codePoints: [UnicodeScalar] = self.unicodeScalars.map {
if emojiSet.contains(Int($0.value)) {
return UnicodeScalar(32)
}
return $0
}
return String(codePoints)
}
Am I approaching this problem the wrong way? Is replacing emojis the best solution here? If so, how can I do it?
Swift 5
Don't use this hardcoded way to detect emojis. In Swift 5 you can do it easily
let inputText = "Some 🖐string 😂😂😂 with 👹👹 👹 emoji 🖐"
let textWithoutEmoij = inputText.unicodeScalars
.filter { !$0.properties.isEmojiPresentation }
.reduce("") { $0 + String($1) }
print(textWithoutEmoij) // Some string with emoji
You can use pattern matching (for emoji patterns) to filter out emoji characters from your String.
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F601)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x2702)...UnicodeScalar(0x27B0)]
return self.unicodeScalars
.filter { ucScalar in !(emojiPatterns.contains{ $0 ~= ucScalar }) }
.reduce("") { $0 + String($1) }
}
}
/* example usage */
let str = "I'm gonna do this callenge as soon as I can swing again 😂😂😂\n http://youtu.be/SW_d3fGz1hk"
print(str.emojilessStringWithSubstitution)
/* I'm gonna do this callenge as soon as I can swing again
http://youtu.be/SW_d3fGz1hk */
Note that the above only makes use of the emoji intervals as presented in your question, and is in no way representative for all emojis, but the method is general and can swiftly be extended by including additional emoji intervals to the emojiPatterns array.
I realize reading your question again that you'd prefer substituting emojis with whitespace characters, rather than removing them (which the above filtering solution does). We can achieve this by replacing the .filter operation above with a conditional return .map operation instead, much like in your question
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F600)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x1F300)...UnicodeScalar(0x1F5FF),
UnicodeScalar(0x1F680)...UnicodeScalar(0x1F6FF),
UnicodeScalar(0x2600)...UnicodeScalar(0x26FF),
UnicodeScalar(0x2700)...UnicodeScalar(0x27BF),
UnicodeScalar(0xFE00)...UnicodeScalar(0xFE0F)]
return self.unicodeScalars
.map { ucScalar in
emojiPatterns.contains{ $0 ~= ucScalar } ? UnicodeScalar(32) : ucScalar }
.reduce("") { $0 + String($1) }
}
}
I the above, the existing emoji intervals has been extended, as per your comment to this post (listing these intervals), such that the emoji check is now possibly exhaustive.
Swift 4:
extension String {
func stringByRemovingEmoji() -> String {
return String(self.filter { !$0.isEmoji() })
}
}
extension Character {
fileprivate func isEmoji() -> Bool {
return Character(UnicodeScalar(UInt32(0x1d000))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f77f))!)
|| Character(UnicodeScalar(UInt32(0x2100))!) <= self && self <= Character(UnicodeScalar(UInt32(0x26ff))!)
}
}
Emojis are classified as symbols by Unicode. Character sets are typically used in searching operations. So we will use Character sets a property that is symbols.
var emojiString = "Hey there 🖐, welcome"
emojiString = emojiString.components(separatedBy: CharacterSet.symbols).joined()
print(emojiString)
Output is
Hey there , welcome
Now observe the emoji is replaced by a white space so there is two white space and we replace it by the following way
emojiString.replacingOccurrences(of: " ", with: " ")
The above method replace parameter of: "two white space" to with: "single white space"
Getting all emoji is more complicated than you would think. For more info on how to figure out which characters are emoji, check out this stackoverflow post or this article.
Building on that information, I would propose to use the extension on Character to more easily let us understand which characters are emoji. Then add a String extension to easily replace found emoji with another character.
extension Character {
var isSimpleEmoji: Bool {
guard let firstProperties = unicodeScalars.first?.properties else {
return false
}
return unicodeScalars.count == 1 &&
(firstProperties.isEmojiPresentation ||
firstProperties.generalCategory == .otherSymbol)
}
var isCombinedIntoEmoji: Bool {
return unicodeScalars.count > 1 &&
unicodeScalars.contains {
$0.properties.isJoinControl ||
$0.properties.isVariationSelector
}
}
var isEmoji: Bool {
return isSimpleEmoji || isCombinedIntoEmoji
}
}
extension String {
func replaceEmoji(with character: Character) -> String {
return String(map { $0.isEmoji ? character : $0 })
}
}
Using it would simply become:
"Some string 😂😂😂 with emoji".replaceEmoji(with: " ")
I found that the solutions given above did not work for certain characters such as 🏋️🏻♂️ and 🧰.
To find the emoji ranges, using regex I converted the full list of emoji characters to a file with just hex values. Then I converted them to decimal format and sorted them. Finally, I wrote a script to find the ranges.
Here is the final Swift extension for isEmoji().
extension Character {
func isEmoji() -> Bool {
let emojiRanges = [
(8205, 11093),
(12336, 12953),
(65039, 65039),
(126980, 129685)
]
let codePoint = self.unicodeScalars[self.unicodeScalars.startIndex].value
for emojiRange in emojiRanges {
if codePoint >= emojiRange.0 && codePoint <= emojiRange.1 {
return true
}
}
return false
}
}
For reference, here are the python scripts I wrote to parse the hex strings to integers and then find the ranges.
convert-hex-to-decimal.py
decimals = []
with open('hex.txt') as hexfile:
for line in hexfile:
num = int(line, 16)
if num < 256:
continue
decimals.append(num)
decimals = list(set(decimals))
decimals.sort()
with open('decimal.txt', 'w') as decimalfile:
for decimal in decimals:
decimalfile.write(str(decimal) + "\n")
make-ranges.py
first_line = True
range_start = 0
prev = 0
with open('decimal.txt') as hexfile:
for line in hexfile:
if first_line:
prev = int(line)
range_start = prev
first_line = False
continue
curr = int(line)
if prev + 1000 < curr: # 100 is abitrary to reduce number of ranges
print("(" + str(range_start) + ", " + str(prev) + ")")
range_start = curr
prev = curr
Don't hard-code the range of emojis, use this instead.
func 去除表情符号(字符串:String) -> String {
let 转换为Unicode = 字符串.unicodeScalars//https://developer.apple.com/documentation/swift/string
let 去除表情后的结果 = 转换为Unicode.filter { (item) -> Bool in
let 判断是否表情 = item.properties.isEmoji
return !判断是否表情//是表情就不保留
}
return String(去除表情后的结果)
}
I have a huge text in String.
For example "... value=word. ...". How can I get the string "word" if I know that before I have "value=" and after "."?
for example:
for str in string {
if str == "value=" {
// then get the strings until .
}
}
Thanks!
You can extend String with a kind of sliceBetween method:
import Foundation
extension String {
func sliceFrom(start: String, to: String) -> String? {
guard let s = rangeOfString(start)?.endIndex else { return nil }
guard let e = rangeOfString(to, range: s..<endIndex)?.startIndex else { return nil }
return self[s..<e]
}
}
And you'd use it like this:
"... value=word. ...".sliceFrom("value=", to: ". ") // "word"
NSRegularExpression should solve your issue.
In order to use it, you will need to understand Regex first. In your case, you can use value=[\\w]+[^.]+ as your regex pattern.
The following code will give you a [String] object contains value=allCharacterBeforeFirstPeriod
let regex = try NSRegularExpression(pattern: "value=[\\w]+[^.]+", options: [])
let nsStr = str as NSString
let array = regex.matchesInString(str, options: [], range: NSMakeRange(0, nsStr.length))
let results = array.map({ nsStr.substringWithRange($0.range) })
And then if you only need the value after value=, you can use another map function to do it:
results.map({ $0.stringByReplacingOccurrencesOfString("value=", withString: "") })
I have tested the code with a 10,000 characters String. It finishes in ~0.3 sec
The most straight forward way to do this would be to use NSRegularExpression. Tutorial
Given an input String like this
let text = "key0=value0&key1=value1&key2=value2"
You can organireduce method
let dict = text.characters.split("&").reduce([String:String]()) { (var result, keyValue) -> [String:String] in
let chunks = keyValue.split("=")
guard let first = chunks.first, last = chunks.last else { return result }
let key = String(first)
let value = String(last)
result[key] = value
return result
}
Now everything is stored inside dict and you can easily access it
dict["key2"] // "value2"
I need a way to remove the first character from a string which is a space. I am looking for a method or even an extension for the String type that I can use to cut out a character of a string.
To remove leading and trailing whitespaces:
let trimmedString = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
Swift 3 / Swift 4:
let trimmedString = string.trimmingCharacters(in: .whitespaces)
The correct way when you want to remove all kinds of whitespaces (based on this SO answer) is:
extension String {
var stringByRemovingWhitespaces: String {
let components = componentsSeparatedByCharactersInSet(.whitespaceCharacterSet())
return components.joinWithSeparator("")
}
}
Swift 3.0+ (3.0, 3.1, 3.2, 4.0)
extension String {
func removingWhitespaces() -> String {
return components(separatedBy: .whitespaces).joined()
}
}
EDIT
This answer was posted when the question was about removing all whitespaces, the question was edited to only mention leading whitespaces. If you only want to remove leading whitespaces use the following:
extension String {
func removingLeadingSpaces() -> String {
guard let index = firstIndex(where: { !CharacterSet(charactersIn: String($0)).isSubset(of: .whitespaces) }) else {
return self
}
return String(self[index...])
}
}
This String extension removes all whitespace from a string, not just trailing whitespace ...
extension String {
func replace(string:String, replacement:String) -> String {
return self.replacingOccurrences(of: string, with: replacement, options: NSString.CompareOptions.literal, range: nil)
}
func removeWhitespace() -> String {
return self.replace(string: " ", replacement: "")
}
}
Example:
let string = "The quick brown dog jumps over the foxy lady."
let result = string.removeWhitespace() // Thequickbrowndogjumpsoverthefoxylady.
Swift 3
You can simply use this method to remove all normal spaces in a string (doesn't consider all types of whitespace):
let myString = " Hello World ! "
let formattedString = myString.replacingOccurrences(of: " ", with: "")
The result will be:
HelloWorld!
Swift 4, 4.2 and 5
Remove space from front and end only
let str = " Akbar Code "
let trimmedString = str.trimmingCharacters(in: .whitespacesAndNewlines)
Remove spaces from every where in the string
let stringWithSpaces = " The Akbar khan code "
let stringWithoutSpaces = stringWithSpaces.replacingOccurrences(of: " ", with: "")
You can also use regex.
let trimmedString = myString.stringByReplacingOccurrencesOfString("\\s", withString: "", options: NSStringCompareOptions.RegularExpressionSearch, range: nil)
For Swift 3.0+ see the other answers. This is now a legacy answer for Swift 2.x
As answered above, since you are interested in removing the first character the .stringByTrimmingCharactersInSet() instance method will work nicely:
myString.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
You can also make your own character sets to trim the boundaries of your strings by, ex:
myString.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "<>"))
There is also a built in instance method to deal with removing or replacing substrings called stringByReplacingOccurrencesOfString(target: String, replacement: String). It can remove spaces or any other patterns that occur anywhere in your string
You can specify options and ranges, but don't need to:
myString.stringByReplacingOccurrencesOfString(" ", withString: "")
This is an easy way to remove or replace any repeating pattern of characters in your string, and can be chained, although each time through it has to take another pass through your entire string, decreasing efficiency. So you can do this:
myString.stringByReplacingOccurrencesOfString(" ", withString: "").stringByReplacingOccurrencesOfString(",", withString: "")
...but it will take twice as long.
.stringByReplacingOccurrencesOfString() documentation from Apple site
Chaining these String instance methods can sometimes be very convenient for one off conversions, for example if you want to convert a short NSData blob to a hex string without spaces in one line, you can do this with Swift's built in String interpolation and some trimming and replacing:
("\(myNSDataBlob)").stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "<>")).stringByReplacingOccurrencesOfString(" ", withString: "")
For swift 3.0
import Foundation
var str = " Hear me calling"
extension String {
var stringByRemovingWhitespaces: String {
return components(separatedBy: .whitespaces).joined()
}
}
str.stringByRemovingWhitespaces // Hearmecalling
Swift 4
The excellent case to use the regex:
" this is wrong contained teee xt "
.replacingOccurrences(of: "^\\s+|\\s+|\\s+$",
with: "",
options: .regularExpression)
// thisiswrongcontainedteeext
If you are wanting to remove spaces from the front (and back) but not the middle, you should use stringByTrimmingCharactersInSet
let dirtyString = " First Word "
let cleanString = dirtyString.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
If you want to remove spaces from anywhere in the string, then you might want to look at stringByReplacing...
I'd use this extension, to be flexible and mimic how other collections do it:
extension String {
func filter(pred: Character -> Bool) -> String {
var res = String()
for c in self.characters {
if pred(c) {
res.append(c)
}
}
return res
}
}
"this is a String".filter { $0 != Character(" ") } // "thisisaString"
Yet another answer, sometimes the input string can contain more than one space between words. If you need to standardize to have only 1 space between words, try this (Swift 4/5)
let inputString = " a very strange text ! "
let validInput = inputString.components(separatedBy:.whitespacesAndNewlines).filter { $0.count > 0 }.joined(separator: " ")
print(validInput) // "a very strange text !"
Try functional programming to remove white spaces:
extension String {
func whiteSpacesRemoved() -> String {
return self.filter { $0 != Character(" ") }
}
}
Hi this might be late but worth trying. This is from a playground file. You can make it a String extension.
This is written in Swift 5.3
Method 1:
var str = "\n \tHello, playground "
if let regexp = try? NSRegularExpression(pattern: "^\\s+", options: NSRegularExpression.Options.caseInsensitive) {
let mstr = NSMutableString(string: str)
regexp.replaceMatches(in: mstr, options: [], range: NSRange(location: 0, length: str.count), withTemplate: "")
str = mstr as String
}
Result: "Hello, playground "
Method 2:
if let c = (str.first { !($0 == " " || $0 == "\t" || $0 == "\n") }) {
if let nonWhiteSpaceIndex = str.firstIndex(of: c) {
str.replaceSubrange(str.startIndex ..< nonWhiteSpaceIndex, with: "")
}
}
Result: "Hello, playground "
Code less do more.
"Hello World".filter({$0 != " "}) // HelloWorld
You can try This as well
let updatedString = searchedText?.stringByReplacingOccurrencesOfString(" ", withString: "-")
extension String {
var removingWhitespaceAndNewLines: String {
return removing(.whitespacesAndNewlines)
}
func removing(_ forbiddenCharacters: CharacterSet) -> String {
return String(unicodeScalars.filter({ !forbiddenCharacters.contains($0) }))
}
}
If anybody remove extra space from string e.g = "This is the demo text remove extra space between the words."
You can use this Function in Swift 4.
func removeSpace(_ string: String) -> String{
var str: String = String(string[string.startIndex])
for (index,value) in string.enumerated(){
if index > 0{
let indexBefore = string.index(before: String.Index.init(encodedOffset: index))
if value == " " && string[indexBefore] == " "{
}else{
str.append(value)
}
}
}
return str
}
and result will be
"This is the demo text remove extra space between the words."
Swift 3 version
//This function trim only white space:
func trim() -> String
{
return self.trimmingCharacters(in: CharacterSet.whitespaces)
}
//This function trim whitespeaces and new line that you enter:
func trimWhiteSpaceAndNewLine() -> String
{
return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
Trimming white spaces in Swift 4
let strFirstName = txtFirstName.text?.trimmingCharacters(in:
CharacterSet.whitespaces)
For me, the following line used to remove white space.
let result = String(yourString.filter {![" ", "\t", "\n"].contains($0)})
string = string.filter ({!" ".contains($0) })
OK, this is old but I came across this issue myself and none of the answers above worked besides removing all white spaces which can be detrimental to the functionality of your app. My issue was like so:
["This", " is", " my", " array", " it is awesome"]
If trimmed all white spaces this would be the output:
["This", "is", "my", "array", "itisawesome"]
So I needed to eliminate the leading spacing and simply switching from:
let array = jsonData.components(separatedBy: ",")
To
let array = jsonData.components(separatedBy: ", ")
Fixed the issue. Hope someone find this useful in the future.
This worked for me in swift 5
var myString = " Kwame Ch ef "
myString = myString.replacingOccurrences(of: " ", with: "")
print(myString)
output: Kwame Chef
For anyone looking for an answer to remove only the leading whitespaces out of a string (as the question title clearly ask), Here's an answer:
Assuming:
let string = " Hello, World! "
To remove all leading whitespaces, use the following code:
var filtered = ""
var isLeading = true
for character in string {
if character.isWhitespace && isLeading {
continue
} else {
isLeading = false
filtered.append(character)
}
}
print(filtered) // "Hello, World! "
I'm sure there's better code than this, but it does the job for me.
Swift 5+
Remove All whitespace from prefix(start) of the string, you can use similar for sufix/end of the string
extension String {
func deletingPrefix(_ prefix: String) -> String {
guard self.hasPrefix(prefix) else { return self }
return String(self.dropFirst(prefix.count))
}
func removeWhitespacePrefix() -> String {
let prefixString = self.prefix(while: { char in
return char == " "
})
return self.deletingPrefix(String(prefixString))
}
}
Really FAST solution:
usage:
let txt = " hello world "
let txt1 = txt.trimStart() // "hello world "
let txt2 = txt.trimEnd() // " hello world"
usage 2:
let txt = "rr rrr rrhello world r r r r r r"
let txt1 = txt.trimStart(["r", " "]) // "hello world r r r r r r"
let txt2 = txt.trimEnd(["r", " "]) // "rr rrr rrhello world"
if you need to remove ALL whitespaces from string:
txt.replace(of: " ", to: "")
public extension String {
func trimStart(_ char: Character) -> String {
return trimStart([char])
}
func trimStart(_ symbols: [Character] = [" ", "\t", "\r", "\n"]) -> String {
var startIndex = 0
for char in self {
if symbols.contains(char) {
startIndex += 1
}
else {
break
}
}
if startIndex == 0 {
return self
}
return String( self.substring(from: startIndex) )
}
func trimEnd(_ char: Character) -> String {
return trimEnd([char])
}
func trimEnd(_ symbols: [Character] = [" ", "\t", "\r", "\n"]) -> String {
var endIndex = self.count - 1
for i in (0...endIndex).reversed() {
if symbols.contains( self[i] ) {
endIndex -= 1
}
else {
break
}
}
if endIndex == self.count {
return self
}
return String( self.substring(to: endIndex + 1) )
}
}
/////////////////////////
/// ACCESS TO CHAR BY INDEX
////////////////////////
extension StringProtocol {
subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
subscript(range: Range<Int>) -> SubSequence {
let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
return self[startIndex..<index(startIndex, offsetBy: range.count)]
}
subscript(range: ClosedRange<Int>) -> SubSequence {
let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
return self[startIndex..<index(startIndex, offsetBy: range.count)]
}
subscript(range: PartialRangeFrom<Int>) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] }
subscript(range: PartialRangeThrough<Int>) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] }
subscript(range: PartialRangeUpTo<Int>) -> SubSequence { self[..<index(startIndex, offsetBy: range.upperBound)] }
}
Technically not an answer to the original question but since many posts here give an answer for removing all whitespace, here is an updated, more concise version:
let stringWithouTAnyWhitespace = string.filter {!$0.isWhitespace}
Swift 3 version of BadmintonCat's answer
extension String {
func replace(_ string:String, replacement:String) -> String {
return self.replacingOccurrences(of: string, with: replacement, options: NSString.CompareOptions.literal, range: nil)
}
func removeWhitespace() -> String {
return self.replace(" ", replacement: "")
}
}
To remove all spaces from the string:
let space_removed_string = (yourstring?.components(separatedBy: " ").joined(separator: ""))!
let str = "tHIS is A test"
let swapped_case = "This IS a TEST"
Swift noob here, how to do the second statement programatically?
This function works with all upper/lowercase characters
defined in Unicode, even those from "foreign" languages such as Ä or ć:
func swapCases(_ str : String) -> String {
var result = ""
for c in str.characters { // Swift 1: for c in str {
let s = String(c)
let lo = s.lowercased() //Swift 1 & 2: s.lowercaseString
let up = s.uppercased() //Swift 1 & 2: s.uppercaseString
result += (s == lo) ? up : lo
}
return result
}
Example:
let str = "tHIS is a test ÄöÜ ĂćŒ Α" // The last character is a capital Greek Alpha
let swapped_case = swapCases(str)
print(swapped_case)
// This IS A TEST äÖü ăĆœ α
Use switch statement in-range checks to determine letter case, and use NSString-bridged methods to convert accordingly.
let str = "tHIS is A test"
let swapped_case = "This IS a TEST"
func swapCase(string: String) -> String {
var swappedCaseString: String = ""
for character in string {
switch character {
case "a"..."z":
let uppercaseCharacter = (String(character) as NSString).uppercaseString
swappedCaseString += uppercaseCharacter
case "A"..."Z":
let lowercaseCharacter = (String(character) as NSString).lowercaseString
swappedCaseString += lowercaseCharacter
default:
swappedCaseString += String(character)
}
}
return swappedCaseString
}
swapCase(str)
I'm a bit too late but this works too :-)
let str = "tHIS is A test"
var res = ""
for c in str {
if contains("ABCDEFGHIJKLMNOPQRSTUVWXYZ", c) {
res += "\(c)".lowercaseString
} else {
res += "\(c)".uppercaseString
}
}
res
In Swift 5 I achieved it by creating a function which iterates through each character of the string, and using string methods to change each character I appended each character back into a new variable:
func reverseCase(string: String) -> String {
var newCase = ""
for char in string {
if char.isLowercase {
newCase.append(char.uppercased())
}
else if char.isUppercase {
newCase.append(char.lowercased())
}
else {
newCase.append(char)
}
}
return newCase
}
Then just pass your string through to the function when you call it in a print statement:
print(reverseCase(string: str))
You already have plenty of good succinct answers but here’s an over-elaborate one for fun.
Really this is a job for map – iterate over a collection (in this case String) and do a thing to each element (here, each Character). Except map takes any collection, but only gives you back an array, which you’d have to then turn into a String again.
But here’s a version of map that, given an extensible collection, gives you back that same kind of extensible collection.
(It does have the limitation of needing both collections to contain the same type, but that’s fine for strings. You could make it return a different type, but then you’d have to tell it which type you wanted i.e. map(s, transform) as String which would be annoying)
func map<C: ExtensibleCollectionType>(source: C, transform: (C.Generator.Element) -> C.Generator.Element) -> C {
var result = C()
for elem in source {
result.append(transform(elem))
}
return result
}
Then to write the transform function, first here’s an extension to character similar to the other answers. It does seem quite unsatisfying that you have to convert to a string just to uppercase a character, is there really no good (international characterset-friendly) way to do this?
extension Character {
var uppercaseCharacter: Character {
let s = String(self).uppercaseString
return s[s.startIndex]
}
var lowercaseCharacter: Character {
let s = String(self).lowercaseString
return s[s.startIndex]
}
}
And the function to flip the case. What I wonder is whether this pattern matching is international-friendly. It seems to be – "A"..."Z" ~= "Ä" returns true.
func flipCase(c: Character) -> Character {
switch c {
case "A"..."Z":
return c.lowercaseCharacter
case "a"..."z":
return c.uppercaseCharacter
default:
return c
}
}
Finally:
let s = map("Hello", flipCase)
// s is a String = "hELLO"
I hope this helps. inputString and resultString are the input and output respectively.
let inputString = "Example"
let outputString = inputString.characters.map { (character) -> Character in
let string = String(character)
let lower = string.lowercased()
let upper = string.uppercased()
return (string == lower) ? Character(upper) : Character(lower)
}
let resultString = String(outputString)