How to make a Swift Class Singleton instance thread safe? - ios

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")
}
}

Related

Swift: Struct thread safe array crashing with NSLock

I'm trying to implement a thread-safe array component in the most efficient and safe way, backed by unit tests.
So far, I would prefer a struct array, to keep a value type and not a reference type.
But when I run the test below, I still have random crashes that I don't explain :
Here's my ThreadSafe array class :
public struct SafeArray<T>: RangeReplaceableCollection {
public typealias Element = T
public typealias Index = Int
public typealias SubSequence = SafeArray<T>
public typealias Indices = Range<Int>
private var array: [T]
private var locker = NSLock()
private func lock() { locker.lock() }
private func unlock() { locker.unlock() }
// MARK: - Public methods
// MARK: - Initializers
public init<S>(_ elements: S) where S: Sequence, SafeArray.Element == S.Element {
array = [S.Element](elements)
}
public init() { self.init([]) }
public init(repeating repeatedValue: SafeArray.Element, count: Int) {
let array = Array(repeating: repeatedValue, count: count)
self.init(array)
}
}
extension SafeArray {
// Single action
public func get() -> [T] {
lock(); defer { unlock() }
return Array(array)
}
public mutating func set(_ array: [T]) {
lock(); defer { unlock() }
self.array = Array(array)
}
}
And here's my XCUnitTest code :
final class ConcurrencyTests: XCTestCase {
private let concurrentQueue1 = DispatchQueue.init(label: "concurrentQueue1",
qos: .background,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: nil)
private let concurrentQueue2 = DispatchQueue.init(label: "concurrentQueue2",
qos: .background,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: nil)
private var safeArray = SafeArray(["test"])
func wait(for expectations: XCTestExpectation, timeout seconds: TimeInterval) {
wait(for: [expectations], timeout: seconds)
}
func waitForMainRunLoop() {
let mainRunLoopExpectation = expectation(description: "mainRunLoopExpectation")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { mainRunLoopExpectation.fulfill() }
wait(for: mainRunLoopExpectation, timeout: 0.5)
}
func waitFor(_ timeout: TimeInterval) {
let mainRunLoopExpectation = expectation(description: "timeoutExpectation")
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) { mainRunLoopExpectation.fulfill() }
wait(for: mainRunLoopExpectation, timeout: timeout + 0.5)
}
override func setUpWithError() throws {
try super.setUpWithError()
safeArray = SafeArray(["test"])
}
func testSafeArrayGet() {
var thread1: Thread!
var thread2: Thread!
concurrentQueue1.async {
thread1 = Thread.current
let startTime = Date()
for i in 0...1_000_000 {
self.safeArray.set(["modification"])
print("modification \(i)")
}
print("time modification: \(Date().timeIntervalSince(startTime))")
}
concurrentQueue2.async {
thread2 = Thread.current
let startTime = Date()
for i in 0...1_000_000 {
let _ = self.safeArray.get()
print("read \(i)")
}
print("time read: \(Date().timeIntervalSince(startTime))")
}
waitFor(10)
XCTAssert(!thread1.isMainThread && !thread2.isMainThread)
XCTAssert(thread1 != thread2)
}
}
Edit: Event with a class and a simple approach to make it thread safe, I get a crash. Here's a very simple test that crashes :
class TestClass {
var test = ["test"]
let nsLock = NSLock()
func safeSet(_ string: String) {
nsLock.lock()
test[0] = string // crash
nsLock.unlock()
}
}
func testStructThreadSafety() {
let testClass = TestClass()
DispatchQueue.concurrentPerform(iterations: 1_000_000) { i in
testClass.safeSet("modification \(i)")
let _ = testClass.test[0]
}
XCTAssert(true)
}
Why is it crashing? What am I doing wrong?
Note that if I make it a class I don't get crashes, but I would prefer to keep it a struct.

Instantiating classes stored in metatype Dictionary

I've followed the solution at Make a Swift dictionary where the key is "Type"? to create dictionaries that can use a class type as keys.
What I want to do is: I have one dictionary that should store class types with their class type (aka metatype) as keys, too:
class MyScenario {
static var metatype:Metatype<MyScenario> {
return Metatype(self)
}
}
var scenarioClasses:[Metatype<MyScenario>: MyScenario.Type] = [:]
Then I have methods to register and execute scenarios:
public func registerScenario(scenarioID:MyScenario.Type) {
if (scenarioClasses[scenarioID.metatype] == nil) {
scenarioClasses[scenarioID.metatype] = scenarioID
}
}
public func executeScenario(scenarioID:MyScenario.Type) {
if let scenarioClass = scenarioClasses[scenarioID.metatype] {
let scenario = scenarioClass()
}
}
... Problem is in the last line:
Constructing an object of class type 'MyScenario' with a metatype
value must use a 'required' initializer.
It looks like the compiler is confused at that point since I cannot use 'required' at that assignment. Does anyone have an idea how I would have to instantiate the scenarioClass in executeScenario()?
This must do the job.
import Foundation
struct Metatype<T> : Hashable
{
static func ==(lhs: Metatype, rhs: Metatype) -> Bool
{
return lhs.base == rhs.base
}
let base: T.Type
init(_ base: T.Type)
{
self.base = base
}
var hashValue: Int
{
return ObjectIdentifier(base).hashValue
}
}
public class MyScenario
{
var p: String
public required init()
{
self.p = "any"
}
static var metatype:Metatype<MyScenario>
{
return Metatype(self)
}
}
var scenarioClasses:[Metatype<MyScenario>: MyScenario.Type] = [:]
public func registerScenario(scenarioID:MyScenario.Type)
{
if (scenarioClasses[scenarioID.metatype] == nil)
{
scenarioClasses[scenarioID.metatype] = scenarioID
}
}
public func executeScenario(scenarioID:MyScenario.Type)
{
if let scenarioClass = scenarioClasses[scenarioID.metatype]
{
let scenario = scenarioClass.init()
print("\(scenario.p)")
}
}
// Register a new scenario
registerScenario(scenarioID: MyScenario.self)
// Execute
executeScenario(scenarioID: MyScenario.self)
// Should print "any"

Parse/Swift fatal error: use of unimplemented initializer 'init()

If anyone has any experience working with Parse using Swift, specifically subclassing PFObject..... I cannot figure out why the saveinbackground call below is throwing the above error?
Thanks!
func saveNewPerson(name: String) {
var myPeeps = [Person]()
if let currentUser = PFUser.currentUser() {
if currentUser.valueForKey("myPeeps")?.count < 1 {
myPeeps = []
} else {
myPeeps = currentUser.valueForKey("myPeeps") as! [Person]
}
let newPerson = Person(name: name, stores: [:])
myPeeps.append(newPerson)
currentUser.setObject(myPeeps, forKey: "myPeeps")
println(currentUser.valueForKey("myPeeps")?.count)
//WHY DOES THIS SAVE THROW ERROR FOR NOT INITIALZING?
currentUser.saveInBackgroundWithBlock{ succeeded, error in
if succeeded {
//3
println("Saved")
} else {
//4
if let errorMessage = error?.userInfo?["error"] as? String {
self.showErrorView(error!)
}
}
}
}
}
This is my Person class:
class Person: PFObject, PFSubclassing {
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
static func parseClassName() -> String {
return "Person"
}
var name: String = ""
var stores: [String : Store] = [:]
init(name: String, stores: [String : Store]) {
self.name = name
self.stores = stores
super.init()
}
}
My Store Class:
class Store: PFObject, PFSubclassing {
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
static func parseClassName() -> String {
return "Store"
}
var name: String = ""
var clothingSizes: [String: String] = [:]
init(name: String, clothingSizes: [String: String]){
self.name = name
self.clothingSizes = clothingSizes
super.init()
}
}
For both Parse subclasses, you need to make your inits convenience inits. Basically, what's going on is there is no implementation of init(), which you could do, by calling
override init() {
super.init()
}
Another option is to make your init a convenience init, and calling self.init()
convenience init(name: String, stores: [String : Store]) {
self.init()
self.name = name
self.stores = stores
}

How to retrieve models in parse.com with swift

I am developing an application with swift and parse.com, but I have problems with the model of the application.
I have an Event class that contains a Category class.
this is my Category class :
class Category : PFObject, PFSubclassing{
override class func load() {
self.registerSubclass()
}
class func parseClassName() -> String! {
return "Category"
}
var nameCategory:String="";
var image:String="";
private let KeyName:String = "label";
private let KeyImage:String = "image";
func fromPFObject(object:PFObject)->Category{
self.nameCategory = object[self.KeyName] as String;
self.image = object[self.KeyImage] as String;
return self;
}
And this is my Event class :
class Event {
//Variables de classe
var name:String;
var category:Category;
// Descripteur de colone en base
private let KeyEventName:String = "event_name";
private let KeyCategory:String = "category";
func insert(){
var event = self.toPFObject()
event.saveInBackgroundWithBlock {
(success: Bool!, error: NSError!) -> Void in
if (success != false) {
println("Object created with id: \(event.objectId)")
self.id = event.objectId;
} else {
println(error)
}
}
}
func select(id:String){
var query = PFQuery(className: self.KeyClassName)
query.getObjectInBackgroundWithId(id) {
(event: PFObject!, error: NSError!) -> Void in
if (error == nil) {
self.fromPFObject(event);
} else {
println(error)
self.eventKO("Une erreur c'est produite");
}
}
}
func toPFObject()->PFObject{
var event = PFObject(className: self.KeyClassName)
event[self.KeyCategory] = self.category;
event[self.KeyEventName] = self.name
return event;
}
func fromPFObject(event:PFObject)->Event{
self.activ = event[self.KeyActif] as Int;
var test = event[self.KeyCategory] as PFObject;
var label = test["label"] as String;
self.category = event[self.KeyCategory] as Category;
self.name = event[self.KeyEventName] as String;
return self;
}
I get a good retrieve the name of the event but not the category. I have missed something in my models.
Do you have any ideas?
I found it simpler to work directly with the objects instead of subclassing.
Simply refer to properties on your classes as follows:
// non optional of type String that defaults to an empty string
let firstName = object["firstName"] as? String ?? ""
if you have linked classes, e.g. if Category is another class you have a reference to, the following works cleanly
if let category = object["category"] as? PFObject {
let name = category["name"] as? String ?? ""
}
Again, no need to mess around with subclassing everything for simple objects.
I've used this patter to populate PFQueryTableViewControllers, custom tables, custom views etc.

iOS Swift: Array of Objects conforming to a Protocol: How to check if array contains

I'm trying to implement an image downloader class. It's a singleton implementation. The idea is to ask the instance to download an image and register as an observer for that image download. This is what I came up with so far:
public protocol ImageDownloaderDelegate {
func imageDownloadFinished(success: Bool)
}
public class ImageDownloader {
var list: [Int64] = []
var observer: [Int64:[ImageDownloaderDelegate]] = [:]
var downloading: Bool = false
public func downloadImageWithId(immutableId: Int64, delegate: ImageDownloaderDelegate) {
// Add Id to download list
if (!contains(list, immutableId)) {
list.append(immutableId)
}
// Add Observer
var observerList = observer[immutableId]
if (observerList == nil) {
observerList = [delegate]
} else if (!contains(observerList, delegate)) {
observerList!.append(delegate)
}
observer[immutableId] = observerList
// Start to download
if (!downloading) {
self.downloadNextImage()
}
}
private func downloadNextImage() {
...
}
/// A shared instance of the class
public class var defaultImageDownloader: ImageDownloader {
struct Singleton {
static let instance = ImageDownloader()
}
return Singleton.instance
}
}
I get the following error:
'ImageDownloaderDelegate' is not convertible to 'S.Generator.Element -> L'
Any help is much appreciated
You're not passing the correct arguments to the contains function, which expects a collection and a predicate (closure).
You need to do
public protocol ImageDownloaderDelegate : class {
func imageDownloadFinished(success: Bool)
}
public class ImageDownloader {
var list: [Int64] = []
var observer: [Int64:[ImageDownloaderDelegate]] = [:]
var downloading: Bool = false
public func downloadImageWithId(immutableId: Int64, delegate: ImageDownloaderDelegate) {
// Add Id to download list
if (!contains(list, immutableId)) {
list.append(immutableId)
}
// Add Observer
var observerList = observer[immutableId]
if (observerList == nil) {
observerList = [delegate]
} else if !contains(observerList!, { observer in observer === delegate }) {
observerList!.append(delegate)
}
observer[immutableId] = observerList
// Start to download
if (!downloading) {
self.downloadNextImage()
}
}
private func downloadNextImage() {
...
}
/// A shared instance of the class
public class var defaultImageDownloader: ImageDownloader {
struct Singleton {
static let instance = ImageDownloader()
}
return Singleton.instance
}
}

Resources