Singleton and their synchronization in swift - ios

I am working on project where I have requirements of using singleton.
while using them I stuck on following code
struct User{
var someUserProperty: String?{
willSet{
print(UserManager.shared.sharedUser ?? "")
}
}
}
class UserManager{
static var shared: UserManager = UserManager()
public var sharedUser: User?
/* Private to make this class singleton */
private init() {
self.sharedUser = User(someUserProperty: "someInitialValue")
}
}
class Managers{
static var userManager = UserManager.shared
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Managers.userManager.sharedUser?.someUserProperty = "someDifferentValue"
}
}
This code throws error in viewDidLoad as it is setting User's property and in that property's observer we are getting same User object.
To solve this, I searched and get solution, which led me to modification in UserManager as follows
class UserManager{
private let concurrentQueue = DispatchQueue(label: "ConcurrentQueue", attributes: .concurrent, target: nil)
static var shared: UserManager = UserManager()
public var sharedUser: User?{
get{
return concurrentQueue.sync {
return self.privateSharedUser
}
}set{
concurrentQueue.async(flags: .barrier){[unowned self] in
self.privateSharedUser = newValue
}
}
}
private var privateSharedUser: User?
/* Private to make this class singleton */
private init() {
self.sharedUser = User(someUserProperty: "someInitialValue")
}
}
I have used dispatch queue to allow read from different places but write in barrier mode so that it will not interrupt read operations. and it worked.
But also following solution works, and that is making me wonder because now I have removed all queuing task and just put get and set.
class UserManager{
private let concurrentQueue = DispatchQueue(label: "ConcurrentQueue", attributes: .concurrent, target: nil)
static var shared: UserManager = UserManager()
public var sharedUser: User?{
get{
return self.privateSharedUser
}set{
self.privateSharedUser = newValue
}
}
private var privateSharedUser: User?
/* Private to make this class singleton */
private init() {
self.sharedUser = User(someUserProperty: "someInitialValue")
}
}
My thinking is that what above code does is that just creating wrapper named sharedUser around actual variable privateUser. So when we set something on sharedUser behind the scenes, it will give you private privateSharedUser whose address is different than sharedUser. Is this the reason ? Or Swift is doing something great behind the scenes ? Please tell me, if any one knows

Related

Swift - toggle model to readonly momentarily

I have a phone number model which looks like this:
import UIKit
import Foundation
struct PhoneValidation : OptionSet {
let rawValue: Int
static let phoneInValid = PhoneValidation(rawValue: 1 << 0)
static let phoneValid = PhoneValidation(rawValue: 1 << 1)
static let smsValidationAttempted = PhoneValidation(rawValue: 1 << 2)
static let smsValidationFailed = PhoneValidation(rawValue: 1 << 3)
static let smsValidationSuccessful = PhoneValidation(rawValue: 1 << 4) // OTP is successfully validated in backend. The field should be non-editable in this duration
static let smsValidationOTPTriggered = PhoneValidation(rawValue: 1 << 5) // OTP validation triggered. The field should be non-editable in this duration
}
class PhonesViewModel: NSCopying {
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
public var phone: String?
public var code: String?
public var countryCode: String?
public var isValid : PhoneValidation?
func copy(with zone: NSZone? = nil) -> Any {
let copy = PhonesViewModel()
copy.phoneType = phoneType
copy.phone = phone
copy.code = code
copy.countryCode = countryCode
copy.isValid = isValid
return copy
}
}
As you can see above the phone model can transition between different states. The SMS validation is available for few countries and for few it is not applicable. So, I plan on setting smsValidationOTPTriggered state when SMS validation is applicable for a country and while the validation is in progress.
What I need here is, while the states smsValidationOTPTriggered or smsValidationSuccessful are set I would not want any module of the application to modify the values(phoneType, phone, code, countryCode) of the model. In other words, I would like the model to switch to a read-only mode while these 2 states are set in model and would like the module to be informed with an error or exception when a modification is attempted.
Is there a best practice already available for what I am trying to achieve here? I have searched before raising this question but did not find any. How can I achieve this?
Thanks,
Raj Pawan Gumdal
How about something like this, I think its better to use property wrappers for your case! The below is not an exact solution but can modify/change to accommodate your need
import UIKit
enum PhoneNumberType {
case mobile
}
enum PhoneValidation {
case phoneInValid
case phoneValid
case smsValidationAttempted
case smsValidationFailed
case smsValidationSuccessful
case smsValidationOTPTriggered
}
struct PhonesViewModel {
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
public var phone: String?
public var code: String?
public var countryCode: String?
public var phoneValidation : PhoneValidation?
func validate(value: [PhoneValidation]) -> Bool {
//add proper check here
return false
}
}
#propertyWrapper
struct Wrapper {
private(set) var value: PhonesViewModel? = nil
var validators: [PhoneValidation] = []
var wrappedValue: PhonesViewModel? {
get { value }
set {
if let model = newValue, model.validate(value: validators) {
value = newValue
print("Value assigned")
} else {
print("Value not assigned")
}
}
}
}
struct SomeOtherClass {
#Wrapper(validators: [PhoneValidation.phoneInValid])
var model: PhonesViewModel?
}
var a = SomeOtherClass()
a.model = PhonesViewModel()
a.model = PhonesViewModel()
You can use a technique with the name "popsicle immutability". An object is initially mutable, but can be "frozen". Modifications for frozen objects are forbidden. In your case PhonesViewModel become frozen when isValid property have value smsValidationOTPTriggered or smsValidationSuccessful.
Let's add Freezable protocol for requirements to objects that can become immutable and conforming for PhonesViewModel:
protocol Freezable: class {
var isFrozen: Bool { get }
}
extension PhonesViewModel: Freezable {
var isFrozen: Bool {
isValid == .smsValidationOTPTriggered || isValid == .smsValidationSuccessful
}
}
Now we must add validation for isFrozen value when a property is assigned. It can be added in property observers like:
...
public var phone: String? {
didSet {
validate()
}
}
...
private func validate() {
assert(!isFrozen)
}
Or using property wrapper:
#propertyWrapper
struct Guarded<Value> {
private var value: Value
init(wrappedValue: Value) {
value = wrappedValue
}
#available(*, unavailable)
var wrappedValue: Value {
get { fatalError("only works on instance properties of classes that conforms to Freezable protocol") }
set { fatalError("only works on instance properties of classes that conforms to Freezable protocol") }
}
static subscript<EnclosingSelf: Freezable>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
) -> Value {
get {
object[keyPath: storageKeyPath].value
}
set {
precondition(!object.isFrozen, "Object \(object) is frozen! Modifications are forbidden")
object[keyPath: storageKeyPath].value = newValue
}
}
}
So your class will look like:
class PhonesViewModel: NSCopying {
#Guarded
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
#Guarded
public var phone: String?
#Guarded
public var code: String?
#Guarded
public var countryCode: String?
#Guarded
public var isValid : PhoneValidation?
func copy(with zone: NSZone? = nil) -> Any {
let copy = PhonesViewModel()
copy.phoneType = phoneType
copy.phone = phone
copy.code = code
copy.countryCode = countryCode
copy.isValid = isValid
return copy
}
}

Create singleton in swift 4 in a class having public init

I am having a public class in a different code like this -
public class Person {
public required init() {}
}
This class is in a framework and cannot be modified.
Now the problem is I have been told to inherit from this class and create a singleton object of the new class
public class Jon: Person {
public static var shared: Jon = Jon()
required init() {}
}
But the required init is letting users create multiple instances of the class Jon.
Can anybody suggest on how to do this in swift 4.
I found a solution to achieve this. Look into my solution hopefully it will serve your purpose.
public class ParentClass {
public required init() {}
}
class SubClass: ParentClass {
static let sharedInstance = SubClass(foo: "")
#available(*, unavailable)
required init() { }
//You can change the init parameter as per your requirement
private init(foo: String) {
super.init()
//Write your initialization code
}
}
If you dont want to pass any argument to the init you can do something like this.
private init(_ foo: String? = nil) {
super.init()
//Write your initialization code
}
Now you initialize your object without passing any argument like
static let sharedInstance = SubClass()
One solution is to use Composition rather than Inheritance as the design solution. Since the Person object is supposed to be singleton as well we make it immutable and initialise it in the private init method using the required init
public class Jon {
static let instance = Jon()
private init() {
person = Person()
}
public let person: Person
}
And it can be used like this (with a name property for instance)
var jon = Jon.instance
jon.person.name = "John"
or Person could be made private and computed properties added to the singleton class
public class Jon {
static let instance = Jon()
private init() {
person = Person()
}
private let person: Person
var name: String? {
get { return person.name }
set { person.name = newValue}
}
}
and then the name property is accessed directly
var jon = Jon.instance
jon.name = "John"

How to make a Swift Class Singleton instance thread safe?

I have a singleton class as so:
class Database {
static let instance:Database = Database()
private var db: Connection?
private init(){
do {
db = try Connection("\(path)/SalesPresenterDatabase.sqlite3")
}catch{print(error)}
}
}
Now I access this class using Database.instance.xxxxxx to perform a function within the class. However when I access the instance from another thread it throws bizarre results as if its trying to create another instance. Should I be referencing the instance in the same thread?
To clarify the bizarre results show database I/o errors because of two instances trying to access the db at once
Update
please see this question for more info on the database code: Using transactions to insert is throwing errors Sqlite.swift
class var shareInstance: ClassName {
get {
struct Static {
static var instance: ClassName? = nil
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token, {
Static.instance = ClassName()
})
return Static.instance!
}
}
USE: let object:ClassName = ClassName.shareInstance
Swift 3.0
class ClassName {
static let sharedInstance: ClassName = { ClassName()} ()
}
USE: let object:ClassName = ClassName.shareInstance
In Swift 3.0 add a private init to prevent others from using the default () initializer.
class ClassName {
static let sharedInstance = ClassName()
private init() {} //This prevents others from using the default '()' initializer for this class.
}
Singleton thread class.
final public class SettingsThreadSafe {
public static let shared = SettingsThreadSafe()
private let concurrentQueue = DispatchQueue(label: "com.appname.typeOfQueueAndUse", attributes: .concurrent)
private var settings: [String: Any] = ["Theme": "Dark",
"MaxConsurrentDownloads": 4]
private init() {}
public func string(forKey key: String) -> String? {
var result: String?
concurrentQueue.sync {
result = self.settings[key] as? String
}
return result
}
public func int(forKey key: String) -> Int? {
var result: Int?
concurrentQueue.sync {
result = self.settings[key] as? Int
}
return result
}
public func set(value: Any, forKey key: String) {
concurrentQueue.async( flags: .barrier ) {
self.settings[key] = value
}
}
}
Unit to test the singleton class.
func testConcurrentUsage() {
let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
let expect = expectation(description: "Using SettingsThreadSafe.shared from multiple threads shall succeed")
let callCount = 300
for callIndex in 1...callCount {
concurrentQueue.async {
SettingsThreadSafe.shared.set(value: callIndex, forKey: String(callIndex))
}
}
while SettingsThreadSafe.shared.int(forKey: String(callCount)) != callCount {
// nop
}
expect.fulfill()
waitForExpectations(timeout: 5) { (error) in
XCTAssertNil(error, "Test expectation failed")
}
}

iOS swift singleton clear data

class ShareData {
class var sharedInstance: ShareData {
struct Static {
static var instance: ShareData?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = ShareData()
}
return Static.instance!
}
var someString : String! //Some String
var selectedTheme : AnyObject! //Some Object
var someBoolValue : Bool!
}
This is my singleton design.However , I want to know how I can clear all its data as and when required?
Also can i have more than one singleton Class??
Since you've only got 3 properties on your singleton it would be far easier just to set up a method that nils each property in turn.
Once you start getting in to how to destroy and recreate your singleton, you get in to the realm of do you actually even want a singleton or should you just be using a regular object.
You are creating a Singleton with the syntax available in... 2014
Today there's a better syntax to define a Singleton class
final class SharedData {
static let sharedInstance = SharedData()
private init() { }
var someString: String?
var selectedTheme: AnyObject?
var someBoolValue: Bool?
func clear() {
someString = nil
selectedTheme = nil
someBoolValue = nil
}
}
As you can see I also added the clearData() method you were looking for.

Singleton class for sharing data

I'm an android app developer and a beginner in swift. I'm trying to implement a singleton class whose data members are shared throughout the app (like Settings).
Getting this done in android is pretty simple but I'm breaking my head to do it in swift.
Below is the code I've tried ..
public class DataSet
{
public var notificationOnOff: Bool!
public var interval: Int!
public var alert: String!
init()
{
self.notificationOnOff = true
self.interval = 1;
self.alert = nil;
}
init (onOff: Bool) {
self.notificationOnOff = onOff
}
init (time: Int) {
self.interval = time
}
init (stop: String) {
self.alert = stop
}
}
This class implementation couldn't persist the data.
Is this the right way of doing it?
EDIT
For example, when I click switch in Settings view controller, I'm setting notificationOnOff like ..
dataset.notificationOnOff = DataSet(onOff: true) // value is set and able to print it
and using this value in another class like...
if dataset.notificationOnOff
{
// do some stuff
}
So basically, the value is persisting only within the Setting class but not when I switch to other class.
Solved!
I have used the below code to successfully implement this..
var instance: DataSet?
class Dataset {
...
class var sharedInstance: DataSet {
struct Static {
static var instance: DataSet?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = DataSet()
}
return Static.instance!
}
}
and in the other classes ..
dataset = Dataset.sharedInstance;

Resources