I was playing around with a Singleton to generate unique ID's to conform to Hashable. Then I realised it wasn't really thread safe. So I switched to OSAtomicIncrement32 because it does not use a closure, so it can be as simple as this:
class HashID {
static let sharedID = HashID()
private var storedGlobalID : Int32 = 0
var newID : Int {
get {
return Int(OSAtomicIncrement32(&storedGlobalID))
}
}
private init() {}
}
Then I wanted to try a HashID for each Type. So I use _stdlib_getDemangledTypeName reflecting to feed a Dictionary. But then again it wasn't thread safe. Different threads can now manipulate the Dictionary at the same time.
class HashID {
static let sharedID = HashTypeID()
func idForType(type: Any.Type) -> Int {
let typeName = _stdlib_getDemangledTypeName(type)
guard let currentID = storedGlobalIDForType[typeName] else {
let currentID : Int32 = 0
storedGlobalIDForType[typeName] = currentID
return Int(currentID)
}
let newID = atomicIncrement(currentID)
storedGlobalIDForType[typeName] = newID
return Int(newID)
}
private var storedGlobalIDForType : [String:Int32] = [String:Int32]()
private func atomicIncrement(var value: Int32) -> Int32 {
return OSAtomicIncrement32(&value)
}
private init() {}
}
I can obviously use GCD, but that means using closures and then I can't use a simple function with a return value. Using a completionHandler is just less "clean" and it also makes it more complicated to set this in the init of a class / struct.
This means default ID's and this would make them unhashable until the ID is fetched, or I would have to have an init with a completionHandler of it's own.
-> forgot how dispatch_sync works.
As I said, I was just playing around with this code. The first function is perfectly fine. The second however also gives a count of all instances of a type that were created. Which is not the most useful thing ever...
Is there some way I am forgetting to make the access to the Dictionary thread safe?
Updated Code:
credits to Martin R and Nikolai Ruhe
class HashTypeID {
// shared instance
static let sharedID = HashTypeID()
// dict for each type
private var storedGlobalIDForType : [String:Int] = [String:Int]()
// serial queue
private let _serialQueue = dispatch_queue_create("HashQueue", DISPATCH_QUEUE_SERIAL)
func idForType(type: Any.Type) -> Int {
var id : Int = 0
// make it thread safe
dispatch_sync(_serialQueue) {
let typeName = String(reflecting: type)
// check if there is already an ID
guard let currentID = self.storedGlobalIDForType[typeName] else {
// if there isn't an ID, store one
let currentID : Int = 0
self.storedGlobalIDForType[typeName] = currentID
id = Int(currentID)
return
}
// if there is an ID, increment
id = currentID
id++
// store the incremented ID
self.storedGlobalIDForType[typeName] = id
}
return id
}
private init() {}
}
Is there some way I am forgetting to make the access to the Dictionary thread safe?
Many. Using dispatch_sync on a serial queue is a good and simple way to go:
class HashID {
static func idForType(type: Any.Type) -> Int {
let typeName = _stdlib_getDemangledTypeName(type)
var id : Int = 0
dispatch_sync(queue) {
if let currentID = storedGlobalIDForType[typeName] {
id = currentID + 1
storedGlobalIDForType[typeName] = id
} else {
storedGlobalIDForType[typeName] = 0
}
}
return id
}
private static var queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_SERIAL)
private static var storedGlobalIDForType : [String : Int] = [:]
}
Using a read write lock could have advantages in your case.
Related
I am attempting to implement a GKGameModel in my application. In it, it holds variables to a few things, but for the purposes of my question I'm interested in the following two variables:
import GameplayKit
final class GameModel: NSObject, GKGameModel {
var players: [GKGameModelPlayer]?
var activePlayer: GKGameModelPlayer?
}
I do something like this to initialise the game with 3 players (not exact)
let game = GameModel.init()
game.players = [Player(),Player(),Player()] // Create 3 players
guard let firstPlayer = game.players.first else {
return
}
game.activePlayer = firstPlayer
A player class is defined as:
class Player : NSObject, GKGameModelPlayer {
var playerId: Int // GKGameModelPlayer protocol variable
let name: String
var cash: Int = 0
}
In my project I have Realm Entities and the models seperated. So there will be a PlayerEntity and a Player class.
I'm wanting to use RealmSwift to save and load the GKGameModelPlayer data, and more specifically the ability to store/re-store the active player.
I think the key here is the playerId variable; but I am not sure.
But what I'm not sure about is retrieving this information and then re-mapping it into a valid GKGameModelPlayer format
My current idea/theory is that I need to map my model to an entity class and vice-versa.
Ie:
// [REALM] Player entity
class PlayerEntity: Object {
#objc dynamic var id = UUID().uuidString
#objc dynamic var playerId: Int = 0
#objc dynamic var name: String = ""
#objc dynamic var cash: Int = 0
override static func primaryKey() -> String {
return "id"
}
}
And then I extend this class to do some "mapping":
extension PlayerEntity {
// Map model -> entity
convenience init(model: Player) {
self.init()
self.playerId = model.playerId
self.name = model.name
self.cash = model.cash
}
}
extension Player {
// Map entity -> model
convenience init(entity: PlayerEntity) {
let playerId = entity.playerId
let name = entity.name
let cash = entity.cash
self.init(id: playerId, name: name, cash: cash)
}
}
Right now, the playerId is always zero (0) because I'm not really sure how to set it.
I can save a player to realm.
The issue comes from when I try to restore the player, and I want to restore the activePlayer variable in the GameModel
Therefore, my question is:
How would I go about saving and restoring the activePlayer variable so that it continues to comply to GKGameModelPlayer?
I appreciate any assistance on this.
With thanks
While you could use those extensions, sometimes simpler is better. Here's a rough example:
class PlayerEntity: Object {
#objc dynamic var playerId: Int = 0
#objc dynamic var name: String = ""
#objc dynamic var cash: Int = 0
convenience init(withPlayer: PlayerClass) {
self.init()
self.playerId = withPlayer.playerId
self.name = withPlayer.name
self.cash = withPlayer.cash
}
func getPlayer() -> Player {
let p = Player()
p.playerId = self.playerId
p.name = self.name
p.cash = self.cash
return p
}
override static func primaryKey() -> String {
return "playerId"
}
}
to load all the players into an array... this will do it
let playerResults = realm.objects(PlayerEntity.self)
for player in playerResults {
let aPlayer = player.getPlayer()
self.playerArray.append(aPlayer)
}
Notice the removal of
#objc dynamic var id = UUID().uuidString
because it's not really being used to identify the object as a primary key.
The primary key is really
var playerId: Int // GKGameModelPlayer protocol variable
which is fine to use as long as it's unique.
I am trying to implement a Thread-Safe PhoneBook object. The phone book should be able to add a person, and look up a person based on their name and phoneNumber. From an implementation perspective this simply involves two hash tables, one associating name -> Person and another associating phone# -> Person.
The caveat is I want this object to be threadSafe. This means I would like to be able to support concurrent lookups in the PhoneBook while ensuring only one thread can add a Person to the PhoneBook at a time. This is the basic reader-writers problem, and I am trying to solve this using GrandCentralDispatch and dispatch barriers. I am struggling to solve this though as I am running into issues.. Below is my Swift playground code:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
public class Person: CustomStringConvertible {
public var description: String {
get {
return "Person: \(name), \(phoneNumber)"
}
}
public var name: String
public var phoneNumber: String
private var readLock = ReaderWriterLock()
public init(name: String, phoneNumber: String) {
self.name = name
self.phoneNumber = phoneNumber
}
public func uniquePerson() -> Person {
let randomID = UUID().uuidString
return Person(name: randomID, phoneNumber: randomID)
}
}
public enum Qos {
case threadSafe, none
}
public class PhoneBook {
private var qualityOfService: Qos = .none
public var nameToPersonMap = [String: Person]()
public var phoneNumberToPersonMap = [String: Person]()
private var readWriteLock = ReaderWriterLock()
public init(_ qos: Qos) {
self.qualityOfService = qos
}
public func personByName(_ name: String) -> Person? {
var person: Person? = nil
if qualityOfService == .threadSafe {
readWriteLock.concurrentlyRead { [weak self] in
guard let strongSelf = self else { return }
person = strongSelf.nameToPersonMap[name]
}
} else {
person = nameToPersonMap[name]
}
return person
}
public func personByPhoneNumber( _ phoneNumber: String) -> Person? {
var person: Person? = nil
if qualityOfService == .threadSafe {
readWriteLock.concurrentlyRead { [weak self] in
guard let strongSelf = self else { return }
person = strongSelf.phoneNumberToPersonMap[phoneNumber]
}
} else {
person = phoneNumberToPersonMap[phoneNumber]
}
return person
}
public func addPerson(_ person: Person) {
if qualityOfService == .threadSafe {
readWriteLock.exclusivelyWrite { [weak self] in
guard let strongSelf = self else { return }
strongSelf.nameToPersonMap[person.name] = person
strongSelf.phoneNumberToPersonMap[person.phoneNumber] = person
}
} else {
nameToPersonMap[person.name] = person
phoneNumberToPersonMap[person.phoneNumber] = person
}
}
}
// A ReaderWriterLock implemented using GCD and OS Barriers.
public class ReaderWriterLock {
private let concurrentQueue = DispatchQueue(label: "com.ReaderWriterLock.Queue", attributes: DispatchQueue.Attributes.concurrent)
private var writeClosure: (() -> Void)!
public func concurrentlyRead(_ readClosure: (() -> Void)) {
concurrentQueue.sync {
readClosure()
}
}
public func exclusivelyWrite(_ writeClosure: #escaping (() -> Void)) {
self.writeClosure = writeClosure
concurrentQueue.async(flags: .barrier) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.writeClosure()
}
}
}
// MARK: Testing the synchronization and thread-safety
for _ in 0..<5 {
let iterations = 1000
let phoneBook = PhoneBook(.none)
let concurrentTestQueue = DispatchQueue(label: "com.PhoneBookTest.Queue", attributes: DispatchQueue.Attributes.concurrent)
for _ in 0..<iterations {
let person = Person(name: "", phoneNumber: "").uniquePerson()
concurrentTestQueue.async {
phoneBook.addPerson(person)
}
}
sleep(10)
print(phoneBook.nameToPersonMap.count)
}
To test my code I run 1000 concurrent threads that simply add a new Person to the PhoneBook. Each Person is unique so after the 1000 threads complete I am expecting the PhoneBook to contain a count of 1000. Everytime I perform a write I perform a dispatch_barrier call, update the hash tables, and return. To my knowledge this is all we need to do; however, after repeated runs of the 1000 threads I get the number of entries in the PhoneBook to be inconsistent and all over the place:
Phone Book Entries: 856
Phone Book Entries: 901
Phone Book Entries: 876
Phone Book Entries: 902
Phone Book Entries: 912
Can anyone please help me figure out what is going on? Is there something wrong with my locking code or even worse something wrong with how my test is constructed? I am very new to this multi-threaded problem space, thanks!
The problem is your ReaderWriterLock. You are saving the writeClosure as a property, and then asynchronously dispatching a closure that calls that saved property. But if another exclusiveWrite came in during the intervening period of time, your writeClosure property would be replaced with the new closure.
In this case, it means that you can be adding the same Person multiple times. And because you're using a dictionary, those duplicates have the same key, and therefore don't result in you're seeing all 1000 entries.
You can actually simplify ReaderWriterLock, completely eliminating that property. I’d also make concurrentRead a generic, returning the value (just like sync does), and rethrowing any errors (if any).
public class ReaderWriterLock {
private let queue = DispatchQueue(label: "com.domain.app.rwLock", attributes: .concurrent)
public func concurrentlyRead<T>(_ block: (() throws -> T)) rethrows -> T {
return try queue.sync {
try block()
}
}
public func exclusivelyWrite(_ block: #escaping (() -> Void)) {
queue.async(flags: .barrier) {
block()
}
}
}
A couple of other, unrelated observations:
By the way, this simplified ReaderWriterLock happens to solves another concern. That writeClosure property, which we've now removed, could have easily introduced a strong reference cycle.
Yes, you were scrupulous about using [weak self], so there wasn't any strong reference cycle, but it was possible. I would advise that wherever you employ a closure property, that you set that closure property to nil when you're done with it, so any strong references that closure may have accidentally entailed will be resolved. That way a persistent strong reference cycle is never possible. (Plus, the closure itself and any local variables or other external references it has will be resolved.)
You're sleeping for 10 seconds. That should be more than enough, but I'd advise against just adding random sleep calls (because you never can be 100% sure). Fortunately, you have a concurrent queue, so you can use that:
concurrentTestQueue.async(flags: .barrier) {
print(phoneBook.count)
}
Because of that barrier, it will wait until everything else you put on that queue is done.
Note, I did not just print nameToPersonMap.count. This array has been carefully synchronized within PhoneBook, so you can't just let random, external classes access it directly without synchronization.
Whenever you have some property which you're synchronizing internally, it should be private and then create a thread-safe function/variable to retrieve whatever you need:
public class PhoneBook {
private var nameToPersonMap = [String: Person]()
private var phoneNumberToPersonMap = [String: Person]()
...
var count: Int {
return readWriteLock.concurrentlyRead {
nameToPersonMap.count
}
}
}
You say you're testing thread safety, but then created PhoneBook with .none option (achieving no thread-safety). In that scenario, I'd expect problems. You have to create your PhoneBook with the .threadSafe option.
You have a number of strongSelf patterns. That's rather unswifty. It is generally not needed in Swift as you can use [weak self] and then just do optional chaining.
Pulling all of this together, here is my final playground:
PlaygroundPage.current.needsIndefiniteExecution = true
public class Person {
public let name: String
public let phoneNumber: String
public init(name: String, phoneNumber: String) {
self.name = name
self.phoneNumber = phoneNumber
}
public static func uniquePerson() -> Person {
let randomID = UUID().uuidString
return Person(name: randomID, phoneNumber: randomID)
}
}
extension Person: CustomStringConvertible {
public var description: String {
return "Person: \(name), \(phoneNumber)"
}
}
public enum ThreadSafety { // Changed the name from Qos, because this has nothing to do with quality of service, but is just a question of thread safety
case threadSafe, none
}
public class PhoneBook {
private var threadSafety: ThreadSafety
private var nameToPersonMap = [String: Person]() // if you're synchronizing these, you really shouldn't expose them to the public
private var phoneNumberToPersonMap = [String: Person]() // if you're synchronizing these, you really shouldn't expose them to the public
private var readWriteLock = ReaderWriterLock()
public init(_ threadSafety: ThreadSafety) {
self.threadSafety = threadSafety
}
public func personByName(_ name: String) -> Person? {
if threadSafety == .threadSafe {
return readWriteLock.concurrentlyRead { [weak self] in
self?.nameToPersonMap[name]
}
} else {
return nameToPersonMap[name]
}
}
public func personByPhoneNumber(_ phoneNumber: String) -> Person? {
if threadSafety == .threadSafe {
return readWriteLock.concurrentlyRead { [weak self] in
self?.phoneNumberToPersonMap[phoneNumber]
}
} else {
return phoneNumberToPersonMap[phoneNumber]
}
}
public func addPerson(_ person: Person) {
if threadSafety == .threadSafe {
readWriteLock.exclusivelyWrite { [weak self] in
self?.nameToPersonMap[person.name] = person
self?.phoneNumberToPersonMap[person.phoneNumber] = person
}
} else {
nameToPersonMap[person.name] = person
phoneNumberToPersonMap[person.phoneNumber] = person
}
}
var count: Int {
return readWriteLock.concurrentlyRead {
nameToPersonMap.count
}
}
}
// A ReaderWriterLock implemented using GCD concurrent queue and barriers.
public class ReaderWriterLock {
private let queue = DispatchQueue(label: "com.domain.app.rwLock", attributes: .concurrent)
public func concurrentlyRead<T>(_ block: (() throws -> T)) rethrows -> T {
return try queue.sync {
try block()
}
}
public func exclusivelyWrite(_ block: #escaping (() -> Void)) {
queue.async(flags: .barrier) {
block()
}
}
}
for _ in 0 ..< 5 {
let iterations = 1000
let phoneBook = PhoneBook(.threadSafe)
let concurrentTestQueue = DispatchQueue(label: "com.PhoneBookTest.Queue", attributes: .concurrent)
for _ in 0..<iterations {
let person = Person.uniquePerson()
concurrentTestQueue.async {
phoneBook.addPerson(person)
}
}
concurrentTestQueue.async(flags: .barrier) {
print(phoneBook.count)
}
}
Personally, I'd be inclined to take it a step further and
move the synchronization into a generic class; and
change the model to be an array of Person object, so that:
The model supports multiple people with the same or phone number; and
You can use value types if you want.
For example:
public struct Person {
public let name: String
public let phoneNumber: String
public static func uniquePerson() -> Person {
return Person(name: UUID().uuidString, phoneNumber: UUID().uuidString)
}
}
public struct PhoneBook {
private var synchronizedPeople = Synchronized([Person]())
public func people(name: String? = nil, phone: String? = nil) -> [Person]? {
return synchronizedPeople.value.filter {
(name == nil || $0.name == name) && (phone == nil || $0.phoneNumber == phone)
}
}
public func append(_ person: Person) {
synchronizedPeople.writer { people in
people.append(person)
}
}
public var count: Int {
return synchronizedPeople.reader { $0.count }
}
}
/// A structure to provide thread-safe access to some underlying object using reader-writer pattern.
public class Synchronized<T> {
/// Private value. Use `public` `value` computed property (or `reader` and `writer` methods)
/// for safe, thread-safe access to this underlying value.
private var _value: T
/// Private reader-write synchronization queue
private let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".synchronized", qos: .default, attributes: .concurrent)
/// Create `Synchronized` object
///
/// - Parameter value: The initial value to be synchronized.
public init(_ value: T) {
_value = value
}
/// A threadsafe variable to set and get the underlying object, as a convenience when higher level synchronization is not needed
public var value: T {
get { reader { $0 } }
set { writer { $0 = newValue } }
}
/// A "reader" method to allow thread-safe, read-only concurrent access to the underlying object.
///
/// - Warning: If the underlying object is a reference type, you are responsible for making sure you
/// do not mutating anything. If you stick with value types (`struct` or primitive types),
/// this will be enforced for you.
public func reader<U>(_ block: (T) throws -> U) rethrows -> U {
return try queue.sync { try block(_value) }
}
/// A "writer" method to allow thread-safe write with barrier to the underlying object
func writer(_ block: #escaping (inout T) -> Void) {
queue.async(flags: .barrier) {
block(&self._value)
}
}
}
In some cases you use might NSCache class. The documentation claims that it's thread safe:
You can add, remove, and query items in the cache from different threads without having to lock the cache yourself.
Here is an article that describes quite useful tricks related to NSCache
I don’t think you are using it wrong :).
The original (on macos) generates:
0 swift 0x000000010c9c536a PrintStackTraceSignalHandler(void*) + 42
1 swift 0x000000010c9c47a6 SignalHandler(int) + 662
2 libsystem_platform.dylib 0x00007fffbbdadb3a _sigtramp + 26
3 libsystem_platform.dylib 000000000000000000 _sigtramp + 1143284960
4 libswiftCore.dylib 0x0000000112696944 _T0SSwcp + 36
5 libswiftCore.dylib 0x000000011245fa92 _T0s24_VariantDictionaryBufferO018ensureUniqueNativeC0Sb11reallocated_Sb15capacityChangedtSiF + 1634
6 libswiftCore.dylib 0x0000000112461fd2 _T0s24_VariantDictionaryBufferO17nativeUpdateValueq_Sgq__x6forKeytF + 1074
If you remove the ‘.concurrent’ from your ReaderWriter queue, "the problem disappears”.©
If you restore the .concurrent, but change the async invocation in the writer side to be sync:
swift(10504,0x70000896f000) malloc: *** error for object 0x7fcaa440cee8: incorrect checksum for freed object - object was probably modified after being freed.
Which would be a bit astonishing if it weren’t swift?
I dug in, replaced your ‘string’ based array with an Int one by interposing a hash function, replaced the sleep(10) with a barrier dispatch to flush any laggardly blocks through, and that made it more reproducibly crash with the somewhat more helpful:
x(10534,0x700000f01000) malloc: *** error for object 0x7f8c9ee00008: incorrect checksum for freed object - object was probably modified after being freed.
But when a search of the source revealed no malloc or free, perhaps the stack dump is more useful.
Anyways, best way to solve your problem: use go instead; it actually makes sense.
I've made a class to manage my CoreData entities [named : DBHelper] (All CRUD operations, which works fine. However, I need to get the sum of a property called subtotal but I'm receiving the following error:
thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)
What is wrong with my code ?
Entity class
import Foundation
import CoreData
#objc(Odetails)
public class Odetails: NSManagedObject {
public let entityName = "Odetails"
}
Entity Extension :
extension Odetails {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Odetails> {
return NSFetchRequest<Odetails>(entityName: "Odetails")
}
#NSManaged public var pname: String?
#NSManaged public var price: Int16?
#NSManaged public var qty: Int16?
#NSManaged public var subtotal: Int16?
}
Core Data Helper Class :
import CoreData
class DBHelper {
var context: NSManagedObjectContext
init(context: NSManagedObjectContext){
self.context = context
}
// ......
func getsum() -> Int16{
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Odetails")
do {
let response = try context.fetch(fetchRequest)
var sum : Int16 = 0
for res in response as! [Odetails]{
sum += res.subtotal
}
return sum
} catch let error as NSError {
// failure
print(error.localizedDescription)
return 0
}
}
}
The Use :
override func viewDidLoad() {
super.viewDidLoad()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let dbhelper = DBHelper(context: context)
print("count : \(dbhelper.getAll().count)")
print("Sum : \(dbhelper.getsum())") // ERROR HERE
var sum : Int16 = 0
for r in dbhelper.getAll() { // Method Return [Odetails]
sum += r.subtotal // OR ERROR HERE
}
print(" Sum : \(sum)")
}
Any help will be much appreciated
A bit offtopic, this is a more efficient way to get the sum of a particular property in Core Data
func getSum() -> Int16
{
let description = NSExpressionDescription()
description.name = "sumSubtotal"
description.expression = NSExpression(forKeyPath: "#sum.subtotal")
description.expressionResultType = .integer16AttributeType
let request : NSFetchRequest<NSDictionary> = NSFetchRequest(entityName: "Odetails")
request.propertiesToFetch = [description]
request.resultType = .dictionaryResultType
do {
let results = try context.fetch(request)
return results.isEmpty ? 0 : results[0]["sumSubtotal"] as! Int16
} catch {
print(error)
return 0
}
}
Rather than forcing unwrapping. Can you try optional binding to make sure that the data was being fetched correctly.
guard let response: [Odetails] = context.fetch(fetchRequest) else { return 0}
var sum = 0
for res in response {
sum = sum + Int(res.subtotal)
}
return sum
The problem is you got overflow on Int16. According to Apple docs
In most cases, you don’t need to pick a specific size of integer to
use in your code. Swift provides an additional integer type, Int,
which has the same size as the current platform’s native word size:
On a 32-bit platform, Int is the same size as Int32. On a 64-bit
platform, Int is the same size as Int64.
Unless you need to work with a specific size of integer, always use
Int for integer values in your code. This aids code consistency and
interoperability. Even on 32-bit platforms, Int can store any value
between -2,147,483,648 and 2,147,483,647, and is large enough for many
integer ranges.
I am using RealmSwift. What is the best / canonical way to generate ids for objects?
Here is what I came up with:
class MyObject: Object {
dynamic var id = ""
dynamic var createdAt = NSDate()
override class func primaryKey() -> String {
return "id"
}
func save() {
let realm = try! Realm()
if(self.id == "") {
while(true) {
let newId = NSUUID().UUIDString
let saying = realm.objectForPrimaryKey(MyObject.self, key: newId)
if(saying == nil) {
self.id = newId
break
}
}
}
try! realm.write {
realm.add(self)
}
}
}
I wanted a function that persists the object to Realm and either overwrites or creates a new one based on the id. This seems to work ok, but I wasn't sure if there was something built into realm to do this. Or is there a better way?
Thanks.
I know this is a few months old, but this is how I implement auto incrementing Primary Keys.
This code is untested, but you'll get the general idea
class MyObject: Object {
/**
Primary Key
Since you can't change the primary key after the object is saved,
we'll use 0 to signify an unsaved object. When we set the primary key,
we'll never use 0.
*/
dynamic var id: Int = 0
/**
Some persisted value
*/
dynamic var someString: String?
var nextID: Int {
do {
let realm = try Realm()
/// I would use .max() but Realm only supports String and signed Int for primary keys
/// so using overflow protection, the `next` primary key would be Int.min if the
/// current value is Int.max
var id = realm.objects(MyObject.self).sorted("id", ascending: true).last?.id ?? 0
/// add 1 to id until we find one that's not in use... skip 0
repeat {
id = Int.addWithOverflow(id, 1).0 /// Add with overflow in case we hit Int.max
} while id == 0 || realm.objectForPrimaryKey(MyObject.self, key: id) != nil
return id
} catch let error as NSError {
/// Oops
fatal(error: error.localizedDescription)
}
}
convenience init(someString: String?) {
self.init()
id = nextID
self.someString = someString
save()
}
override class func primaryKey() -> String? {
return "id"
}
func save() {
/// Gotta check this in case the object was created without using the convenience init
if id == 0 { id = nextID }
do {
let realm = try Realm()
try realm.write {
/// Add... or update if already exists
realm.add(self, update: true)
}
} catch let error as NSError {
fatalError(error.localizedDescription)
}
}
}
The process for creating a unique string ID (IE: a UUID) is very similar:
class MyObject: Object {
/**
Primary Key
*/
dynamic var id: String = ""
/**
Some persisted value
*/
dynamic var someString: String?
var nextID: String {
do {
let realm = try Realm()
var id: String = NSUUID().UUIDString
/// NSUUID().UUIDString almost always makes a unique ID on the first try
/// but we'll check after we generate the first one just to be sure
while realm.objectForPrimaryKey(MyObject.self, key: id) != nil {
id = NSUUID().UUIDString
}
return id
} catch let error as NSError {
/// Oops
fatal(error: error.localizedDescription)
}
}
convenience init(someString: String?) {
self.init()
id = nextID
self.someString = someString
save()
}
override class func primaryKey() -> String? {
return "id"
}
func save() {
/// Gotta check this in case the object was created without using the convenience init
if id == "" { id = nextID }
do {
let realm = try Realm()
try realm.write {
/// Add... or update if already exists
realm.add(self, update: true)
}
} catch let error as NSError {
fatalError(error.localizedDescription)
}
}
}
Realm(Swift) does not currently support auto-incrementing primary keys. You can set a primary like you are above, but for auto-incrementing and unique keys, there are a couple routes that you can go:
UUID (like you have above)
Query the max "id" (primary key) of your object and set the object to be inserted as id + 1. Something like...
let id = realm.objects(MyObject).max("id") + 1
Create your own hash signature (one potential example: SHA256(epoch timestamp + SHA256(object.values))
This won't compile:
I've tried a couple different things; different ways of declaring the Dictionary, changing its type to match the nested-ness of the data. I also tried explicitly saying my Any was a collection so it could be subscripted. No dice.
import UIKit
import Foundation
class CurrencyManager {
var response = Dictionary<String,Any>()
var symbols = []
struct Static {
static var token : dispatch_once_t = 0
static var instance : CurrencyManager?
}
class var shared: CurrencyManager {
dispatch_once(&Static.token) { Static.instance = CurrencyManager() }
return Static.instance!
}
init(){
assert(Static.instance == nil, "Singleton already initialized!")
getRates()
}
func defaultCurrency() -> String {
let countryCode = NSLocale.currentLocale().objectForKey(NSLocaleCountryCode) as String
let codesToCountries :Dictionary = [ "US":"USD" ]
if let localCurrency = codesToCountries[countryCode]{
return localCurrency
}
return "USD"
}
func updateBadgeCurrency() {
let chanCurr = defaultCurrency()
var currVal :Float = valueForCurrency(chanCurr, exchange: "Coinbase")!
UIApplication.sharedApplication().applicationIconBadgeNumber = Int(currVal)
}
func getRates() {
//Network code here
valueForCurrency("", exchange: "")
}
func valueForCurrency(currency :String, exchange :String) -> Float? {
return response["current_rates"][exchange][currency] as Float
}
}
Let's take a look at
response["current_rates"][exchange][currency]
response is declared as Dictionary<String,Any>(), so after the first subscript you try to call another two subscripts on an object of type Any.
Solution 1. Change the type of response to be a nested dictionary. Note that I added the question marks because anytime you access a dictionary item you get back an optional.
var response = Dictionary<String,Dictionary<String,Dictionary<String, Float>>>()
func valueForCurrency(currency :String, exchange :String) -> Float? {
return response["current_rates"]?[exchange]?[currency]
}
Solution 2. Cast each level to a Dictionary as you parse. Make sure to still check if optional values exist.
var response = Dictionary<String,Any>()
func valueForCurrency(currency :String, exchange :String) -> Float? {
let exchanges = response["current_rates"] as? Dictionary<String,Any>
let currencies = exchanges?[exchange] as? Dictionary<String,Any>
return currencies?[currency] as? Float
}
You can get nested dictionary data by following these steps:
let imageData: NSDictionary = userInfo["picture"]?["data"]? as NSDictionary
let profilePic = imageData["url"] as? String
func valueForCurrency(currency :String, exchange :String) -> Float? {
if let exchanges = response["current_rates"] as? Dictionary<String,Any> {
if let currencies = exchanges[exchange] as? Dictionary<String,Any> {
return currencies[currency] as? Float
}
}
return nil
}
response is declared as such:
var response = Dictionary<String,Any>()
So the compiler thinks response["current_rates"] will return an Any. Which may or may not be something that is subscript indexable.
You should be able to define you type with nested Dictionaries, 3 levels and eventually you get to a float. You also need to drill in with optional chaining since the dictionary may or may not have a value for that key, so it's subscript accessor returns an optional.
var response = Dictionary<String,Dictionary<String,Dictionary<String,Float>>>()
// ... populate dictionaries
println(response["current_rates"]?["a"]?["b"]) // The float