Convert from switch case statement to a value in Swift - swift

Maybe this is a stupid question
I have a switch case statement like this:
self.text = type.rawValue
switch type {
case .teuro:
self.backgroundColor = UIColor.sapphireColor()
case .lesson:
self.backgroundColor = UIColor.orangeColor()
case .profession:
self.backgroundColor = UIColor.pinkyPurpleColor()
}
Is there any way to write it in something like this example:
self.backgroundColor = {
switch type {
case .teuro:
return UIColor.sapphireColor()
case .lesson:
return UIColor.orangeColor()
case .profession:
return UIColor.pinkyPurpleColor()
}
}
Any comment or answer is appreciated. Thanks.

You are nearly there!
You've created a closure that returns a color, and are assigning it to a UIColor property, but a closure is not a color!
You just need to call (invoke) the closure to run it, so that it returns the color you want:
self.backgroundColor = {
switch type {
case .teuro:
return UIColor.sapphireColor()
case .lesson:
return UIColor.orangeColor()
case .profession:
return UIColor.pinkyPurpleColor()
}
}() // <---- notice the brackets!

Usually you don't wanna mix your models and your UI, and only need to use this colors inside one view
That's why I ended up creating such extensions, which are only accessible inside current file of the view:
private extension Type {
var backgroundColor: UIColor {
switch self {
case .teuro:
return .sapphireColor()
case .lesson:
return .orangeColor()
case .profession:
return .pinkyPurpleColor()
}
}
}

Sweeper is absolutely correct, that you can initialize this with a closure by adding the missing () at the end. So that is the literal answer to your question.
But Philip also is correct, that it would be best to add an extension to your enumeration type to define a mapping between colors and your cases. It abstracts the color scheme from both the calling point (e.g. ensuring that you have a consistent application of colors throughout the app, while never repeating yourself), but at the same time, avoids entangling the UI color scheme with some basic enumeration type.
But I would like to take it a step further, namely to extend this observation to the text property, too. You should not use the rawValue strings in your UI. The raw codes (if you have them at all) should not be conflated with the strings you want to display in the UI. One is a coding question, while the other is a UI question.
So, I would not only move color into an extension, but also the display text, e.g. I would define a text property:
extension MyEnumerationType {
var text: String { rawValue }
}
Then you could do:
self.text = myEnumerationInstance.text
Now here I am still using rawValue, but I am abstracting it away from the UI. The reason is that you might want to eventually support different strings, but not change your rawValue codes. E.g., you might want to support localization at some point:
extension MyEnumerationType {
var text: String { NSLocalizedString(rawValue, comment: "MyEnumerationType") }
}
Or you might have a switch statement inside this text computed property. But it avoids the tight coupling of your enumeration’s internal representation (the rawValue) from the UI.
So, bottom line, not only should you abstract the color scheme out of the type itself, but you should abstract display text, too. That way, you can change your display text at some future date, but not break code that relied on the old rawValue values.

It's better to create one color var inside the enum.
Here is the example
enum Type {
case type1, type2
var color: UIColor {
switch self {
case .type1:
return .black
case .type2:
return .yellow
}
}
}
Use
class ViewController: UIViewController {
let type: Type = .type1
override func viewDidLoad() {
self.view.backgroundColor = type.color
}
}

Related

Is there a way in Swift to partially match a generic?

That is, if I have a class C that takes two generics A and B, is there a way where I can cast an object to C where I don't care what B is?
My specific use case is that I need to bridge between NSView functionality and the new SwiftUI in a multi-window, but non-document based application. The problem I am having is, given an NSView, I need to obtain the SwiftUI View that it is managing (in my case a View called ContentView).
Note that I do have a solution, which I include below, but it involves the use of Mirror based reflection and I am wondering if there is a better way, most likely involving the use of as? to cast to a partial match of a generic.
The bridging is done using the NSHostingView hence it should seem that one would just do the following:
if let hostingView = NSApplication.shared.keyWindow?.contentView as? NSHostingView<ContentView> {
// do what I need with 'hostingView.rootView'
}
Unfortunately, NSHostingView.rootView does not return the actual ContentView that I created, it returns a modified version of that view dependant on the modifiers used. (In my case I'm using .environmentObject modifier.) As a result the if statement above never returns true because the type is not NSHostingView<ContentView> but rather NSHostingView<ModifiedContent<ContentView, _bunch_Of_Gobbletygook_Representing_The_Modifiers>>. One way to "solve" the problem is to print out the result of type(of: hostingView) when I create the window, and then change my cast to include the current version of the "gobbledygook", but that is brittle for the following two reasons:
If I change the modifiers, the compiler will not warn me that I need to update the cast, and
Since the "gobbledygook" contains single underscored values, I must assume those are internal details that could change. Hence without my changing any code, an OS update could cause the cast to start failing.
So I have created a solution in the form of the following NSView extension:
extension NSView {
func originalRootView<RootView: View>() -> RootView? {
if let hostingView = self as? NSHostingView<RootView> {
return hostingView.rootView
}
let mirror = Mirror(reflecting: self)
if let rootView = mirror.descendant("_rootView") {
let mirror2 = Mirror(reflecting: rootView)
if let content = mirror2.descendant("content") as? RootView {
return content
}
}
return nil
}
}
This allows me to handle my needs using the following:
private func currentContentView() -> ContentView? {
return NSApplication.shared.keyWindow?.contentView?.originalRootView()
}
... sometime later ...
if let contentView = currentContentView() {
// do what I need with contentView
}
What I would like to know is if there is a way to implement originalRootView without the use of reflection, presumably by allowing a partially specified cast to the ModifiedContent object. For example, something like the following (which does not compile):
extension NSView {
func originalRootView<RootView: View>() -> RootView? {
if let hostingView = self as? NSHostingView<RootView> {
return hostingView.rootView
}
if let hostingView = self as? NSHostingView<ModifiedContent<RootView, ANY>> {
return hostingView.rootView.content
}
return nil
}
}
The problem is what to put for "ANY". I would think some form of Any or AnyObject, but the complier complains about that. Essentially I would like to tell the compiler that I don't care what ANY is so long as the ModifiedContent has RootView as its content type.
Any ideas would be appreciated.
Just to make the results official, the answer is "no" there is no way to partially match a generic as of Swift 5.1.

Select random value from a enum

select random value from given enum
public class SwiftConfettiView: UIView {
public enum ConfettiType {
case confetti
case triangle
case star
case diamond
case image(UIImage)
}
// usage in other class
confettiView.type = .confetti
Would like to set randomly confettiView triangle, star,diamond,confetti
//This will not work
confetti.type = ConfettiType.allCases.randomElement()!
Type 'SwiftConfettiView.ConfettiType' has no member 'allCases'
//So, all all confetti to an array list and load in from there!
var confettiList = [SwiftConfettiView.ConfettiType.confetti
, SwiftConfettiView.ConfettiType.diamond
, SwiftConfettiView.ConfettiType.triangle
, SwiftConfettiView.ConfettiType.star
]
confettiView.type = confettiList.randomElement()!
It's working properly!
is this approach is correct or wrong?
Here your enum is associated type. So if case of type image you have to provide a image as parameter. I have considered a default image.
extension ConfettiType: CaseIterable {
static var allCases: [ConfettiType] {
let img: UIImage = UIImage(named: "default_image")! // change as your expectation
return [.confetti, .triangle, .star, .diamond, .image(img)]
}
}
let randomEnum = ConfettiType.allCases.randomElement()
Otherwise if your image type would be something like this image(UIImage?) then we can put nil as default value. In that case it would be more convenient.
#Alexander identified your issue in the comments: Your issue is that you need to conform to CaseIterable to get access to allCases, and that only works if your enum doesn't have associated values (because there's no way to enumerate all possible UIImages).
Your workaround is fine, and it can be made nicer by taking advantage of Swift's type inference.
You might add a randomChoice(from:) function to your enum class that lets the caller specify which items to choose from. Because of type inference, you can specify the cases without fully qualifying them (eg. .confetti and .triange is sufficient).
Here is a full example:
public class SwiftConfettiView: UIView {
public enum ConfettiType {
case confetti
case triangle
case star
case diamond
case image(UIImage)
static func randomChoice(from choices: ConfettiType...) -> ConfettiType {
return choices.randomElement()!
}
}
func test() {
for _ in 1...10 {
let choice = ConfettiType.randomChoice(from: .confetti, .triangle, .star, .diamond)
print(choice)
}
}
}
SwiftConfettiView().test()
triangle
triangle
diamond
diamond
triangle
star
confetti
confetti
star
triangle
Notes:
Alternatively, you could have randomChoice take [ConfettiType]. In that case, you need to decide how to handle an empty array (either return an optional so that you can return nil for an empty array, or provide a default value such as .confetti for that case).
return choices.randomElement() ?? .confetti
Taking an array gives you the ability to provide a default value, such as:
static func randomChoice(from choices: [ConfettiType] = [.confetti, .triangle, .star, .diamond]) -> ConfettiType {
return choices.randomElement() ?? .confetti
}
For example:
// take the default values
let choice = ConfettiType.randomChoice()
// specify specific choices
let choice = ConfettiType.randomChoice(from: [.star, .diamond])
If you always just want to choose from those 4 enum values, you could just have randomChoice take no arguments and just hard code the choice from those 4 values. In that case, randomChoice could be implemented as a computed property:
static var randomChoice: ConfettiType { return [.confetti, .triangle, .star, .diamond].randomElement()! }
and called like this:
let choice = ConfettiType.randomChoice

Sub enumeration rawValue

Consider the following code, where I declared an enum with sub enums inside of it.
enum LocalizeKey {
case message(Messages)
case buttons(Buttons)
enum Buttons: String {
case remove = "Remove"
case add = "Add"
}
enum Messages: String {
case success = "Success"
case failure = "Failure"
}
}
In a normal enum with no sub enums we can easily access .rawValue property and get the raw value of whatever case we picked.
For this case, i created a function like this just to check out what am i getting .
func keyString(for type: LocalizeKey) {
print(type)
}
keyString(for: .message(.failure)) // usage
Problem : there are no other properties than .self to access for this LocalizeKey enum .
What I am trying to achieve: perhaps you can relate by the naming, i am trying to wrap my localized keys, so i can access them easily based on the key type etc, and the rawValue that is refering to the actual key will go into the getLocalizedValue function .
Playground Output : using the function above the playground output was
message(__lldb_expr_21.LocalizeKey.Messages.failure)
Edit: without having to create a variable that switches self on every case, imagine if we had +400 key that would be a huge mess probably.
You need to switch on the type parameter and do pattern matching:
switch type {
case .message(let messages): return messages.rawValue
case .buttons(let buttons): return buttons.rawValue
}
You can also make this an extension of LocalizeKey:
extension LocalizeKey {
var keyString: String {
switch self {
case .message(let messages): return messages.rawValue
case .buttons(let buttons): return buttons.rawValue
}
}
}
You are going to have to switch somewhere. If there are only a handful of "sub-enums", it is probably the easiest to just write a switch manually:
func keyString(for type: LocalizeKey) {
switch type {
case .message(let message):
print(message.rawValue)
case .buttons(let button):
print(button.rawValue)
}
}
If you don't want to write this manually, you either have to change your data structure so it is not needed, or use a code generation tool that generates the boilerplate for you.
Although The mentioned answers do provide the solution, I'd mention the issue of the approach itself:
At this point, each new case (key) has to be added in your switch statement with an associated value, which seems to be undesired boilerplate coding; I assume that you could imagine how it will look like when having many cases in the enums.
Therefore, I'd recommend to follow an approach to be more dynamic instead of adding the value of each case manually in a switch statement. Example:
protocol Localizable {
var value: String { get }
}
extension RawRepresentable where Self: Localizable, Self.RawValue == String {
var value: String { return rawValue }
}
extension CustomStringConvertible where Self: RawRepresentable, Self.RawValue == String {
var description: String { return rawValue }
}
struct LocalizeKey {
enum Buttons: String, Localizable, CustomStringConvertible {
case remove = "Remove"
case add = "Add"
}
enum Messages: String, Localizable, CustomStringConvertible {
case success = "Success"
case failure = "Failure"
}
}
We are applying the same logic for your code, with some improvements to make it easier to maintain.
Based on that, you still able to implement your function as:
func keyString(for type: Localizable) {
print(type)
}
Usage:
keyString(for: LocalizeKey.Buttons.add) // Add
keyString(for: LocalizeKey.Messages.success) // Success
IMO, I find calling it this way seems to be more readable, straightforward rather than the proposed approach (keyString(for: .message(.failure))).

What's the difference between an Extension func, Extension static func & Extension class func in Swift?

I was trying to create an extension function on the UIColor which could take a parameter of type Card.Colour and return a UIColor back to the caller.
button.backgroundColor = UIColor.getColour(cardColour: cardToDeal.colour)
extension UIColor {
func getColour(cardColour: Card.Colour) -> UIColor {
switch cardColour {
case .Red:
return UIColor.red
case .Green:
return UIColor.green
case .Blue:
return UIColor.blue
}
}
}
When I tried to do this, the extension function of UIColor.getColour required me to enter a parameter of type UIColor rather than the specified type of Card.Colour in the extension method.
However, when I changed the extension function of getColour to either:
static func getColour(cardColour: Card.Colour) -> UIColor {
class func getColour(cardColour: Card.Colour) -> UIColor {
It allowed me to pass a parameter of type Card.Colour
Why is this? Why does changing the function to either a static function or a class function change the type required to be passed in?
Thanks in advance!
(A detailed answer would be much appreciated)
Remember that UIColor is a class. A class is kind of like a blueprint that you use to create instances or objects that conform to that class. UIColor.red is an example of an instance of the UIColor class.
When you define a func inside a class (in your case, as an extension), Swift assumes that you want to add that func to the blueprint, which will in turn be available in all instances of the UIColor class, like UIColor.red.
You could also define your func outside of all classes, by just placing it on the top level of the module, instead of inside an extension.
However, to keep your functions organized, you can place functions like that inside the class name. You just have to tell Swift that you're not trying to add the function to the blueprint that will be applied to all instances, and that all you want instead is to have a function whose name is prefixed with the name of the class.
Here's an example to illustrate the difference in usage:
class Test {
func notStatic() {
print("called from an instance")
}
static func thisIsStatic() {
print("called on class name directly")
}
}
let instance = Test() // this is an *instance* of Test
instance.notStatic() // we can call a non static func on instance
Test.thisIsStatic() // we can call a static func directly on the class only
Now, let's go back to your specific example for a second. Notice that in your example, you're starting with a instance of Card.Colour and trying to create a new instance of UIColor.
In other words, adding a func to UIColor instances (i.e., a non-static or class) is useless to you, because you don't have an instance of UIColor yet.
The idiomatic way of creating a new instance of a class is using an initializer (init). So you could turn your function into an initializer on UIColor like this:
extension UIColor {
convenience init(cardColour: Card.Colour) {
switch cardColour {
case .Red: self.init(cgColor: UIColor.red.cgColor)
case .Blue: self.init(cgColor: UIColor.blue.cgColor)
case .Green: self.init(cgColor: UIColor.green.cgColor)
}
}
}
Now you just call UIColor(cardColour: .Red) to get what you want. Note that in the implementation, I'm converting UIColor.red to cgColor and back as a quick hack. Feel free to use the initializer you see fit on UIColor for each case of Card.Colour.
But there's another way, which I think is even more elegant. Since you already have an instance of Card.Colour, you can extend Card.Colour with a function that gives you the UIColor corresponding to the instance. Within that function, you can refer to the Card.Colour instance using the keyword self.
Since you already have the Card.Colour instance through self, you don't need to pass any arguments to that function. This allows you to use a cool feature called computed properties to make the usage even nicer.
This is how you'd add such an extension to Card.Colour:
extension Card.Colour {
var uiColor: UIColor {
switch self {
case .Red: return .red
case .Blue: return .blue
case .Green: return .green
}
}
}
And you can then get a UIColor from a Card.Colour like this Card.Colour.Red.uiColor or mainColour.uiColor, where mainColour is of type Card.Colour.
Finally, as Leo Dabus noted in a comment, Swift's naming conventions is that cases should start with a lowercase letter. You should be using Card.Colour.red instead of Card.Colour.Red, etc. Those conventions came out around Swift 3 time. It was common to capitalize case names before then.
An extension method operates on an instance of provided type. You can use all of the internal properties and methods of an instance inside the method block.
static methods are methods that are namespaced by the class name and do not operate on any specific instance of your class. class methods are pretty much the same, just the difference between class and static is, that you can override class methods in a subclass.

EXC_BAD_ACCESS (code=2) when using NSNumberFormatter

I'm having a problem, which I can't figure out for the life of me. I've searched the internet, trying to understand Swifts's EXC_BAD_ACCESS, but nothing seemed to help.
The following code is quite long, but most of the time the comments are all the information needed to understand the item of relevance.
I have a class CalculatorController, which contains the following relevant methods and properties:
import UIKit
class CalculatorController: UIViewController {
// the actual `#IBOutlet` which is never accessed directly
#IBOutlet private weak var _mainDisplay: UILabel!
// an instance of `MainDisplayMicroController`
// holds a reference to `_mainDisplay`
// is used to manipulate `_mainDisplay` in a controlled way
private var mainDisplay: MainDisplayMicroController!
override func viewDidLoad() {
super.viewDidLoad()
// connects `mainDisplay` with `_mainDisplay`
mainDisplay = MainDisplayMicroController(label: _mainDisplay)
// sets `_mainDisplay`'s `text` property to "0"
mainDisplay.content = .Number(0)
//...
}
//...
}
In order to manage _mainDisplay in a certain way, I have created a class MainDisplayMicroController, which on the one hand contains a reference to the the UILabel itself, and on the other hand contains methods and properties, which perform actions on the UILabel:
import UIKit
class MainDisplayMicroController {
// used to express what `label.text` is currently showing
private enum DisplayState {
case ShowingNumber
case ShowingConstant
case ShowingErrorMessage
case Unknown
}
// holds the current state of what `label.text` is showing
private var state = DisplayState.Unknown
// used to pass different types of values in and out of this class
enum ContentType {
case Number(Double)
case Constant(String)
case ErrorMessage(String)
case Unknown(Any?)
}
// holds the reference to the label which is being manipulated/managed
private var label: UILabel?
// makes `label`'s `text` property directly accessible, as `label` is `private`
var text: String? {
get {
return label?.text
}
set {
label?.text = newValue
removeLeadingZeros()
transformToInteger()
}
}
// a property to allow controlled retrieval and manipulation of `label.text`
// uses `ContentType` to make clear what the information in `label.text` is/ is supposed to be
var content: ContentType {
get {
switch state {
case .ShowingNumber:
if let string = text {
if let value = NSNumberFormatter().numberFromString(string)?.doubleValue {
return .Number(value)
}
}
case .ShowingConstant:
if let symbol = text {
return .Constant(symbol)
}
case .ShowingErrorMessage:
if let message = text {
return .ErrorMessage(message)
}
default:
break
}
state = .Unknown
return .Unknown(text)
}
set {
switch newValue {
case .Number(let value):
text = "\(value)"
state = .ShowingNumber
removeLeadingZeros()
transformToInteger()
case .Constant(let symbol):
text = symbol
state = .ShowingConstant
case .ErrorMessage(let message):
text = message
state = .ShowingErrorMessage
case .Unknown(let thing):
text = "Error: Passed unknown value: \(thing)"
state = .ShowingErrorMessage
}
}
}
// removes the ".0" from `label.text`, if it is a whole number
private func transformToInteger() {
if state == .ShowingNumber {
switch content {
case .Number(let value):
if round(value) == value {
var doubleString = "\(value)"
if doubleString.rangeOfString("e") == nil {
dropLast(doubleString)
dropLast(doubleString)
}
text = doubleString
}
default:
break
}
}
}
// removes leading "0"s from `label.text` if they are redundant
private func removeLeadingZeros() {
if state == .ShowingNumber {
switch content {
case .Number(let displayedValue):
content = .Number(displayedValue)
default:
break
}
}
}
//...
}
Now, when I run the code I get the following error:
From what I've read on EXC_BAD_ACCESS, the error often occurs when trying to call methods on released objects. I've tried using NSZombieto check the issue, but I didn't find anything (probably due to my incompetence when using NSZombie).
If I try to follow what is happening by logic, I come to following conclusion:
mainDisplay is set successfully in viewDidLoad()
mainDisplay.content is called
in the content's setter the switch-statement executes the .Number case
text and state are successfully set
removeLeadingZeros() is called
the switch-statement accesses content's getter
the switch-statement in content's getter executes the .ShowingNumber case
the if-statements resolve to true, finally trying to evaluate the NSNumberFormatter expression
the EXC_BAD_ACCESS occurs
Does anyone know why this is happening? Does it have to do with me manipulating an #IBOutlet in a different class?
Any help is greatly appreciated!
Here are links to the complete CalculatorController and MainDisplayMicroController.
Update #1:
As #abdullah suggested I have tried directing the NSNumberFormatter expression in to multiple expressions. I still get the error though:
Update #2:
I've removed all references and external classes, to make it as simple as possible, while maintaining the same functionality.
All of the methods and properties defined in MainDisplayMicroController have been moved to CalculatorModel.
These methods and properties now access the original #IBOutlet, not any reference to it.
But still when trying to run it I get EXC_BAD_ACCESS(code=2) at the same line of code.
I'm just super confused, as it can't have anything to do with weird references, or objects being released too soon anymore.
Here's the complete code for the new CalculatorController.
Update #3:
I've removed the NSNumberFormatter line, by changing it to:
Now I get the following error though:
I assume there's some fundamental problem with the code, so I'm scrapping it. But thanks for all the help, and attempts at figuring this out.
Update #4:
This is what I get when adding a breakpoint on throw for all exceptions:
I don't really see anything in that line that can cause a crash. I suggest you do the following:
Make a clean build (clean, nuke your derived data folder, then build) and see if the crash persists
If the crash persists, set a breakpoint on throw for all exceptions to see which operation in the callstack caused the crash, and take it from there
#WarrenBurton is on to something.
Take your line that crashes out of your big class and run it in the playground and it works fine:
let string = "1.213"
if let value = NSNumberFormatter().numberFromString(string)?.doubleValue
{
println("value = \(value)")
}
Displays the result
value = 1.213
Where is your variable "string" defined in your class?
Notice that string is blue, like a keyword, not black, like other local variables.
I'd try local variable string ==> myString
just to know for sure.
Just 'cuz I was seeing the same thing and noticed no one had commented past your last edit (and maybe a fellow Googler for this issue will see this someday):
For both of our situations the issue is infinite recursion - we're calling a method from itself infinitely. That's the bug. The implication in the crash of NSNumberFormatter is a red herring.