Why I can print a String, but throws error when I try to store it in Swift 5? [duplicate] - swift

This question already has an answer here:
Why does String.subscript(_:) require the types `String.Index` and `Int` to be equal when there is no `Int` involved?
(1 answer)
Closed 2 years ago.
I have a pretty basic code, that makes 2 String.Index ranges from an existing websiteContent String like so:
let test1 = websiteContent.range(of: startString, options: NSString.CompareOptions.literal, range: websiteContent.startIndex..<websiteContent.endIndex)
let test2 = websiteContent.range(of: endString, options: NSString.CompareOptions.literal, range: websiteContent.startIndex..<websiteContent.endIndex)
Then it prints the text between the 2 ranges:
if let beginning = test1, let end = test2 {
print(websiteContent[beginning.lowerBound..<end.upperBound])
}
This works just fine, my problem is that when I try to store this String value I just printed it throws an error message. The way I tried to store it is this:
if let beginning = test1, let end = test2 {
let why : String = websiteContent[beginning.lowerBound..<end.upperBound]
}
And the error message says: Subscript 'subscript(_:)' requires the types 'String.Index' and 'Int' be equivalent
Could you please help me understand what do I do wrong?

This is actually a bug, the error is misleading.
You have to create a String from the Substring, annotating the type is not sufficient.
if let beginning = test1, let end = test2 {
let why = String(websiteContent[beginning.lowerBound..<end.upperBound])
}
Side note:
You can omit , range: websiteContent.startIndex..<websiteContent.endIndex, the range of the whole string is the default.

Related

How to get SubString From Range in iOS Swift 4.2 [duplicate]

This question already has answers here:
Get nth character of a string in Swift
(47 answers)
Closed 3 years ago.
I am new to swift, I want to get substring from specified range. But I am getting some errors.
I have found similar questions on getting subString from range, but those couldn't work for me. I am using XCode 10.1 and swift 4.2. I have getting error while getting substring from specified range. I have tried like this (Sample Code)
let textStr = "Sample text here"
let newRange = NSMakeRange(4,9)
let startIndex = textStr?.index((textStr?.startIndex)!, offsetBy: newRange.location)
let endIndex = textStr?.index((textStr?.startIndex)!, offsetBy: newRange.length)
let newHashtagRange = startIndex..<endIndex
let newHashTagFound = textStr[newHashtagRange]
Error:
I am getting below error for this line of code
let newHashtagRange = startIndex..<endIndex
Binary operator '..<' cannot be applied to two 'String.Index?'
operands
I just struct here from last two days.
But in Objective-C just need one line of code like SubStringFromRange(range).
You are getting an error because both startIndex and endIndex are optionals due to the optional chain you have when defining them.
That is odd because, in your example code, textStr is not actually optional and the optional chain wouldn't even compile. My guess is you were attempting to shorten the example. I will assume textStr is meant to be optional and show you how to avoid using forced unwrapping which is almost always a bad idea. Also, you are getting the end index incorrectly. You need to do an offset from the calculated start index with the length. That leaves you with this:
let textStr: String? = "Sample text here"
if let textStr = textStr {
let newRange = NSMakeRange(4,9)
let startIndex = textStr.index(textStr.startIndex, offsetBy: newRange.location)
let endIndex = textStr.index(startIndex, offsetBy: newRange.length)
let newHashtagRange = startIndex..<endIndex
let newHashTagFound = textStr[newHashtagRange]
}
However, a Range can be initialized from an NSRange which is reliable and built in (it returns an optional in case the range is out of bounds):
let textStr: String? = "Sample text here"
let newRange = NSMakeRange(4,9)
if let textStr = textStr
, let newHashtagRange = Range(newRange, in: textStr)
{
let newHashTagFound = textStr[newHashtagRange]
}

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

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.

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.

Reversing a string in Swift [duplicate]

This question already has answers here:
Reversing the order of a string value
(13 answers)
Closed 5 years ago.
Needed to reverse a swift string, managed to do so with this.
var str = "Hello, playground"
let reactedText = str.characters.reversed()
let nowBackwards = Array(reactedText)
let answer = String(nowBackwards)
And since I find nothing on SO in the subject I post it for some positive votes :) or indeed a better [shorter, as in different] solution.
Assuming you are using Swift 4 :
let string = "Hello, playground"
let reversedString = String(string.reversed())
Since in Swift 4, Strings are Character arrays again, you can call reversed on the String itself and the result will be of type [Character], from which you can initialize a String.
let stringToBeReversed = "Hello, playground"
let reversedString = String(stringToBeReversed.reversed()) //"dnuorgyalp ,olleH"
reversed method is available in String library
let str = "Hello, world!"
let reversed = String(str.reversed())
print(reversed)

How to print call stack in Swift?

In Objective-C, you can print the call stack by doing the following:
NSLog(#"%#", [NSThread callStackSymbols]);
How do you do this in Swift without using Foundation class?
As Jacobson says, use the following:
Swift 2:
print(NSThread.callStackSymbols())
Swift 3 / Swift 4:
print(Thread.callStackSymbols)
That's Swift code. It's using a Foundation method, but so does 90%+ of what you do on iOS.
EDIT:
Note that the formatting looks better if you use:
Thread.callStackSymbols.forEach{print($0)}
From the debugger command line you can type
e Thread.callStackSymbols.forEach{print($0)}
For Swift 3 use:
print(Thread.callStackSymbols)
or for better formatting
for symbol: String in Thread.callStackSymbols {
print(symbol)
}
This improves the output a little.
for symbol: String in NSThread.callStackSymbols() {
NSLog("%#", symbol)
}
print(Thread.callStackSymbols.joined(separator: "\n"))
With this code, one can see the calls in different lines each.
1 MyApp 0x0000000100720780 $s9MyAppModule....
2 CoreFoundation 0x0000000181f04c4c EA9C1DF2-94C7-379B-BF8D-970335B1552F + 166988
3 CoreFoundation 0x0000000181f99554 EA9C1DF2-94C7-379B-BF8D-970335B1552F + 775508
4 CoreFoundation 0x0000000181f6eb34 EA9C1DF2-94C7-379B-BF8D-970335B1552F + 600884
5 CoreFoundation 0x0000000181f19754 _CFXNotificationPost + 696
6 Foundation 0x0000000183634138 86D8A58D-B71F-34C6-83E0-014B2B835F1D + 106808
Here's a great utility class I found on github:
https://github.com/nurun/swiftcallstacktrace
You get a tuple (class,method) of any stack trace symbol so you can do a clean printout.
CallStackAnalyser.classAndMethodForStackSymbol(NSThread.callStackSymbols()[2])
Edit: swift 4.1 update
https://github.com/GDXRepo/CallStackParser
I needed to write the callstack to a log file so I tweaked it like so.
var ErrorStack = String()
Thread.callStackSymbols.forEach {
print($0)
ErrorStack = "\(ErrorStack)\n" + $0
}
Thread.callStackSymbols() is nice to have. But the traceback is ugly. Demangling would be nice. The Swift 4.1+ demangler linked in #MikeS's answer is extremely comprehensive and impressive, but it's also over 4000 lines of code, overkill if you just need app, class and method, and it's quite a lot to add to a project, which I'd prefer to not to risk forgetting not to ship my app with :-)
This is a quick prototype of something that does some basic demangling of appname, class and method (which are the easy part to figure out). It's not polished. For example, it doesn't check for nil/failures in the regex ops, since it just gets a line from the callstack, which should be consistent enough to avoid problems. However, improved versions of it are welcome answers.
I added it to a class I named Debug, where I keep other debugging stuff, and invoke it from wherever in my app as:
Debug.whence()
    ... note: "where" is a Swift reserved word, and whence means basically the same thing.
It prints a line of this form (only one line, not full stack):
EventEditorViewController.configureNavigationItem():419 I'll probably add an argument to take an optional object arg and then do a refined display of the object and its address without some of the parameters and syntax swift's builtin obj dump logging does, so that it would display obj info and where it is being traced.
This probably can only parse lines inside the app. It probably can't demangle non-Swift calls (like Foundation), not sure. If you need a more comprehensive demangler, check #MikeS's answer.
static func whence(_ lineNumber: Int = #line) {
func matchRegex(_ matcher: String, string : String) -> String? {
let regex = try! NSRegularExpression(pattern: matcher, options: [])
let range = NSRange(string.startIndex ..< string.endIndex, in: string)
guard let textCheckingResult = regex.firstMatch(in: string, options: [], range: range) else {
return nil
}
return (string as NSString).substring(with:textCheckingResult.range(at:1)) as String
}
func singleMatchRegex(_ matcher: String, string : String) -> String? {
let regex = try! NSRegularExpression(pattern: matcher, options: [])
let range = NSRange(string.startIndex ..< string.endIndex, in: string)
let matchRange = regex.rangeOfFirstMatch(in: string, range: range)
if matchRange == NSMakeRange(NSNotFound, 0) {
return nil
}
return (string as NSString).substring(with: matchRange) as String
}
var string = Thread.callStackSymbols[1]
string = String(string.suffix(from:string.firstIndex(of: "$")!))
let appNameLenString = matchRegex(#"\$s(\d*)"#, string: string)!
let appNameLen = Int(appNameLenString)!
string = String(string.dropFirst(appNameLenString.count + 2))
let appName = singleMatchRegex(".{\(appNameLen)}", string: string)!
string = String(string.dropFirst(appNameLen))
let classNameLenString = singleMatchRegex(#"\d*"#, string: string)!
let classNameLen = Int(classNameLenString)!
string = String(string.dropFirst(classNameLenString.count))
let className = singleMatchRegex(".{\(classNameLen)}", string: string)!
string = String(string.dropFirst(classNameLen))
let methodNameLenString = matchRegex(#".(\d*)"#, string: string)!
let methodNameLen = Int(methodNameLenString)!
string = String(string.dropFirst(methodNameLenString.count + 1))
let methodName = singleMatchRegex(".{\(methodNameLen)}", string: string)!
let _ = appName
print("\(className).\(methodName)():\(lineNumber)")
}