Swift: unwrapping cause swift compile slowly - ios

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.

Related

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() }))

strange optional behaviour in Swift

I have created my own class in Swift as below.
class Product: NSObject {
var product_id:Int?
var product_number:String?
var product_price:Float?
var product_descrption:String?
}
Now i am setting value in each property like this
let p=Product()
p.product_id=1
p.product_price=220.22
p.productdescrption="Some description"
p.product_number="W2_23_233"
But when i get the value from price then for price i get value like "Optional 220.22" But i don't get appended word "Optional" in description".So to resolve this i added "!" for unwrapping the value of float but i did not have to do this for String please tell why this is happening?
If you are printing any of these values should say Optional(...). If you are assigning the values to a label, that will not include the Optional(...), The reason that it shows Optional(...) when you print the value using print(), is just to show you its an optional. For safety, instead of using the !, try using if lets.
An example with your code,
if let id = p.product_id {
print(id) //Does not contain Optional()
}
You can also combine them, to do them all at one time. (Only do this if you don't want to print unless all values are non-nil)
if let id = p.product_id,
let price = p.product_price,
let description = p.productdescrption,
let productNumber = p.product_number {
//Enter code here that does something with these values
}
Note, if you aren't on swift 3, I believe you only have to write let on the first condition.
If you print any optional variable without unwrapping no matter what type it is, Optional will be appended to the variable's value.
print(p.product_price) will print Optional(220.220001)
print(p.product_descrption) will print Optional("Some description")
To print only value you need to unwrap the optional variables.
print(p.product_price!) will print 220.22
print(p.product_descrption!) will print Some description
This forced unwrapping will only work if the optionals does not contain nil. Otherwise it will give you a runtime error.
So to check for nil you can use if let statement.
No matter what type of variable. If you assign a value to an optional variable, It always enclosed with Optional(...)
Optional without forced unwrapping:
print("product_price = \(p.product_price) \n product_descrption = \(p.product_descrption)")
Output:
product_price = Optional(220.22)
product_descrption = Optional(Some description)
Optional with forced unwrapping:
print("product_price = \(p.product_price!) \n product_descrption = \(p.product_descrption!)")
Output:
product_price = 220.22
product_descrption = Some description

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

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"

Swift syntax discrepancy between parameters in initializer and functions

It seems to me that there is a discrepancy in Swift's syntax between calling an initializer and a function with at least one paremeter.
Let's consider these two examples:
class SimpleClass {
var desc: String
init(desc: String) {
self.desc = desc
}
}
let aClass = SimpleClass(desc: "description")
and
func simpleFunc(a: Int, b:Int) -> Int {
return a + b;
}
let aVal = simpleFunc(5, b: 6)
It seems odd to me that the compiler forces you to omit the first label in a function call, otherwise you will get an error "Extraneous argument label 'a:' in call". Whereas if we want to omit the first label during initilization, you get the error "Missing argument label 'desc:' in call".
The language guide says:
When calling a function with more than one parameter, any argument after the first is labeled according to its corresponding parameter name.
Source: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html
The arguments to the initializer are passed like a function call when
you create an instance of the class.
Source: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html
I'm new to Swift so I hope I didn't miss something, but this seems like a syntax discrepancy, because initializers/ constructors are just kind of functions and forcing to omit the first label in a function call seems inconsistent to me.
That's because Swift focuses on readability; function calls to be able to be read like a sentence. See this, specifically the section on "Local and External Parameter Names for Methods". Your function, to comply with this style, should be more like:
func add(a: Int, to b: Int) -> Int {
return a + b
}
let c = add(1, to: 2)

Storing dictionaries into Firebase?

I have a Firebase instance where I want to store a dictionary of values I want to store into firebase. I have looked on the documentation https://www.firebase.com/docs/ios/guide/saving-data.html as a reference but can't seem to get it to work. The following is my attempt:
//Declared above are the currentUser values as so:
var currentUserFirstName: String!
var currentUserLastName: String!
var currentUserObjectID: String!
var attendeesArray = ["objectID": currentUserObjectID, "name": currentUserFirstName + " " + currentUserLastName]
var eventRefChild = EventReference.childByAutoId()
eventRefChild.setValue([
"eventName":eventName.text,
"attendees": attendeesArray,
"eventCreator": currentUserFirstName
])
But I keep getting an error saying: Could not find an overload for '+' that accepts the supplied arguments when I try to do eventRefChild.setValue([... and I'm honestly not too sure why I am getting this issue. Any help would be appreciated!
EDIT: The variable EventReference is assigned as so: EventReference = Firebase(url:"<Insert Firebase URL>")
And inside currentUserFirstName and currentUserLastName are an individual's first and last name grabbed from Facebook so it would look something like Bob Smith respectively.
There is nothing wrong with your code. The issue is the values that are being loaded into
var currentUserFirstName: String!
var currentUserLastName: String!
As a test, I created a sample project with the following code, which is a duplicate of your posted code but with normal strings loaded into the var's:
var myRootRef = Firebase(url:"https://myproject.firebaseIO.com/")
var currentUserFirstName = "Test"
var currentUserLastName = "User"
var currentUserObjectID = "object ID"
var attendeesArray = ["objectID": currentUserObjectID, "name": currentUserFirstName + " " + currentUserLastName]
var eventRefChild = myRootRef.childByAutoId()
eventRefChild.setValue([
"eventName": "eventName",
"attendees": attendeesArray
])
the project compiled and runs correctly and the expected data is written to Firebase. Note that the eventName.text was replaced with a string as well but that doesn't affect the answer.
The investigation needs to turn to what's loaded in the var's, and the answer is that one of those var's, currentUserFirstName or currentUserLastName is being loaded with an OBJECT (class), not a string.
As a side note, why are the var's declared as implicitly unwrapped optionals (the !)
edit: adding additional info to deal with the optionals
if let actualString = currentUserFirstName {
println(actualString) //proceed working with the the optional string
}
else {
return // something bad happened! currentUserFirstName does not contain a string
}
To prevent code from errors when an optional contains no value, add the above code directly above the concatenation line of code. What happens here is the we are assigning the string in currentUserFirstName (optional var) to a actual string (a standard, non-optional var).
If the expression evaluates to true then we can proceed evaluating currentUserFirstName.
If it's false, then currentUserFirstName does not contain a string so handle the error elegantly.

Resources