Swift - check if attributed text is empty - swift

I have a UITextField on which I set a current value as attributed text as follows:
public var currentValue: NSAttributedString {
get {
return inputField.attributedText ?? NSAttributedString()
}
set {
inputField.attributedText = newValue
}
}
This was previously just a String but I now need to add formatting to parts of the text.
However, I have a method I need to pass on these fields which begins with the condition to see if the field is empty. Previously I started this with:
if textField.currentValue.isEmpty {
// Perform logic
}
However, obviously NSAttributedText has no isEmpty member. How can I perform the same check on NSAttributedText?

NSAttributedText has no isEmpty property but it has one called length. You can simply check if it is equal to zero:
if currentValue.length == .zero {
// Perform logic
}

There's also attributedText.string.isEmpty

Related

SwiftUI TextField won't change its contents under weird corner case with Binding<String> computed property

I have a TextField inside a SwiftUI body. I have bound it to a #State var through an intermediary binding which lets me get and set a computed value...
struct ExampleView: View {
#State var symbolToBeValidated: String = ""
var body: some View {
let binding = Binding<String> (get: {
return self.symbolToBeValidated
}, set: {
var newString = $0.uppercased()
self.symbolToBeValidated = newString // <- fig. 1: redundant assignment I wish I didn't have to put
newString = newString.replacingOccurrences(
of: #"[^A-Z]"#,
with: "",
options: .regularExpression
)
self.symbolToBeValidated = newString // <- fig. 2: the final, truly valid assignment
})
let form = Form {
Text("What symbol do you wish to analyze?")
TextField("ex.AAPL", text: binding)
// [...]
I'm using the intermediary Binding so that I can transform the string to always be an Uppercased format only containing letters A-Z (as referenced by my .regularExpression). (I'm trying to make it so that the TextField only shows a validly formatted Stock Symbol on each keypress).
This works, somewhat. The problem I discovered is that if I don't call the assignment twice (as seen in fig 1) the TextField will begin to show numbers and letters (even though it isn't included in the symbolToBeValidated string. This happens, I suspect, because SwiftUI is checking the oldValue against the newValue internally, and because it hasn't changed in the background, it doesn't call a refresh to get the internal value again. The way I've found to thwart this is to include an extra assignment before the .replacingOccurences call.
This results in the number or symbol being flashed on the screen for a blip as it is being typed by the user, then it is correctly removed by the .replacingOccurences call.
There must be a more elegant way to do this. I went down the Formatter class type and tried this alternative only because Formatter resulted in a similar behavior where the errant character would blip on the screen before being removed.
If someone knows a way for this to be intercepted before anything is displayed on the screen, I would appreciate it. This is super nit-picky, but I'm just fishing for the right answer here.
Try this:
extension Binding where Value == String {
public func validated() -> Self {
return .init(
get: { self.wrappedValue },
set: {
var newString = $0.uppercased()
newString = newString.replacingOccurrences(
of: #"[^A-Z]"#,
with: "",
options: .regularExpression
)
self.wrappedValue = newString
}
)
}
}
// ...
TextField("ex.AAPL", text: self.$symbolToBeValidated.validated())
This way also allows you to reuse and test the validation code.

Declaring a Swift Character type that could hold only ASCII characters?

Swift's Character data type is a very broad set and I have a use case where I need to declare a variable which can only hold ascii characters. The type should not be able to accept characters outside ascii.
Do we have any other in built data types that are suitable for my use case?
Or do I need to write a custom solution?
If it requires custom solution what are the best possible ways to achieve it?
As pointed in the question comments, it seems it's not possible to have a check on compile time.
A runtime solution could be:
You can check if a Character is Ascii by <#Character#>.isASCII, then you can create a custom class that only stores a value if the condition is satisfied.
struct CustomASCIIClass {
private var storedValue : Character? = nil
var value : Character? {
get {
return self.storedValue ?? nil
}
set (newValue) {
if (newValue?.isASCII ?? false) {
self.storedValue = newValue
} else {
// handle not possible value, throw something or make the variable to have nil value.
print("invalid: ", newValue)
}
}
}
}
Usage:
var a = CustomASCIIClass()
a.value = Character("A")
print(a.value) // Optional("A")
var b = CustomASCIIClass()
b.value = Character("😎")
print(b.value) // nil

Localize String comparison

I am trying to compare the strings from searchbar textfield with array of strings. My array is in English strings. I am having localize strings(Burmsese string in localisation file) for the items in the array. I have tried the string method given by apple to compare. Could anyone resolve my problem or this is still mystery and I am wasting my time. I am posting one of the method I have tried, where localized is the custom extension of string for localisation string
searchResultList = keyArray.filter {
return $0.localized.lowercased().contains(ketText.lowercased())
}
I resolved it. If we are using normal contains methods it returns false. But if we are using range, its working perfect for this issue. I have used the following,
searchResultList = keyArray.filter {
guard let _ = $0.keyString.localized.localizedStandardRange(of: ketText) else { return false }
return true
}

How to perform a logical XOR operation in swift to find out all textfields are empty or full?

I am doing a signup module which consists of 3 different pages. 1st page personal details , 2nd page login details and 3rd page payment details. While signup the 1st and 2nd pages are mandatory but the 3rd page is not. the 3rd page has 6 textfields. However the 3rd page has a validation that the user must enter all the values or must leave all the fields empty. to do this a logical XOR operation is necessary for me which i can't find out how to implement. help me. i now use the following code which is faulty
if((txtCardNumber.text!.isEmpty && txtCardType.text!.isEmpty && txtNameofCard.text!.isEmpty && txtMonthExpriy.text!.isEmpty && txtYearExpiry.text!.isEmpty && txtPayment.text!.isEmpty) == false )
i am using Swift 2.0 xcode 7.1.1. so ^ doesn't work
could any one give a logical solution or alternative solution for this?
You can do this without requiring an xor operator. This should be pretty simple to do if all your textfields are in an array. You can either create it in code or from your storyboard/xib using IBOutletCollection.
This code gets all the Bools from calling isEmpty on the textfields and puts them into a Set. If they are all true or all false, the set will only contain one value, if it's a mix of true/false then it will have two values.
let textFields: [UITextField] = ... // an array of the textfields to check
let emptyValues = Set(textFields.map { $0.text?.isEmpty ?? true })
if emptyValues.count == 1 {
print("All textfields are full or empty")
}
I would not use XOR for this problem; I do realize that they do provide the answer to your particular problem but in general I wouldn't use them for high level logic. Understandable > clever.
They are hard to understand; they represent the space of solutions which are all true and all false but most people reading your code won't understand that. Especially when you first look at the code.
How would you modify the logic? What if you wanted to require all 6 be empty or only 5 be filled.
In general, massive if statements rapidly become difficult to understand.
This is the solution that I came up with:
//Create a function which determines if all the Bool values in an array are true
func allTrue(booleanValues: [Bool]) -> Bool {
for bool in booleanValues {
if bool == false {
return false
}
}
return true
}
//And a function which determines if all Bool values are false
func allFalse(booleanValues: [Bool]) -> Bool {
for bool in booleanValues {
if bool == true {
return false
}
}
return true
}
//This is a function which determines if a UITextField is empty or not (this could also be written as a standalone function if you need it elsewhere).
//It takes in a UITextField! and returns a Bool
let isTextFieldEmpty: ((textField: UITextField!) -> Bool) = { (textfield) -> Bool in
return textfield.text?.isEmpty ?? true
}
//Create an array of all of the textfields - this makes it easy to modify if you add or remove a textfield. It's also easy to verify that you didn't miss one of your six textfields in a giant if statement
let textfields = [txtCardNumber, txtCardType, txtNameofCard, txtMonthExpriy, txtYearExpiry, txtPayment]
//Turns the array of textfields into an array of Bool values representing if they are empty; we then use our allTrue() and allFalse() functions.
let textFields = [txtCardNumber, txtCardType, txtNameofCard, txtMonthExpriy, txtYearExpiry, txtPayment]
let textFieldsAreEmpty = textFields.map(isTextFieldEmpty)
if allTrue(textFieldsAreEmpty) || allFalse(textFieldsAreEmpty) {
//Do the stuff
}

How to get length of text enter in search bar?

I want to get the length of the text that is entered in a UISearchBar
I use the function that is called when the the searcbbar text starts editing.
func searchBarTextDidBeginEditing(searchBar: UISearchBar!) {
println("EDITING")
//TODO: Get length of text entered in searchBar
}
This doesn't work:
func searchBarTextDidBeginEditing(searchBar: UISearchBar!) {
println("EDITING")
//TODO: Get length of text entered in searchBar
//Doesn't work. Error: String doesn't have a member named length
searchBar.text.length
}
How can i retrieve the length of the entered text?
Ended up doing this:
searchBar.text.bridgeToObjectiveC().length
The problem is simply that the text property, like most objects arriving from the Cocoa API, might be nil. You have to assure Swift that it is not. Like this:
let len = countElements(searchBar.text!)