Best practices with custom NSManagedObjects - swift

I receive lots of messages as a part of app logic which I want to store to persistent storage , I am using core data for the same , I have created a NSManagedObject subclass which represents message entity in my model. I want to know what is the best approach to create the object and save it,
1. The object will be created and saved
2. The object will be retrieved, updated and saved.
I want to make sure I use same managed context while saving the object , currently app is facing random freezes or crashes, which I suspect are due to improper use of context. Below is how the object is created and saved.
class MessageFactory: NSObject {
static func createMessage(state:Int,type:MessageType) -> Message {
let appDelegate = AppDelegate.shared()
let context = appDelegate.persistentContainer.viewContext // persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Message", in: context)
let message = Message(entity: entity!, insertInto: context)
message.setup(state:state,type:type)
return message
}
}
class Message : NSManagedObject {
var currentState: MessageState?
#NSManaged var type : String
#NSManaged var duration : String
#NSManaged var mediaUrl : String
func save() -> Bool {
do {
try self.managedObjectContext?.save()
return true
}
catch {
return false
}
}
}
Also I am passing this object to few functions as well , modify them there and save .
When I am passing this object to closure I am passing its ID and retrieving object using ID inside closure and them processing it.
I want to know if my above method is correct , if not what changes are needed.

Related

How can you create Results after creating records?

I have a method that should return Results, either by successfully querying, or by creating the records if they don't exist.
Something like:
class MyObject: Object {
dynamic var token = ""
static let realm = try! Realm()
class func findOrCreate(token token: String) -> Results<MyObject> {
// either it's found ...
let tokenResults = realm.objects(MyObject.self).filter("token = '\(token)'")
if !tokenResults.isEmpty {
return tokenResults
}
// ... or it's created
let newObject = MyObject()
newObject.token = token
try! realm.write {
realm.add(newObject)
}
// However, the next line results in the following error:
// 'Results<_>' cannot be constructed because it has no accessible initializers
return Results(newObject)
}
}
Maybe I should just be returning [MyObject] from this method. Is there any benefit to trying to keep it as Results instead of Array? I guess I'd lose any benefit of postponed evaluation since I'm already using isEmpty within the method, correct?
Results is an auto-updating view into underlying data in a Realm, which is why you can't construct it directly. So instead of return Results(newObject), you should return tokenResults, which will contain your newly added object, again because Results is an auto-updating view.

Fetching from CoreData and casting

When fetching data from CoreData the results is an array of AnyObjects. When I cast this array to the related class, I can use the data with no problems.
My question is, I want to do something when the objects are being initialized but I don't know where and when the objects are getting initialised after it has been fetched from CoreData.
A sample of how I get the data from CoreData:
let request = NSFetchRequest(entityName: "Buildings")
let results = (try? context.executeFetchRequest(request)) as? [Buildings] ?? []
with my class as:
class Buildings: NSManagedObject {
#NSManaged var name: String
convenience init(context: NSManagedObjectContext, name: String) {
let description = NSEntityDescription.entityForName("Buildings", inManagedObjectContext: context)!
self.init(entity: description, insertIntoManagedObjectContext: context)
self.name = name
}
}
You need to overwrite awakeFromFetch() method in your Buildings class like
func awakeFromFetch() {
super.awakeFromFetch()
// do your initialization
}
This method is called on every fetch of the object from the persistent store.
What you are talking about is faulting, basically Core Data tries to keep its memory footprint as low as possible and one of the strategies it uses to accomplish this is faulting. When you fetch the records for your entity, Core Data executed the fetch request, but it didn't fully initialize the managed objects representing the fetched records.
The moment you access an attribute or relationship of a managed object, the fault is fired, which means that Core Data changes the fault into a realized managed object.

Dynamic attributes (getters/setters) in Swift on data models

I'm trying to use Swift (which I'm very new at) to create a clean interface for my data models. These models are backed by Firebase (though this is ultimately irrelevant to the actual question). What I want to avoid is writing tons of setter/accessor boiler plate code over and over.
For example:
class MyData {
let connection: Firebase!
private var _name: String? // internal storage, like an iVar in ObjC
init(connection: Firebase!) {
self.connection = connection
self.connection.observeEventType(.Value, withBlock: { snapshot in
_name = snapshot["name"]
}
}
var name: {
get {
return _name
}
set(name) {
// When the client sets the name, write it to Firebase
_name = name
self.connection.childByAppendingPath("name").setValue(name)
}
}
}
I'm sure I'm making a lot of mistakes in there. The idea is that the data is first loaded from the server when the instance is instantiated. Subsequently, we could call my_data_instance.name to get that name, or my_data_instance.name = "foo" and the name would be automatically written to the server.
This requires ~10 lines of code for a single attribute (of which there will be many). Nuts! There must be a better way!
EDIT: to be clear, I want to obviate the need to write as MUCH boiler plate code as possible. Consider a library like Mantle, where merely defining a #property is sufficient to do everything you want. In my opinion, anything more than one single line of code to say I have an attribute called "name", handle it via Firebase is overly verbose.
You can use Key-Value Observing to monitor changes in your properties. More info in Adopting Cocoa Design Patterns in Swift.
import Foundation
private var KVOContext = 0
// Your class must inherit from NSObject
class MyData : NSObject {
private let propertiesToObserve = ["name", "location"]
dynamic var name: String
dynamic var location: String
init(name: String, location: String) {
self.name = name
self.location = location
super.init()
// Add the properties that you want to observe
for property in self.propertiesToObserve {
self.addObserver(self, forKeyPath: property, options: [.New, .Old], context: &KVOContext)
}
}
// This method is called whenever an observed property is changed
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if let property = keyPath,
newValue = change![NSKeyValueChangeNewKey],
oldValue = change![NSKeyValueChangeOldKey] {
print("\(property) changed from \(oldValue) to \(newValue)")
// If oldValue != newValue, write back to Firebase
}
}
// Remove self as observer of self
deinit {
for property in self.propertiesToObserve {
self.removeObserver(self, forKeyPath: property)
}
}
}
let data = MyData(name: "John", location: "Chicago")
data.name = "David" // print: name changed from John to David
data.location = "New York" // print: location changed from Chicago to New York
Swift provides that functionality called property observer
var name: String {
didSet {
self.connection.childByAppendingPath("name").setValue(name)
}
}
There is a second observer willSet which is called before the value is changed.
Note (from the documentation):
When you assign a default value to a stored property, or set its
initial value within an initializer, the value of that property is set
directly, without calling any property observers.

How can I easily duplicate/copy an existing realm object

I have a Realm Object which has several relationships, anyone has a good code snippet that generalizes a copy method, to create a duplicate in the database.
In my case i just wanted to create an object and not persist it. so segiddins's solution didn't work for me.
Swift 3
To create a clone of user object in swift just use
let newUser = User(value: oldUser);
The new user object is not persisted.
You can use the following to create a shallow copy of your object, as long as it does not have a primary key:
realm.create(ObjectType.self, withValue: existingObject)
As of now, Dec 2020, there is no proper solution for this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your Realm Model Object classes conform to codable
class Dog: Object, Codable{
#objc dynamic var breed:String = "JustAnyDog"
}
Create this helper class
class RealmHelper {
//Used to expose generic
static func DetachedCopy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need detached / true deep copy of your Realm Object, like this:
//Suppose your Realm managed object: let dog:Dog = RealmDBService.shared.getFirstDog()
guard let detachedDog = RealmHelper.DetachedCopy(of: dog) else{
print("Could not detach Dog")
return
}
//Change/mutate object properties as you want
detachedDog.breed = "rottweiler"
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our realm object. Just make sure all your Realm Model Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.
I had a similar issue and found a simple workaround to get a copy of a realm object. Basically you just need to make the object conform to the NSCopying protocol, something like:
import RealmSwift
import Realm
import ObjectMapper
class Original: Object, NSCopying{
dynamic var originalId = 0
dynamic var firstName = ""
dynamic var lastName = ""
override static func primaryKey() -> String? {
return "originalId"
}
init(originalId: Int, firstName: String, lastName: String){
super.init()
self.originalId = originalId
self.firstName = firstName
self.lastName = lastName
}
func copy(with zone: NSZone? = nil) -> Any {
let copy = Original(originalId: originalId, firstName: firstName, lastName: lastName)
return copy
}
}
then you just call the "copy()" method on the object:
class ViewController: UIViewController {
var original = Original()
override func viewDidLoad() {
super.viewDidLoad()
var myCopy = original.copy()
}
}
The nice thing about having a copy is that I can modify it without having to be in a realm write transaction. Useful when users are editing some data but didn't hit save yet or simply changed their mind.
Since this problem is still alive I post my solution which works but still needs to be improved.
I've created an extension of Object class that has this method duplicate that takes an object objOut and fills the flat properties by looking at self. When a non-flat property is found (aka a nested object) that one is skipped.
// Duplicate object with its flat properties
func duplicate(objOut: Object) -> Object {
// Mirror object type
let objectType: Mirror = Mirror(reflecting: self);
// Iterate on object properties
for child in objectType.children {
// Get label
let label = child.label!
// Handler for flat properties, skip complex objects
switch String(describing: type(of: child.value)) {
case "Double", "Int", "Int64", "String":
objOut.setValue(self.value(forKey: label)!, forKey: label)
break
default:
break
}
}
return objOut
}
Inside the Manager class for my Realms I have the method copyFromRealm() that I use to create my copies of objects.
To give you a practical example this is the structure of my Appointment class:
Appointment object
- flat properties
- one UpdateInfo object
- flat properties
- one AddressLocation object
- flat properties
- one Address object
- flat properties
- one Coordinates object
- flat properies
- a list of ExtraInfo
- each ExtraInfo object
- flat properties
This is how I've implemented the copyFromRealm() method:
// Creates copy out of realm
func copyFromRealm() -> Appointment {
// Duplicate base object properties
let cpAppointment = self.duplicate(objOut: Appointment()) as! Appointment
// Duplicate UIU object
cpAppointment.uiu = self.uiu?.duplicate(objOut: UpdateInfo()) as? UpdateInfo
// Duplicate AddressLocation object
let cpAddress = self.addressLocation?.address?.duplicate(objOut: Address()) as? Address
let cpCoordinates = self.addressLocation?.coordinates?.duplicate(objOut: Coordinates()) as? Coordinates
cpAppointment.addressLocation = self.addressLocation?.duplicate(objOut: AddressLocation()) as? AddressLocation
cpAppointment.addressLocation?.address = cpAddress
cpAppointment.addressLocation?.coordinates = cpCoordinates
// Duplicate each ExtraInfo
for other in self.others {
cpAppointment.others.append(other.duplicate(objOut: ExtraInfo()) as! ExtraInfo)
}
return cpAppointment
}
I wasn't able to find out a good and reasonable way to work with nested objects inside my duplicate() method. I thought of recursion but code complexity raised too much.
This is not optimal but works, if I'll find a way to manage also nested object I'll update this answer.
Swift 5+
Creates a Realm managed copy of an existing Realm managed object with ID
extension RLMObject {
func createManagedCopy(withID newID: String) -> RLMObject? {
let realmClass = type(of: self)
guard let realm = self.realm, let primaryKey = realmClass.primaryKey() else {
return nil
}
let shallowCopy = realmClass.init(value: self)
shallowCopy.setValue(newID, forKey: primaryKey)
do {
realm.beginWriteTransaction()
realm.add(shallowCopy)
try realm.commitWriteTransaction()
} catch {
return nil
}
return shallowCopy
}
}

Swift - access to Dictionary of a singleton causes EXC_BAD_ACCESS

I have an app managing a simple stocks portfolio. Amongst other things, it keeps a record of the required exchange rates in a dictionary, like so:
[ EURUSD=X : 1.267548 ]
This disctionary is a Dictionary property of a singleton called CurrencyRateStore.
When updating the stocks quotations, it checks for an updated exchange rate and updates the dictionary with the following code:
CurrencyRateStore.sharedStore()[symbol] = fetchedRate.doubleValue
That calls:
subscript(index: String) -> Double? {
get {
return dictionary[index]
}
set {
// FIXME: crashes when getting out of the app (Home button) and then relaunching it
dictionary[index] = newValue!
println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
}
}
The first time the app is started, it works fine.
But if I quit the app (with the Home button) and then relaunch it, the currency rates are updated again, but this time, I get a EXC_BAD_ACCESS at the line
dictionary[index] = newValue!
Here is a screenshot:
[EDIT] Here is the thread in the debug navigator:
I tried to update the dictionary without a subscript, like so:
CurrencyRateStore.sharedStore().dictionary[symbol] = fetchedRate.doubleValue
but without more success. Same if I use the function updateValue:forKey:
I didn't have the issue in Objective-C.
Thanks for your help !
[EDIT] Here is the whole class CurrencyRateStore:
class CurrencyRateStore {
// MARK: Singleton
class func sharedStore() -> CurrencyRateStore! {
struct Static {
static var instance: CurrencyRateStore?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = CurrencyRateStore()
}
return Static.instance!
}
// MARK: Properties
/** Dictionary of currency rates used by the portfolio, presented like [ EURUSD=X : 1.3624 ] */
var dictionary = [String : Double]()
/** Returns a sorted array of all the keys on the currency rates dictionary */
var allKeys: [String] {
var keysArray = Array(dictionary.keys)
keysArray.sort {$0 < $1}
return keysArray
}
init() {
if let currencyRateDictionary: AnyObject = NSKeyedUnarchiver.unarchiveObjectWithFile(currencyRateArchivePath) {
dictionary = currencyRateDictionary as [String : Double]
}
}
subscript(index: String) -> Double? {
get {
return dictionary[index]
}
set {
// FIXME: crashes when getting out of the app (Home button) and then relaunching it
// (ApplicationWillEnterForeground triggers updateStocks)
dictionary[index] = newValue!
println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
}
}
func deleteRateForKey(key: String) {
dictionary.removeValueForKey(key)
}
/** Removes all currency rates from the Currency rate store */
func deleteAllRates()
{
dictionary.removeAll()
}
// MARK: Archive items in CurrencyRateStore
var currencyRateArchivePath: String { // Archive path
var documentDirectories: Array = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
// Get the only document directory from that list
let documentDirectory: AnyObject = documentDirectories.first!
return documentDirectory.stringByAppendingPathComponent("currencyRates.archive")
}
func saveChanges()-> Bool
{
// return success or failure
return NSKeyedArchiver.archiveRootObject(dictionary, toFile: currencyRateArchivePath)
}
}
This looks to me like a concurrency issue. Swift dictionaries aren't thread safe, and using them from a singleton can lead to multiple reader/writer issues.
Edit: I am pretty sure this is the real answer, based on the given source/debugging dump. To correct what I wrote, specifically MUTABLE dictionaries and arrays (as well as NSMutableDictionary and NSMutableArray) aren't thread safe, and problems arise when using them within Singletons that are accessed from multiple threads, and that appears to be what the sample source code is doing, or enabling other parts of the code to do.
I don't have an Apple link discussing Swift collection class thread safety, but I"m pretty sure common knowledge. But the following tutorial on Grand Central Dispatch discusses the problem in depth and how to solve it using GCD.
http://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1
The error, and the line itself:
dictionary[index] = newValue!
makes me think the problem is newValue being nil - and the error is caused by the forced unwrapping.
I would suggest to set a breakpoint and check its value, or otherwise print it before adding to the dict.
Moreover, it wouldn't be a bad idea to protect that statement with an optional binding:
if let value = newValue {
dictionary[index] = value
}
because if the value type is optional, it can be nil.
So in the end, I contacted Apple Technical Support.
They couldn't reproduce the issue.
I thought that maybe I don't need to save the currency rates, because during the quotes update, the function will check which currency rates it needs anyway, and repopulate the dictionary as needed.
So I deactivated the methods i created to save the CurrencyRateStore and to reload it again with NSKeyedUnarchiver.
Apparently, the crash is gone!