Xcode - Unit Testing UIColor from asset catalog - swift

I wrote an extension for my Theme class that returns my mainColor based on my current theme. My unit tests cover theme changes but I still have problem with UIColor optional value. I can't cover the situation that UIColor could not able returns my specific color from my color catalog.
extension Theme {
var mainColor: UIColor {
switch self {
case .system:
if let color = UIColor(named: "MainColor") {
return color
} else {
return .clear
}
case .custom:
return .yellow
}
}
}
I also used this line instead of unwrapping the optional but it didn't work too.
return UIColor(named: "MainColor") ?? .clear
How I unit test that the correct color is retrieved from the asset catalog?

I would personally just force unwrap UIColor(named: "MainColor")! the color in my tests to assert the correct behaviour is occurring (that we are indeed able to access the desired color in the asset catalog). If we cannot access this color we will get a crash which, in a unit testing context, is actually desirable to isolate any problems.
I would also abstract the name of the color into an enum to ensure that the name is only defined in 1 place, then the possibility of naming inconsistencies between your app target and test target are removed.
enum Colors: String {
case main = "MainColor"
}
Then use UIColor(named: Colors.main.rawValue) to access the color.

The problem is that the name MainColor is hard coded, and either this color is in your asset catalog or it isn’t, and you cannot control for that in a test. You need to abstract this into a method to which you can pass the color name from the test.

Related

Convert from switch case statement to a value in 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
}
}

Value of type 'UIColor' has no member 'adjust'

I am trying to get a lighter version of a UIColor defined by user in my custom view like-
public var borderColor : UIColor?{ //needs to be set by user
didSet{
setNeedsLayout()
}
}
private var backgroundColor : UIColor{ // computed property
if let color = borderColor{
return color.adjust(by: abs(20))
}
}
I searched for possible solution and got this
Get lighter and darker color variations for a given UIColor
However, I am getting a compile time error stating-
Value of type 'UIColor' has no member 'adjust'
Also in the Apple documentation of UIColor, I don't find any method named "adjust(by:)".
Can anyone please help me with this.

How to unit test a textView using a spy

I have a cell that contains a textView and i would like to test that the properties of that textView are set correctly using unit tests. However i seem to have hit a blocker when it comes to having access to the textView in the test since its private.
Is there a way I can test my textView:-
Here is my code
class MyCell {
private let myText: UITextView = {
let textView = UITextView()
textView.isScrollEnabled = false
textView.isEditable = false
return textView
}()
func setup(viewModel: MYViewModel) {
if viewModel.someValue {
myText.backgroundColor = UIColor.red
} else {
myText.backgroundColor = .clear
}
}
}
Is it possible to test something like the testView's background color is set to clear ?
Unless you want to relax the access level of myText from private to internal (the default if you don't specify it), there is no direct way to test it.
The only suggestion I have to test this indirectly would be to use snapshot testing.
You could write two snapshot tests, one for each value of .someValue from your MYViewModel.
Another option to make the testing –and maintainability– of your view straightforward is to introduce a ViewConfiguration value type, following the humble view pattern.
Basically, you can have a struct in between MYViewModel and MyCell that describes each of the view properties for MyCell.
struct MyCellViewConfiguration {
let textFieldBackgroundColor: UIColor
}
extension MYViewModel {
var viewConfiguration: MyCellViewConfiguration = {
return MyCellViewConfiguration(
textFieldBackgroundColor: someValue ? .red : .clear
)
}
}
extension MyCell {
func setup(with configuration: MyCellViewConfiguration) {
myText.backgroundColor = configuration.textFieldBackgroundColor
}
}
The code in setup(with configuration: MyCellViewConfiguration) is so simple –just a 1-to-1 assignment– that you can get away without testing it.
You can then write test for how MyCellViewConfiguration is computed from MYViewModel.
Simply remove private from your declaration. Then the access control will be the default internal. In the test code, make sure to #testable import so gain access to internal features.
Unit testing a few attributes isn't hard. But if you want a test that records the appearance, look into snapshot testing. This can be done without XCUITestCase. It's an order of magnitude slower than a regular unit test, but probably another order of magnitude faster than a UI test.

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.