Create non-optional in guard to test guarded condition [duplicate] - swift

This question already has answers here:
Using guard with a non-optional value assignment
(2 answers)
Closed 5 years ago.
I currently have a function like this...
// this is a property
var currentString: String = ""
func doSomething() {
let newString: String = goGetANewString()
guard newString != currentString else {
return
}
currentString = newString
}
But I find it a bit odd that I am creating the newString outside of the guard.
If I move it into the guard then it complains that it needs to be optional.
Is there a way of creating that newString inside the guard statement and checking the condition?
Ideally I'd like something like this, but like I said, it doesn't work this way.
func doSomething() {
guard let newString: String = goGetANewString(), newString != currentString else {
return
}
currentString = newString
}

The "trick" is to use guard case with a value-binding pattern for the assignment:
func doSomething() {
guard case let newString = goGetANewString(), newString != currentString else {
return
}
currentString = newString
}

Related

How to reduce if-condition looping - Swift

I know it sounds crazy, but just curious how I can reduce the if loop iteration for following? I have tried using guard let but stucked at some place.
{
if arenaEventItems == nil || arenaEventItems.count <= 0 {
return
}
if (arenaEventItems.count > 0 && (self.arenaEvents?.monthsDictObjList.count)! > 0){
if (self.tableView != nil){
if let arrVisibleRows = self.tableView.indexPathsForVisibleRows as? [IndexPath]{
if (self.tableView.indexPathsForVisibleRows!.count > 0){
let indexPath : IndexPath = self.tableView.indexPathsForVisibleRows!.first!
if let dict = self.arenaEvents?.monthsDictObjList[indexPath.row] {
if(self.arenaHeaderView != nil) && (dict.count) > 0 {
self.arenaHeaderView?.setMonthTitle(string: (dict.keys.first!))
let selectedMonthTitle = (dict.keys.first!)
for month in (self.arenaEvents?.uniqueMonthOnlyList)! {
if (selectedMonthTitle.contains(month)){
selectedMonthIndex = (self.arenaEvents?.uniqueMonthOnlyList.index(of: month)!)!
break
}
}
}
}
}
}
}
}
}
You can reduce it like that, without any forced unwrapping or nesting:
guard let arenaEventItems = arenaEventItems,
!arenaEventItems.isEmpty,
let arenaEvents = self.arenaEvents,
!arenaEvents.monthsDictObjList.isEmpty,
let arenaHeaderView = self.arenaHeaderView,
let indexPath = self.tableView?.indexPathsForVisibleRows?.first,
let selectedMonthTitle = arenaEvents.monthsDictObjList[indexPath.row].keys.first
else {
return
}
arenaHeaderView.setMonthTitle(string: selectedMonthTitle)
if let monthIndex = arenaEvents.uniqueMonthOnlyList.index(where: { selectedMonthTitle.contains($0) }) {
selectedMonthIndex = monthIndex
}
you replace if ... return with guard !... else return to avoid nesting
you replace .count > 0 with !...isEmpty as best practice
you replace multiple access to self.something? with if let something = self.something to avoid threading issues
you unloop for ... in ... { if (...) { ... } } to .index(where: ...)
You can combine all the conditions in "if" and get something like this:
if let eventItems = arenaEventItems,
eventItems.count > 0,
let events = self.arenaEvents,
!events.monthsDictObjList.isEmpty,
let tableView = self.tableView,
let arrVisibleRows = self.tableView.indexPathsForVisibleRows as? [IndexPath],
!arrVisibleRows.isEmpty,
let indexPath : IndexPath = arrVisibleRows.first,
let dict = events.monthsDictObjList[indexPath.row],
let headerView = self.arenaHeaderView,
!dict.isEmpty {
headerView.setMonthTitle(string: (dict.keys.first!))
let selectedMonthTitle = (dict.keys.first!)
for month in events.uniqueMonthOnlyList! {
if (selectedMonthTitle.contains(month)){
selectedMonthIndex = (events.uniqueMonthOnlyList.index(of: month)!)!
break
}
}
}
You should consider restructuring your code, your code is not readable and incomprehensible for anyone who look at it. Since, you are using Swift, it is really easy to write such code with guard ... else, if ... let
pattern.
Some improvements that you can do on class is have your view non nil ie make them implicitly unwrapped optional, since you will always be connecting them to storyboard.
#IBOutlet var tableView: UITableView!
#IBOutlet var arenaHeaderView: ArenaHeaderView!
Also, you have arrays which can go to nil, why do you want it to be nil. You could simply initialize an empty array and dictionaries. That way you can reduce some more comparison code like so,
arenaEventItems: [String: String] = [:]
With that changes and a bit of refactoring, you could possibly rewrite your code to something like this,
guard !arenaEventItems.isEmpty,
let arenaEvents = arenaEvents,
let indexPath = tableView.indexPathsForVisibleRows?.first,
let dict = arenaEvents.monthsDictObjList[indexPath.row],
let selectedMonthTitle = dict.keys.first
else {
return
}
arenaHeaderView.setMonthTitle(string: selectedMonthTitle)
for month in arenaEvents.uniqueMonthOnlyList where selectedMonthTitle.contains(month) {
if let selectedIndex = arenaEvents.uniqueMonthOnlyList.index(of: month) {
selectedMonthIndex = selectedIndex
break
}
}

unexpectedly found nil while unwrapping [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 6 years ago.
My code is crashing when i am unwrapping Double(display.text!)!
i put an if condition but it didn't work
#IBAction private func buttonpress(sender: UIButton)
{
let digit = sender.currentTitle!
if userisinthemiddle
{
let currenttext = display.text!
display.text = currenttext + digit
}
else
{
display.text = digit
}
userisinthemiddle = true
}
till here it is working properly but when i try to make it as a property it is not working
var DisplayValue : Double
{
get
{
return Double(display.text!)! // thread 1
}
set
{
display.text = String(newValue)
}
}
It's generally not a good idea to force unwrap a variable unless you actually want your program to crash in the event of a failure (in my experience this is rare). In this case it sounds like it's inhibiting your ability to diagnose the problem. Try something like this to (1) avoid force unwrapping and (2) be in a better position to react to unexpected values.
#IBAction private func buttonPress(sender: UIButton)
{
guard let digit = sender.currentTitle else
{
assertionFailure("digit is nil.")
return
}
print("digit: \(digit)"
if userIsInTheMiddle
{
let currentText = display.text ?? "" // If display.text is nil, set currentText to an empty string
print("currentText: \(currentText)"
display.text = currentText + digit
}
else
{
display.text = digit
}
print("display.text: \(display.text)"
userIsInTheMiddle = true
}
var displayValue: Double
{
get
{
let text = display.text ?? ""
guard let double = Double(text) else
{
// You probably want to replace this `assertionFailure` and return a default value like 0
assertionFailure("text could not be converted to a Double")
return
}
return double
}
set
{
display.text = String(newValue)
}
}
A few questions:
How does the property displayValue relate to the #IBAction?
You're using the + operator to concatenate two strings here display.text = currentText + digit. Just confirming you're not trying to add two numbers?

Best way to unwrap optional type [duplicate]

This question already has answers here:
When should I compare an optional value to nil?
(5 answers)
Closed 6 years ago.
I have a situation
let a: Int? = getFromSomewhere()
if a == nil {
return
}
let b = a!
I don't like too many layer. But I think this is not elegant.
Do you have more elegant way?
Omit a, omit the nil check, omit the force-unwrap; one line:
guard let b = getFromSomewhere() else {return}
You can also use guard or if let to unwrap optionals:
so instead of :
if a != nil {
return
}
let b = a!
if let a = getFromSomewhere() {
// Do what you want with a
} else {
// a is nil
}
And with a guard statement :
guard let a = getFromSomewhere() else {
NSLog("error a is nil")
return
}
// You can use a now if it's not nil

check if string contains anything outside of the character set swift

Hi I want to check if the user types in a string that contains anything outside of the character set (contains characters that are not specified in my set):
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-."
How can I achieve this? thanks in advance!
update
I tried using
var characterSet:NSCharacterSet = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789")
if (searchTerm!.rangeOfCharacterFromSet(characterSet.invertedSet).location == NSNotFound){
println("No special characters")
}
but i'm getting a type 'range?' has no member 'location' error
You can use custom NSCharacterSet charactersInString and check if the character type is member of that invertedSet:
extension NSCharacterSet {
func characterInStringIsMember(aString: String) -> Bool {
var result = false
aString
.characters
.map{ UInt16(String($0).unicodeScalars.first?.value ?? 0) }
.forEach { result = characterIsMember($0) }
return result
}
}
let customCharSet = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-.").invertedSet
let stringTest = "abc"
if customCharSet.characterInStringIsMember(stringTest) {
print(true) //
} else {
print(false) // "false\n"
}
let stringTest2 = "abc%"
if customCharSet.characterInStringIsMember(stringTest2) {
print(true) // "true\n"
} else {
print(false)
}
Here is a simple solution, Just use NSCharacterSet like this :-
let name1="myname" // Input
let characterSet: NSMutableCharacterSet = NSMutableCharacterSet.alphanumericCharacterSet()
characterSet.addCharactersInString("_-.")
name1.stringByTrimmingCharactersInSet(characterSet).isEmpty // If this return true, you enetered the right charcters

How to use optional chaining while searching through a dictionary in swift?

I want to safely search through values in a swift Dictionary using if lets and making sure it is type safe as I get deeper and deeper into the dictionary. The dictionary contains dictionaries that contains NSArray that contains more dictionary.
At first attempt my code looks like this:
if let kkbox = ticket["KKBOX"] as? Dictionary<String, AnyObject> {
if let kkboxDlUrlDict = kkbox["kkbox_dl_url_list"] as? Dictionary<String, AnyObject> {
if let kkboxDlUrlArray = kkboxDlUrlDict["kkbox_dl_url"] as? NSArray {
for dict in kkboxDlUrlArray {
if let name = dict["name"] as? String {
if name == mediaType.rawValue {
urlStr = dict["url"] as String
}
}
}
} else { return nil }
} else { return nil }
} else { return nil }
How do I shorten it to perhaps one or 2 line?
I realised I can chain it if it is 2 layers. This works:
if let kkboxDlUrlArray = ticket["KKBOX"]?["kkbox_dl_url_list"] as? NSArray {
}
But any chain longer than that, will not compile.
Is there a way to chain through a dictionary more than once?
Thank you
You can chain, but with proper downcast at each step:
if let kkboxDlUrlArray = ((((ticket["KKBOX"] as? Dictionary<String, AnyObject>)?["kkbox_dl_url_list"]) as? Dictionary<String, AnyObject>)?["kkbox_dl_url"]) as? NSArray {
for dict in kkboxDlUrlArray {
println(dict)
}
}
That doesn't look good though - it's one line, but not readable.
Personally, without using any fancy functional way to do it, I would make the chain more explicit with just one optional binding:
let kkbox = ticket["KKBOX"] as? Dictionary<String, AnyObject>
let kkboxDlUrlDict = kkbox?["kkbox_dl_url_list"] as? Dictionary<String, AnyObject>
if let kkboxDlUrlArray = kkboxDlUrlDict?["kkbox_dl_url"] as? NSArray {
for dict in kkboxDlUrlArray {
println(dict)
}
}
In my opinion much easier to read, and with no unneeded indentation
Alternatively, you could use this extension, which mimics the original valueForKeyPath method that used to exist in NSDictionary but got axed for whatever reason:
Swift 4.1
extension Dictionary where Key: ExpressibleByStringLiteral {
func valueFor(keyPath: String) -> Any? {
var keys = keyPath.components(separatedBy: ".")
var val : Any = self
while keys.count > 0 {
if let key = keys[0] as? Key {
keys.remove(at: 0)
if let dic = val as? Dictionary<Key, Value> {
if let leaf = dic[key] {
val = leaf
} else {
return nil
}
} else {
return nil
}
} else {
return nil
}
}
return val
}
Your code would then read:
if let array = ticket.valueFor("KKBOX.kkbox_dl_url_list.kkbox_dl_url") as? [] {
// do something with array
}
Seems like swift 1.2 has added this feature.
"More powerful optional unwrapping with if let — The if let construct can now unwrap multiple optionals at once, as well as include intervening boolean conditions. This lets you express conditional control flow without unnecessary nesting."
https://developer.apple.com/swift/blog/