String basics with Swift - swift

Just trying to remove the first character from a string in Swift. I use the code written below, but the second line keeps crashing my application.
Is this not the correct way to unwrap a String Index? What is?
var tempText = text
let toRemove = tempText?.startIndex ?? String.Index(0)
tempText?.remove(at: toRemove)

You can use Collection method dropFirst:
if let text = text { // you need also to unwrap your optional
let tempText = String(text.characters.dropFirst()) // And initialize a new String with your CharacterView
}
In Swift 4 String conforms to Collection so you can use it directly on your string:
if let text = text {
let tempText = text.dropFirst() // "bc"
}

You are initializing a String.Index type instead of getting the index of the tempText string.
Moreover, startIndex is not an optional, tempText, however, is.
You should check if tempText exists and is not empty (you can simply do this with an if let), and remove the character at startIndex if it matches those conditions.
var tempText = text
if let toRemove = tempText?.startIndex {
tempText?.remove(at: toRemove)
}

If you are using swift 4, you can use:
var tempText = text.dropFirst()

Related

How can I convert unicode values in a string "U+XXXXXX" to the corresponding characters in Swift?

I accept from backend the following json
{
"id": "f33919f6-3554-4246-9e78-bca3a690c119",
"title": "Category3",
"slug": "category3",
"hex_up": "#eb4034",
"hex_down": "#80302a",
"emoji": "U+1F602",
"parent_id": "aa3f651b-f068-4ae1-a9d8-a18a9945b111"
}
There is a field "emoji": "U+1F602",
I need show emoji icon like 😃 in UILabel
I tried to google and found results like
let scalarValue = UnicodeScalar(emojiString)
let myString = String(scalarValue!)
Unfortunately app crashes at the second line.
Thanks for your answers.
There's no U+... syntax in Swift. (There is a \u{...} syntax that does the same thing, but it's not necessary here.)
You'll need to parse the String yourself:
func parseUnicode(_ string: String) -> String? {
guard string.hasPrefix("U+"), // Make sure it's a U+ string
let value = Int(string.dropFirst(2), radix: 16), // Convert to Int
let scalar = UnicodeScalar(value) // Convert to UnicodeScalar
else { return nil }
return String(scalar) // Convert to String
}
if let myString = parseUnicode(emoji) { ... }
Don't use ! here. The U+... string may be invalid, and you wouldn't want to crash in that case.
You can simply apply a string transform from "Hex/Unicode" to "Any" (a set of all characters):
"U+1F602".applyingTransform(.init("Hex/Unicode-Any"), reverse: false) // "😂"
or as instance properties of StringProtocol to encode/decode from/to hexa unicode:
extension StringTransform {
static let unicodeToAny: Self = .init("Hex/Unicode-Any")
static let anyToUnicode: Self = .init("Any-Hex/Unicode")
}
extension StringProtocol {
var decodingHexaUnicode: String {
applyingTransform(.unicodeToAny, reverse: false)!
}
var encodingHexaUnicode: String {
applyingTransform(.anyToUnicode, reverse: false)!
}
}
Usage:
let hexaUnicode = "U+1F602"
let emoji = hexaUnicode.decodingHexaUnicode // "😂"
let unicodeFromEmoji = emoji.encodingHexaUnicode // "U+1F602"
The reason your app crashed is due to the fact that the scalarValue you attempted to initialize is nil, and you're force-unwrapping using (!) that nil value on line 2. Rob's answer shows how to unwrap the optional safely.
You can get the emoji by using the value following the U+. So you'll need to drop the first two characters of the string. So use this code to accomplish that:
let parsedEmoji = emojiString.substring(from:2)
Now you'll convert that emoji unicode using the code below.
let emoji = String(UnicodeScalar(Int(parsedEmojiHex,radix: 16)!)!)
print(emoji)

How to get the range of the first line in a string?

I would like to change the formatting of the first line of text in an NSTextView (give it a different font size and weight to make it look like a headline). Therefore, I need the range of the first line. One way to go is this:
guard let firstLineString = textView.string.components(separatedBy: .newlines).first else {
return
}
let range = NSRange(location: 0, length: firstLineString.count)
However, I might be working with quite long texts so it appears to be inefficient to first split the entire string into line components when all I need is the first line component. Thus, it seems to make sense to use the firstIndex(where:) method:
let firstNewLineIndex = textView.string.firstIndex { character -> Bool in
return CharacterSet.newlines.contains(character)
}
// Then: Create an NSRange from 0 up to firstNewLineIndex.
This doesn't work and I get an error:
Cannot convert value of type '(Unicode.Scalar) -> Bool' to expected argument type 'Character'
because the contains method accepts not a Character but a Unicode.Scalar as a parameter (which doesn't really make sense to me because then it should be called a UnicodeScalarSet and not a CharacterSet, but nevermind...).
My question is:
How can I implement this in an efficient way, without first slicing the whole string?
(It doesn't necessarily have to use the firstIndex(where:) method, but appears to be the way to go.)
A String.Index range for the first line in string can be obtained with
let range = string.lineRange(for: ..<string.startIndex)
If you need that as an NSRange then
let nsRange = NSRange(range, in: string)
does the trick.
You can use rangeOfCharacter, which returns the Range<String.Index> of the first character from a set in your string:
extension StringProtocol where Index == String.Index {
var partialRangeOfFirstLine: PartialRangeUpTo<String.Index> {
return ..<(rangeOfCharacter(from: .newlines)?.lowerBound ?? endIndex)
}
var rangeOfFirstLine: Range<Index> {
return startIndex..<partialRangeOfFirstLine.upperBound
}
var firstLine: SubSequence {
return self[partialRangeOfFirstLine]
}
}
You can use it like so:
var str = """
some string
with new lines
"""
var attributedString = NSMutableAttributedString(string: str)
let firstLine = NSAttributedString(string: String(str.firstLine))
// change firstLine as you wish
let range = NSRange(str.rangeOfFirstLine, in: str)
attributedString.replaceCharacters(in: range, with: firstLine)

how to get the index value in dictionary using swift

As I am new to swift programming language .I am using the dictionary of two items now i need to take the index path of particular dictionary value .I am using the following code
var dictionaryitems = ["computer":"something to make work easy","pen":"used for writing something"]
print(dictionaryitems["pen"])
Use firstIndex for this
let index = dictionaryitems.firstIndex(where: {$0.key == "pen"})
you can get index of key or value by
let index = Array(Dictionary.keys).index(of: key/value)
by this you will get an optional value which you can unwrap using if-let or guard statement for further use
var dictionaryitems = ["computer":"something to make work easy","pen":"used for writing something"]
if let index = dictionaryitems.index(forKey: "pen") {
print(dictionaryitems[index].key, ":", dictionaryitems[index].value)
}
This is an example how you can get the index of a dictionary using swift >
if let index = carDataArray?.index(where: {$0["carName"] as! String == "BMW"}) {
print("Car Found")
}

Cannot convert value of type Substring to expected argument type String - Swift 4

Trying to get substring of String and append it to array of Strings:
var stringToSplit = "TEST TEXT"
var s = [String]()
let subStr = anotherString[0 ..< 6]
s.append(subStr) // <---- HERE I GET THE ERROR
As #Leo Dabus mentioned, you need to initialize a new String with your substring:
Change:
s.append(subStr)
To:
s.append(String(subStr))
my two cents for serro in different context.
I was trying to get an array of "String" splitting a string.
"split" gives back "Substring", for efficiency reason (as per Swift.org litre).
So I ended up doing:
let buffer = "one,two,three"
let rows = buffer.split(separator:",")
let realStrings = rows.map { subString -> String in
return String(subString)
}
print(realStrings)
Ape can help someone else.

String may not be indexed with 'Int' Swift3

i'm trying to display the first letter of my contact name in uppercase:
var firstLetter: String? = !(contact?.firstName == "") ? (contact?.firstName as? String)?.substring(to: 1).uppercased() : (contact?.lastName as? String)?.substring(to: 1).uppercased()
but i have got this error:
String may not be indexed with 'Int', it has variable size elements
Try this:
if let contact = contact
{
var firstLetter = !contact.firstName.isEmpty ? contact?.firstName.characters.first?.description.uppercased() : contact?.lastName.characters.first?.description.uppercased()
}
I will be able to help you more if you tell the type/definition of "contact".
Try this function to get first letter in capital format. Before the call check if your string is empty then don't call as you are doing in your code
func capitalizeFirst() -> String {
let firstIndex = self.index(startIndex, offsetBy: 1)
return self.substring(to: firstIndex).capitalized + self.substring(from: firstIndex).lowercased()
}
If you just need to find the first character, you can use contact?.uppercased().characters.first.
In general, to get the n-th character in uppercase, use contact?.uppercased()[contact?.index(contact?.startIndex, offsetBy: n)]
To avoid the long chain of indirections and make the function a bit clearer you could also write it like this :
firstLetter = ( (contact?.firstName ?? "") + (contact?.lastName ?? "") )
.characters.first?.description.uppercased()
var firstLetter = (contact?.firstName.characters.first ?? contact?.lastName.characters.first)?.description.uppercased()