Realm Swift Composite Keys for Optional Properties - swift

Is there any way I could make composite keys for a Realm class with optional properties?
for example:
class Item: Object {
dynamic var id = 0
let importantNumber = RealmOptional<Int>()
let importantNumber2 = RealmOptional<Int>()
func setCompoundID(id: Int) {
self.id = id
compoundKey = compoundKeyValue()
}
func setCompoundImportantNumber(importantNumber: Int) {
self.importantNumber = importantNumber
compoundKey = compoundKeyValue()
}
func setCompoundImportantNumber2(importantNumber2: Int) {
self.importantNumber2 = importantNumber2
compoundKey = compoundKeyValue()
}
dynamic lazy var compoundKey: String = self.compoundKeyValue()
override static func primaryKey() -> String? {
return "compoundKey"
}
func compoundKeyValue() -> String {
return "\(id)\(importantNumber)\(importantNumber2)"
}
}
When I write my code like this, the compiler complains that I can't assign to my constant properties and it recommends me changing 'let' to 'var'; however, according to the Realm Swift Documentation, I need to have optional properties set as constants.
I'm not sure if this is even possible because I can't find anything in the Realm documentation about optional primary keys.

You need to set RealmOptional's value member. RealmOptional properties cannot be var because Realm can't detect assignment to property types that cannot be represented by the Objective-C runtime, which is why RealmOptional, List and LinkingObjects properties must all be let.
class Item: Object {
dynamic var id = 0
let importantNumber = RealmOptional<Int>()
let importantNumber2 = RealmOptional<Int>()
func setCompoundID(id: Int) {
self.id = id
compoundKey = compoundKeyValue()
}
func setCompoundImportantNumber(importantNumber: Int) {
self.importantNumber.value = importantNumber
compoundKey = compoundKeyValue()
}
func setCompoundImportantNumber2(importantNumber2: Int) {
self.importantNumber2.value = importantNumber2
compoundKey = compoundKeyValue()
}
dynamic lazy var compoundKey: String = self.compoundKeyValue()
override static func primaryKey() -> String? {
return "compoundKey"
}
func compoundKeyValue() -> String {
return "\(id)\(importantNumber)\(importantNumber2)"
}
}

Related

Deleting Objects with subclasses in Swift Realm

I have a really simple database in Swift Realm for a todo app:
Items and their parent Categories.
The user can delete both the Items and the Categories with a simple swipe action. The action works fine, there are no issues when deleting Items. If I delete a Category, that works too, but I can still see the Items in the Realm Browser, those remain in the database even though there are no parent anymore. Obviously the user can't see these, they are doing nothing but still, it would be better to get rid of these with the parent Category. Are there any simple ways to do this?
class Category: Object{
#objc dynamic var name: String = ""
#objc dynamic var color: String = ""
#objc dynamic var order = 0
let items = List<Item>()
override static func primaryKey() -> String? {
return "order"
}
static func incrementalIDCat() -> Int {
let realm = try! Realm()
return (realm.objects(Category.self).max(ofProperty: "order") as Int? ?? 0) + 1
}
}
class Item: Object {
#objc dynamic var title: String = ""
#objc dynamic var done: Bool = false
#objc dynamic var dateCreated: Date?
#objc dynamic var order = 0
var parentCategory = LinkingObjects(fromType: Category.self, property: "items")
override static func primaryKey() -> String? {
return "order"
}
static func incrementalIDItem() -> Int {
let realm = try! Realm()
return (realm.objects(Item.self).max(ofProperty: "order") as Int? ?? 0) + 1
}
}
override func updateModel(at indexPath: IndexPath) {
if let categoryForDeletion = self.categories?[indexPath.row] {
do {
try self.realm.write {
self.realm.delete(categoryForDeletion)
}
} catch {
print("Error deleting category, \(error)")
}
}
tableView.reloadData()
}
You just delete items first.
self.realm.delete(categoryForDeletion.items)
self.realm.delete(categoryForDeletion)
Or, with this extension, you can do this.
self.realm.delete(categoryForDeletion, cascading: true)

Extract self-properties in block-method

I have some trouble in understanding my specific request, in Swift.
There is a class named Origin and multiple its subclasses.
How I can update my block-method written ONLY in Origin class?
class Origin: NSObject {
func mod(_ block: (() throws -> Void)) {
try! block()
}
}
I need use mod from all Origin subclasses, and I need to have this usage effect:
var originSubclassObject = OriginSubclass()
originSubclassObject.mod {
.age = 12 //age is OriginSubclass property
.name = "Bob" //name is OriginSubclass property
}
So, you see, I need extract OriginSubclass properties for using in mod-block. I need to create usage exactly likes in usage effect code (extract mod-caller properties from ".").
Thanks all for help!
You could consider a protocol with a default implementation, e.g.:
protocol Modifiable { }
extension Modifiable {
func mod(_ block: ((Self) throws -> Void)) {
try! block(self)
}
}
class Origin: Modifiable { }
class OriginSubclass: Origin {
var name: String?
var age: Int?
}
And then:
let originSubclassObject = OriginSubclass()
originSubclassObject.mod { object in
object.age = 12
object.name = "Bob"
}
Or
let originSubclassObject = OriginSubclass()
originSubclassObject.mod {
$0.age = 12
$0.name = "Bob"
}
Or, if that base class was only there for the mod method, you could lose it entirely:
protocol Modifiable { }
extension Modifiable {
func mod(_ block: ((Self) throws -> Void)) {
try! block(self)
}
}
class MyObject: Modifiable {
var name: String?
var age: Int?
}
And
let myObject = MyObject()
myObject.mod { object in
object.age = 12
object.name = "Bob"
}
Or
let myObject = MyObject()
myObject.mod {
$0.age = 12
$0.name = "Bob"
}

Filter is returning all children for parent with matching child

I'm using Realm for Swift and I have a structure as follows:
class Navigation: Object {
dynamic var key = 0
dynamic var title: String?
let companies = List<Companies>()
override static func primaryKey() -> String? {
return "key"
}
}
class Companies: Object {
dynamic var key = 0
dynamic var name: String?
let locations = List<Locations>()
override static func primaryKey() -> String? {
return "key"
}
}
class Locations: Object {
dynamic var key = 0
...
dynamic var zip: String?
let contacts = List<Contacts>()
override static func primaryKey() -> String? {
return "key"
}
}
class Contacts: Object {
dynamic var key = 0
dynamic var firstName: String?
dynamic var lastName: String?
...
override static func primaryKey() -> String? {
return "key"
}
}
I'm trying to filter out locations by zip code, so that only locations that match the given zip code are displayed. I'm doing that like this
companies = realm.objects(Navigation.self).filter("key = 4").first!.companies.filter(predicate).sorted(byKeyPath: "key")
The key = 4 bit is because the filter is only supposed to search in companies under one specific category.
The problem that I'm having is that it returns all locations for a company that has a matching location. So if my zip to find is 12345, and companyA has a location that matches, all the locations under companyA are returned, even if they aren't a match.
How can I limit the results to be only locations with a match?
How can I limit the results to be only locations with a match?
Right now you're returning a Results<Companies>, but it seems like you want locations. I accomplished what I think you're looking for by adding some inverse relationship properties to the Companies and Locations models, and then queried the Realm for Locations matching zip == '12345' && ANY parentCompanies.parentNavigation.key == 4.
Here's a sample app that demonstrates this:
import UIKit
import RealmSwift
class Navigation: Object {
dynamic var key = 0
dynamic var title: String?
let companies = List<Companies>()
override static func primaryKey() -> String? {
return "key"
}
}
class Companies: Object {
dynamic var key = 0
dynamic var name: String?
let locations = List<Locations>()
let parentNavigation = LinkingObjects(fromType: Navigation.self, property: "companies")
override static func primaryKey() -> String? {
return "key"
}
}
class Locations: Object {
dynamic var key = 0
// ...
dynamic var zip: String?
let contacts = List<Contacts>()
let parentCompanies = LinkingObjects(fromType: Companies.self, property: "locations")
override static func primaryKey() -> String? {
return "key"
}
}
class Contacts: Object {
dynamic var key = 0
dynamic var firstName: String?
dynamic var lastName: String?
// ...
override static func primaryKey() -> String? {
return "key"
}
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
_ = try? FileManager.default.removeItem(at: Realm.Configuration.defaultConfiguration.fileURL!)
let realm = try! Realm()
// Objects Matching Query
try! realm.write {
// Locations
let locations = Locations()
locations.zip = "12345"
// Companies
let companies = Companies()
companies.name = "Companies A"
companies.locations.append(locations)
// Nav
let nav = Navigation()
nav.key = 4
nav.companies.append(companies)
// Add to Realm
realm.add(nav)
}
let locationsIn12345AndNavigationKey4 = realm.objects(Locations.self)
.filter("zip == '12345' && ANY parentCompanies.parentNavigation.key == 4")
print(locationsIn12345AndNavigationKey4)
return true
}
}
This prints:
Results<Locations> (
[0] Locations {
key = 0;
zip = 12345;
contacts = RLMArray <0x6000000f2100> (
);
}
)

Swift: How to retrieve different type and named primary keys from generic memory stores?

I have two protocols and two objects that implement them. One object uses name:String as its primary key, the other uses code:Int.
protocol AlphaProtocol{
var name:String {get set}
init(name: String)
}
protocol BetaProtocol{
var code: Int {get set}
init(code:Int)
}
class AlphaObject: AlphaProtocol{
var name: String
required init(name: String){
self.name = name
}
}
class BetaObject: BetaProtocol{
var code: Int
required init(code: Int){
self.code = code
}
}
Right now, to store the these objects I use two different memory stores that implement two different protocols, one for each kind of object.
protocol AlphaStoreProtocol{
func addObject(object anObject: AlphaProtocol)
func getObject(name aName:String)->AlphaProtocol?
func removeObject(name aName: String)
}
protocol BetaStoreProtocol{
func addObject(object anObject: BetaProtocol)
func getObject(code aCode:Int)->BetaProtocol?
func removeObject(code aCode: Int)
}
class AlphaStore{
fileprivate var objects = [AlphaProtocol]()
func addObject(object anObject: AlphaProtocol){
if getObject(name: anObject.name) == nil{
objects.append(anObject)
}
}
func getObject(name aName:String)->AlphaProtocol?{
for o in objects{
if o.name == aName{
return o
}
}
return nil
}
func removeObject(name aName: String){
self.objects = self.objects.filter({$0.name != aName})
}
}
class BetaStore: BetaStoreProtocol{
fileprivate var objects = [BetaProtocol]()
func addObject(object anObject: BetaProtocol){
if getObject(code: anObject.code) == nil{
objects.append(anObject)
}
}
func getObject(code aCode:Int)->BetaProtocol?{
for o in objects{
if o.code == aCode{
return o
}
}
return nil
}
func removeObject(code aCode: Int){
self.objects = self.objects.filter({$0.code != aCode})
}
}
Test Code using two tailor made stores.
let alpha = AlphaObject(name: "Alpha")
let beta = BetaObject(code: 12345)
let alphaStore = AlphaStore()
let betaStore = BetaStore()
alphaStore.addObject(object: alpha)
if (alphaStore.getObject(name: alpha.name) != nil){
print("alpha object has been added to alphaStore")
}
alphaStore.removeObject(name: alpha.name)
if (alphaStore.getObject(name: alpha.name) == nil){
print("alpha object has been removed from alphaStore")
}
betaStore.addObject(object: beta)
if (betaStore.getObject(code: beta.code) != nil){
print("beta object has been added to betaStore")
}
betaStore.removeObject(code: beta.code)
if (betaStore.getObject(code: beta.code) == nil){
print("beta object has been removed from betaStore")
}
The goal: using a single generic class for both the stores but I'm stuck because the two objects use two different primary keys (different type and different name) and I can't simply force a generic "Id" as the primary key in the objects. One has to be named "name" and the other "code".
Is there a way to write the getObject and removeObject methods to accept both kind of objects?
protocol GenericStoreProtocol{
associatedtype T
func addObject(object anObject: T)
// func getObject()->T // One object use a name:String, the other code:Int as its primary key!
// func removeObject() // One object use a name:String, the other code:Int as its primary key!
}
class GenericStore<T>: GenericStoreProtocol{
fileprivate var objects = [T]()
func addObject(object anObject: T){
objects.append(anObject)
}
// ...
}
let genericAlphaStore = GenericStore<AlphaProtocol>()
let genericBetaStore = GenericStore<BetaProtocol>()
To generalize the problem, we need a store that can:
add items of any types (or ones we specify)
look up and delete items by id
use the correct id property for different stored objects
First, I'd create a protocol called Storable which has an identifier computed property. This should be of type Equatable as we will eventually be using equality comparisons when looking up objects by id in our Store.
protocol Storable {
associatedtype Identifier: Equatable
var identifier: Identifier { get }
}
We can now define the classes of the objects we are going to store (AlphaObject and BetaObject). Both of these classes should conform to their own protocol as well as the Stored protocol. Here is where you'd define what property should be used as the identifier. For AlphaObject it's name and for BetaObject it's code. These can be read-only computed properties that return the values of name and code respectively.
protocol AlphaProtocol {
var name: String { get set }
init(name: String)
}
protocol BetaProtocol {
var code: Int { get set }
init(code: Int)
}
class AlphaObject: AlphaProtocol, Storable {
typealias Identifier = String
internal var identifier: Identifier {
return self.name
}
var name: String
required init(name: String) {
self.name = name
}
}
class BetaObject: BetaProtocol, Storable {
typealias Identifier = Int
internal var identifier: Identifier {
return self.code
}
var code: Int
required init(code: Int){
self.code = code
}
}
Finally, our Store will take any objects that are Storable and will access, insert, and delete based on T's specified identifier.
class Store<T: Storable> {
fileprivate var objects = [T]()
func addObject(object: T) {
if getObject(identifier: object.identifier) == nil {
objects.append(object)
}
}
func getObject(identifier: T.Identifier) -> T? {
for o in objects {
if o.identifier == identifier {
return o
}
}
return nil
}
func removeObject(identifier: T.Identifier) {
self.objects = self.objects.filter({$0.identifier != identifier})
}
}
The full code with tests!
protocol Storable {
associatedtype Identifier: Equatable
var identifier: Identifier { get }
}
protocol AlphaProtocol {
var name: String { get set }
init(name: String)
}
protocol BetaProtocol {
var code: Int { get set }
init(code: Int)
}
class AlphaObject: AlphaProtocol, Storable {
typealias Identifier = String
internal var identifier: Identifier {
return self.name
}
var name: String
required init(name: String) {
self.name = name
}
}
class BetaObject: BetaProtocol, Storable {
typealias Identifier = Int
internal var identifier: Identifier {
return self.code
}
var code: Int
required init(code: Int){
self.code = code
}
}
class Store<T: Storable> {
fileprivate var objects = [T]()
func addObject(object: T) {
if getObject(identifier: object.identifier) == nil {
objects.append(object)
}
}
func getObject(identifier: T.Identifier) -> T? {
for o in objects {
if o.identifier == identifier {
return o
}
}
return nil
}
func removeObject(identifier: T.Identifier) {
self.objects = self.objects.filter({$0.identifier != identifier})
}
}
/* Tests */
let alpha = AlphaObject(name: "Alpha")
let beta = BetaObject(code: 12345)
let alphaStore = Store<AlphaObject>()
let betaStore = Store<BetaObject>()
alphaStore.addObject(object: alpha)
if (alphaStore.getObject(identifier: alpha.name) != nil){
print("alpha object has been added to alphaStore")
}
alphaStore.removeObject(identifier: alpha.name)
if (alphaStore.getObject(identifier: alpha.name) == nil){
print("alpha object has been removed from alphaStore")
}
betaStore.addObject(object: beta)
if (betaStore.getObject(identifier: beta.code) != nil){
print("beta object has been added to betaStore")
}
betaStore.removeObject(identifier: beta.code)
if (betaStore.getObject(identifier: beta.code) == nil){
print("beta object has been removed from betaStore")
}
I can't simply force a generic "Id" as the primary key in the objects.
Yep, you totally can if you use a single protocol instead of two unrelated ones (AlphaProtocol and BetaProtocol).
protocol KeyedObject {
associatedtype PrimaryKey : Equatable
var key: PrimaryKey { get }
}
Just make your objects conform to this protocol; they can declare whatever equatable type you require for the key, they just have to provide some way to access it.
class AlphaObject: KeyedObject {
typealias PrimaryKey = String
var name: String
required init(name: String) {
self.name = name
}
var key: String {
return self.name
}
}
Then you can use a straightforward generic class that contains only objects you provided:
class GenericStore<T : KeyedObject> {
fileprivate var objects = [T]()
func addObject(object anObject: T){
objects.append(anObject)
}
func getObject(key: T.PrimaryKey) -> T? {
for o in objects{
if o.key == key {
return o
}
}
return nil
}
...
}

Type casting in a generic swift function

Supposing I have a UICollectionViewCell and a UITableViewCell with identical properties. Rather than have two functions which populate those cells, could I have a generic that takes something , determine what that was and then cast it to the correct thing to perform actions on it before returning?
my thinking is:
func setUpCell<T>(event: Event, cell:T) -> T {
// figure out what T is and cast it
cell.event.bar = event.bar
return cell
}
is this a good way of avoiding large amounts of code duplication?
Given your model type
struct Event {
let title: String
let desc: String
}
define this protocol
protocol EventCell: class {
var id: String? { get set }
var desc: String? { get set }
}
Now conform your UITabelViewCell and UICollectionViewCell to it
class TableCell: UITableViewController, EventCell {
var id: String?
var desc: String?
}
class CollectionCell: UICollectionViewCell, EventCell {
var id: String?
var desc: String?
}
And finally define this extension
extension EventCell {
func populate(event:Event) {
self.id = event.id
self.desc = event.desc
}
}
That's it. Now both your cells (UITabelViewCell and UICollectionViewCell) have the populate method!
Does this match what you were thinking?
import UIKit
struct Event {
var bar:Int = 0
}
// Protocol to group common additions
protocol ViewCellAdditions {
init()
var separatorInset:Int { get set }
var event:Event { get set}
}
// Generic function to work on any class that adopts ViewCellAdditions
func setUpCell<T: ViewCellAdditions>(event: Event, cell:T, foo:Int) -> T {
var newCell = T()
newCell.separatorInset = foo
newCell.event.bar = event.bar
return newCell
}
// Class that adopts ViewCellAdditions
class NewCellClass: ViewCellAdditions {
required init() {}
var separatorInset:Int = 10
var event:Event = Event()
}
// How to use it
let aCell = NewCellClass()
let aEvent = Event()
let newCell = setUpCell(aEvent, cell: aCell, foo: 5)