Swift 2.2: Optional Binding in a function - swift

Heys guys,
I am pretty new into programming and therefore I've followed I course on Udemy to teach me Swift 2.2.
For learning purpose I have been trying to program a BMI-calculator where I have a textfield (which just displays the value) and a slider where I can put my weight in kg. After dragging the slider the value is visible in the textfield. I cannot put a value into the textfield so that it is displayed on the slider!
The same textfield-slider relation is used with the height in cm. Now I created an IBAction the bring my kgSlider.value into my kgField.text and it looks like this:
#IBAction func kgSet(sender: AnyObject) {
kgField.text! = String(Int(kgSlider.value))
}
Thats works fine but I unwrapped (like the teacher in the course) without knowing, if there really is a value. Okay, I know in this case that there will be a value, but I would like to go more real and therefore I tried to use an Optional-Binding to find out, if there is a value instead of directly unwrap it.
Therefore I used the cm.Field and the cm.Slider in my code but it doesn't work for now and I don't know why. The code is the following:
#IBAction func cmSet(sender: AnyObject) {
if let tempCm = String(Int(cmSlider.value)) as String! {
cmField.text = tempCm
}
}
So I created the constant called tempCM which will got the value from the cmSlider, if there is a value. Therefore I casted the cmSlider.value like in the other IBAction into an Int and then into a String. If there is the value it will carry it into the cmField.text. This didn't work, therefore I tried to use the "as String!" statement but know I get always 0 instead of the correct value.
So what am I doing wrong there?

So, this should compile fine and provide you with your desired result.
#IBAction func cmSet(sender: AnyObject) {
if let tempCm = String(Int(cmSlider.value)) {
cmField.text = tempCm
}
}
You could also try this
cmField.text = String(Int(cmSlider.value)) ?? " "
in the second example, you are using the optional operator to say if you can convert this to an Int then that Int to a string set the cmField.text property to its value, otherwise use a blank space as the default value.

Related

Swift Dictionaries with enum as Identifier not auto completing in completion block

I have a function that returns a completion. The completion has as in parameter a custom class with a dictionary that uses an enum as an identifier.
The problem I am facing is that Xcode does not recognises the type of the variable and does not auto complete when I use it inside of completion block.
My code looks like that
Function
func testFunc (completion:(PrxServiceResponseCallback) ->()){
let responseCallback = PrxServiceResponseCallback()
completion(responseCallback)
}
Class
class PrxServiceResponseCallback:NSObject{
var success = false
var resultCode:Int32 = 0
var response:[PrxResponseAttributes:Any] = [PrxResponseAttributes:Any]()
}
enum PrxResponseAttributes{
case sourceProtocolInfoArray
case sinkProtocolInfoArray
case connectionIDsArray
case connectionInfo
}
Calling the function
testFunc { (testResonse) in
testResonse.response[.]//Not Auto completing
}
The whole idea of making a dictionary with an enum as identifier was to make easier which attributes the dictionary returns but, If I can't auto complete, the idea is more pointless.
Any ideas?
Xcode doesn't give you autocomplete doesn't mean that your code doesn't compile. It just means that Xcode is too stupid to figure things out. I have encountered such situations many times before. It seems to always happen inside closures.
You can just ignore the fact that no autocomplete shows up and type the case name yourself:
testResonse.response[.sourceProtocolInfoArray]
It will compile.
You can also consider creating a struct instead of storing the values in a dictionary. Here's a sample struct (I guessed the types):
struct Response {
let sourceProtocolInfo: [String]
let sinkProtocolInfo: [String]
let connectionIDs: [Int]
let connectionInfo: String
}
If you use var response:[UIColor:Any] = [UIColor:Any]()
it is still not giving auto complete so it is not your issue and you can not do much on it .
If you need auto complete then use PrxResponseAttributes with . (dot)
you can do it like
testResonse.response[PrxResponseAttributes.sourceProtocolInfoArray]
EDIT
Note: It is only happening with implemented closure if you add one property in your PrxServiceResponseCallback class like
var anyValue:Any? {
return response[.connectionInfo] // it is showing completion
}

Casting an Int as a String from a Realm result Swift

I am asking this hesitantly as I know this is probably a dumb question.
I am returning a Realm result and then have gone ahead and tried to cast it to a String as normal (to put in a text label).
However I'm getting an error 'init' has been renamed to 'init(describing:)'.
When I try use the describing method instead, the label prints "Optional" inside it which obviously isn't what I want.
Is there a reason I can't use :
previousTimeLabel.text = String(lastRecord?.time)
I'm sure I've done this before and it's been fine, am I missing something? (lastRecord.time is an Int).
I've checked the answer here about Interpolation Swift String Interpolation displaying optional? and tried changing to something like this :
if let previousRounds = String(lastRecord?.rounds) {
previousRoundsLabel.text = previousRounds
}
but get the same error + Initializer for conditional binding must have Optional type, not 'String'
The issue isn't String(lastRecord?.time) being Optional. The issue is lastRecord being Optional, so you have to unwrap lastRecord, not the return value of String(lastRecord?.time).
if let lastRecord = lastRecord {
previousRoundsLabel.text = "\(lastRecord.time)"
}
To summarize Dávid Pásztor's answer, here's a way you can fix it:
previousTimeLabel.text = String(lastRecord?.time ?? 0)
This may not be the best way for your application. The point Dávid was making is that you need to deal with lastRecord possibly being nil before trying to pass its time Int into the String initializer. So the above is one simple way to do that, if you're ok with having "0" string as your previousTimeLabel's text if there was no lastRecord.

Getting current NSPopUpButton's selection with titleOfSelectedItem returns "optional value"

Edit: I figured out how to prevent this, but I still have a question as to why it returns an optional value. You may skip to the end.
I'm quite new to Swift, and I'm getting behavior I can't understand. Let's say I drag a popup button called myButton into the ViewController. I want to print the currently selected item to the console. Here's how I would do it:
#IBOutlet weak var myButton: NSPopUpButton!
override func viewDidLoad() {
super.viewDidLoad()
let myVariable = myButton.titleOfSelectedItem
print(myVariable)
// Do any additional setup after loading the view.
}
I expect Item1 to be printed, as that's the option selected by default when the view loads. However, I actually get Optional("Item 1").
This has been causing a few problems for me. For example, the print(myVariable) line gives me this cryptic error:
Expression explicitly coerced from 'String?' to Any
Also, I can't do something like this:
if myButton.titleOfSelectedItem == "Item1" || "Item3" {
let currentSelection = "odd"
} else {
let currentSelection = "even"
}
as I get a bunch of errors – because of the || and the else as far as I can tell, even though I think it should work fine.
I've tried searching for why this occurs, but couldn't find any explanations. From the warning, it seems like when I get the selection with titleOfSelectedItem, it gives me an optional value. This doesn't make sense, as a menu item has to be selected. Its value can't be nil.
I looked up a bunch of tutorials to see how they implement popup button functionality. The only thing I could see was when someone
Made an array
Removed all items from the popup button with func removeAllItems()
Added items to the popup button from the array with func addItems(withTitles: [String])
Obtained the index of the selected item with var indexOfSelectedItem: Int
Retrieved the respective value from the array
and then used that. This, however, seems unnecessarily complicated and I can't understand why I wouldn't be able to get just the title of the selected popup item simply with myButton.titleOfSelectedItem. Can anyone suggest to me what to do?
Edit:
So I realized you need to "unwrap" the optional value to make it a normal value, which is as simple as adding a ! to the end of the variable, as follows:
#IBOutlet weak var myButton: NSPopUpButton!
override func viewDidLoad() {
super.viewDidLoad()
let myVariable = myButton.titleOfSelectedItem!
print(myVariable)
// Do any additional setup after loading the view.
}
Now there's no error, and Item1 is printed.
What I can't yet understand is, why was an optional value printed in the first place? There are three items in the NSPopUpButton. Any one of the three has to be selected. There's no opportunity for myButton.titleOfSelectedButton to be nil. Why then do I need to unwrap it to use it as a string with myButton.titleOfSelectedButton! if it's not optional?
titleOfSelectedItem returns an optional because no item could be selected.
You need optional bindings to unwrap the optional safely and you have to evaluate both "Item1" and "Item3" against the title string:
if let title = myButton.titleOfSelectedItem {
print(title)
let currentSelection : String
if title == "Item1" || title == "Item3" {
currentSelection = "odd"
} else {
currentSelection = "even"
}
}
In Objective C you can do it in awakeFromNib:
[[your_NSView your_NSPopUpButton] selectItemAtIndex:your_Int];
... of course, you must have declared 'your_NSView' and 'your_NSPopUpButton' as properties.

Swift 3 cast UITextField to Int

I'm receiving a compiler error and I'm not really sure why. I'm sure there is a simple answer for this. I have a core data attribute I'm trying to assign before saving. In my Core Data Property file it's defined as this:
#NSManaged public var age: Int32
I am using a UIPicker to select it and put it into an inputView. That works fine, so ageTextField: UITextField! holds the value. As I try to assign this to the CoreData object just before saving I get the following
person.age = ageTextField.text -> Cannot assign String? to Int32.
Ok, I understand that, so I cast it
person.age = Int(ageTextField.text) -> Value of Optional String not unwrapped...
Ok, I get that, so I unwrapped it, it asks to unwrap again and I agree:
person.age = Int(ageTextField.text!)! -> Type of expression is ambiguous without more context
I'm not sure what is wrong here, just looking over some old Swift 2 code of mine and this worked. This is my first code with Swift 3 though.
That compiler error is obscure at best and misleading at worst. Change your cast to Int32:
person.age = Int32(ageTextField.text!)!
Also: unless you are absolutely sure that the user will always enter a valid number into the textfield, use optional binding instead of force unwrap:
if let text = ageTextField.text,
let age = Int32(text)
{
person.age = age
}
The immediate issue is the use of the wrong type. Use Int32, not Int. But even once that is fixed, you have lots of other issues.
You should safely unwrap the text and then the attempt to convert the string to an integer.
if let text = ageTextField.text, let num = Int32(text) {
person.age = num
}
The use of all of those ! will cause a crash if the text is nil or it contains a value that isn't a valid number. Always use safe unwrapping.
Just make sure to unwrap the optional before convert. Make your code safe.

Swift basic expression

I'm very new to swift, but proficient in other languages like Java, JavaScript, C, ... I'm lost with Swift syntax when it comes to create expressions. Look at this basic example where I just try to find out if one string is contained into another by calling String.rangeOfString that returns an Optional Range (Range?)
This works as expected:
let LEXEMA:String="http://"
let longUrl:String="http://badgirls.many/picture.png"
let range=longUrl.rangeOfString(LEXEMA);
if (range? != nil) {
// blah
}
Now I'm trying to combine the expression inside the if, something like:
if (longUrl.rangeOfString(LEXEMA)? !=nil) {
// blah
}
But I always get syntax errors, the above yields a "Expected Separator" and can't understand why. Done some more tests:
if (absolutePath.rangeOfString(URL_LEXEMA) !=nil) { }
Expected Separator before "!"
if absolutePath.rangeOfString(URL_LEXEMA) !=nil { }
Braced block of statements is an unused closure
What am I doing wrong?
If you’re coming from other like Java, you might be thinking of optionals like pointers/references, and so used to equating them to nil and if non-nil, using them. But this is probably going to lead to more confusion. Instead, think of them like a container for a possible result, that you need to unwrap to use. if let combines the test and unwrapping operation.
With this in mind, here’s how you could adapt your code:
let LEXEMA: String="http://"
let longUrl: String="http://badgirls.many/picture.png"
if let range = longUrl.rangeOfString(LEXEMA) {
// use range, which will be the unwrapped non-optional range
}
else {
// no such range, perhaps log an error if this shouldn’t happen
}
Note, that ? suffixing behaviour you were using changes in Swift 1.2 so even the code in your question that compiles in 1.1 won’t in 1.2.
It’s possible that sometimes you are whether there was a value returned, but you don’t actually need that value, just to know it wasn’t nil. In that case, you can compare the value to nil without the let:
if longUrl.rangeOfString(LEXEMA) != nil {
// there was a value, but you don't care what that value was
}
That said, the above is probably better expressed as:
if longUrl.hasPrefix(LEXEMA) { }
For starters:
You don't need parenthesis with if statements unless you have nested parenthetical subexpressions that require it.
You don't need to specify the type on the left side of the = of a let or var declaration if Swift can figure it out from the right side of the =. Very often Swift can figure it out, and you can tell that Swift can figure it out, so you can avoid that redundant clutter.
You do need to specify the type if Swift cannot figure out the type from
the right side. Example:
For example, consider the following lines:
let LEXEMA = "http://"
let longUrl = "http://badgirls.many/picture.png"
Swift can figure out that they're strings.
Similarly for this function or class that returns a UIView:
var myView = ViewReturningClassOrFunc()
Consider this:
#IBOutlet var myView : UIView!
In the above line, Swift cannot figure out ahead of time it will be assigned a UIView, so you have to provide the type. By providing a ! at the end you've made it an implicitly unwrapped optional. That means, like ?, you're indicating that it can be nil, but that you are confident it will never be nil at the time you access it, so Swift won't require you to put a ! after it when you reference it. That trick is a time saver and big convenience.
You should NOT add the ? to the line:
if (longUrl.rangeOfString(URL_LEXEMA) !=nil) {
As another answer pointed out, you're missing the let.
if let longUrl.rangeOfString(URL_LEXEMA) {
println("What do I win? :-)")
}
swift is case sensitive language. you need to check about whitespaces as well
if longUrl.rangeOfString(LEXEMA) != nil {
//your condition
}
there should be space between statement != nil
Just add a space between != and nil like:
if longUrl.rangeOfString(LEXEMA) != nil {
// blah
}
I tested your code in playground, an error of Expected ',' separator reported.
And do not forget the rules that 1s and 0s and Airspeed Velocity said.