Swift 4 saying these two lines are deprecated... tried everything [duplicate] - swift

characters - an instance property of String, is deprecated from with Xcode 9.1
It was very useful to get a substring from String by using the characters property but now it has been deprecated and Xcode suggests to use substring. I've tried to check around SO questions and apple developer tutorials/guidelines for the same. But could not see any solution/alternate as suggested.
Here is warning message:
'characters' is deprecated: Please use String or Substring
I've so many string operations are performed/handled using property characters.
Anyone have any idea/info about this update?

Swift 4 introduced changes on string API.
You can just use !stringValue.isEmpty instead of stringValue.characters.count > 0
for more information you get the sample from here
for e.g
let edit = "Summary"
edit.count // 7

Swift 4 vs Swift 3 examples:
let myString = "test"
for char in myString.characters {print(char) } // Swift 3
for char in myString { print(char) } // Swift 4
let length = myString.characters.count // Swift 3
let length = myString.count // Swift 4

One of the most common cases for manipulating strings is with JSON responses. In this example I created an extension in my watch app to drop the last (n) characters of a Bitcoin JSON object.
Swift 3:
func dropLast(_ n: Int = 0) -> String {
return String(characters.dropLast(n))
Xcode 9.1 Error Message:
'characters' is deprecated: Please use String or Substring directly
Xcode is telling us to use the string variable or method directly.
Swift 4:
func dropLast(_ n: Int = 0) -> String {
return String(dropLast(n))
}
Complete Extension:
extension String {
func dropLast(_ n: Int = 0) -> String {
return String(dropLast(n))
}
var dropLast: String {
return dropLast()
}
}
Call:
print("rate:\(response.USDRate)")
let literalMarketPrice = response.USDRate.dropLast(2)
print("literal market price: \(literalMarketPrice)")
Console:
//rate:7,101.0888 //JSON float
//literal market price: 7,101.08 // JSON string literal
Additional Examples:
print("Spell has \(invisibleSpellName.count) characters.")
return String(dropLast(n))
return String(removeLast(n))
Documentation:
You'll often be using common methods such as dropLast() or removeLast() or count so here is the explicit Apple documentation for each method.
droplast()
removelast()
counting characters

Use this characters because String stopped being a collection in Swift 2.0. However this is still valid code in Swift 4 but is no longer necessary now that String is a Collection again.
For example a Swift 4 String now has a direct count property that gives the character count:
// Swift 4
let spString = "Stack"
spString.count // 5
Examples for String and SubString.
String
Swift 4 String now directly get Element that gives the first character of String: (string.characters.first)
let spString = "Stack"
let firstElement = spString.first //S
SubString
Using SubString get first character.
let spstring = "Welcome"
let indexStartOfText = spstring.index(spstring.startIndex, offsetBy: 1)
let sub = spstring.substring(to: indexStartOfText)
print(sub) //W

That warning is just a top of the iceberg, there were a loot of string changes, strings are again a collection of characters, but we got soemthing new and cool, subStrings :)
This is a great read about this:
https://useyourloaf.com/blog/updating-strings-for-swift-4/

Just remove characters
For example:
stringValue.characters.count
to
stringValue.count

You can also use this code for dictionary grouping without using { $0.characters.first! }.
let cities = ["Shanghai": 24_256_800, "Karachi": 23_500_000, "Beijing": 21_516_000, "Seoul": 9_995_000]
let groupedCities = Dictionary(grouping: cities.keys) { $0.first! }
print(groupedCities)

func validatePhoneNumber(number:String) -> Bool{
if number.count < 10. //deprecated ->(number.characters.count)
{
return false;
}else{
return true;
}
}
You use directly .count and characters is deprecated.

Related

What am I missing in my random lowercase string generator function ? Using Swift 5.5

I have a function I use to generate random strings for email addresses or passwords (for example). It was originally set like this:
static func random(length: Int) -> String {
let characters = "abcdefghijklmnopqrstuvwxyz"
return String((0..<length).map { _ in characters.randomElement()! })
}
So I changed it to this:
static func random(length: Int) -> String {
let characters = CharacterSet.lowercaseLetters
return String((0..<length).map { _ in characters.randomElement()! })
}
But I get an error saying "Value of type 'CharacterSet' has no member 'randomElement'.
I'm very new to Swift and I've done a lot of searching and so far I haven't found a good solution. I want to keep this function short and sweet. And I've been at this for a while now. Any help would be greatly appreciated! Please let me know if any more context is needed.
Edit: My question got closed because it was seen as a duplicate but, I looked at the solution page and tried to apply it to my issue and still no resolution. I'm not sure if that's because the previous answers are from 2015 and older or they were for obj-c
As I said in my comment of the possible duplicated post you can use that extension from the accepted answer to get all characters from a CharacterSet and get a randomElement from the resulting collection. Also as stated in the accepted answer some characters may not be present in the font used to display the result:
extension CharacterSet {
var array: [Character] {
var result: [Character] = []
for plane: UInt8 in 0...16 where hasMember(inPlane: plane) {
for unicode in UInt32(plane) << 16 ..< UInt32(plane + 1) << 16 {
if let uniChar = UnicodeScalar(unicode), contains(uniChar) {
result.append(Character(uniChar))
}
}
}
return result
}
}
let lowercaseLettersArray = CharacterSet.lowercaseLetters.array
let randomCharacter = lowercaseLettersArray.randomElement()! // "ᵳ"

Count the number of lines in a Swift String

After reading a medium sized file (about 500kByte) from a web-service I have a regular Swift String (lines) originally encoded in .isolatin1. Before actually splitting it I would like to count the number of lines (quickly) in order to be able to initialise a progress bar.
What is the best Swift idiom to achieve this?
I came up with the following:
let linesCount = lines.reduce(into: 0) { (count, letter) in
if letter == "\r\n" {
count += 1
}
}
This does not look too bad but I am asking myself if there is a shorter/faster way to do it. The characters property provides access to a sequence of Unicode graphemes which treat \r\n as only one entity. Checking this with all CharacterSet.newlines does not work, since CharacterSet is not a set of Character but a set of Unicode.Scalar (a little counter-intuitively in my book) which is a set of code points (where \r\n counts as two code points), not graphemes. Trying
var lines = "Hello, playground\r\nhere too\r\nGalahad\r\n"
lines.unicodeScalars.reduce(into: 0) { (cnt, letter) in
if CharacterSet.newlines.contains(letter) {
cnt += 1
}
}
will count to 6 instead of 3. So this is more general than the above method, but it will not work correctly for CRLF line endings.
Is there a way to allow for more line ending conventions (as in CharacterSet.newlines) that still achieves the correct result for CRLF? Can the number of lines be computed with less code (while still remaining readable)?
If it's ok for you to use a Foundation method on an NSString, I suggest using
enumerateLines(_ block: #escaping (String, UnsafeMutablePointer<ObjCBool>) -> Void)
Here's an example:
import Foundation
let base = "Hello, playground\r\nhere too\r\nGalahad\r\n"
let ns = base as NSString
ns.enumerateLines { (str, _) in
print(str)
}
It separates the lines properly, taking into account all linefeed types, such as "\r\n", "\n", etc:
Hello, playground
here too
Galahad
In my example I print the lines but it's trivial to count them instead, as you need to - my version is just for the demonstration.
As I did not find a generic way to count newlines I ended up just solving my problem by iterating through all the characters using
let linesCount = text.reduce(into: 0) { (count, letter) in
if letter == "\r\n" { // This treats CRLF as one "letter", contrary to UnicodeScalars
count += 1
}
}
I was sure this would be a lot faster than enumerating lines for just counting, but I resolved to eventually do the measurement. Today I finally got to it and found ... that I could not have been more wrong.
A 10000 line string counted lines as above in about 1.0 seconds , but counting through enumeration using
var enumCount = 0
text.enumerateLines { (str, _) in
enumCount += 1
}
only took around 0.8 seconds and was consistently faster by a little more than 20%. I do not know what tricks the Swift engineers hide in their sleves, but they sure manage to enumerateLines very quickly. This just for the record.
You can use the following extension
extension String {
var numberOfLines: Int {
return self.components(separatedBy: "\n").count
}
}
Swift 5 Extension
extension String {
func numberOfLines() -> Int {
return self.numberOfOccurrencesOf(string: "\n") + 1
}
func numberOfOccurrencesOf(string: String) -> Int {
return self.components(separatedBy:string).count - 1
}
}
Example:
let testString = "First line\nSecond line\nThird line"
let numberOfLines = testString.numberOfLines() // returns 3
I use this, a CharacterSet which Apple provides, made for this task:
let newLines = text.components(separatedBy: .newlines).count - 1

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.

Remove the first six characters from a String (Swift)

What's the best way to go about removing the first six characters of a string? Through Stack Overflow, I've found a couple of ways that were supposed to be solutions but I noticed an error with them. For instance,
extension String {
func removing(charactersOf string: String) -> String {
let characterSet = CharacterSet(charactersIn: string)
let components = self.components(separatedBy: characterSet)
return components.joined(separator: "")
}
If I type in a website like https://www.example.com, and store it as a variable named website, then type in the following
website.removing(charactersOf: "https://")
it removes the https:// portion but it also removes all h's, all t's, :'s, etc. from the text.
How can I just delete the first characters?
In Swift 4 it is really simple, just use dropFirst(n: Int)
let myString = "Hello World"
myString.dropFirst(6)
//World
In your case: website.dropFirst(6)
Why not :
let stripped = String(website.characters.dropFirst(6))
Seems more concise and straightforward to me.
(it won't work with multi-char emojis either mind you)
[EDIT] Swift 4 made this even shorter:
let stripped = String(website.dropFirst(6))
length is the number of characters you want to remove (6 in your case)
extension String {
func toLengthOf(length:Int) -> String {
if length <= 0 {
return self
} else if let to = self.index(self.startIndex, offsetBy: length, limitedBy: self.endIndex) {
return self.substring(from: to)
} else {
return ""
}
}
}
It will remove first 6 characters from a string
var str = "Hello-World"
let range1 = str.characters.index(str.startIndex, offsetBy: 6)..<str.endIndex
str = str[range1]
print("the end time is : \(str)")

Syntax changes from Swift to Swift 2, cannot invoke enumerate with a string and extra NSErrorPointer argument

I am working on Swift 1.2 with Xcode 6 but now I've installed Xcode 7 with Swift 2.1. There are many errors in Swift 2.1 and many syntax changes, even though my code works well with Swift 1.2. The first problem is with this method:
func getSubstringUpToIndex(index: Int, fromString str: String) -> String
{
var substring = ""
for (i, letter) in enumerate(str) {
substring.append(letter)
if i == index - 1 {
break
}
}
return substring
}
Another problem occurs on this line, "extra argument 'error' in call":
let jsonResult: Dictionary = NSJSONSerialization.JSONObjectWithData(self.mutableData, options: NSJSONReadingOptions.MutableContainers, error: &error) as! Dictionary<String, AnyObject>
In both cases it's Swift 1.2 code, not 2.0.
In the first snippet of code, the enumerate method does not exist anymore, you can update it this way:
func getSubstringUpToIndex(index: Int,
fromString str: String) -> String
{
var substring = ""
for (i, letter) in str.characters.enumerate() {
substring.append(letter)
if i == index - 1 {
break
}
}
return substring
}
Or this way, using substringRithRange :
func getSubstringUpToIndex2(index: Int,
fromString str: String) -> String {
let range = str.startIndex.advancedBy(0)..<str.startIndex.advancedBy(index)
return str.substringWithRange(range)
}
Try them out on swiftstub.
The second line you pasted does not work anymore because swift 2 now handles errors with try/do/catch, the last parameter NSErrorPointer does not exist anymore, learn more about swift2 error handling here.