Swift 3: Joining multiple Strings with "+" operator no longer possible - ios

In swift 2.3 I had this working simple piece of code:
let joinedString = partOne! + PartTwo! + PartThree! + PartFour!
Now with the conversion to swift 3 I've been bashing my head in over about 24 errors out of the blue with the most vague explanations.. This is one of them:
The same line of code gives error:
Ambiguous reference to member '+'
However if I split them up like so:
let OneAndTwo = partOne! + partTwo!
let ThreeAndFour = partThree! + PartFour!
let joinedString = OneAndTwo + ThreeAndFour
This works... Did they remove linking multiple strings like this or is it buggy? Seems like the compiler thinks the '+' is a variable or something else named the same?
EDIT:
Even though it's another error this seems to be related to: This Question
Also crashes once you go upwards of 2 optional strings. I guess optional binding is the way to go then. Seems like this bug has been there for quite some time.

This seems like a bug and I'll investigate further. If we simulate the behaviour of ! with another operator it works just fine:
postfix operator |! {}
postfix func |! <T>(rhs: T?) -> T {
return rhs!
}
let s1: String? = "Hello"
let s2: String? = " "
let s3: String? = "World"
let joined = s1|! + s2|! + s3|! // "Hello World"

Related

Non-optional expression of type 'String' used in a check for optionals

I am getting this warning from Xcode Swift 5, here is my code I don't get what is wrong, I use this to remove any new line or tab at the end of my String (line)
My code:
let url: String = String(line.filter { !" \n\t\r".contains($0) })
UPDATE
I was doing it inside an if let and was using the type cast operator here is the solution and the rest of code and an example of the line value.
let line = " http://test.com/testing.php \n"
if let url: String = line.filter({!" \n\t\r".contains($0)}) as String?
{
//More action here
}
Thank you
to me this line looks good, but you may be missing the parentheses for the string filter method. Here's two ways I did it in playground. Let me know if this works for you, or how I can help further.
var line = "\t Hello, line removal \n \t Another new line \n"
let filteredClosure = line.filter { (char) -> Bool in
return !"\n\t\r".contains(char)
}
let filterShorthand = line.filter({!"\n\t\r".contains($0)})
With the line you provided, I would expect white-space to be removed too. If that's what you're looking for, add a space inside the filter string: " \n\t\r"

Compiler Error expression was too complex to be resolved

I am getting this error:
Expression was too complex to be resolved in reasonable time
Please help me out. What should I do? I am using the same line in the previous view controller and it's working perfectly.
let url = URL(string: self.con+"loc?email="+email+"&lat="+lati+"&log="+logi!)
The reason behind this error is that Xcode gets confused when you use too many + signs. Always try to use the String Interpolation:
let paramsStr = "loc?email=\(email)&lat=\(lati)&log=\(logi)"
Also a good read about this topic:
https://stackoverflow.com/a/29931329/3403364
Break it down to smaller expressions. Swift compiler is too dumb to understand your expression ))
Like this:
let paramsStr = "loc?email=" + email + "&lat=" + lati + "&log=" + logi
let url = URL(string: self.con + paramsStr)

correct way to create a lowercase search string swift 3

I need string to use in the search function in my app. I don´t have much coding experience, but get by by googling a lot, tutorials, and some trial and error.
The below approach works, it creates just what I need, but I noticed it slows down compiling time substantially, so I assume that there is a better/smarter way to do this? I did not get much in a way of answers by googling on this one.
let searchText: String? = ("\(self.noseTextView?.text)"
+"\(self.palateTextField?.text)"+"\(self.finishTextField?.text)"
+"\(self.overallTextField?.text)"+"\(self.otherTextField?.text)"
+"\(self.glassTextField?.text)"+"\(bottleName2)"
+"\(self.distilleryTextField?.text)").lowercased()
Anyone that can teach me a better way to achieve the same result?
Edit:
One answer adapted to my code, (or my interpretation of it from Özgür Ersil, below), that works.
//creates lowercase String for labels to save compiling time
func getText(textLabel: UILabel)->String{
return textLabel.text!.lowercased()
}
//creates lowercase String for TextFields to save compiling time
func getTextTF(ui: UITextField)->String{
return ui.text!.lowercased()
}
//creates lowercase String for TextViewsto save compiling time
func getTextTV(ui: UITextView)->String{
return ui.text!.lowercased()
}
let searchText: String = self.getTextTV(ui: self.noseTextView) +
self.getTextTF(ui: self.palateTextField) + self.getTextTF(ui:
self.finishTextField) +
self.getTextTF(ui: self.overallTextField) + self.getTextTF(ui:
self.otherTextField) +
self.getTextTF(ui: self.glassTextField) + self.getTextTF(ui: self.bottleName) +
self.getTextTF(ui: self.distilleryTextField)
I use this function to append optional strings into a single String
func append(optionalStrings arrStrings : [String?],separator: String = " ") -> String {
return arrStrings.flatMap{$0}.joined(separator: separator)
}
This function takes an array of optional Strings as parameter
flatMap is a higher order function purpose of which here is to eliminate the nil value in arrayStrings(if exists) and provide us a array of non-Optional Strings which we can join with joined(separator: String) method.
You can use this as
let arrOptionalStrings = [noseTextView?.text,
palateTextField?.text,
finishTextField?.text,
overallTextField?.text,
otherTextField?.text,
glassTextField?.text,
bottleName2,
distilleryTextField?.text
]
let searchText: String? = append(optionalStrings: [str1,str2].map({ $0?.lowercased() }))

Swift String API Alternate for HackerRank

So I'm trying to prepare myself for coding interviews by doing HackerRank's test case samples. If you're familiar with the process, you usually take a standard input that has various lines of strings and you extract the information based on what the question is asking. I have come across numerous questions where they will give you a line (as a String) with n number of integers separated by a space (i.e. 1 2 3 4 5). In order to solve the problem I need to extrapolate an array of Int ([Int]) from a String. I came up with this nifty method:
func extractIntegers(_ s: String) -> [Int] {
let splits = s.characters.split { [" "].contains(String($0)) }
return splits.map { Int(String($0).trimmingCharacters(in: .whitespaces))! }
}
So I code it in my Playground and it works fantastic, I even run multiple test cases I make up, and they all pass with flying colors...then I copy the code to HackerRank and try running it for submission. And I get this:
solution.swift:16:29: error: value of type 'String' has no member 'trimmingCharacters'
return splits.map { Int(String($0).trimmingCharacters(in: .whitespaces))! }
So... okay maybe HR hasn't updated everything for Swift 3 yet. No big deal! I have an idea for an even cleaner solution! Here it is:
func extractIntegers(_ s: String) -> [Int] {
return s.components(separatedBy: " ").map { Int($0)! }
}
....AAAAANDDD of course:
solution.swift:15:12: error: value of type 'String' has no member 'components'
return s.components(separatedBy: " ").map { Int($0)! }
So now I'm forced to use a really sloppy method where I loop through all the characters, check for spaces, append substrings from ranges between spaces into an array, and then map that array and return it.
Does anyone have any other clean ideas to work around HR's inadequacies with Swift? I would like any recommendations I can get!
Thanks in advance!
The String methods
func trimmingCharacters(in set: CharacterSet) -> String
func components(separatedBy separator: String) -> [String]
are actually methods of the NSString class, defined in the Foundation
framework, and "bridged" to Swift. Therefore, to make your code compile,
you have go add
import Foundation
But a slightly simplified version of your first method compiles
with pure Swift, without importing Foundation. I handles leading, trailing, and intermediate whitespace:
func extractIntegers(_ s: String) -> [Int] {
let splits = s.characters.split(separator: " ").map(String.init)
return splits.map { Int($0)! }
}
let a = extractIntegers(" 12 234 -567 4 ")
print(a) // [12, 234, -567, 4]
Update for Swift 4 (and simplified):
func extractIntegers(_ s: String) -> [Int] {
return s.split(separator: " ").compactMap { Int($0) }
}

Swift: unwrapping cause swift compile slowly

In func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{
this line of code makes swift compile extremely slow:
cell!.detailTextLabel.text = child.year! + " " + child.make! + " " + child.model!
It takes me 1 min and 44 secs to build the project if I have this line of code. And 99% of the time it stuck at "Compiling Swift source files".
If I change this line to
cell!.detailTextLabel.text = " "//child.year! + " " + child.make! + " " + child.model!
It only take me 5 or 6 sec to build the project.
I would like to know why this line of code will cause so much time compiling.
In my Child model, they are declared as :
var name:String?
var year:String?
var make:String?
var model:String?
and the init:
init(name:String!, ... ,year:String!, make:String!, model:String!, ...){
self.name = name
...
self.year = year
self.make = make
self.model = model
}
The part I construct a child:
Child(name:cName,...,year:cYear,make:cMake, model:cModel,...)
Yes, I filed a bug report (17585851) on this slow compilation issue, and you should do the same; the more clear use cases Apple is sent, the better. My slow code was several occurrences of this form:
let title = obj.valueForProperty(MPMediaItemPropertyTitle) as? String
self.titles += title ? title! : ""
(which, as you can see, is doing nil testing / unwrapping). It was cumbersome but not difficult for me to work around the problem by doing the same thing in a different way, and you should do likewise. But file that bug report first!
You need to do unwrapping when your types are declared as optionals. In swift an optional type is a type that may or may not have a value. For example i could declare a string as:
var collegeName : String?
The "?" at the end of type declaration shows that the collegeName may or may not have a value and in order to get that value you will have to unwrap it in order to get its value by using ! operator.
As far as i can tell in your case the name field is declared as a string while year , make and model are declared as optional strings that is why you need to unwrap those fields to get the value out of them.
You can also declared a type using "!" operator like:
var collegeName : String!
This means that this is an optional string but it is automatically unwrapped for you so you don't need to use the "!" afterwards to unwrap this optional.

Resources