Component separated by string crashing app swift - ios

I have an app that converts textField input into an array of Int's by using componentSeparatedByString(",") but when i enter more than one comma in the textfield the app crashes, been trying to find a solution online but no luck, how can i fix this? I can keep it from crashing by checking for characters.first == "," ||characters.last == ",", but not consecutive commas.
enterValueLabel.text = ""
let circuits = circuitNumbersTextField.text!.componentsSeparatedByString(",")
let circuitNumbers = circuits.map { Int($0)!}
CircuitColors(circuitNumber: circuitNumbers, phaseColors: circuitPhaseColors )
if /*circuitNumbersTextField.text!.characters.first != "," || */circuitNumbersTextField.text!.characters.last != "," || (circuitNumbersTextField.text!.characters.first != "," && circuitNumbersTextField.text!.characters.last != ",")

Here's what I would do to make your code work. What is important here is the general idea, not the specific example I'm using (although it should work for you).
First, let's safely unwrap the text label:
if let text = circuitNumbersTextField.text {
}
Now that we avoid using circuitNumbersTextField.text! we know that an error wouldn't come from there.
Then we cut the sentence in components:
if let text = circuitNumbersTextField.text {
let circuits = text.componentsSeparatedByString(",")
}
We use flatMap to safely unwrap the Optionals returned by Int():
if let text = circuitNumbersTextField.text {
let circuits = text.componentsSeparatedByString(",")
let circuitNumbers = circuits.flatMap { Int($0) }
// circuitNumbers will only contain the successfully unwrapped values
}
Your code snippet:
if let text = circuitNumbersTextField.text {
let circuits = text.componentsSeparatedByString(",")
let circuitNumbers = circuits.flatMap { Int($0) }
if (circuits.first != "," && circuits.last != ",") || circuits.first != "," || circuits.last != "," {
// condition is met
} else {
// condition is not met
}
}
You can now safely use circuitNumbers in this code block without crashing.

Related

How to Check if String begins with Alphabet Letter in Swift 5?

Problem: i am currently trying to Sort a List in SwiftUI according to the Items First Character. I also would like to implement a Section for all Items, which doesn't begin with a Character of the Alphabet (Numbers, Special Chars).
My Code so far:
let nonAlphabetItems = items.filter { $0.name.uppercased() != /* beginns with A - Z */ }
Does anyone has a Solution for this Issue. Of course I could do a huge Loop Construct, however I hope there is a more elegant way.
Thanks for your help.
You can check if a string range "A"..."Z" contains the first letter of your name property:
struct Item {
let name: String
}
let items: [Item] = [.init(name: "Def"),.init(name: "Ghi"),.init(name: "123"),.init(name: "Abc")]
let nonAlphabetItems = items.filter { !("A"..."Z" ~= ($0.name.first?.uppercased() ?? "#")) }
nonAlphabetItems // [{name "123"}]
Expanding on this topic we can extend Character to add a isAsciiLetter property:
extension Character {
var isAsciiLetter: Bool { "A"..."Z" ~= self || "a"..."z" ~= self }
}
This would allow to extend StringProtocol to check is a string starts with an ascii letter:
extension StringProtocol {
var startsWithAsciiLetter: Bool { first?.isAsciiLetter == true }
}
And just a helper to negate a boolean property:
extension Bool {
var negated: Bool { !self }
}
Now we can filter the items collection as follow:
let nonAlphabetItems = items.filter(\.name.startsWithAsciiLetter.negated) // [{name "123"}]
If you need an occasional filter, you could simply write a condition combining standard predicates isLetter and isASCII which are already defined for Character. It's as simple as:
let items = [ "Abc", "01bc", "Ça va", "", " ", "𓀫𓀫𓀫𓀫"]
let nonAlphabetItems = items.filter { $0.isEmpty || !$0.first!.isASCII || !$0.first!.isLetter }
print (nonAlphabetItems) // -> Output: ["01bc", "Ça va", "", " ", "𓀫𓀫𓀫𓀫"]
If the string is not empty, it has for sure a first character $0.first!. It is tempting to use isLetter , but it appears to be true for many characters in many local alphabets, including for example the antique Egyptian hieroglyphs like "𓀫" or the French alphabet with "Ç"and accented characters. This is why you need to restrict it to ASCII letters, to limit yourself to the roman alphabet.
You can use NSCharacterSet in the following way :
let phrase = "Test case"
let range = phrase.rangeOfCharacter(from: characterSet)
// range will be nil if no letters is found
if let test = range {
println("letters found")
}
else {
println("letters not found")
}```
You can deal with ascii value
extension String {
var fisrtCharacterIsAlphabet: Bool {
guard let firstChar = self.first else { return false }
let unicode = String(firstChar).unicodeScalars
let ascii = Int(unicode[unicode.startIndex].value)
return (ascii >= 65 && ascii <= 90) || (ascii >= 97 && ascii <= 122)
}
}
var isAlphabet = "Hello".fisrtCharacterIsAlphabet
The Character type has a property for this:
let x: Character = "x"
x.isLetter // true for letters, false for punctuation, numbers, whitespace, ...
Note that this will include characters from other alphabets (Greek, Cyrillic, Chinese, ...).
As String is a Sequence with Element equal to Character, we can use the .first property to get the first char.
With this, you can filter your items:
let filtered = items.filter { $0.name.first?.isLetter ?? false }
You can get this done through this simple String extension
extension StringProtocol {
var isFirstCharacterAlp: Bool {
first?.isASCII == true && first?.isLetter == true
}
}
Usage:
print ("H1".isFirstCharacterAlp)
print ("ابراهيم1".isFirstCharacterAlp)
Output
true
false
Happy Coding!
Reference

How do I fix error: Binary operator '==' cannot be applied to operands of type 'NSExpression.ExpressionType' and '_'

I am going through old code from HomeKit Catalog: Creating Homes, Pairing and Controlling Accessories, and Setting Up Triggers when I ran into an expression that says
.KeyPathExpressionType
I can't tell what the
.
in
.KeyPathExpressionType
is referring to on the left side of the
.
I find nothing when I search Google and stackoverflow for "KeyPathExpressionType". It's the same with
.ConstantValueExpressionType
I find nothing.
Each of those equality comparisons
comparison.leftExpression.expressionType == .KeyPathExpressionType
and
comparison.rightExpression.expressionType == .ConstantValueExpressionType
in the code below, generate an error message that says:
Binary operator '==' cannot be applied to operands of type 'NSExpression.ExpressionType' and '_'
extension NSPredicate {
/**
Parses the predicate and attempts to generate a characteristic-value `HomeKitConditionType`.
- returns: An optional characteristic-value tuple.
*/
private func characteristic() -> HomeKitConditionType? {
guard let predicate = self as? NSCompoundPredicate else { return nil }
guard let subpredicates = predicate.subpredicates as? [NSPredicate] else { return nil }
guard subpredicates.count == 2 else { return nil }
var characteristicPredicate: NSComparisonPredicate? = nil
var valuePredicate: NSComparisonPredicate? = nil
for subpredicate in subpredicates {
if let comparison = subpredicate as? NSComparisonPredicate, comparison.leftExpression.expressionType == .KeyPathExpressionType && comparison.rightExpression.expressionType == .ConstantValueExpressionType {
switch comparison.leftExpression.keyPath {
case HMCharacteristicKeyPath:
characteristicPredicate = comparison
case HMCharacteristicValueKeyPath:
valuePredicate = comparison
default:
break
}
}
}
if let characteristic = characteristicPredicate?.rightExpression.constantValue as? HMCharacteristic,
characteristicValue = valuePredicate?.rightExpression.constantValue as? NSCopying {
return .Characteristic(characteristic, characteristicValue)
}
return nil
}
I get the error to go away when I replace
comparison.leftExpression.expressionType == .KeyPathExpressionType
with
comparison.leftExpression.expressionType.rawValue == NSExpression.ExpressionType.keyPath.rawValue
and
comparison.rightExpression.expressionType == .ConstantValueExpressionType
with
comparison.rightExpression.expressionType.rawValue == NSExpression.ExpressionType.constantValue.rawValue
Is this correct? Can anyone tell me enlighten me on this problem?
The code is outdated Swift 2 code.
Replace .KeyPathExpressionType with .keyPath and .ConstantValueExpressionType with .constantValue
The type is NSExpression.ExpressionType, please read the documentation

backspace not work in outside of regex in swift

I use this method for patterning the phone number in UITextField at the .editingChange event
But the delete key only removes the numbers
extension String{
func applyPatternOnNumbers(pattern: String) -> String {
let replacmentCharacter: Character = "#"
let pureNumber = self.replacingOccurrences( of: "[^۰-۹0-9]", with: "", options: .regularExpression)
var result = ""
var pureNumberIndex = pureNumber.startIndex
for patternCharacter in pattern {
if patternCharacter == replacmentCharacter {
guard pureNumberIndex < pureNumber.endIndex else { return result }
result.append(pureNumber[pureNumberIndex])
pureNumber.formIndex(after: &pureNumberIndex)
} else {
result.append(patternCharacter)
}
}
return result
}
}
use at the editingChange event
let pattern = "+# (###) ###-####"
let mobile = textField.text.substring(to: pattern.count-1)
textfield.text = mobile.applyPatternOnNumbers(pattern: pattern)
// print(textfield.text) +1 (800) 666-8888
the problem is space & - , ( , ) chars can not to be removed
The RegEx you are trying is to not consider digits only:
[^۰-۹0-9]
I'm not sure, but you may change it to:
[^۰-۹0-9\s-\(\)]
and it may work. You might just add a \ before your special chars inside [] and you can any other chars into it that you do not need to be replaced.
Or you may simplify it to
[^\d\s-\(\)]
and it might work.
Method 2
You may use this RegEx which is an exact match to the phone number format you are having:
\+\d+\s\(\d{3}\)\s\d{3}-\d{4}
You may remove the first +, if it is unnecessary
\d+\s\(\d{3}\)\s\d{3}-\d{4}

String formatting with characters is crashing

var modifiedString: String {
var outString = String()
for thisChar in self.characters {
if (thisChar == " "){
outString.appendingFormat("%%02X",thisChar ) // This line is asking me unwrap as CVarArg
outString.appendingFormat("%%02X",thisChar as! CVarArg) // This line crashes
}
}
}
}
I am trying to use outString.appendingFormat("%%02X",thisChar )
But this line is forcing me to unwrap as CVarArg but it is crashing when I am doing it. what is wrong in this
I assume this is placed in a String extension.
The error occurs because Character does not conform to CVarArg. Instead of converting the character directly to CVarArg, you need to convert it to a type that conforms to CVarArg. Here, the best choice would be String:
outString.appendingFormat("%%02X", "\(thisChar)")
You are also missing a return statement.
However, even if you did this and your code compiles, the property you wrote still does not make much sense. I don't think it works as you intended it to, but without more information I can't really be sure.
Use this code-
var modifiedString: String {
var outString = String()
for thisChar in self.characters {
if (thisChar == " "){
// outString = outString.appendingFormat("%%02X",thisChar.description )
outString = outString.appendingFormat("%%02X", "\(thisChar)")
}
else
{
outString = outString.appending("\(thisChar)")
}
}
return outString
}
Hope this helps!
It is not clear enough what you want to do, but I guess you want to do something like this:
extension String {
var modifiedString: String {
var outString = ""
for thisChar in self.characters {
if thisChar == " " {
let charCode = String(thisChar).unicodeScalars.first!.value
outString += "%" + String(format: "%02X", charCode)
//### Or if you prefer... (Do not miss, you need 3 '%'s in the format string.)
//outString = outString.appendingFormat("%%%02X", charCode)
} else {
outString.append(thisChar)
}
}
return outString
}
}
print("what you really want to do?".modifiedString)
//->what%20you%20really%20want%20to%20do?
With looping with for thisChar in self.characters, the type of thisChar becomes Character, it's not easy to get a character code from Character in Swift.
There may be some better ways but let charCode = String(thisChar).unicodeScalars.first!.value works as expected, when thisChar contains only one UnicodeScalar.
And you should better know Swift has a method named addingPercentEncoding:
let result = "what you really want to do?".addingPercentEncoding(withAllowedCharacters: CharacterSet.whitespaces.inverted)!
print(result)
//->what%20you%20really%20want%20to%20do?

Replace part of string with lower case letters - Swift

I have a Swift based iOS app and one of the features allows you to comment on a post. Anyway, users can add "#mentions" in their posts to tag other people. However I want to stop the user from adding a username with a capital letter.
Is there anyway I can convert a string, so that the #usernames are all in lowercase?
For example:
I really enjoy sightseeing with #uSerABC (not allowed)
I really enjoy sightseeing with #userabc (allowed)
I know there is a property for the string in swift called .lowercaseString - but the problem with that, is that it makes the entire string lowercase and thats not what I want. I only want the #username to be in lower case.
Is there any way around this with having to use the .lowercase property.
Thanks for your time, Dan.
This comes from a code I use to detect hashtags, I've modified to detect mentions:
func detectMentionsInText(text: String) -> [NSRange]? {
let mentionsDetector = try? NSRegularExpression(pattern: "#(\\w+)", options: NSRegularExpressionOptions.CaseInsensitive)
let results = mentionsDetector?.matchesInString(text, options: NSMatchingOptions.WithoutAnchoringBounds, range: NSMakeRange(0, text.utf16.count)).map { $0 }
return results?.map{$0.rangeAtIndex(0)}
}
It detects all the mentions in a string by using a regex and returns an NSRange array, by using a range you have the beginning and the end of the "mention" and you can easily replace them with a lower case version.
Split the string into two using the following command -
let arr = myString.componentsSeparatedByString("#")
//Convert arr[1] to lower case
//Append to arr[0]
//Enjoy
Thanks to everyone for their help. In the end I couldn't get any of the solutions to work and after a lot of testing, I came up with this solution:
func correctStringWithUsernames(inputString: String, completion: (correctString: String) -> Void) {
// Create the final string and get all
// the seperate strings from the data.
var finalString: String!
var commentSegments: NSArray!
commentSegments = inputString.componentsSeparatedByString(" ")
if (commentSegments.count > 0) {
for (var loop = 0; loop < commentSegments.count; loop++) {
// Check the username to ensure that there
// are no capital letters in the string.
let currentString = commentSegments[loop] as! String
let capitalLetterRegEx = ".*[A-Z]+.*"
let textData = NSPredicate(format:"SELF MATCHES %#", capitalLetterRegEx)
let capitalResult = textData.evaluateWithObject(currentString)
// Check if the current loop string
// is a #user mention string or not.
if (currentString.containsString("#")) {
// If we are in the first loop then set the
// string otherwise concatenate the string.
if (loop == 0) {
if (capitalResult == true) {
// The username contains capital letters
// so change it to a lower case version.
finalString = currentString.lowercaseString
}
else {
// The username does not contain capital letters.
finalString = currentString
}
}
else {
if (capitalResult == true) {
// The username contains capital letters
// so change it to a lower case version.
finalString = "\(finalString) \(currentString.lowercaseString)"
}
else {
// The username does not contain capital letters.
finalString = "\(finalString) \(currentString)"
}
}
}
else {
// The current string is NOT a #user mention
// so simply set or concatenate the finalString.
if (loop == 0) {
finalString = currentString
}
else {
finalString = "\(finalString) \(currentString)"
}
}
}
}
else {
// No issues pass back the string.
finalString = inputString
}
// Pass back the correct username string.
completion(correctString: finalString)
}
Its certainly not the most elegant or efficient solution around but it does work. If there are any ways of improving it, please leave a comment.

Resources