Best approach to create manager in Swift using singleton pattern - swift

I have two examples of simple manager in our application.
enum SomeStatus {
case none
case inProgress
}
class SomeManager {
static let shared = SomeManager()
private var _status: SomeStatus = .none
var status: SomeStatus {
get { return _status }
}
func changeStatus(to status: SomeStatus) {
_status = status
}
}
let manager = SomeManager.shared
print (manager.status)
// none
manager.changeStatus(to: .inProgress)
print (manager.status)
// inProgress
In this example I hide possibility to change status outside our manager with directly assign status variable. Anyway looks good.
But we can do this another way
class SomeManager {
private static let shared = SomeManager()
private var _status: SomeStatus = .none
static var status: SomeStatus {
get { return shared._status }
}
static func changeStatus(to status: SomeStatus) {
shared._status = status
}
}
print (SomeManager.status)
// none
SomeManager.changeStatus(to: .inProgress)
print (SomeManager.status)
// inProgress
The point to show how we can hide shared instance at all, but using same pattern of singleton
So, the question is, what is best performance/approach to use cases in example?

Related

Didomi trying to add events with variables

I'm trying to add these values ​​according to what the user does but I can't figure out how to do it:
enum DidomiStatusType: String, Codable {
case custom = "custom"
case disableAll = "all-denied"
case enabledAll = "all-accepted"
case none = "no-gdpr" }
In this function I have a listener where I call the actions that the user does, the problem is that I don't know what function to put if the user has customized it and I don't know if the way I was putting it is right
//If the user agreed all
let didomiEventListener = EventListener()
didomiEventListener.onNoticeClickAgree = { _ in
if self.type == .fakeSplash {
DispatchQueue.main.async {
self.statusType = .enabledAll
self.delegate?.continueNavigation()
}
}
}
And if the user performs another action without accepting:
didomiEventListener.onHideNotice = { _ in
if self.type == .fakeSplash {
DispatchQueue.main.async {
self.statusType = .none
self.statusType = .custom
self.statusType = .disableAll
self.delegate?.continueNavigation()
}
}
}
I was following this documentation
I was doing so bad, so there's the solution:
I upgraded the Didomi version too.
First I changed my enum to only String with the params I need
enum DidomiUserType: String {
case custom = "custom"
case disableAll = "all-denied"
case enabledAll = "all-accepted"
case none = "no-gdpr" }
Then I added a Struct like this:
struct DidomiUserData {
var vendorEnabled: Int
var vendorDisabled: Int
var purposeEnabled: Int
var purposeDisabled: Int
var isUserConsentStatusPartial: Bool
var type: DidomiUserType = .none }
The next thing I did was declare a constant that is and a variable with the struct of DidomiUserData.
let didomiUserStatusData = Didomi.shared.getUserStatus()
var didomiUserData = DidomiUserData(and the struct)
Finally only have to do an if-if-else.

Session sharing is not working from privileged class in os x

I'm working on an os x app that deals with the user keychain in mac os.
I have a Singleton class that manages the session data, and a privileged mechanism(which doesn't have any GUI). I want to share the data from the privileged class through Singleton object. But it's not working.
Here is my code sample
The Privileged Class:
class ReadKeychain: AuthorizationContext {
#objc func run(){
var singletonObj = SingletonState.shared
singletonObj.oktaAuthenticationStatus = status
}
}
And The singleton class object:
class SingletonState {
static let shared = SingletonState()
private init(){}
private let internalQueue = DispatchQueue(label: "com.tecmfa.singletonstateinternal.queue",
qos: .default,
attributes: .concurrent)
private struct _oktaAuthentication
{
var status: String?
}
private var OktaAuthStatus = _oktaAuthentication()
/// oktaAuthentication reference
var oktaAuthenticationStatus: String {
get {
return internalQueue.sync {
return OktaAuthStatus.status!
}
}
set (newStatus) {
internalQueue.async(flags: .barrier) {
self.OktaAuthStatus.status = newStatus
}
}
}
}
And it is working fine if I declare the class as unprivileged
But I need it to be privileged
Not sure if it’s related to the issue, but.
You've got a potential force-unwrap-induced crash here.
It happens when the value for oktaAuthenticationStatus hasn't yet been set, but already gets read. I suggest you change the type of this variable to optional and remove the force-unwrap:
var oktaAuthenticationStatus: String? {
get {
return internalQueue.sync {
return OktaAuthStatus.status
}
}
set (newStatus) {
internalQueue.async(flags: .barrier) {
self.OktaAuthStatus.status = newStatus
}
}
}
Or better yet, if you have a small number of statuses, make it an enum and add an option like .none for it to be the default value. Then the variable won't be optional.

How to Unit-Test with global structs?

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

Can I add associated object to Swift Struct?

I would like to add an additional property to the Swift String. I used this approach few times on objects, but it seems that it does not work on struct. Although, I don't get any error...
This is what I tried:
var str = "Hello, StackOverflow"
fileprivate struct AssociatedKeys {
static var myBool = "myBool"
}
extension String {
public var myBool: Bool {
get {
guard let myBoolObject = objc_getAssociatedObject(self, &AssociatedKeys.myBool) as? NSNumber else {
return false
}
return myBoolObject.boolValue // execution never reaches this line
}
set(value) {
let object = NSNumber(value: value)
objc_setAssociatedObject(self, &AssociatedKeys.myBool, object, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
str.myBool = true
print(str.myBool) // prints "false"
It prints out that it is false.
At first, I tried it without wrapping the Bool into NSNumber, but the result was the same.
Is this even possible to add an associated object to a struct at all? If not, can anyone tell me why?
Based on #Hamish's comment, I created the following solution to workaround the issue.
Preconditions:
Have a framework which proposes prefilled objects, the app works on these objects and the framework should know which of the properties are modified during the processing of this object later.
Not using looooong initializers to setup all properties of MyObject is a design decision.
In my example, the usage of the myObject is a dummy and shows what happens in the framework and what happens in the app.
// protocol is used, as we could handle more modifiable structs/classes in a common way
protocol PropertyState {
var isModified: Bool {get set}
}
internal struct ModifiableString : PropertyState {
var string: String
var isModified: Bool
}
class MyObject: PropertyState {
internal var _name = ModifiableString(string: "", isModified: false)
public var name: String {
get {
return _name.string
}
set(value) {
_name.string = value
_name.isModified = true
}
}
// + N similar properties (they can be other types than String, by implementing other structs, like ModifiableBool)
var isModified: Bool {
get {
return _name.isModified // || _myAnotherProperty.isModified
}
set(value) {
_name.isModified = value
// _myAnotherProperty.isModified = value
}
}
}
// internal filling of the object inside of the framework
let myObject = MyObject()
myObject.name = "originalValue"
print(myObject.isModified) // true
// filling the object with values ended, so we can set the state
myObject.isModified = false
print(myObject.isModified) // false
// the app can work with the object
// let myObject = Framework.getObject()
myObject.name = "modifiedValue"
// now the framework should now which properties are modified
print(myObject._name.isModified) // true

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.