is there a way to count the characters of an XCUIElement Textfield? - ios

Using the UITextfield class in Swift you can do a function like this:
private func signIn() {
if emailTextField.text?.characters.count == 0 {
invalidEmailLabel.text = NSLocalizedString("Required", comment: "Required text field")
Constants.FormValidation.showValidationLabel(invalidEmailLabel)
return
}
Is there any way to do the same or something similar with XCUIElements?
I would like to do something like this:
func testExample() {
let app = XCUIApplication()
let emailTextField = app.textFields["username"]
if emailTextField.text.characters.count == "0"{
let emailTextField = app.textFields["username"]
emailTextField.tap()
emailTextField.typeText("example")
let passwordTextField = app.secureTextFields["password"]
passwordTextField.tap()
passwordTextField.typeText("whatever")
app.buttons["Done"].tap()
}
if emailTextField.text.characters.count != "0"{
let passwordTextField = app.secureTextFields["password"]
passwordTextField.tap()
passwordTextField.typeText("whatever")
app.buttons["Done"].tap()
}
How could it be done so that it works?
Thanks.

You can count the characters of a text field element by doing:
let emailValue = emailTextField.value as! String
if emailValue.characters.count == 0 {
// do something
}

Related

Issue with emojis in TextView in Swift

I’m using the below code to get the cursor position when the user types something in the textView.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
inputChar = text
inputRange = range
inputIndex = inputRange?.upperBound
self.presenter.hashtagDataArray.removeAll()
if(text == "\n") {
textView.resignFirstResponder()
toggleTableView(toggle: true)
return false
}
let str = (textView.text + text)
if str.utf16.count <= MediaPostViewController.descCharacterLimits {
return true
}
let numberOfChars = str.utf16.count
lbbbl_DescCount.text = "\(textView.text.utf16.count)/\(MediaPostViewController.descCharacterLimits)"
return (numberOfChars <= MediaPostViewController.descCharacterLimits) || (str.utf16.count < textView.text.utf16.count)
}
func textViewDidChange(_ textView: UITextView) {
var indexPosition : Int?
updateCharacterCount()
self.mainScrollView.isScrollEnabled = false
if textView.text != "" {
inputIndex = (String(textView.text.utf16) as NSString?)?.substring(with: NSRange(location: 0, length: inputRange!.location)).count
cursorPosition = inputIndex! + 1
inputText = textView.text!
textViewEndIndex = textView.text.unicodeScalars.endIndex.utf16Offset(in: textView.text)
if #available(iOS 10.2, *) {
inputText = textView.text.replaceEmoji(with: "#")
} else {
//Fallback on earlier versions
}
if inputChar == "" || inputChar == " "{
indexPosition = cursorPosition!-2
}
else {
indexPosition = cursorPosition!-1
}
guard let enteredText = inputText?.utf16.subString(from: 0, to: indexPosition!) else { return }
guard let lastdelimiterposition = enteredText.lastIndexPosition(of: "#") else { return }
hashwordstartIndex = lastdelimiterposition
checkhashinword = inputText?.utf16.subString(from: lastdelimiterposition, to: indexPosition!)
if inputChar == "" || inputChar == " "{
spaceCharactersCheck = 1
}
if spaceCharactersCheck == 1{
checkhashword = checkhashinword?.components(separatedBy: " ").filter({!$0.contains("#")}).joined(separator: " ")
checkhashedword = checkhashinword?.components(separatedBy: " ").filter({$0.contains("#")}).joined(separator: " ")
}
if let checkhashinword = checkhashinword {
if checkhashinword.utf16.count > 1 && !(checkhashinword.contains(" ")){
self.presenter.returnHashTagsData((checkhashinword.utf16.subString(from: 1, to: checkhashinword.utf16.count-1))!)
}
}
}
else {
self.toggleTableView(toggle: true)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let rowSelected = tableView.cellForRow(at: indexPath)?.textLabel?.text
var textViewValue : String?
textViewValue = textView_Desc.text!
let startIndex = String.Index(utf16Offset: hashwordstartIndex!, in: String(textViewValue!))
let hashwordendIndex = String.Index(utf16Offset: inputIndex!, in: String(textViewValue!))
let range = startIndex...hashwordendIndex
if var strNewText = textViewValue?.components(separatedBy: "#") {
if strNewText.count > 1 {
if let textlabel = rowSelected {
strNewText[strNewText.count - 1] = textlabel
}
}
if var rowSelected = rowSelected {
if let checkhashword = checkhashword
{
if checkhashword != ""
{
rowSelected = rowSelected.appending(" ").appending(checkhashword).appending(String(checkhashedword!))
}
}
if textViewValue != ""{
if textViewEndIndex != inputIndex {
do {
if let result = textViewValue?.replaceSubrange(range, with: rowSelected)
{
print("result:\(result)")
}
else {
throw RangeException.notaValidRange
}
}
catch {
}
}
}
spaceCharactersCheck = 0
checkhashword = ""
checkhashedword = ""
}
let combinedText = strNewText.joined(separator: "#")//.appending(" ")
if combinedText.count-1 > MediaPostViewController.descCharacterLimits {
if textView_Desc.text.count-1 > MediaPostViewController.descCharacterLimits {
toggleTableView(toggle: true)
return
}
}
if textViewEndIndex != inputIndex {
textView_Desc.text = textViewValue
}
else {
textView_Desc.text = combinedText.replacingOccurrences(of: "##", with: "#")
}
lbbbl_DescCount.text = "\(textView_Desc.text.count)/\(MediaPostViewController.descCharacterLimits)"
}
toggleTableView(toggle: true)
self.hideScrollView.isHidden = false
self.view.bringSubviewToFront(hideScrollView)
}
}
Use case :
Trying to implement hashtags similar to Instagram.
Approach:
There is a textView and I’ve added a tableView beneath that. The tableView gets data from API call based on the user input in textView. For instance, if user types #a then I show the tableView and tableView is loaded with suggestions like (#abc,#abcd, etc) from API call. The user can select a row and after selection, I hide the tableView. It works perfectly fine when the user enters hashtags between a text like #abc #insta and say if the user tries to type #ba between this #abc #insta it gets perfectly inserted after user chooses a suggestion from tableView (like #abc#bat#insta)
Problem:
When I have emojis the text replaces the emoji. For eg: if the user enters emoi#a then it will get a list starting with #a (like #abc,#ab etc) and now if the user selects #abc then it populates the textView like #abc#a and emoji disappears.
I don’t see a solution to this problem in any Github repositories.
Has anyone faced a similar problem?

How to add two numbers in Swift only when both of the text fields are filled in

I'm trying to add two numbers in Swift 5, and I want to add some error checks. I don't want to make it possible for a user to click on the plus button if both of the text fields are not filled in. I tried with the if state below but it did not work.
the whole function:
#IBAction func sum(_ sender: Any) {
let one = input1.text
let oneInt = Int(one!)
let two = input2.text
let twoInt = Int(two!)
let total = oneInt! + twoInt!
label.text = "\(total)"
if(input2.text == nil){
addBtn.isEnabled = false
}
if(input1.text == nil){
addBtn.isEnabled = false
}
}
Try to use guard like this. If your input field does not contain any value that field return blank string and when you try to get integer value from that string it will return nil and your add button will be disable.
#IBAction func sum(_ sender: Any) {
guard let text1 = input1.text, let intValue1 = Int(text1) else {
addBtn.isEnabled = false
return
}
guard let text2 = input2.text, let intValue2 = Int(text2) else {
addBtn.isEnabled = false
return
}
label.text = "\(intValue1 + intValue2)"
}
A nice and simple way is to addTarget to your textFiels. This will enable you to handle the events on the text field. In this scenario we'll use .editingChanged and use a single selector to achieve our goal:
What we'll do : We will listen for when someone types something in the textfield. Whenever a text changed was made, we'll check to see if all the textfields was populated and then if it was we enable the sum button.
A small controller sample :: Make sure to read the comments to understand the code faster
import UIKit
class ViewController: UIViewController {
#IBOutlet var textfield1: UITextField!
#IBOutlet var textfield2: UITextField!
#IBOutlet var sumButton: UIButton!
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
sumButton.isEnabled = false /// Disable the button first thing
[textfield1, textfield2].forEach {
$0.addTarget(self, action: #selector(editingChanged(_:)), for: .editingChanged) /// add targets to handle the events (in your case it listens for the 'editingChanged' event )
}
}
#objc func editingChanged(_ textField: UITextField) {
/// Here we just loop through all our textfields
for each in [textfield1, textfield2] {
if let text = each?.text { /// Just make sure the textfields text is not nil
if text.count < 1 {
// If the textfiels text has no value in, then we keep the button disabled and return
sumButton.isEnabled = false
return
}
} else {
/// Else if the text field's text is nill, then return and keep the button disabled
sumButton.isEnabled = false
return
}
}
sumButton.isEnabled = true /// If the code reaches this point, it means the textfields passed all out checks and the button can be enabled
}
#IBAction func sum(_ sender: Any) {
let one = textfield1.text!
let two = textfield2.text!
guard let oneInt = Int(one), let twoInt = Int(two) else {
print("Whatever was in that text fields, couldn't be converted to an Int")
label.text = "Be sure to add numbers."
return
}
let total = oneInt + twoInt
label.text = "\(total)"
}
}
textfields are not nil but empty strings. so make your comparison like :
if input1.text == "" {
// do your check here
}
Seems like you want to start with addBtn.isEnabled = false then update it whenever the user enters two valid integers into the text fields, i.e. Int(input1.text ?? "") != nil && Int(input2.text ?? "") != nil. You can do this by adding a target to your textfields (input1 and input2) for .editingChanged events. For example, if you're doing this in a UIViewController, you can do this in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
addBtn.isEnabled = false
input1.addTarget(self, action: #selector(textFieldDidEdit(_:)), for: .editingChanged)
input2.addTarget(self, action: #selector(textFieldDidEdit(_:)), for: .editingChanged)
}
Where textFieldDidEdit(_:) action looks like:
#objc func textFieldDidEdit(_ sender: UITextField) {
addBtn.isEnabled = Int(input1.text ?? "") != nil && Int(input2.text ?? "") != nil
}
Finally your sum function becomes:
#IBAction func sum(_ sender: UIButton) {
guard let oneInt = Int(input1.text ?? ""), let twoInt = Int(input2.text ?? "") else {
return
}
let total = oneInt + twoInt
label.text = "\(total)"
}
As all of the number validation has moved to the textFieldDidEdit(_:) function.

Adding Double and a String to then display on a UILabel

I am trying to add up a value that is entered in the text field with a value specified as a double and then returning the value on a label. The code that I have is :
#IBOutlet weak var enterField: UITextField!
var weekOneTotal:Double = 0
#IBAction func addButton(_ sender: Any) {
addCorrectValue()
}
func addCorrectValue () {
guard let addAmount = convertAmount(input: enterField.text!) else {
print("Invalid amount")
return
}
let newValue = weekOneTotal += addAmount
secondScreen.weekOneAmountLabel.text = String(newValue)
}
func convertAmount (input:String) -> Double? {
let numberFormatter = NumberFormatter ()
numberFormatter.numberStyle = .decimal
return numberFormatter.number(from: input)?.doubleValue
}
Try this:
func addCorrectValue () {
guard let addAmount = Double(enterField.text!) else {
print("Invalid amount")
return
}
let newValue = weekOneTotal + addAmount
secondScreen.weekOneAmountLabel.text = "\(String(format: "%.1f", newValue))"
}
The .1 is the number of decimals that are shown. You can adjust that to your needs. Hope I understood the question and this works for you!
You probably want to increase value of weekOneTotal variable by converted amount and then you want to use this value as text of some label
weekOneTotal += addAmount
secondScreen.weekOneAmountLabel.text = String(weekOneTotal)

How to extract street, city, etc. from GMSPlace Address Components

I'm using Google Places API for iOS and can successfully retrieve nearby places and present the address as a string. What I'm trying to do is extract address components such as city to store in a database.
The documentation indicates a GMSPlace has an addressComponents property (an array), but I can't seem to figure how to use this property.
The code below provides sets the entire address to the text of a label, but I can't get beyond that:
Edit ---- added code that shows how I'm trying to access Address Components
venueLabel.isHidden = false
venueLabel.text = selectedPlace?.name
addressLabel.isHidden = false
addressLabel.text = selectedPlace?.formattedAddress
let addressComponents = selectedPlace?.addressComponents
for component in addressComponents! {
let city = component["city"] //Error Type GMSPaceAddressComponent has no subscript members
print(city)
}
A safe Swift 4.2 solution:
let name = place.addressComponents?.first(where: { $0.type == "city" })?.name
selectedPlace.addressComponents is a array of GMSAddressComponent which have 2 properties type and name.
In you case it will be like
for component in addressComponents! {
if component.type == "city" {
print(component.name)
}
}
GMSAddressComponent is a class not a dictionary or array that's you are getting this error.
Additional component types can be referred from the link.
Swift 4
var keys = [String]()
place.addressComponents?.forEach{keys.append($0.type)}
var values = [String]()
place.addressComponents?.forEach({ (component) in
keys.forEach{ component.type == $0 ? values.append(component.name): nil}
})
print(values)
For the latest Google Places API (July 2019) this is what could help you.
Actually, Google now putting a couple of types for each element. So the "type" is now deprecated and "types" is new filed.
You can do something like this:
for addressComponent in (self.mySelectedPlace?.addressComponents)! {
for type in (addressComponent.types){
switch(type){
case "country":
var country = addressComponent.name
case "postal_code":
var postCode = addressComponent.name
default:
break
}
}
}
It's working in Swift 5 and iOS 13.3
1. Create a function for showing GMSAutocompleteViewController
func googlePlacesSearchVC() {
let acController = GMSAutocompleteViewController()
acController.delegate = self
// Specify the place data types to return.
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.formattedAddress.rawValue) |
UInt(GMSPlaceField.addressComponents.rawValue))!
acController.placeFields = fields
// Specify a filter.
let filter = GMSAutocompleteFilter()
filter.country = "IN"
acController.autocompleteFilter = filter
acController.secondaryTextColor = .darkGray
acController.primaryTextColor = .lightGray
acController.primaryTextHighlightColor = .black
acController.tableCellBackgroundColor = .whiteThree
acController.tableCellSeparatorColor = .lightGray
// Display the autocomplete view controller.
present(acController, animated: true) {
let views = acController.view.subviews
let subviewsOfSubview = views.first!.subviews
let subOfNavTransitionView = subviewsOfSubview[1].subviews
let subOfContentView = subOfNavTransitionView[1].subviews
let searchBar = subOfContentView[0] as! UISearchBar
searchBar.searchTextField.placeholder = "places_picker_hint_add_address".localized
searchBar.searchBarStyle = .minimal
searchBar.searchTextField.font = UIFont.screenTitle16Pt
searchBar.searchTextField.backgroundColor = UIColor.white
searchBar.searchTextField.leftView?.tintColor = .darkGray
searchBar.delegate?.searchBar?(searchBar, textDidChange: "")
}
}
2. Call that function where ever you need, for ex:-
override func viewDidLoad() {
super.viewDidLoad()
googlePlacesSearchVC()
}
3. Call GMSAutoCompleteViewControllerDelegates Methods.
Here will get all details like Street, City, etc. from GMSPlace Address Components
extension ViewController {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
// Show HouseAndFlat
if place.name?.description != nil {
yourTxtField.text = place.name?.description ?? ""
}
// Show latitude
if place.coordinate.latitude.description.count != 0 {
var latitude = place.coordinate.latitude
}
// Show longitude
if place.coordinate.longitude.description.count != 0 {
selectedLongitude = place.coordinate.longitude
}
// Show AddressComponents
if place.addressComponents != nil {
for addressComponent in (place.addressComponents)! {
for type in (addressComponent.types){
switch(type){
case "sublocality_level_2":
yourTxtField.text = addressComponent.name
case "sublocality_level_1":
yourTxtField.text = addressComponent.name
case "administrative_area_level_2":
yourTxtField.text = addressComponent.name
case "administrative_area_level_1":
yourTxtField.text = addressComponent.name
case "country":
yourTxtField.text = addressComponent.name
case "postal_code":
yourTxtField.text = addressComponent.name
default:
break
}
}
}
}
dismiss(animated: true, completion: nil)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
dismiss(animated: true, completion: nil)
}
}
I have found an answer from this link
I am using this extension:
extension Array where Element == GMSAddressComponent {
func valueFor(placeType: String, shortName: Bool = false) -> String? {
// bug in the google or apple framework. This cast must stay.
// Withou it crashing.
guard let array = self as? NSArray else { return nil }
let result = array
.compactMap { $0 as? GMSAddressComponent }
.first(where: {
$0.types
.first(where: { $0 == placeType }) == placeType
})
return shortName ? result?.shortName : result?.name
}
}
Usage:
let place: GMSPlace
// ...
place.addressComponents?.valueFor(placeType: kGMSPlaceTypeRoute, shortName: true)
I will extend #Ramis answer, in any case you want to check for multiple types if one of them exist, e.x
addressComponents?.valueFor(placeTypes: kGMSPlaceTypePostalTown, kGMSPlaceTypeLocality)
implementation
extension Array where Element == GMSAddressComponent {
func valueFor(placeTypes: String..., shortName: Bool = false) -> String? {
let array = self as NSArray
let result = array
.compactMap { $0 as? GMSAddressComponent }
.first(where: {
placeTypes.contains($0.types.first(where: { placeTypes.contains($0) }) ?? "")
})
return shortName ? result?.shortName : result?.name
}
}
I could not find this anywhere for Objective-C, for those who follow behind me:
for (int i = 0; i < [place.addressComponents count]; i++)
{
NSLog(#"name %# = type %#", place.addressComponents[i].name, place.addressComponents[i].type);
}
for ( GMSAddressComponent * component in place.addressComponents) {
if ( [component.type isEqual: #"locality"] )
{
NSLog(#"Current city %# ", component.name);
}
}
var keys = [String]()
place.addressComponents?.forEach{keys.append($0.type)}
var values = [String]()
place.addressComponents?.forEach({ (component) in
keys.forEach{ component.type == $0 ? values.append(component.name): nil}
print("All component type \(component.type)")
if component.type == "locality"{
print("This is city name \(component.name)")
}
})
print(values)
I found this, I hope I help you
let arrays : NSArray = place.addressComponents! as NSArray
for i in 0..<arrays.count {
let dics : GMSAddressComponent = arrays[i] as! GMSAddressComponent
let str : NSString = dics.type as NSString
if (str == "country") {
print("Country: \(dics.name)")
self.pais = dics.name
}
else if (str == "administrative_area_level_2") {
print("City: \(dics.name)")
self.ciudad = dics.name
}
print("City: (String(describing: place.addressComponents?.first(where: {$0.type == "locality"})?.name))")
print("Postal Code: (String(describing: place.addressComponents?.first(where: {$0.type == "postal_code"})?.name))")
print("State: (String(describing: place.addressComponents?.first(where: {$0.type == "administrative_area_level_1"})?.name))"
Use this description for work like this.
https://developers.google.com/maps/documentation/places/ios-sdk/place-data-fields

Converting String to Int with Swift

The application basically calculates acceleration by inputting Initial and final velocity and time and then use a formula to calculate acceleration. However, since the values in the text boxes are string, I am unable to convert them to integers.
#IBOutlet var txtBox1 : UITextField
#IBOutlet var txtBox2 : UITextField
#IBOutlet var txtBox3 : UITextField
#IBOutlet var lblAnswer : UILabel
#IBAction func btn1(sender : AnyObject) {
let answer1 = "The acceleration is"
var answer2 = txtBox1
var answer3 = txtBox2
var answer4 = txtBox3
Updated answer for Swift 2.0+:
toInt() method gives an error, as it was removed from String in Swift 2.x. Instead, the Int type now has an initializer that accepts a String:
let a: Int? = Int(firstTextField.text)
let b: Int? = Int(secondTextField.text)
Basic Idea, note that this only works in Swift 1.x (check out ParaSara's answer to see how it works in Swift 2.x):
// toInt returns optional that's why we used a:Int?
let a:Int? = firstText.text.toInt() // firstText is UITextField
let b:Int? = secondText.text.toInt() // secondText is UITextField
// check a and b before unwrapping using !
if a && b {
var ans = a! + b!
answerLabel.text = "Answer is \(ans)" // answerLabel ie UILabel
} else {
answerLabel.text = "Input values are not numeric"
}
Update for Swift 4
...
let a:Int? = Int(firstText.text) // firstText is UITextField
let b:Int? = Int(secondText.text) // secondText is UITextField
...
myString.toInt() - convert the string value into int .
Swift 3.x
If you have an integer hiding inside a string, you can convertby using the integer's constructor, like this:
let myInt = Int(textField.text)
As with other data types (Float and Double) you can also convert by using NSString:
let myString = "556"
let myInt = (myString as NSString).integerValue
You can use NSNumberFormatter().numberFromString(yourNumberString). It's great because it returns an an optional that you can then test with if let to determine if the conversion was successful.
eg.
var myString = "\(10)"
if let myNumber = NSNumberFormatter().numberFromString(myString) {
var myInt = myNumber.integerValue
// do what you need to do with myInt
} else {
// what ever error code you need to write
}
Swift 5
var myString = "\(10)"
if let myNumber = NumberFormatter().number(from: myString) {
var myInt = myNumber.intValue
// do what you need to do with myInt
} else {
// what ever error code you need to write
}
edit/update: Xcode 11.4 • Swift 5.2
Please check the comments through the code
IntegerField.swift file contents:
import UIKit
class IntegerField: UITextField {
// returns the textfield contents, removes non digit characters and converts the result to an integer value
var value: Int { string.digits.integer ?? 0 }
var maxValue: Int = 999_999_999
private var lastValue: Int = 0
override func willMove(toSuperview newSuperview: UIView?) {
// adds a target to the textfield to monitor when the text changes
addTarget(self, action: #selector(editingChanged), for: .editingChanged)
// sets the keyboard type to digits only
keyboardType = .numberPad
// set the text alignment to right
textAlignment = .right
// sends an editingChanged action to force the textfield to be updated
sendActions(for: .editingChanged)
}
// deletes the last digit of the text field
override func deleteBackward() {
// note that the field text property default value is an empty string so force unwrap its value is safe
// note also that collection remove at requires a non empty collection which is true as well in this case so no need to check if the collection is not empty.
text!.remove(at: text!.index(before: text!.endIndex))
// sends an editingChanged action to force the textfield to be updated
sendActions(for: .editingChanged)
}
#objc func editingChanged() {
guard value <= maxValue else {
text = Formatter.decimal.string(for: lastValue)
return
}
// This will format the textfield respecting the user device locale and settings
text = Formatter.decimal.string(for: value)
print("Value:", value)
lastValue = value
}
}
You would need to add those extensions to your project as well:
Extensions UITextField.swift file contents:
import UIKit
extension UITextField {
var string: String { text ?? "" }
}
Extensions Formatter.swift file contents:
import Foundation
extension Formatter {
static let decimal = NumberFormatter(numberStyle: .decimal)
}
Extensions NumberFormatter.swift file contents:
import Foundation
extension NumberFormatter {
convenience init(numberStyle: Style) {
self.init()
self.numberStyle = numberStyle
}
}
Extensions StringProtocol.swift file contents:
extension StringProtocol where Self: RangeReplaceableCollection {
var digits: Self { filter(\.isWholeNumber) }
var integer: Int? { Int(self) }
}
Sample project
swift 4.0
let stringNumber = "123"
let number = Int(stringNumber) //here number is of type "Int?"
//using Forced Unwrapping
if number != nil {
//string is converted to Int
}
you could also use Optional Binding other than forced binding.
eg:
if let number = Int(stringNumber) {
// number is of type Int
}
In Swift 4.2 and Xcode 10.1
let string = "789"
if let intValue = Int(string) {
print(intValue)
}
let integerValue = 789
let stringValue = String(integerValue)
OR
let stringValue = "\(integerValue)"
print(stringValue)
//Xcode 8.1 and swift 3.0
We can also handle it by Optional Binding, Simply
let occur = "10"
if let occ = Int(occur) {
print("By optional binding :", occ*2) // 20
}
Swift 3
The simplest and more secure way is:
#IBOutlet var textFieldA : UITextField
#IBOutlet var textFieldB : UITextField
#IBOutlet var answerLabel : UILabel
#IBAction func calculate(sender : AnyObject) {
if let intValueA = Int(textFieldA),
let intValueB = Int(textFieldB) {
let result = intValueA + intValueB
answerLabel.text = "The acceleration is \(result)"
}
else {
answerLabel.text = "The value \(intValueA) and/or \(intValueB) are not a valid integer value"
}
}
Avoid invalid values setting keyboard type to number pad:
textFieldA.keyboardType = .numberPad
textFieldB.keyboardType = .numberPad
In Swift 4:
extension String {
var numberValue:NSNumber? {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter.number(from: self)
}
}
let someFloat = "12".numberValue
Useful for String to Int and other type
extension String {
//Converts String to Int
public func toInt() -> Int? {
if let num = NumberFormatter().number(from: self) {
return num.intValue
} else {
return nil
}
}
//Converts String to Double
public func toDouble() -> Double? {
if let num = NumberFormatter().number(from: self) {
return num.doubleValue
} else {
return nil
}
}
/// EZSE: Converts String to Float
public func toFloat() -> Float? {
if let num = NumberFormatter().number(from: self) {
return num.floatValue
} else {
return nil
}
}
//Converts String to Bool
public func toBool() -> Bool? {
return (self as NSString).boolValue
}
}
Use it like :
"123".toInt() // 123
i have made a simple program, where you have 2 txt field you take input form the user and add them to make it simpler to understand please find the code below.
#IBOutlet weak var result: UILabel!
#IBOutlet weak var one: UITextField!
#IBOutlet weak var two: UITextField!
#IBAction func add(sender: AnyObject) {
let count = Int(one.text!)
let cal = Int(two.text!)
let sum = count! + cal!
result.text = "Sum is \(sum)"
}
hope this helps.
Swift 3.0
Try this, you don't need to check for any condition I have done everything just use this function. Send anything string, number, float, double ,etc,. you get a number as a value or 0 if it is unable to convert your value
Function:
func getNumber(number: Any?) -> NSNumber {
guard let statusNumber:NSNumber = number as? NSNumber else
{
guard let statString:String = number as? String else
{
return 0
}
if let myInteger = Int(statString)
{
return NSNumber(value:myInteger)
}
else{
return 0
}
}
return statusNumber
}
Usage:
Add the above function in code and to convert use
let myNumber = getNumber(number: myString)
if the myString has a number or string it returns the number else it returns 0
Example 1:
let number:String = "9834"
print("printing number \(getNumber(number: number))")
Output: printing number 9834
Example 2:
let number:Double = 9834
print("printing number \(getNumber(number: number))")
Output: printing number 9834
Example 3:
let number = 9834
print("printing number \(getNumber(number: number))")
Output: printing number 9834
About int() and Swift 2.x: if you get a nil value after conversion check if you try to convert a string with a big number (for example: 1073741824), in this case try:
let bytesInternet : Int64 = Int64(bytesInternetString)!
Latest swift3 this code is simply to convert string to int
let myString = "556"
let myInt = Int(myString)
Because a string might contain non-numerical characters you should use a guard to protect the operation. Example:
guard let labelInt:Int = Int(labelString) else {
return
}
useLabelInt()
I recently got the same issue. Below solution is work for me:
let strValue = "123"
let result = (strValue as NSString).integerValue
Swift5 float or int string to int:
extension String {
func convertStringToInt() -> Int {
return Int(Double(self) ?? 0.0)
}
}
let doubleStr = "4.2"
// print 4
print(doubleStr.convertStringToInt())
let intStr = "4"
// print 4
print(intStr.convertStringToInt())
Use this:
// get the values from text boxes
let a:Double = firstText.text.bridgeToObjectiveC().doubleValue
let b:Double = secondText.text.bridgeToObjectiveC().doubleValue
// we checking against 0.0, because above function return 0.0 if it gets failed to convert
if (a != 0.0) && (b != 0.0) {
var ans = a + b
answerLabel.text = "Answer is \(ans)"
} else {
answerLabel.text = "Input values are not numberic"
}
OR
Make your UITextField KeyboardType as DecimalTab from your XIB or storyboard, and remove any if condition for doing any calculation, ie.
var ans = a + b
answerLabel.text = "Answer is \(ans)"
Because keyboard type is DecimalPad there is no chance to enter other 0-9 or .
Hope this help !!
// To convert user input (i.e string) to int for calculation.I did this , and it works.
let num:Int? = Int(firstTextField.text!);
let sum:Int = num!-2
print(sum);
This works for me
var a:Int? = Int(userInput.text!)
for Swift3.x
extension String {
func toInt(defaultValue: Int) -> Int {
if let n = Int(self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)) {
return n
} else {
return defaultValue
}
}
}
Swift 4, Swift 5
There are different cases to convert from something to something data type, it depends the input.
If the input data type is Any, we have to use as before convert to actual data type, then convert to data type what we want. For example:
func justGetDummyString() -> Any {
return "2000"
}
let dummyString: String = (justGetDummyString() as? String) ?? "" // output = "2000"
let dummyInt: Int = Int(dummyString) ?? 0 // output = 2000
for Alternative solution. You can use extension a native type. You can test with playground.
extension String {
func add(a: Int) -> Int? {
if let b = Int(self) {
return b + a
}
else {
return nil
}
}
}
"2".add(1)
My solution is to have a general extension for string to int conversion.
extension String {
// default: it is a number suitable for your project if the string is not an integer
func toInt(default: Int) -> Int {
if let result = Int(self) {
return result
}
else {
return default
}
}
}
#IBAction func calculateAclr(_ sender: Any) {
if let addition = addition(arrayString: [txtBox1.text, txtBox2.text, txtBox3.text]) {
print("Answer = \(addition)")
lblAnswer.text = "\(addition)"
}
}
func addition(arrayString: [Any?]) -> Int? {
var answer:Int?
for arrayElement in arrayString {
if let stringValue = arrayElement, let intValue = Int(stringValue) {
answer = (answer ?? 0) + intValue
}
}
return answer
}
Question : string "4.0000" can not be convert into integer using Int("4.000")?
Answer : Int() check string is integer or not if yes then give you integer and otherwise nil. but Float or Double can convert any number string to respective Float or Double without giving nil. Example if you have "45" integer string but using Float("45") gives you 45.0 float value or using Double("4567") gives you 45.0.
Solution : NSString(string: "45.000").integerValue or Int(Float("45.000")!)! to get correct result.
An Int in Swift contains an initializer that accepts a String. It returns an optional Int? as the conversion can fail if the string contains not a number.
By using an if let statement you can validate whether the conversion succeeded.
So your code become something like this:
#IBOutlet var txtBox1 : UITextField
#IBOutlet var txtBox2 : UITextField
#IBOutlet var txtBox3 : UITextField
#IBOutlet var lblAnswer : UILabel
#IBAction func btn1(sender : AnyObject) {
let answer1 = "The acceleration is"
var answer2 = txtBox1
var answer3 = txtBox2
var answer4 = txtBox3
if let intAnswer = Int(txtBox1.text) {
// Correctly converted
}
}
Swift 5.0 and Above
Working
In case if you are splitting the String it creates two substrings and not two Strings . This below method will check for Any and convert it t0 NSNumber its easy to convert a NSNumber to Int, Float what ever data type you need.
Actual Code
//Convert Any To Number Object Removing Optional Key Word.
public func getNumber(number: Any) -> NSNumber{
guard let statusNumber:NSNumber = number as? NSNumber else {
guard let statString:String = number as? String else {
guard let statSubStr : Substring = number as? Substring else {
return 0
}
if let myInteger = Int(statSubStr) {
return NSNumber(value:myInteger)
}
else{
return 0
}
}
if let myInteger = Int(statString) {
return NSNumber(value:myInteger)
}
else if let myFloat = Float(statString) {
return NSNumber(value:myFloat)
}else {
return 0
}
}
return statusNumber }
Usage
if let hourVal = getNumber(number: hourStr) as? Int {
}
Passing String to check and convert to Double
Double(getNumber(number: dict["OUT"] ?? 0)
As of swift 3, I have to force my #%#! string & int with a "!" otherwise it just doesn't work.
For example:
let prefs = UserDefaults.standard
var counter: String!
counter = prefs.string(forKey:"counter")
print("counter: \(counter!)")
var counterInt = Int(counter!)
counterInt = counterInt! + 1
print("counterInt: \(counterInt!)")
OUTPUT:
counter: 1
counterInt: 2

Resources