Should I set my Strings to Nil or Empty? - swift

I have a Meeting class where I parse the user's calendar to determine the properties of that class. Most of these fields are Strings that can potentially have nothing in them. When I discover these fields are empty is it better to set them to empty "" or make them optional and set them to nil?
/**
Parses calendar entry to create a Meeting object.
- parameter calendarEvent: The calendar entry we want to parse
- returns: A Meeting Object with the relevant information extracted from the calendar entry.
*/
public static func parse(calendarEvent: EKEvent) -> Meeting {
let location = calendarEvent.location ?? ""
let description = calendarEvent.notes ?? ""
let allInput = "\(calendarEvent.title)\n\(location)\n\(description)"
let parsedHostCodes = parseHostCode(from: allInput)
let parsedPasscodes = parseParticipantCode(from: allInput, hostCodes: parsedHostCodes)
let parsedPhoneNumbers = parsePhoneNumber(from: allInput, codes: parsedPasscodes + parsedHostCodes)
return Meeting(
UUID: calendarEvent.eventIdentifier,
title: calendarEvent.title,
description: description,
location: location,
startTime: calendarEvent.startDate,
endTime: calendarEvent.endDate,
allday: calendarEvent.isAllDay,
participantCodes: parsedPasscodes,
hostcodes: parsedHostCodes,
phoneNumbers: parsedPhoneNumbers,
host: retrieveHost(from: calendarEvent.organizer),
attendees: parseParticipants(from: calendarEvent.attendees),
provider: allInput.contains(pattern: attRegex) ? .att : .unknown)
}

Optionals are designed to determine the absence of data. If that's what your program checks for, then you really should use the feature. It's well worth it in the long run in situations where you can take advantage of "Optional Chaining" and "Auto-unwrap":
struct Event {
let location: String?
}
struct Day {
let events: [Event]?
}
var events: [Event]?
let currentDay: Day?
events = [Event(location: "conf room A1")]
currentDay = Day(events: events)
if let events = currentDay?.events {
for event in events {
if let location = event.location {
print(location)
}
}
}
There's a lot more going on here than what you see. First of all, I made everything Optional since it's likely the case in your code.
The first line of the "if let" is using "Optional Chaining". If either currentDay or currentDay.events is nil, the "if" block won't get executed. If currentDay and currentDay.events is not nil, currentDay.events will be auto-unwrapped and get assigned to "events" and then code execution will drop into the "if" block. Next, depending if there are any events for that day (i.e. array count), the "if let location = event.location" also checks if location is nil or not, and if it's not nil, will auto-unwrap and assign to "location" and will print the value.
If any of those Optionals are nil, then nothing in your code needs to change. Here I removed the assignment of "events" and "currentDay" so that they're both nil:
var events: [Event]?
let currentDay: Day? = nil
if let events = currentDay?.events {
for event in events {
if let location = event.location {
print(location)
}
}
}
As you can see, taking advantage of Optionals will result and less maintenance and generally cleaner code in the long run.

In general, if you need to check if a variable has a valid value at some point in your code, it is better to set up the variable as an Optional rather than set it up as an initial value. This is exactly what Optionals were designed for, so that you can check at any point in your code whether that specific variable has been set up or not without giving it any meaningful value.
For strings, "" could easily work as well, since in most cases an empty String literal("") is not a meaningful value, so you can easily check for it, but it is better practice to use Optionals, since they provide more sophisticated methods to check if they have been set or not then to check if a String equals "" or not.

This question must have been asked a lot of times.
Well, nil or "" is depend on your property data, nil used when an object or value is may exist or not. and also assigning nil is very handy when you check it's value using guard and if let. nil raised the question of possibility.
You can also use "". it's work also well, but sometimes it's become headache while comparing default empty value from response.

Related

most concise way of unwrapping and casting optional

I have a bit of code to get a string out of userDefaults:
if let userString = (userDefaults.objectForKey("user")) {
userTextField.stringValue = userString as! String
}
First, I have to see if the optional is not nil. Then I have to cast it as a string from AnyObject.
Is there a better way of doing this? maybe a one liner?
Note that your forced cast as! String will crash if a default value for the key "user" exists, but
is not a string. Generally, you can combine optional binding (if let) with an optional cast (as?):
if let userString = userDefaults.objectForKey("user") as? String {
// ... default value for key exists and is a string ...
userTextField.stringValue = userString
}
But actually NSUserDefaults has a dedicated method for that purpose:
if let userString = userDefaults.stringForKey("user") {
// ... default value for key exists and is a string ...
userTextField.stringValue = userString
}
If you want to assign a default string in the case that
the default does not exist, or is not a string, then use the
nil-coalescing operator ??, as demonstrated in
Swift issue with nil found while unwrapping an Optional value NSDefautlts, e.g.:
userTextField.stringValue = userDefaults.stringForKey("user") ?? "(Unknown)"
For the special case NSUserDefaults the best – and recommended – way is to use always non-optional values.
First register the key / value pair in AppDelegate as soon as possible but at least before using it.
let defaults = NSUserDefaults.standardUserDefaults()
let defaultValues = ["user" : ""]
defaults.registerDefaults(defaultValues)
The benefit is you have a reliable default value of an empty string until a new value is saved the first time. In most String cases an empty string can be treated as no value and can be easily checked with the .isEmpty property
Now write just
userTextField.stringValue = userDefaults.stringForKey("user")!
Without arbitrary manipulation of the defaults property list file the value is guaranteed to be never nil and can be safely unwrapped, and when using stringForKey there is no need for type casting.
Another way that i like much to clean this up is to do each of your checks
first, and exit if any aren’t met. This allows easy understanding of what
conditions will make this function exit.
Swift has a very interesting guard statements which can also be used to avoid force unwrap crashes like :
guard let userString = userDefaults.objectForKey("user") as? String else {
// userString var will accessible outside the guard scope
return
}
userTextField.stringValue = userString
Using guards you are checking for bad cases early, making your
function more readable and easier to maintain. If the condition is not
met, guard‘s else statement is run, which breaks out of the function.
If the condition passes, the optional variable here is automatically
unwrapped for you within the scope that the guard statement was
called.

Array of String printing Optional, why?

I am playing with Arrays in playground and I am bit confused. Here is code:
var players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
print(players.isEmpty) // False
var currentPlayer = players.first // "tob"
print(currentPlayer) // "Optional("tob")\n"
Why does it says "Optional"?
I found explanation: "The property first actually returns an optional, because if the array were empty, first would return nil."
But it is not empty. .isEmpty //false, So I am not understanding this.
Thanks for help in advance.
The correct way to think of Optional is that this may or may not have a value. What is the first element of an empty list? There is no such thing. It is not a value. We call that lack of a value nil or .None.
In Swift a variable must have a specific type. So your example:
let currentPlayer = players.first
What is the type of currentPlayer? It may be a String, or it may be nothing at all. It is a "maybe string" and in Swift that's called an Optional<String>. Whether players has elements or doesn't have elements doesn't change the type of currentPlayer.
If you want to do something if-and-only-if the variable has a value, then there are many ways. The simplest is if-let.
let players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
print(players.isEmpty) // False
if let currentPlayer = players.first {
print(currentPlayer)
}
This will print tob as you're expecting.
Another very common approach is the guard let
let players = ["tob", "cindy", "mindy"] //["tob", "cindy", "mindy"]
guard let currentPlayer = players.first else { return }
print(currentPlayer)
This lets you avoid nesting the rest of your function inside of curly braces, but otherwise is the same approach.
It is possible to convert an Optional into its underlying type using !, but this is very dangerous and should be avoided except where absolutely necessary. Tools like if-let and guard-let (and also Optional.map) are almost always preferred.
But the key here is to understand that all Swift variables have a single type, and sometimes that type is "maybe it has a value, maybe it doesn't."
If we look at the description of first, we will see that it always returns optional type:
public var first: Self.Generator.Element? { get }

NSNull into a Struct with a property of type NSDate

I have an object from the server that is recognized by Swift 2.1 as either NSDate or NSNull. I want to put it into a struct with a property of type NSDate.
Is that possible? If not, how should I handle this to be type safe later when I use it?
struct Data {
var completedAt: [NSDate]
var name: [String]
var gender: [Bool]
}
but sometimes completedAt comes from the server as NSNull:
completedAt = "<null>";
Any help is very much appreciated, thank you.
Based on my interpretation of the text in the question you didn't mean to declare the variables as arrays.
This is how I handle my parson and I think it works pretty neatly.
The date formatter should probable not be initiated in every iteration of the constructor. If you won't use the date regularly you might want to keep the detesting until you need to parse the date or you can have a static date formatter utility that you only instantiate once.
struct Data {
var completedAt: NSDate?
var name: String
var gender: Bool
init?(dictionary: [String:AnyObject]) {
//Guessing that you want some of the values non optional...
guard let name = dictionary["name"] as? String,
let gender = dictionary["gender"] as? String
else {
return nil
}
self.name = name
self.gender = gender
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
//safe handle of optional values
if let completedAtString = dictionary["completedAt"] as? String, completedAt = dateFormater.dateFromString(completedAtString) {
self.completedAt = completedAt
}
}
}
Take a step back. For each item that the server might provide, there is no guarantee whatsoever that you receive what you expect, since you cannot control the server. So you need to decide how to react to which input.
In the case of expecting a date for example (if your data comes in JSON, that means you likely expect a string formatted in a certain way), the actual data that you receive might be an array, dictionary, string, number, bool, null, or nothing. You might then for example decide that you want to interpret nothing or null or an empty string as nil, that you want to interpret a string containing a well-formatted date as an NSDate, and anything else a fatal error in a debug version, and as either nothing or a fatal error in a release version. On the other hand, if an NSDate is absolutely required then you might interpret anything that doesn't give an NSDate as an error.
Then you write a function that delivers exactly what you want and use it. That way you can parse complex data, with your code warning you when something isn't as it should be, and with your code either surviving any possible input, or deliberately crashing on wrong input, as you want it.

Swift NSUserDefaults first time nil

Hi my app crashes the first time I run it. This is my code:
let State = save.stringForKey("StateSave")
let City = save.stringForKey("CitySave")
let Vehicle = save.stringForKey("ModelNumberSave")
let ExtensionPeriod = save.stringForKey("ExtensionPeriodChoosed")
let Location = "Location"
if ExtensionPeriod == nil {
let name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
save.synchronize()
var DetailNames = ["\(State!)","\(City!)","\(Location)","\(Vehicle!)","\(ExtensionPeriod!)"]
I get a nil on this line:
var DetailNames =
["(State!)","(City!)","(Location)","(Vehicle!)","(ExtensionPeriod!)"]
In fact ExtensionPeriod is nil. But I don't understand why... ExtensionPeriod is nil, but with the code I write, ExtensionPeriod will be like "" so it's not nil. Please help me.
stringForKey returns nil when there has not been a value saved already.
You need to give your values a default. The easiest way to do this is with the ?? operator, that replaces nil with another value:
let State = save.stringForKey("StateSave") ?? "-"
Some general advice: you need to stop using ! so much. Usually when something returns nil, it’s for a good reason – it might be nil. When you unwrap it with !, your program will crash (with not much helpful info as to why). Similarly, it’s usually a bad sign if you’re comparing values to nil.
Instead, take a look at this list of optional handling techniques for some alternatives.
Airspeed Velocity has a good solution for the proper way to accomplish what you want to do, but he did not really explain why what you did does not work, so I will address that aspect of this question.
if ExtensionPeriod == nil {
let name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
That block of code does not set ExtensionPeriod, thus ExtensionPeriod is still nil. All it does is set the value for the key "ExtensionPeriodChoosed" in the NSUserDefaults to no longer be nil. The local variable ExtensionPeriod, however, still has nil. ExtensionPeriod doesn't just magically point to the variable stored in NSUserDefaults, such that when you update NSUserDefaults, it automatically updates the variable. Instead, it copies the variable at the time that it is created.
Here is some sample code that demonstrates this:
NSUserDefaults.standardUserDefaults().setValue("string", forKey: "s")
NSUserDefaults.standardUserDefaults().synchronize()
var s = NSUserDefaults.standardUserDefaults().valueForKey("s")
NSUserDefaults.standardUserDefaults().setValue("string2", forKey: "s")
NSUserDefaults.standardUserDefaults().synchronize()
var y = NSUserDefaults.standardUserDefaults().valueForKey("s")
println(s)
println(y)
outputs:
"string"
"string2"
For your code to work, if you were to keep it the same structure (although you really shouldn't), you would need to change ExtensionPeriod to a var, and then in your if block, after you synchronize save, you would have to reassign ExtensionPeriod to save.valueForKey("ExtensionPeriodChoosed").
One way to make sure that your app's defaults are set is to use NSUserDefault's registerDefaults(_: [NSObject : AnyObject]) function. In Objective-C, I often put in the + (void)initialize class method, but overriding the init() of the application delegate should work just as well.

Filter Array if Current Time is within TimeRange

My current function filters the array and returns an array of PFObjects with only "Type" = "Sushi". Now, I am trying to filter the array if current time is within a time range ("OpenHours" and "CloseHours")
The new Function passes dayOfWeek: Int, timeNow: String
"OpenHours" Example: [0, 6] = [sunday, monday]
["0011","0011","0011","0011","0011","0011","0011"]
"CloseHours" Example:
["2350","2350","2350","2350","2350","2350","2350"]
Current Function that filters "Type":
func filterRestaurants(filteredObject: String) {
//func filterOpenNow(dayOfWeek: Int, timeNow: String){
filteredRestaurantArray = unfilteredRestaurantArray.filter() {
if let type = ($0 as PFObject)["Type"] as? String { // Get value of PFObject
return type.rangeOfString("Sushi") != nil
} else {
println("nope")
return false
}
}
Basically I need to filter for objects where current time timeNow is between OpenHours and CloseHours for a given dayOfWeek
Edit:
What I've Tried so far:
I'm unsure how to get the position of a value in the PFObject array. Normal I would check if the timeNow is between OpenNow[dayOfWeek] and CloseNow[dayOfWeek]
Something like this (except with filter):
if OpenNow[dayOfWeek] ... CloseNow[dayOfWeek] ~= timeNow {
println("success")
}
I am not entirely sure what the properties of your PFObject instance are, so I'm going to make some assumptions and you will have to correct the keys to meet your needs.
Working with the function signature you provided is not entirely possible. This is because in order for it to work you would be accessing a list of PFObjects that were not provided to the function. While Swift does not keep you from doing that, it is generally not a wise design choice as you cannot be confident of the results of the function at any given point in time. So, to provide what is called referential transparency we will modify your function to also take in the collection of PFObjects.
With that collection we will then use the native filter function to determine which ones to return from the function. The filter function takes a collection and a closure. The closure takes one parameter, an element of the collection, and return a Bool. The closure is what determines if an element is kept or discarded.
Knowing that, we can build up the functionality you requested. I cannot guarantee there isn't a better way to do the first if let dance at the beginning, but it should get the job done.
func filterAreOpen(restaurants: [PFObject], forTime time: String, onDay day: Int) -> [PFObject] {
let openRests = filter(restaurants) { r in
if let openHours = r["OpenHours"] as AnyObject? as? [String] {
if let closeHours = r["CloseHours"] as AnyObject? as? [String] {
switch (openHours[day].toInt(), closeHours[day].toInt(), time.toInt()) {
case let (.Some(oh), .Some(ch), .Some(t)):
return oh...ch ~= t
default:
return false
}
}
}
return false
}
return openRests
}
And you would use it like this
let rests = [ // example objects that are replaced by your PFObject instances
[
"OpenHours":["0011","0012","0013"],
"CloseHours":["0023","0023","0023"],
"Name":"Restaurant1"
],
[
"OpenHours":["0014","0015","0016"],
"CloseHours":["0020","0020","0020"],
"Name":"Restaurant2"
]
]
let openRestaurants = filterAreOpen(rests, forTime: "0012", onDay: 1)
/* Results
[{
CloseHours = (
0023,
0023,
0023
);
Name = Restaurant1;
OpenHours = (
0011,
0012,
0013
);
}]
*/
Edit:
A quick explanation about the switch inside the closure. In Swift the switch statement is much more powerful than it was in the Objective-C days. It is capable of matching a value against a pattern, not just numbers.
So, in this case, the switch is matching a 3-tuple of Int?, (Int?, Int?, Int?). This is because String's toInt() method returns an Int?. switch is also able to bind matched patterns to local scoped constants. To do that we use the let keyword. To pattern match on an Optional value, you use the .Some(x) pattern. Since we have a 3-tuple we use .Some(x) three time, with the x replaced by some meaningful name. This way we have access to the three values we are interested in if the three toInt() calls evaluated to non-nil values.
If any of the String values could not evaluate to an Int, and so was nil, the default case is used, and returns false.
You can view the language book on conditional statements here.