Given a Bool?, I'd like to be able to do this:
let a = BoolToString(optbool) ?? "<None>"
which would either give me "true", "false", or "<None>".
Is there a built-in for BoolToString?
String(Bool) is the easiest way.
var myBool = true
var boolAsString = String(myBool)
let b1: Bool? = true
let b2: Bool? = false
let b3: Bool? = nil
print(b1?.description ?? "none") // "true"
print(b2?.description ?? "none") // "false"
print(b3?.description ?? "none") // "none"
or you can define 'one liner' which works with both Bool and Bool? as a function
func BoolToString(b: Bool?)->String { return b?.description ?? "<None>"}
let trueString = String(true) //"true"
let trueBool = Bool("true") //true
let falseBool = Bool("false") //false
let nilBool = Bool("foo") //nil
You could use the ?: ternary operator:
let a = optBool == nil ? "<None>" : "\(optBool!)"
Or you could use map:
let a = optBool.map { "\($0)" } ?? "<None>"
Of the two, optBool.map { "\($0)" } does exactly what you want BoolToString to do; it returns a String? that is Optional(true), Optional(false), or nil. Then the nil coalescing operator ?? unwraps that or replaces nil with "<None>".
Update:
This can also be written as:
let a = optBool.map(String.init) ?? "<None>"
or:
let a = optBool.map { String($0) } ?? "<None>"
var boolValue: Bool? = nil
var stringValue = "\(boolValue)" // can be either "true", "false", or "nil"
Or a more verbose custom function:
func boolToString(value: Bool?) -> String {
if let value = value {
return "\(value)"
}
else {
return "<None>"
// or you may return nil here. The return type would have to be String? in that case.
}
}
You can do it with extensions!
extension Optional where Wrapped == Bool {
func toString(_ nilString: String = "nil") -> String {
self.map { String($0) } ?? nilString
}
}
Usage:
let b1: Bool? = true
let b2: Bool? = false
let b3: Bool? = nil
b1.toString() // "true"
b2.toString() // "false"
b3.toString() // "nil"
b3.toString("<None>") // "<None>"
Related
In my application I want to implement separate class to keep all the temporary variables of Now Playing item for Music Player.
It has lots of properties with different types, but they should be handled in the same way. They should be handled in the class method "updateData" (see the end of code)
This is my code:
struct DataDefaults {
//MARK: Default properties
let albumTitle: String? = "Unknown Album"
let albumArtist: String? = "Unknown Artist"
let title: String? = "Unknown Title"
let artist: String? = "Unknown Artist"
let artwork: UIImage? = UIImage(named: "noartwork")!
let genre: String? = ""
let lyrics: String? = "No Lyrics"
let releaseDate: Date? = nil
let playbackDuration: TimeInterval? = 0
let rating: Int? = 0
let assetURL: URL? = nil
let isExplicitItem: Bool? = false
let isCloudItem: Bool? = false
let hasProtectedAsset: Bool? = false
}
class SongInfo: NSObject {
static let sharedData = SongInfo()
let defaults = DataDefaults()
//MARK: Properties
var albumTitle: String
var albumArtist: String
var title: String
var artist: String
var artwork: UIImage
var genre: String
var lyrics: String
var releaseDate: Date?
var playbackDuration: TimeInterval
var rating: Int
var assetURL: URL?
var isExplicitItem: Bool
var isCloudItem: Bool
var hasProtectedAsset: Bool
//MARK: Init
private override init () {
self.albumTitle = defaults.albumTitle!
self.albumArtist = defaults.albumArtist!
self.title = defaults.title!
self.artist = defaults.artist!
self.artwork = defaults.artwork!
self.genre = defaults.genre!
self.lyrics = defaults.lyrics!
self.releaseDate = defaults.releaseDate
self.playbackDuration = defaults.playbackDuration!
self.rating = defaults.rating!
self.assetURL = defaults.assetURL
self.isExplicitItem = defaults.isExplicitItem!
self.isCloudItem = defaults.isCloudItem!
self.hasProtectedAsset = defaults.hasProtectedAsset!
}
//MARK: Set properties
func updateData(allData: DataDefaults) {
var wasUpdated: Bool = false
if allData.albumTitle == self.albumTitle {
//pass
} else if allData.albumTitle == nil || allData.albumTitle == "" {
self.albumTitle = defaults.albumTitle!
wasUpdated = true
} else {
self.albumTitle = allData.albumTitle!
wasUpdated = true
}
//Need to repeat same IF for all properties
}
}
Is there any way I can use property name to make some reusage of the same code instead of duplicating it?
Rather than trying to find a solution to a weird design, I re-designed for what you're trying to accomplish 🙂
struct SongData: Equatable {
static let defaultData = SongData(albumTitle: "Unknown Album",
albumArtist: "Unknown Artist",
title: "Unknown Title",
artist: "Unknown Artist",
artwork: UIImage(named: "noartwork"),
genre:"",
lyrics: "No Lyrics",
releaseDate: nil,
playbackDuration: 0,
rating: 0,
assetURL: nil,
isExplicitItem: false,
isCloudItem: false,
hasProtectedAsset: false)
//MARK: Default properties
var albumTitle: String?
var albumArtist: String?
var title: String?
var artist: String?
var artwork: UIImage?
var genre: String?
var lyrics: String?
var releaseDate: Date?
var playbackDuration: TimeInterval?
var rating: Int?
var assetURL: URL?
var isExplicitItem: Bool?
var isCloudItem: Bool?
var hasProtectedAsset: Bool?
/// This initializer will set the properties to the defaultData properties if a passed value is nil
init(albumTitle: String?, albumArtist: String?, title: String?, artist: String?, artwork: UIImage?, genre: String?, lyrics: String?, releaseDate: Date?, playbackDuration: TimeInterval?, rating: Int?, assetURL: URL?, isExplicitItem: Bool?, isCloudItem: Bool?, hasProtectedAsset: Bool?) {
// initialize properties where the default is nil
self.releaseDate = releaseDate
self.assetURL = assetURL
//initialize other properties with the passed values, or use the default value if nil
self.albumTitle = SongData.valueOrDefault(albumTitle, SongData.defaultData.albumTitle)
self.albumArtist = SongData.valueOrDefault(albumArtist, SongData.defaultData.albumArtist)
self.title = SongData.valueOrDefault(title, SongData.defaultData.title)
self.artist = SongData.valueOrDefault(artist, SongData.defaultData.artist)
self.artwork = artwork ?? SongData.defaultData.artwork
self.genre = SongData.valueOrDefault(genre, SongData.defaultData.genre)
self.lyrics = SongData.valueOrDefault(lyrics, SongData.defaultData.lyrics)
self.playbackDuration = playbackDuration ?? SongData.defaultData.playbackDuration
self.rating = rating ?? SongData.defaultData.rating
self.isExplicitItem = isExplicitItem ?? SongData.defaultData.isExplicitItem
self.isCloudItem = isCloudItem ?? SongData.defaultData.isCloudItem
self.hasProtectedAsset = hasProtectedAsset ?? SongData.defaultData.hasProtectedAsset
}
static func ==(leftItem: SongData, rightItem: SongData) -> Bool {
return (leftItem.albumTitle == rightItem.albumTitle) &&
(leftItem.albumArtist == rightItem.albumArtist) &&
(leftItem.title == rightItem.title) &&
// Comparing a reference type here. may need to be handled differently if that's a problem
(leftItem.artwork === rightItem.artwork) &&
(leftItem.genre == rightItem.genre) &&
(leftItem.lyrics == rightItem.lyrics) &&
(leftItem.releaseDate == rightItem.releaseDate) &&
(leftItem.playbackDuration == rightItem.playbackDuration) &&
(leftItem.rating == rightItem.rating) &&
(leftItem.assetURL == rightItem.assetURL) &&
(leftItem.isExplicitItem == rightItem.isExplicitItem) &&
(leftItem.isCloudItem == rightItem.isCloudItem) &&
(leftItem.hasProtectedAsset == rightItem.hasProtectedAsset)
}
//simple helper function to avoid long turneries in the init
static func valueOrDefault(_ value: String?, _ defaultValue: String?) -> String? {
guard let value = value, !value.isEmpty else {
return defaultValue
}
return value
}
}
class SongInfo {
static let sharedData = SongInfo()
var data: SongData
//MARK: Init
private init ()
{
self.data = SongData.defaultData
}
//MARK: Set properties
func updateData(newData: SongData) {
if(newData != self.data) {
self.data = newData
}
}
}
I changed your struct to act more like it appears you're wanting it to be used, and the struct's init will fall back to using the default values if the init values are nil. My design also contains no force unwraps, which are almost always bad.
You could set the defaults directly in your class definition without using a separate struct and have a static unaltered instance with the default values.
For example:
class SongInfo: NSObject {
static let sharedData = SongInfo()
static let defaults = SongInfo()
//MARK: Properties
var albumTitle: String? = "Unknown Album"
var albumArtist: String? = "Unknown Artist"
var title: String? = "Unknown Title"
var artist: String? = "Unknown Artist"
var artwork: UIImage? = UIImage(named: "noartwork")!
var genre: String? = ""
var lyrics: String? = "No Lyrics"
var releaseDate: Date? = nil
var playbackDuration: TimeInterval? = 0
var rating: Int? = 0
var assetURL: URL? = nil
var isExplicitItem: Bool? = false
var isCloudItem: Bool? = false
var hasProtectedAsset: Bool? = false
//MARK: Init
private override init ()
{
// nothing to do here
}
//MARK: Set properties
func updateData(allData: DataDefaults) {
var wasUpdated: Bool = false
if allData.albumTitle == self.albumTitle {
//pass
} else if allData.albumTitle == nil || allData.albumTitle == "" {
self.albumTitle = SongInfo.defaults.albumTitle!
wasUpdated = true
} else {
self.albumTitle = allData.albumTitle!
wasUpdated = true
}
//Need to repeat same IF for all properties
}
}
If you also need to manipulate the basic data without the whole class functionality, you could define a SongInfoData class with only the properties and make SingInfo inherit from that class. Then the static variable for defaults could be in the SongInfoData class and the SingInfo subclass wouldn't need any property declarations.
[EDIT] avoiding code repetition in update function ...
You can generalize the property update process by adding a generic function to your class:
For example:
func assign<T:Equatable>(_ variable:inout T?, _ getValue:(SongInfo)->T?) -> Int
{
let newValue = getValue(self)
if variable == newValue
{ return 0 }
var valueIsEmpty = false
if let stringValue = newValue as? String, stringValue == ""
{ valueIsEmpty = true }
if newValue == nil || valueIsEmpty
{
variable = getValue(SongInfo.defaults)
return 1
}
variable = newValue
return 1
}
func update(with newInfo:SongInfo)
{
let updates = newInfo.assign(&albumTitle) {$0.albumTitle}
+ newInfo.assign(&albumArtist) {$0.albumArtist}
+ newInfo.assign(&title) {$0.title}
+ newInfo.assign(&artist) {$0.artist}
+ newInfo.assign(&artwork) {$0.artwork}
+ newInfo.assign(&genre) {$0.genre}
+ newInfo.assign(&lyrics) {$0.lyrics}
// ...
if updates > 0
{
// react to update
}
}
It seems to me, that you're using MPMedia item.
If so, you don't have to store all these properties at all.
You just need to store persistent ID of the item (convert from UInt64 to string), and later fetch MPMediaItem by using MPMediaQuery with predicate, something like this:
func findSong(persistentIDString: String) -> MPMediaItem? {
let predicate = MPMediaPropertyPredicate(value: persistentIDString, forProperty: MPMediaItemPropertyPersistentID)
let songQuery = MPMediaQuery()
songQuery.addFilterPredicate(predicate)
return songQuery.items.first
}
I'm using an map function and must map an empty string to nil, otherwise the string. Problem is that the input is Any?
var arr : Any? = ["hallo", "", nil, "hihi"]
let res = arr.map{ (($0 ?? "") as! String).isEmpty ? nil : $0 }
print(res)
Do you know how to do this?
The map function is this here:
id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
https://github.com/Hearst-DD/ObjectMapper#custom-transforms
Do you mean like this:
var arr : Any? = ["hallo", "", nil, "hihi"]
if let array = arr as? [String?] {
let result = array.map { ($0?.isEmpty ?? true) ? nil : $0 }
print(result)
}
Storing an array is Any? is questionable, but if you must...
let arr: Any? = ["hallo", "", nil, "hihi"]
guard let casted = arr as? [String?] else {
// handle error
fatalError("arr is not a [String?]")
}
let result: [String?] = casted.map{
guard let s = $0 else { return nil }
return s.isEmpty ? nil : s
}
print(result as Any)
Let's say we have something like this:
static func convertBoolToString(source: [String: AnyObject]) -> [String:AnyObject]? {
var destination = [String:AnyObject]()
for (key, value) in source {
switch value {
case is Bool:
destination[key] = "\(value as! Bool)"
default:
destination[key] = value
}
}
if destination.isEmpty {
return nil
}
return destination
}
The problem is that if value is Double or Int or anything convertible to Bool it will pass the first case.
Pls, check the doc: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html
How to check the value to be exactly and only a Bool?
This is a tricky problem. Note that neither Bool, Double or Int are AnyObject, they are all value types. That means they are represented in the dictionary as NSNumber. However, NSNumber can convert any value it holds to a Bool.
Checking which type is inside NSNumber is not easy. One way to check is to compare references with the result of NSNumber(bool:) constructors because NSNumber returns always the same instance:
func convertBoolToString(source: [String: AnyObject]) -> [String:AnyObject]? {
var destination = [String:AnyObject]()
let theTrue = NSNumber(bool: true)
let theFalse = NSNumber(bool: false)
for (key, value) in source {
switch value {
case let x where x === theTrue || x === theFalse:
destination[key] = "\(value as! Bool)"
default:
destination[key] = "not a bool"
}
}
if destination.isEmpty {
return nil
}
return destination
}
let dictionary: [String: AnyObject] = ["testA": true, "testB": 0, "testC": NSNumber(bool: true)]
print("Converted: \(convertBoolToString(dictionary))")
For other options, see get type of NSNumber
Swift 3 version:
static func convertBoolToString(_ source: [String: Any]?) -> [String:Any]? {
guard let source = source else {
return nil
}
var destination = [String:Any]()
let theTrue = NSNumber(value: true)
let theFalse = NSNumber(value: false)
for (key, value) in source {
switch value {
case let x as NSNumber where x === theTrue || x === theFalse:
destination[key] = "\(x.boolValue)"
default:
destination[key] = value
}
}
return destination
}
There was a similarly named topic but the example was an error due to user mistake. I believe this example is an actual XCode issue.
I was following a treehouse tutorial and in the spirit of swift 2.0 I used guard statements instead of if lets in the initializer. My code was identical to the instruction except for the use of guard statements. It had one error that said "return from initializer without initializing all stored properties". Once I changed it to if let statements, it worked. Perhaps I made a mistake somewhere but I stared at it for atleast an hour, no properties were left un-initialized.
I made the properties equal to nil in the else clauses just in case but that didnt affect anything.
struct DailyWeather {
let maxTemp: Int?
let minTemp: Int?
let humidity: Int?
let precipChance: Int?
var summary: String?
var icon: UIImage? = UIImage(named: "default.png")
var largeIcon: UIImage? = UIImage(named: "default_large.png")
var sunriseTime: String?
var sunsetTime: String?
var day: String?
let dateFormatter = NSDateFormatter()
init(dailyWeatherDictionary: [String:AnyObject]) {
minTemp = dailyWeatherDictionary["temperatureMin"] as? Int
maxTemp = dailyWeatherDictionary["temperatureMax"] as? Int
guard let humidityFloat = dailyWeatherDictionary["humidity"] as? Double else { humidity = nil ; return }
humidity = Int(humidityFloat * 100)
guard let precipFloat = dailyWeatherDictionary["precipProbability"] as? Double else { precipChance = nil ; return }
precipChance = Int(precipFloat * 100)
summary = dailyWeatherDictionary["summary"] as? String
guard let
iconString = dailyWeatherDictionary["icon"] as? String,
iconEnum = Icon(rawValue: iconString) else { icon = nil ; largeIcon = nil ; return }
(icon, largeIcon) = iconEnum.toImage()
guard let sunriseDate = dailyWeatherDictionary["sunriseTime"] as? Double else { sunriseTime = nil ; return }
sunriseTime = timeStringFromUnixTime(sunriseDate)
guard let sunsetDate = dailyWeatherDictionary["sunsetTime"] as? Double else { sunsetTime = nil ; return }
sunsetTime = timeStringFromUnixTime(sunsetDate)
guard let time = dailyWeatherDictionary["time"] as? Double else { day = nil ; return }
day = dayStringFromUnixTime(time)
}
func timeStringFromUnixTime(unixTime: Double) -> String {
let date = NSDate(timeIntervalSince1970: unixTime)
dateFormatter.dateFormat = "hh:mm a"
return dateFormatter.stringFromDate(date)
}
func dayStringFromUnixTime(unixTime: Double) -> String {
let date = NSDate(timeIntervalSince1970: unixTime)
dateFormatter.locale = NSLocale(localeIdentifier: NSLocale.currentLocale().localeIdentifier)
dateFormatter.dateFormat = "EEEE"
return dateFormatter.stringFromDate(date)
}
}
let's have
struct S {
var i: Int?
init(b: Bool){
guard b == false else { return }
//if b == true { return }
i = 0 // if b == true, this statement doesn't execute
}
}
let s1 = S(b: true)
let s2 = S(b: false)
print(s1, s2) // S(i: nil) S(i: Optional(0))
because var i: Int? has a default value nil, even though i = 0 is not reachable if parameter of init is true, the compiler doesn't complain.
struct S {
let i: Int?
init(b: Bool){
guard b == false else { return }
//if b == true { return }
i = 0 // if b == true, this statement doesn't execute
}
}
will NOT compile, with error: return from initializer without initializing all stored properties and note: 'self.i' not initialized, because constant let i: Int? doesn't have any default value
Your trouble is, that you return from init. Normally, avoid return from an initializer if your initializer is not fail-able / init? /. In case of fail-able init? the only accepted return value is nil.
Some of my models have optional properties. I'm trying to write a method that can evaluate if they've been set.
Below is an attempt, but I can't figure out how to determine a nil value from an Any object [edit: (the child variable is of type Any)]. It doesn't compile.
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
let value = child.value
if let optionalValue = value as? AnyObject? { //Does not compile
if optionalValue == nil {
return false
}
}
}
return true
}
Edit:
I forgot to clarify that the child value in the above example is always of type Any. The Any type is difficult in that it cannot be compared to nil and a cast to AnyObject always fails. I've tried to illustrate it in the playground below.
var anyArray = [Any]();
var optionalStringWithValue: String? = "foo";
anyArray.append(optionalStringWithValue);
var nilOptional: String?
anyArray.append(nilOptional)
print(anyArray[0]); // "Optional("foo")\n"
print(anyArray[1]); // "nil\n"
if let optionalString = anyArray[0] as? AnyObject {
//will always fail
print("success")
}
//if anyArray[1] == nil { // will not compile
//}
I used #ebluehands technique of reflecting the Any value to modify the original function. It cycles through the properties with an initial mirror, then reflects each one individually using displayStyle to determine if the property is optional.
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
let value: Any = child.value
let subMirror = Mirror(reflecting: value)
if subMirror.displayStyle == .Optional {
if subMirror.children.count == 0 {
return false
}
}
}
return true
}
Obsolete:
You can simply check if the optional value is nil or not :
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
//child.value being an optional
if child.value == nil {
return false
}
}
return true
}
Edit:
To check if an Any object is optional and contains a value or not using reflection :
let optionalString : String? = "optional string"
let any : Any = optionalString
//First you will need to create a mirror of the any object
let mirror = Mirror(reflecting : any)
//Then you can check the display style to see if it's an optional
if mirror.displayStyle == .Optional {
//If it is, check the count of its children to see if there is a value or not
if mirror.children.count == 0 {
print("I don't have a value")
}
else {
print("I have a value")
}
}
Here is a playground example (based on yours):
var anyArray = [Any]()
var optionalStringWithValue: String? = "foo"
anyArray.append(optionalStringWithValue)
var nilOptional: String?
anyArray.append(nilOptional)
let string = "string not optional"
anyArray.append(string)
print(anyArray[0]) // "Optional("foo")\n"
print(anyArray[1]) // "nil\n"
print(anyArray[2]) // "string not optional\n"
let mirrorOptionalWithValue = Mirror(reflecting: anyArray[0])
if mirrorOptionalWithValue.displayStyle == .Optional
&& mirrorOptionalWithValue.children.count == 1 {
print("Is an optional and contains a value")
}
let mirrorOptionalWithoutValue = Mirror(reflecting: anyArray[1])
if mirrorOptionalWithoutValue.displayStyle == .Optional &&
mirrorOptionalWithoutValue.children.count == 0 {
print("Is an optional but is nil")
}
let mirrorNotAnOptional = Mirror(reflecting: anyArray[2])
if mirrorNotAnOptional.displayStyle != .Optional {
print("Is not an optional")
}
Another option is create a extension.
extension NSManagedObject {
func checkIfAllRequiredMembersAreSet() -> Bool {
let attributes = self.entity.attributesByName
for (attribute, value) in attributes {
if value.attributeValueClassName != nil {
let v: AnyObject? = self.valueForKey(attribute)
if !value.optional && v != nil {
return false
}
}
}
return true
}
}
Based on this answer, I recommend using if case Optional<Any>.some(_).
I did something recently to make sure I have at least one optional set. Here's an example to make sure all are set. You can paste into playgrounds:
import Foundation
struct SomeError: Error {
let code: Int?
let message: String?
let errorDescription: String?
var allValuesSet: Bool {
for aChild in Mirror(reflecting: self).children {
if case Optional<Any>.some(_) = aChild.value {
continue
} else {
return false
}
}
return true
}
}
let errorTest = SomeError(code: nil, message: "failed", errorDescription: nil)
let errorTest2 = SomeError(code: -1, message: "failed", errorDescription: "missing information")
print("is valid: \(errorTest.allValuesSet)") //is valid: false
print("is valid: \(errorTest2.allValuesSet)") //is valid: true