How to Unit-Test with global structs? - swift

It is clear for me that in a UnitTest you
generate an input property
pass this property to the method you want to test
Compare the results with your expected results
However, what if you have a global struct with e.g. the game xp and game level which has private setters and can't be modified. I automatically load this data from the UserDefaults when the app starts. How can you test methods that access that global struct, when you can not alter the input?
Example:
import UIKit
//Global struct with private data
struct GameStatus {
private(set) static var xp: Int = 0
private(set) static var level: Int = 0
/// Holds all winning states
enum MyGameStatus {
case hasNotYetWon
case hasWon
}
/// Today's game state of the user against ISH
static var todaysGameStatus: MyGameStatus {
if xp >= 100 {
return .hasWon
} else {
return .hasNotYetWon
}
}
func restoreXpAndLevel() {
// reads UserData value
}
func increaseXp(for: Int) {
//...
}
}
// class with methods to test
class LevelView: UIView {
enum LevelState {
case showStart
case showCountdown
case showFinalCuontdown
}
var state: LevelState {
if GameStatus.xp > 95 {
return .showFinalCuontdown
} else if GameStatus.xp > 90 {
return .showCountdown
}
return .showStart
}
//...configurations depending on the level
}

First, LevelView looks like it has too much logic in it. The point of a view is to display model data. It's not to include business logic like GameStatus.xp > 95. That should be done elsewhere and set into the view.
Next, why is GameStatus static? This is just complicating this. Pass the GameStatus to the view when it changes. That's the job of the view controller. Views just draw stuff. If anything is really unit-testable in your view, it probably shouldn't be in a view.
Finally, the piece that you're struggling with is the user defaults. So extract that piece into a generic GameStorage.
protocol GameStorage {
var xp: Int { get set }
var level: Int { get set }
}
Now make UserDefaults a GameStorage:
extension UserDefaults: GameStorage {
var xp: Int {
get { /* Read from UserDefaults */ return ... }
set { /* Write to UserDefaults */ }
}
var level: Int {
get { /* Read from UserDefaults */ return ... }
set { /* Write to UserDefaults */ }
}
}
And for testing, create a static one:
struct StaticGameStorage: GameStorage {
var xp: Int
var level: Int
}
Now when you create a GameStatus, pass it storage. But you can give that a default value, so you don't have to pass it all the time
class GameStatus {
private var storage: GameStorage
// A default parameter means you don't have to pass it normally, but you can
init(storage: GameStorage = UserDefaults.standard) {
self.storage = storage
}
With that, xp and level can just pass through to storage. No need for a special "load the storage now" step.
private(set) var xp: Int {
get { return storage.xp }
set { storage.xp = newValue }
}
private(set) var level: Int {
get { return storage.level }
set { storage.level = newValue }
}
EDIT: I made a change here from GameStatus being a struct to a class. That's because GameStatus lacks value semantics. If there are two copies of GameStatus, and you modify one of them, the other may change, too (because they both write to UserDefaults). A struct without value semantics is dangerous.
It's possible to regain value semantics, and it's worth considering. For example, instead of passing through xp and level to the storage, you could go back to your original design that has an explicit "restore" step that loads from storage (and I assume a "save" step that writes to storage). Then GameStatus would be an appropriate struct.
I'd also extract LevelState so that you can more easily test it and it captures the business logic outside of the view.
enum LevelState {
case showStart
case showCountdown
case showFinalCountDown
init(xp: Int) {
if xp > 95 {
self = .showFinalCountDown
} else if xp > 90 {
self = .showCountdown
}
self = .showStart
}
}
If this is only ever used by this one view, it's fine to nest it. Just don't make it private. You can test LevelView.LevelState without having to do anything with LevelView itself.
And then you can update the view's GameStatus as you need to:
class LevelView: UIView {
var gameStatus: GameStatus? {
didSet {
// Refresh the view with the new status
}
}
var state: LevelState {
guard let xp = gameStatus?.xp else { return .showStart }
return LevelState(xp: xp)
}
//...configurations depending on the level
}
Now the view itself doesn't need logic testing. You might do image-based testing to make sure it draws correctly given different inputs, but that's completely end-to-end. All the logic is simple and testable. You can test GameStatus and LevelState without UIKit at all by passing a StaticGameStorage to GameStatus.

The solution is Dependency Injection!
You can create a Persisting protocol and a facade class to interact with the user defaults
protocol Persisting {
func getObject(key: String) -> Any?
func persist(value: Any, key: String)
}
final class Persist: Persisting {
func getObject(key: String) -> Any? {
return UserDefaults.standard.object(forKey: key)
}
func persist(object: Any, key: String) {
UserDefaults.standard.set(value: object, forKey: key)
}
}
class MockPersist: Persisting {
// this is set from the test
var mockObjectToReturn: Any?
func getObject(key: String) -> Any? {
return mockObjectToReturn
}
var didCallPersistObject: (Any?, String)
func persist(object: Any, key: String) {
didCallPersistObject.0 = object
didCallPersistObject.1 = key
}
}
And now on you struct, you gonna need to inject this a var of type Persisting.
When testing you gonna need to inject the MockPersist and assert against the vars defined on the MockPersist class.
Hope this helps

Related

Extends Set's insert in swift for custom logic

I need to have custom logic in a Set that defines when a Hashable can be insert or not.
First I tried to solve this with a observer
var Tenants: Set<Tenant> = [] {
willSet {
// to the business logic here
// ...
But in an observer i can not return an error. So I tried to extend Set to overwrite the insert method.
extension Set where Element == Tenant {
#inlinable mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element){
// .... do my logic here ...
return (true, newMember)
}
}
That works so far and the method will be called. I can return true and if my logic did not pass even a false. Ok, but how do I add the Element into the Set? super.insert(). The return is correct, but the Set is empty. How to add the elements into the concrete set?
Implementation so far
/// Global set of known tenants
var Tenants: Set<Tenant> = [] {
willSet {
let newTenants = newValue.symmetricDifference(Tenants)
guard let newTenant = newTenants.first else {
Logging.main.error("Can not find tenant to add.")
return
}
Logging.main.info("Will add new Tenant \(newTenant.name) [\(newTenant.ident)]")
}
}
extension Set where Element == Tenant {
#inlinable mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element){
print("Check to add...")
// .... do my logic here ...
// ok
return (true, newMember)
}
}
The result is:
Check to add...
error : Can not find tenant to add.
Check to add...
error : Can not find tenant to add.
This seems to work for "do my logic here"
self = self.union([newMember])
Edit: Because this breaks the semantics of Set, I think it is better to write it as something like this:
struct CheckedSet<T: Hashable> {
private(set) var wrappedSet: Set<T> = []
var shouldInsert: (T) -> Bool = { _ in true }
mutating func maybeInsert(_ t: T) {
guard shouldInsert(t) else { return }
wrappedSet.insert(t)
}
}
var cs = CheckedSet<String>()
cs.shouldInsert = { str in str.allSatisfy(\.isLowercase) }
cs.maybeInsert("HELLO")
cs.wrappedSet // []
cs.maybeInsert("hello")
cs.wrappedSet // ["hello"]
I would do it with a property wrapper:
#propertyWrapper
struct TenantsSet {
var wrappedSet: Set<Tenant>
struct Projected {
let error: Bool
}
var projectedValue = Projected(error: false)
var wrappedValue: Set<Tenant> {
get { wrappedSet }
set {
print("some custom logic")
// set projectedValue appropriately
wrappedSet = newValue
}
}
init(wrappedValue: Set<Tenant>) {
wrappedSet = wrappedValue
}
}
This allows error-reporting by checking the error property on the projected value:
#TenantsSet var tenants = []
func f() {
tenants = [Tenant()]
if $tenants.error {
}
}
As the Swift Guide says:
Extensions add new functionality to an existing class, structure, enumeration, or protocol type.
You are not supposed to use them to modify existing behaviour. It would be very confusing to readers of your code. If you want to use an extension to do this, you should declare a new method, with a different signature. Perhaps call it insert(newTenant:)?

Exposing dictionary in Swift property wrappers

I have an internal dictionary that I don't want to expose to the user. Instead, I expose only certain values using properties, like this:
public var artist: String? {
get {
return items["artist"]
}
set {
items["artist"] = newValue
}
}
//...so on for another 20 or so items
As you can imagine, this ends up getting repeated quite a lot. I was thinking that property wrappers would be a nice way to clean this up - however, it's not possible to pass items directly to the wrapper, since property wrappers are created before init (so self would not be accessible).
Is there a way around this, or is this just one of the limitations of propertyWrappers?
You could build a generic solution. I did one, but you can probably improve it:
class PropertyWrapper {
private var items: [String: Any] = ["artist": "some dude"]
enum Key: String {
case artist
}
func getItem<T: Any>(key: Key) -> T {
guard let item = items[key.rawValue] as? T else {
preconditionFailure("wrong type asked for")
}
return item
}
func setItem(value: Any, key: Key) {
items[key.rawValue] = value
}
}
class GetValueClass {
func getValue() {
let wrapper = PropertyWrapper()
let value: String = wrapper.getItem(key: .artist)
}
}
class SetValueClass {
func setValue() {
let wrapper = PropertyWrapper()
wrapper.setItem(value: "some", key: .artist)
}
}

Executiong closure on array modification

I have the following code:
class Note: NSObject {
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
This prints "hi" if I add or remove an item from the array or if I do
Global.notes = []
Is there a way to print("hi") every time when one of the Note objects in the array is modified?
Thanks for your answers
Without changing the class to a struct, I have two basic ways to handle this.
This is the object you asked about
class Note: NSObject {
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
Wrap Notes in a wrapper that is a struct to get the struct behavior.
extension Note {
struct Wrapper { let note: Note }
}
extension Global {
static var wrappedNotes = [Note.Wrapper]() {
didSet {
print("hi")
}
}
}
Global.wrappedNotes.append(Note.Wrapper(note: Note()))
Global.wrappedNotes[0] = Note.Wrapper(note: Note())
Global.wrappedNotes.remove(at: 0)
The other way is to create a note manager to wrap access to the array.
class NoteManager {
subscript(index: Int) -> Note {
get {
return values[index]
}
set {
defer { onUpdate() }
values[index] = newValue
}
}
func append(_ newNote: Note) {
defer { onUpdate() }
values.append(newNote)
}
func remove(at index: Int) -> Note {
defer { onUpdate() }
return values.remove(at: index)
}
private func onUpdate() {
print("hi")
}
private var values = [Note]()
}
extension Global {
static var managedNotes = NoteManager()
}
Global.managedNotes.append(Note())
Global.managedNotes[0] = Note()
Global.managedNotes.remove(at: 0)
As per #staticVoidMan comment , If you make your model , a struct, rather than a class, then the property observer didSet will work for your Note model's own properties as well.
import Foundation
struct Note {
var name: String
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
Global.notes.append(Note(name: "Shubham"))
Global.notes.append(Note(name: "Bakshi"))
Global.notes[0].name = "Boxy"
This will print the following on the console :
hi
hi
hi
Swift Array is a struct, and structs are value-type which means they change completely when elements are added/removed/replaced. Hence when you add/remove/replace a Note, the didSet property observer gets called as the array has been set again.
However, as per you question:
Is there a way to print("hi") every time when one of the Note objects in the array is modified?
By this I am assuming that you want to do something when an element within this array is accessed and an internal property is modified.
This would have been fine if you were dealing with only value-type objects, i.e. had your Note object also been a struct, then changing anything inside one Note would have caused the array to change as well.
But your Note object is a class, i.e. reference-type, and stays as the same object even if it's internal elements change. Hence your array doesn't need to update and didSet does not get called.
Read: Value and Reference Types
KVO Solution:
Now... Since your Note is subclassing NSObject, you can use the KVO concept
As per the following working example, we observe only one property of the Note class.
If you want to observe more properties then you will need to observe those many more keypaths.
Example:
class Note: NSObject {
#objc dynamic var content = ""
init(_ content: String) {
self.content = content
}
}
class NoteList {
var notes: [Note] = [] {
didSet {
print("note list updated")
//register & save observers for each note
self.noteMessageKVOs = notes.map { (note) -> NSKeyValueObservation in
return note.observe(\Note.content, options: [.new, .old]) { (note, value) in
print("note updated: \(value.oldValue) changed to \(value.newValue)")
}
}
}
}
//array of observers
var noteMessageKVOs = [NSKeyValueObservation]()
}
let list = NoteList()
list.notes.append(Note("A")) //note list updated
list.notes.append(Note("B")) //note list updated
list.notes[0].content = "X" //note updated: A changed to X
list.notes[1].content = "Y" //note updated: B changed to Y
Notes:
NSObject is required for KVO
#objc dynamic is required to make a property observable
\Note.message is a keypath
noteMessageKVOs are required to keep the observers alive

Swift - How to manage turn order management?

I'm working on a local game which relies on turn order.
Rules;
There are a number of phases in the game (ie: Buy, Sell)
During each phase, a player takes a single turn
Each phase is not considered complete until every player (in turn order) has completed their turn.
I'm not sure how to manage this data. There are a number of things to track.
The phase we are in
The current player on turn
When all players have completed their turns
When the end of the turn order has been reached so we can move to next phase.
Resetting all turn completions when all phases are complete
I'm thinking that a subscription model is the best approach to this, but I'm not used to such a pattern.
Currently I'm using a similar system to a to-do where the phase itself can be marked complete or incomplete.
This is the way I'm currently handling turn orders and phases in Swift playground.
// Turn order management
class TurnOrderManager: NSObject
{
static var instance = TurnOrderManager()
var turnOrder: [Player] = [Player]()
private var turnOrderIndex = 0
var current: Player {
return turnOrder[turnOrderIndex]
}
func next() {
if (self.turnOrderIndex < (self.turnOrder.count-1)) {
turnOrderIndex += 1
}
else {
print("Reached end of line")
}
}
}
class Player: NSObject {
var name: String = ""
override var description: String {
return self.name
}
init(name: String) {
super.init()
self.name = name
}
}
let p1:Player = Player.init(name: "Bob")
let p2:Player = Player.init(name: "Alex")
TurnOrderManager.instance.turnOrder = [p1,p2]
print (TurnOrderManager.instance.current)
TurnOrderManager.instance.next()
print (TurnOrderManager.instance.current)
TurnOrderManager.instance.next()
print (TurnOrderManager.instance.current)
// ---------------------------------
// Phase management
enum PhaseType: Int {
case buying = 1
case selling
}
struct Phase {
var id: PhaseType
var title: String
var completed: Bool = false {
didSet {
// Notify subscribers of completion
guard completed else { return }
handlers.forEach { $0(self) }
}
}
var description:String {
return "Phase: \(self.title), completed: \(completed)"
}
// Task queue
var handlers = [(Phase) -> Void]()
init(id: PhaseType, title: String, initialSubscription: #escaping (Phase) -> Void =
{_ in})
{
self.id = id
self.title = title
subscribe(completion: initialSubscription)
}
mutating func subscribe(completion: #escaping (Phase) -> Void) {
handlers.append(completion)
}
}
class MyParentController {
lazy var phase1: Phase = {
return Phase(id: .buying, title: "Phase #1") {
print("Do something with phase: \($0.title)")
}
}()
}
let controller = MyParentController()
controller.phase1.completed = true
Question:
I'm wanting to notify:
Turn is complete
All turns are complete (so that it can move to next phase)
How do I make my TurnOrderManager alert the PhaseManager that the current turn is complete.
How do I make my PhaseManager know that when all turns are complete to move to the next phase.
I apologize for the verboseness of my query.
Many thanks
You're going to want to define a delegate relationship PhaseManager and your TurnOrderManager.
Here is the Apple docs on protocols.
Here is a great article on delegation.
First you'll need to define a protocol. Something like this:
protocol TurnManagerDelegate {
func complete(turn: objectType)
func allTurnsComplete()
}
Next you'll have to conform your PhaseManager to the protocol:
class PhaseManager: TurnManagerDelegate {
...
func complete(turn: objectType) {
// implement
}
func allTurnsComplete() {
// implement
}
...
}
Last you'll have to add a property on your TurnOrderManager with the delegate:
class TurnOrderManager {
...
var delegate: TurnManagerDelegate
...
}
and call the functions whenever needed in your TurnOrderManager:
...
delegate?.allTurnsComplete() //example
...
You'll also have to set your PhaseManager as the delegate before your TurnOrderManager would try to call any of the delegate methods.

Swift override all setters and getters of a subclass

I would like to override a setter/getter one time for but for all the properties for a class in swift
This my class. I want to call Realm each time I am adding a new value
class House : Object
{
var a:String
{
set {
do {
let realm = try Realm()
try realm.write {
a = newValue
}
}
catch {
}
}
}
var b:String
{
set {
do {
let realm = try Realm()
try realm.write {
b = newValue
}
}
catch {
}
}
}
}
There is no way in Swift how you can overwrite setters for all properties at once.
What you could generally do though is use:
overwritten setters per property
abstract computed properties wrapping low-level properties
intercept getters and setters by KVC accessor methods (e.g. is<Key>, get<Key>, …) and rely only on untyped dynamic KVC-based access via valueForKey(Path):, if you want to apply the decorated behavior (which you might want to avoid for this reason)
But Realm is using custom getters and setters under the hood, which are dynamically overwritten in an dynamically inserted intermediate class at runtime and relies on the presence of those. So the only approach, which is really feasible is having dynamic stored properties declared and adding for each of those an extra property, based on those.
var storedPropertyA: String = ""
var computedPropertyA: String {
get {
// add your extra behavior here
return storedPropertyA
}
set {
// add your extra behavior here
self.storedPropertyA = newValue
}
}
Beside that there is an alternative way of using the decorator pattern and decorate your whole object with extra behavior. In Swift, you could have your object and your decorator implement a common protocol, which defines your properties.
protocol HousingProperties {
var a: String { get set }
}
class House: HousingProperties {
var a: String = ""
}
class HouseDecorator: HousingProperties {
internal var house: House
init(house: House) { self.house = house }
var a: String {
// add your extra behavior here
self.house.a = a
}
}
Still I would NOT recommend to intercept property setters and getters for the purpose you intend here. Instead I'd advise to structure your application's architecture in a way, that allows you to be aware whether there is a write transaction or not and let the responsibility of making a write transaction in the hands of the code, which tries to modify objects.
Let me explain why:
Realm is using a multiversion concurrency control algorithm to manage persisted data and achieve thread-safety. This makes sure that different threads can read data at any point in time without having to read-lock and trying to synchronize these. Instead when a write is happening, all accessors are notified that there is new data and try to move on to the newest transaction. Until that has happened, all versions between the oldest data version, which is still used by a thread and the one written have to be retained. They can be first released when all threads advanced their commit pointers. If you do a lot of small transactions, you risk that your file size will blew up to unnecessary high values. For that reason, we recommend to batch write transactions to large changesets.
There is one hack to kind of attain what the poster is looking for, however possibly not advisable... Anyway; you can can create your own assignment operators that does whatever you want to do in realm prior to assigning the values
class MyType {
var myInt : Int = 0
var myString : String = ""
init(int: Int, string: String) {
myInt = int
myString = string
}
}
infix operator === {}
func ===<T>(lhs: T, rhs: T) -> T? {
Realm() // replace with whatever Realm()-specific stuff you want to do
return rhs
}
protocol MyAddableTypes {
func + (lhs: Self, rhs: Self) -> Self
}
extension String : MyAddableTypes {}
extension Int : MyAddableTypes {}
infix operator +== {} // ... -== similarily
func +==<T: MyAddableTypes>(lhs: T, rhs: T) -> T? {
Realm() // replace with whatever Realm()-specific stuff you want to do
return lhs+rhs
}
func Realm() {
// ...
print("Called realm")
}
var a = MyType(int: 1, string: "foo")
a.myString === "bar" // calls Realm(). After operation: a.myString = "bar"
a.myInt +== 1 // calls Realm(). After operation: a.myInt = 2
I thought I'd also mention that if you only want to do "Realm stuff" when a value is set (from your example: prior to setting a value, specifically), then the willSet method, used with stored properties, doesn't need to look so messy (nested closures), and personally, I would prefer this method
func Realm() {
print("Called realm")
}
class MyType {
// This isn't so messy, is it?
var myInt : Int = 0 { willSet { priorToSetValue(newValue) } }
var myString : String = "" { willSet { priorToSetValue(newValue) } }
var myDouble : Double = 0.0 { willSet { priorToSetValue(newValue) } }
private func priorToSetValue<T> (myVar: T) {
// replace with whatever Realm()-specific stuff you want to do,
// possibly including doing something with your new value
Realm()
}
init(int: Int, double: Double, string: String) {
myInt = int
myDouble = double
myString = string
}
}
var a = MyType(int: 1, double: 1.0, string: "foo")
a.myString = "bar"
print(a.myString) // calls Realm(). After operation: a.myString = "bar"
a.myInt += 1 // calls Realm(). After operation: a.myInt = 2