How to extract emojis from a string? - ios

I'm looking for a way to take a String and extract Emoji characters.
I know that Emojis are part of Unicode so I need to remove a certain subset of Unicode characters. I don't really know where to start.

The Set of Emoji characters
First of all we need a Set containing the unicode values representing the emoji.
Disclaimer
For this answer I am using the range of Emoticons (1F601-1F64F) and Dingbats (2702-27B0) to show you the solution. However keep in mind that you should add further ranges depending on your needs.
Extending Character
Now we need a way to calculate the Unicode Scalar Code Point of a Character. For this I am using the solution provided here.
extension Character {
private var unicodeScalarCodePoint: Int {
let characterString = String(self)
let scalars = characterString.unicodeScalars
return Int(scalars[scalars.startIndex].value)
}
}
Extending String
This extension does allow you to extract the emoji characters from a String.
extension String {
var emojis:[Character] {
let emojiRanges = [0x1F601...0x1F64F, 0x2702...0x27B0]
let emojiSet = Set(emojiRanges.flatten())
return self.characters.filter { emojiSet.contains($0.unicodeScalarCodePoint) }
}
}
Testing
let sentence = "๐Ÿ˜ƒ hello world ๐Ÿ™ƒ"
sentence.emojis // ["๐Ÿ˜ƒ", "๐Ÿ™ƒ"]

Related

Checking if first 3 characters of UI TextField are numeric in Swift

I want to check if the first three characters entered in the UI Textfield are numbers. I could do this easily in Python, but for some reason in Swift it's a pain.
Here's my python if statements (which I want to 'translate' into swift as it works):
str = "123abc"
if str.isdigit():
if str[:3]:
print(str)
and here's my swift code
#IBOutlet weak var input: UITextField!
#IBAction func checkBarcodeRegion(_ sender: UIButton)
{
let text: String = input.text!
if text.prefix(3)
{
//if numeric
//do something
}
}
Can't get this to work. Any help appreciated.
Three alternatives:
Create an Int from the substring and check if for non-nil.
let isNumeric1 = Int(text.prefix(3)) != nil
Remove all digits from the substring with Regular Expression and check for empty string
let isNumeric2 = text.prefix(3).replacingOccurrences(of: "[0-9]", with: "", options: .regularExpression).isEmpty
Split the substring by the decimalDigits character set and check if the number of components is 4
let isNumeric3 = text.prefix(3).components(separatedBy: .decimalDigits).count == 4
Using prefix is the right direction to go. You can use allSatisfy after that to check if they are all digits. One of the ways to check for digits is to check if the unicode scalar is in the CharacterSet.decimalDigits set.
text.unicodeScalars.prefix(3)
.allSatisfy(CharacterSet.decimalDigits.contains)
This will check for all the digits in Unicode, including the ones that the Indians and Arabic use.
If by digits you mean the ASCII 0123456789, then you could use:
text.prefix(3).allSatisfy(("0"..."9").contains)

String characters (marks) are not counted correctlly

Characters as (eฬ) or in arabic (ุฏูŒ) are counted as one in a string, how do I make it recognise the mark as a character?
It should be like (ุฏ) is a character and (ูŒ) is another character.
I don't want to use NSString because I'm using (startIndex) which is not supported in NSString as far as I know.
Thank you
Iโ€™m by no means sufficiently knowledgeable in this area to be confident there arenโ€™t some gotchas from this approach, but this appears to do what youโ€™re looking for:
let s = "รฉุฏูŒ"
let separated = map(s.unicodeScalars) { Character($0) }
println(" , ".join(separated.map(toString)))
// prints "e , ฬ , ุฏ , ูŒ"
Note, if you create a new string from a sequence of those separated characters, it will recompose them:
println(String(separated)) // prints
// prints "รฉุฏูŒ"

How to list (almost) all emojis in Swift for iOS 8 without using any form of lookup tables?

I'm playing around with emojis in Swift using Xcode playground for some simple iOS8 apps. For this, I want to create something similar to a unicode/emoji map/description.
In order to do this, I need to have a loop that would allow me to print out a list of emojis. I was thinking of something along these lines
for i in 0x1F601 - 0x1F64F {
var hex = String(format:"%2X", i)
println("\u{\(hex)}") //Is there another way to create UTF8 string corresponding to emoji
}
But the println() throws an error
Expected '}'in \u{...} escape sequence.
Is there a simple way to do this which I am missing?
I understand that not all entries will correspond to an emoji. Also, I'm able create a lookup table with reference from http://apps.timwhitlock.info/emoji/tables/unicode, but I would like a lazy/easy method of achieving the same.
You can loop over those hex values with a Range: 0x1F601...0x1F64F and then create the Strings using a UnicodeScalar:
for i in 0x1F601...0x1F64F {
guard let scalar = UnicodeScalar(i) else { continue }
let c = String(scalar)
print(c)
}
Outputs:
๐Ÿ˜๐Ÿ˜‚๐Ÿ˜ƒ๐Ÿ˜„๐Ÿ˜…๐Ÿ˜†๐Ÿ˜‡๐Ÿ˜ˆ๐Ÿ˜‰๐Ÿ˜Š๐Ÿ˜‹๐Ÿ˜Œ๐Ÿ˜๐Ÿ˜Ž๐Ÿ˜๐Ÿ˜๐Ÿ˜‘๐Ÿ˜’๐Ÿ˜“๐Ÿ˜”๐Ÿ˜•๐Ÿ˜–๐Ÿ˜—๐Ÿ˜˜๐Ÿ˜™๐Ÿ˜š๐Ÿ˜›๐Ÿ˜œ๐Ÿ˜๐Ÿ˜ž๐Ÿ˜Ÿ๐Ÿ˜ ๐Ÿ˜ก๐Ÿ˜ข๐Ÿ˜ฃ๐Ÿ˜ค๐Ÿ˜ฅ๐Ÿ˜ฆ๐Ÿ˜ง๐Ÿ˜จ๐Ÿ˜ฉ๐Ÿ˜ช๐Ÿ˜ซ๐Ÿ˜ฌ๐Ÿ˜ญ๐Ÿ˜ฎ๐Ÿ˜ฏ๐Ÿ˜ฐ๐Ÿ˜ฑ๐Ÿ˜ฒ๐Ÿ˜ณ๐Ÿ˜ด๐Ÿ˜ต๐Ÿ˜ถ๐Ÿ˜ท๐Ÿ˜ธ๐Ÿ˜น๐Ÿ˜บ๐Ÿ˜ป๐Ÿ˜ผ๐Ÿ˜ฝ๐Ÿ˜พ๐Ÿ˜ฟ๐Ÿ™€๐Ÿ™๐Ÿ™‚๐Ÿ™ƒ๐Ÿ™„๐Ÿ™…๐Ÿ™†๐Ÿ™‡๐Ÿ™ˆ๐Ÿ™‰๐Ÿ™Š๐Ÿ™‹๐Ÿ™Œ๐Ÿ™๐Ÿ™Ž๐Ÿ™
If you want all the emoji, just add another loop over an array of ranges:
// NOTE: These ranges are still just a subset of all the emoji characters;
// they seem to be all over the place...
let emojiRanges = [
0x1F601...0x1F64F,
0x2702...0x27B0,
0x1F680...0x1F6C0,
0x1F170...0x1F251
]
for range in emojiRanges {
for i in range {
guard let scalar = UnicodeScalar(i) else { continue }
let c = String(scalar)
print(c)
}
}
For those asking, the full list of available emojis can be found here: https://www.unicode.org/emoji/charts/full-emoji-list.html
A parsable list of unicode sequences for all emojis can be found in the emoji-sequences.txt file under the directory for the version you're interested in here: http://unicode.org/Public/emoji/
As of 9/15/2021 the latest version of the emoji standard available on Apple devices is 13.1.

Swift Converting Character to String

I have an issue with converting character type to String type. First of all, I have below extension of String for finding nth character within String.
extension String {
func characterAtIndex(index: Int) -> Character? {
var cur = 0
for char in self {
if cur == index {
return char
}
cur++
}
return nil
}
}
I get what I want with this class extension. However when I use that nth character for title of my custom UIButton, gives an error. My Uibutton Class is
class hareketliHarfler: UIButton {
init(frame: CGRect) {
super.init(frame: frame)
// Initialization code
}
func getLetter(letter:String!){
self.titleLabel.text = letter
}
}
The error show when i try to access "getLetter(letter:String)" function. Here is example of main view Controller codes:
var harfim = hareketliHarfler(frame: CGRectMake(100,100,100,100))
var str="This is my String"
var bufi=str.characterAtIndex(3)
harfim.getLetter(bufi as AnyObject) ****
In * section I try .getLetter(bufi), .getLetter(bufi as String) also I try to change parameter type of function. Look like: func getLetter(letter:Character!) or func getLetter(letter:AnyObject!)...etc
Didn't find a way. Need a help on that. Thank you
How about the simple
String(theCharacter)
Works in Swift 4 and Swift 5
Your problem is quite simple: your characterAtIndex function returns a Character, and self.titleLabel.text is a String. You can't convert between the two implicitly. The easiest way would be to turn the Character into a String using the String initialiser:
// ch will be Character? type.
if let ch = str.characterAtIndex(3) {
// Initialise a new String containing the single character 'ch'
harfim.getLetter(String(ch))
} else {
// str didn't have a third character.
}
Unlike other solutions, this is safe for unusual Unicode characters, and won't initialise a potentially large array or iterate the whole String just to get the third character.
Change this:
var bufi=str.characterAtIndex(3)
harfim.getLetter(bufi as AnyObject)
to this:
harfim.getLetter(String(Array(str)[3]))
So what happening here:
we create an array from our string. Array elements are symbols from original string. Such break down correctly tracks symbols that are presented with a sequences of two or more code points. E.g. emoji or flag as noted by #MartinR.
We access element at 4-th position.
Note that as we crate an array from initial string then performance wise is better to use this method only with short strings and avoid it in oft-repeated routines. But in your case it seems to be OK.
Can also use Character(text).isNumber if you want to get localised numbers.
Reference:
https://developer.apple.com/documentation/swift/character/3127015-isnumber

Voice over doesn't read phone number properly

I have phone number in below format
1-1xx-2xx-9565
Currently VO read it as "One (pause) One x x (pause) two x x (pause) minus nine thousand five hundred sixty five".
VO should read it as "One (pause) One x x (pause) two x x (pause) nine five six five".
What could be the problem? Is this wrong phone format?
Let's break down what is happening. VoiceOver doesn't know that the text you are presenting is a phone number and treats it like a sentence of text. In that text it tries to find distinct components and read them appropriately. For example, the text "buy 60 cantaloupes" has 3 components, "buy", "60", and "cantaloupes". The first is text and is read as text, the second is purely numerical and is best read out as "sixty", and the third is read as text.
Applying the same logic to your phone number.
(I'm not talking about actual implementation, just reasoning.)
If you read 1-1xx-2xx-9565 from the left to the right then the first distinct component is "1" which in it self is numerical and is read as "1". If the phone number would have started with "12-1xx" then the first component would have been read as "twelve" because its purely numerical.
The next component is "1xx" or "-1xx" depending on how you look at it. In either case it is a combination of numbers and letters, e.g. it is not purely numerical and is thus read out as text. If you include the "-" in that component is interpreted as a hyphen which isn't read out. That is why the the "-" is never read out for that component. The next component ("-2xx") is treated in the same way.
The final component is "-9565" which turns out to be a valid number. As seen in the cantaloupe sentence, VoiceOver reads this as a number in which case the "-" is no longer interpreted as a hyphen but as a "minus sign".
Getting VoiceOver to read your own text
On any label, view or other element in your application that is used with Voice Over, you can supply your own "accessibility label" when you know more about how you want the text to be read. This is done by simply assigning your own string to the accessibilityLabel property.
Now, you can create a suitable string in many different ways, a very simple one in your case would be to just add spaces everywhere so that each number is read individually. However, it seems a bit fragile to me, so I went ahead and used a number formatter to translate the individual numbers to their textual representations.
NSString *phoneNumber = #"1-1xx-2xx-9565";
// we want to know if a character is a number or not
NSCharacterSet *numberCharacters = [NSCharacterSet characterSetWithCharactersInString:#"0123456789"];
// we use this formatter to spell out individual numbers
NSNumberFormatter *spellOutSingleNumber = [NSNumberFormatter new];
spellOutSingleNumber.numberStyle = NSNumberFormatterSpellOutStyle;
NSMutableArray *spelledOutComonents = [NSMutableArray array];
// loop over the phone number add add the accessible variants to the array
[phoneNumber enumerateSubstringsInRange:NSMakeRange(0, phoneNumber.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
// check if it's a number
if ([substring rangeOfCharacterFromSet:numberCharacters].location != NSNotFound) {
// is a number
NSNumber *number = #([substring integerValue]);
[spelledOutComonents addObject:[spellOutSingleNumber stringFromNumber:number]];
} else {
// is not a number
[spelledOutComonents addObject:substring];
}
}];
// finally separate the components with spaces (so that the string doesn't become "ninefivesixfive".
NSString *yourAccessiblePhoneNumber = [spelledOutComonents componentsJoinedByString:#" "];
The result when I ran this was
one - one x x - two x x - nine five six five
If you need to do other modifications to your phone numbers to get them to read appropriately then you can do that. I suspect that you will use this is more than one place in your app so creating a custom NSFormatter might be a good idea.
Edit
On iOS 7 you can also use the UIAccessibilitySpeechAttributePunctuation attribute on an attributes string to change how it is pronounced.
Speech Attributes for Attributed Strings
Attributes that you can apply to text in an attributed string to modify how that text is pronounced.
UIAccessibilitySpeechAttributePunctuation
The value of this key is an NSNumber object that you should interpret as a Boolean value. When the value is YES, all punctuation in the text is spoken. You might use this for code or other text where the punctuation is relevant.
Available in iOS 7.0 and later.
Declared in UIAccessibilityConstants.h.
As of iOS 13 you can use a - NSAttributedString.Key.accessibilitySpeechSpellOut as a accessibilityAttributedLabel to make VoiceOver read each letter of the provided string (or a range of string).
So for example:
yourView.accessibilityAttributedLabel = NSAttributedString(string: yourText, attributes: [.accessibilitySpeechSpellOut: true])
If you want to spell all characters individually, a simple solution is to separate the characters by a comma ",".
You can use a String extension to convert the string:
extension String
{
/// Returns string suitable for accessibility (voice over). All characters will be spelled individually.
func stringForSpelling() -> String
{
return stringBySeparatingCharactersWithString(",")
}
/// Inserts a separator between all characters
func stringBySeparatingCharactersWithString(separator: String) -> String
{
var s = ""
// Separate all characters
let chars = self.characters.map({ String($0) })
// Append all characters one by one
for char in chars {
// If there is already a character, append separator before appending next character
if s.characters.count > 0 {
s += separator
}
// Append next character
s += char
}
return s
}
}
And then use it in the code like this:
myLabel.accessibilityLabel = myString.stringForSpelling()
Just Add a comma to each digit of the last number and after the last digit as well,. this will make sure voice over reads the last number as same as previous numbers.
example your number :- 1-1xx-2xx-9565
accessibility label :- 1-1xx-2xx-9,5,6,5,
Here is the code in Swift
public func retrieveAccessiblePhoneNumber(phoneNumber: String) -> String {
// We want to know if a character is a number or not
let characterSet = NSCharacterSet(charactersInString: "0123456789")
// We use this formatter to spell out individual numbers
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = .SpellOutStyle
var spelledOutComponents = [String]()
let range = Range<String.Index>(start: phoneNumber.startIndex, end: phoneNumber.endIndex)
// Loop over the phone number add add the accessible variants to the array
phoneNumber.enumerateSubstringsInRange(range,
options: NSStringEnumerationOptions.ByComposedCharacterSequences) { (substring, substringRange, enclosingRange, stop) -> () in
// Check if it's a number
if let substr = substring where substr.rangeOfCharacterFromSet(characterSet) != nil {
if let number = Int(substr) {
// Is a number
let nsNumber = NSNumber(integer: number)
spelledOutComponents.append(numberFormatter.stringFromNumber(nsNumber)!)
}
} else {
// Is not a number
spelledOutComponents.append(substring!)
}
}
// Finally separate the components with spaces (so that the string doesn't become "ninefivesixfive".
return spelledOutComponents.joinWithSeparator(" ")
}

Resources