How do I assert that a text field is empty? - ios

I have an empty text field on my UI, though it has a placeholder text (whose value is foo) set in the storyboard. In my UI test, I am trying to check that its text value starts out empty, but when I query it's value, it seems to be giving me the placeholder value instead:
func testTextFieldInitiallyEmpty {
let input = XCUIApplication().textFields["My Text Field"]
XCTAssertEqual(input.value as! String, "")
}
as the test fails with this message:
XCTAssertEqual failed: ("foo") is not equal to ("")
Of course, foo is the placeholder value, but it's not the text value of that text field. I would have expected that error message if I had written:
XCTAssertEqual(input.placeholderValue as! String, "")
input is a XCUIElement, which implements XCUIElementAttributes, so I don't see anything else that would do the trick here.
How do I check (assert) that the text field is empty?
Edit
After doing some further research and trying out the suggestions below for using the input's properties of accessibilityValue, label, and title, I have not found any solution that will give me the text field's text value when there is text, and an empty string when only the placeholder is visible.
This seems like either (a) a bug, or (b) a questionable design decision from the test framework to not provide that ability. At a minimum, the documentation for XCUIElementAttributes#value seems inadequate to me, as the only detail is:
The exact type of value varies based on the type of the element.
Still looking for a better solution...

You can compare to the XCUIElementAttributes's placeholderValue variable in addition to checking for a blank string
extension XCUIElement {
func noTextEntered() -> Bool {
return self.value as? String != "" && self.value as? String != placeholderValue
}
}
Then you can run XCAssert(input.noTextEntered(), "Unexpected text entered into field")
Just make sure your placeholder is not something a user would type in. This way you don't have to hardcode placeholder values to check against

Kind of ridiculous that this is actually the case it works and that it needs a workaround.
Anyway, my solution to get the value w/o the placeholder interfering, based on #Jason's answer.
extension XCUIElement {
var valueWithoutPlaceholder: String {
if let v = value as? String, v != placeholderValue {
return v
}
return ""
}
}
Be aware, if the input is actually the placeholder this would break!

Try using accessibilityValue property of input.
func testTextFieldInitiallyEmpty {
let input = XCUIApplication().textFields["My Text Field"]
XCTAssertEqual(input.accessibilityValue, "")
}
If you command+click the property, you can see the following..
/*
Returns a localized string that represents the value of the element, such as the value
of a slider or the text in a text field. Use only when the label of the element
differs from a value. For example: A volume slider has a label of "Volume", but a value of "60%".
default == nil
default on UIKit controls == values for appropriate controls
Setting the property will change the value that is returned to the accessibility client.
*/
public var accessibilityValue: String?

Related

Get the current text inside of a textfield for IOS custom keyboard

I am developing a IOS custom keyboard. I was wondering if there was a way to fetch the current text inside of the text field and how it would work.
For example, we can use textDocumentProxy.hasText() to see if the textfield has text inside but I want to know the exact string that is inside the textfield.
The closest things would be textDocumentProxy.documentContextBeforeInput and textDocumentProxy.documentContextAfterInput. These will respect sentences and such, which means if the value is a paragraph, you will only get the current sentence. Users have been known to retrieve the entire string by repositioning the cursor multiple times until everything is retrieved.
Of course, you generally do not have to worry about this if the field expects a single value like a username, email, id number, etc. Combining the values of both before and after input contexts should suffice.
Sample Code
For the single phrase value, you would do:
let value = (textDocumentProxy.documentContextBeforeInput ?? "") + (textDocumentProxy.documentContextAfterInput ?? "")
For values that might contain sentence ending punctuation, it will be a little more complicated as you need to run it on a separate thread. Because of this, and the fact that you have to move the input cursor to get the full text, the cursor will visibly move. It is also unknown whether this will be accepted into the AppStore (after all, Apple probably did not add an easy way to get the full text on purpose in order to prevent official custom keyboards from invading a user's privacy).
Note: the below code is based off of this Stack Overflow answer except modified for Swift, removed unnecessary sleeps, uses strings with no custom categories, and uses a more efficient movement process.
func foo() {
dispatch_async(dispatch_queue_create("com.example.test", DISPATCH_QUEUE_SERIAL)) { () -> Void in
let string = self.fullDocumentContext()
}
}
func fullDocumentContext() {
let textDocumentProxy = self.textDocumentProxy
var before = textDocumentProxy.documentContextBeforeInput
var completePriorString = "";
// Grab everything before the cursor
while (before != nil && !before!.isEmpty) {
completePriorString = before! + completePriorString
let length = before!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
textDocumentProxy.adjustTextPositionByCharacterOffset(-length)
NSThread.sleepForTimeInterval(0.01)
before = textDocumentProxy.documentContextBeforeInput
}
// Move the cursor back to the original position
self.textDocumentProxy.adjustTextPositionByCharacterOffset(completePriorString.characters.count)
NSThread.sleepForTimeInterval(0.01)
var after = textDocumentProxy.documentContextAfterInput
var completeAfterString = "";
// Grab everything after the cursor
while (after != nil && !after!.isEmpty) {
completeAfterString += after!
let length = after!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
textDocumentProxy.adjustTextPositionByCharacterOffset(length)
NSThread.sleepForTimeInterval(0.01)
after = textDocumentProxy.documentContextAfterInput
}
// Go back to the original cursor position
self.textDocumentProxy.adjustTextPositionByCharacterOffset(-(completeAfterString.characters.count))
let completeString = completePriorString + completeAfterString
print(completeString)
return completeString
}

Swift - How can I force users to import only integer in UITextField?

Normally, in Text Field, users can enter a String, and even if the users entered a number, the program would automatically understand it as a string.
So, here is my problem. I want to make a program evaluating the speed of a motorcyclist.
I have a text field, a text view and a button START. What I want to do is to apply SWITCH - CASE in classifying the number that the users enter in the text field, and then I will print out my evaluations to the text view, such as "Slow", "Fast Enough" or "Dangerously Fast".
However before applying switch - case, I think that I have to force the users to only enter Integer numbers to the text field. And if they enter any alphabet letter, the text view will appear: "Wrong format! Please try again!"
I think that I have to do something with the statement if to solve the problem, but the truth is I've just started learning Swift, and couldnt think of any possible solutions. Please help me.
Thank you.
If you are using storyboard just select the TextField and change the Keyboard type to NumberPad. This will only allow integers to be entered. Then you could just turn it into a Int when you get back the input.
if let convertedSpeed = Int(textField.text) {
// Implement whatever you want
} else {
// Notify user of incorrect input
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
if textField == Number_Txt // your text filed name
{
var result = true
let prospectiveText = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string)
if string.characters.count > 0
{
let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
let resultingStringLengthIsLegal = prospectiveText.characters.count > 0
let scanner = NSScanner(string: prospectiveText)
let resultingTextIsNumeric = scanner.scanDecimal(nil) && scanner.atEnd
result = replacementStringIsLegal && resultingStringLengthIsLegal && resultingTextIsNumeric
}
return result
}
else
{
return true
}
}
You can solve it in two ways.
Convert the typed text to Integer value.
Int(textfield.text!)
This one is very simpler. Choose the keyboard type as Numeric/ Numbers and Punctuation pad. So that, user can type only the nos.
Hope it helps..
You can specify the keyboard type of a textfield in storyboard, under attributes inspector.
"Decimal" would be the way to go for you (assuming that possible input can be e.g. 180.5)
To move on, you still can check the input like this:
if (Int(textfield.text!) != nil ) {
//Valid number, do stuff
} else {
textfield.text = "Wrong format! Please try again!"
}
EDIT:
The ' != nil ' means the following:
The Initializer of Int is failable. That means if you pass a string which does not contain a valid number, it will return nil (null if you are coming from java/c#). But if the string does contain a valid number, it will return a valid Int, therefore its not nil. I hope this makes it clear to you ;)

Set label value dynamically in a table cell

hello I am having a very weird problem in my code and I don't know whats actually going on here.
I have label set in my view controller and when I set the value like this
cell.totalTripsLabel.text = "55"
It works.
But If I try to set value from dictionary,
cell.totalTripsLabel.text = self.dict["totalTrips"]! as? String
It doesn't work at all and nothing displays on ViewController.
If I print the value like this
print(self.dict["totalTrips"]!)
It successfully prints the integer value
But If I do this
print(self.dict["totalTrips"]! as? String)
It prints nil
So I figure out by casting a value to string, it prints nil. So question is how I can set value in label which accepts string value
Dictionary result is like this
{
totalTrips = 2;
}
try this
cell.totalTripsLabel.text = String(self.dict["totalTrips"]!)

UILabel AutoResize Swift

I'm learning Swift, but I Have two "stupid" Problems.
the first, I'd like autosize my UILabel
The second, I have another UIlabel and I want to put name and surname in it's field
I tried with
#IBOutlet weak var title: UILabel!
title.text = currentPerson?.name+""+currentPerson?.surname
But I have this error
Value of optional type 'String?' not unwrapped; did you mean to use
"!" or "?" ?
Generally advised to ask 1 question per post so you get clear responses & don't mix topics, but...
In XCode storyboard "Attributes Inspector" you can change "Autoshrink" from "Fixed Font Size" to minimum font size or scale. Also change "Lines" from default 1 to 0. You'd also need to set some AutoLayout constraints to pin the label to superview or other elements in a way that will allow it to scale. Can't say more w/o seeing storyboard.
By using optional chaining to set the label text you're trying to set the label's .text property to an optional type String? instead of a String. Those aren't equivalent. An optional of type String? might contain a String, or it might be nil. The UILabel expects you to use a String instance, so it's complaining about the mismatch.
One approach is to explicitly check the optional value against nil:
if currentPerson != nil {
title.text = "\(currentPerson.name) \(currentPerson.surname)"
}
else {
title.text = ""
}
Swift's optional binding is similar to the first option, but you create a temporary constant and can reference its properties. If currentPerson is not nil, then the if block executes.
// current convention would be to use "currentPerson" on both sides, which can be confusing. The left side is a temporary constant & the right side is the optional property you've declared somewhere above
if let aPerson = currentPerson {
title.text = "\(aPerson.name) \(aPerson.surname)"
}
else {
title.text = ""
}
Alternatively, as the error message suggests, you could Force Unwrap the optional value to access the name properties:
title.text = currentPerson!.name + " " + currentPerson!.surname
This assumes that currentPerson is never nil. If it is nil, your app will crash here.
Also note you can concatenate using + and " " or with string interpolation.

Weird error in accessing the text of UIButton in swift

When I write a simple function such as this:
#IBAction func buttonTapped(theButton:UIButton) {
println(theButton.titleLabel.text);
}
It gives me an error: UILabel doesn't have a label called text.
However, when I change it to this:
#IBAction func buttonTapped(theButton:UIButton) {
println(theButton.titleLabel?.text);
}
It works fine, but it prints out something like this:
Optional("1");
What I am doing wrong? I am expecting a value of 1. But it is printing out Optional("1") and secondly, it is working fine when println(theButton.titleLabel?.text);
You can get directly from
let title = theButton.currentTitle!
Optional chaining makes the result optional, so you are printing optional value: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/OptionalChaining.html
With optional binding you can print the value only if it exits.
if let text = theButton.titleLabel?.text {
println(text)
} else {
// text doesn't have value
}
#Kirsteins's answer shows how to obtain the button label text in a safe manner.
Remember that:
UIButton has a titleLabel, which is an optional UILabel.
UILabel has a text property, which is an optional String
so there are 2 optionals in the chain. You can use optional binding as in #Kirsteins's answer, or use forced unwrapping:
let text = theButton.titleLabel!.text!
which however I discourage using, because if any of the 2 is nil you'll have a runtime exception. But for completeness it's worth mentioning.
The buttons titleLabel property returns an optional UILabel, that means it's possible that the button doesn't have a titleLabel.
var titleLabel: UILabel? { get }
If you don't set a title to the button, then the button doesn't have a titleLabel property, the iOS framework adds the titleLabel only if the button has a title, I think this happens to reduce memory.
This is why you have to put the "?" (is called optional chaining you can read about it here http://bit.ly/1vrSOi1) in that line, but this usually get auto completed by Xcode itself.
Kirsteins answers it correctly but misses one small detail
if your object can be nil (optional) you need to check first if it exists to then access its value, like this:
if let text = theButton.titleLabel?.text {
println(text)
}
but you can also ignore the if and just call it like this:
let text : String = theButton.titleLabel?.text
// If theButton.titleLabel don't exists then text will be nil
this happen if the IBOutlet was declared with ? but if you declare with ! that means you know that it could be nil, but you never want it to be nil, for a IBOutlet i prefer this approach since if the IBOutlet is not connected then maybe something is worn with my code.
#IBOutlet var theButton : UIButton!
// and get text value as
theButton.titleLabel!.text
this will ensure theButton.titleLabel could be nil, but in this part of code it is required, hope this helps to understand the difference between optional (?) and optional required (!)

Resources