How to create one Realm class from several other classes? - swift

Good afternoon everyone,
Im currently working on a task that called "Bookmark". The short description is whenever i clicked the bookmark button, app will save the article and then display it in the BookmarkVC.
I have 3 types of object called "News", "Documents" and "ITSectionResult" and my idea is to create an object called "BookmarkItem" which contain element of 3 above objects and one property called bookmarkCategory to indicate type of objects, so that i can use it to display in BookmarkVC. I just want to work on only one realm object, so any one can help me an an idea to group these guys together?. Here i would attach my draft code as below:
For News class:
class NewsArticle:Object {
dynamic var title: String?
dynamic var body:String?
dynamic var category:String?
dynamic var createTime:String?
dynamic var image:String?
dynamic var id:String?
convenience init (title:String, body:String, category:String,
image:String, id:String, createTime:String) {
self.init()
self.title = title
self.body = body
self.category = category
self.image = image
self.id = id
self.createTime = createTime
}
Document class:
class Documents {
var id: String?
var title:String?
var createTime:CLong?
var url:String?
init (title:String, id:String, createTime:CLong?, url:String) {
self.title = title
self.id = id
self.createTime = createTime
self.url = url
}
ITSectionResult class:
class SectionSearchResult {
var title:String?
var image:String?
var id:String?
var byCategory:String?
init (title:String, image:String, id:String, byCategory:String) {
self.title = title
self.image = image
self.id = id
self.byCategory = byCategory
}
and finally the drafting BookmarkItem class:
class BookmarkItem:Object {
//Category
dynamic var bookmarkCategory:BookMarkItemCategory?
dynamic var title: NewsArticle?
dynamic var body:NewsArticle?
dynamic var category:NewsArticle?
dynamic var createTime:NewsArticle?
dynamic var image:NewsArticle?
dynamic var id:NewsArticle?
dynamic var link:String?
dynamic var url:String?
}
class BookMarkItemCategory:Object {
dynamic var name = ""
}
Here i would remind that, the BookmarkItem class uses 3 major properties to display in the BookmarkVC, "image" for filter category type(example: book image for Documents object, newspaper icon for News object) , "title" for the title and the url for display in WebView. Thank you so much, and wish you guys have a good weekend ahead.

As mentioned above in the comments by EpicPandaForce, this can be achieved by not having a class for each type and instead use a unified model.
import RealmSwift
final class UnifiedModel: Object {
dynamic var title: String = ""
dynamic var id: String = ""
dynamic var createTime: String = ""
dynamic var category: String = ""
dynamic var body: String?
dynamic var image: String?
override static func primaryKey() -> String {
return "id"
}
}
With the model I've shown above, every instance would need a title, id, create time and category while body and image could be left nil. Please feel free to follow-up if you have any other questions.
Disclaimer: I work for Realm.

Related

How to map a Firestore DocumentID to a RealmObject attribute?

I'm trying to provide some data in the cloud with Firestore that can be downloaded and stored in a Realm database on an iOS device.
The structure of my object that I want to store is:
import Foundation
import RealmSwift
import FirebaseFirestore
import FirebaseFirestoreSwift
#objcMembers class Flashcard: Object, Codable{
#objc dynamic var id: String? = NSUUID().uuidString
#objc dynamic var character: String?
#objc dynamic var title: String?
#objc dynamic var translation: String?
#objc dynamic var created: Date = Date()
let deck = LinkingObjects<FlashcardDeck>(fromType: FlashcardDeck.self, property: "cards")
override class func primaryKey() -> String? {
return "id"
}
private enum CodingKeys: String, CodingKey {
case id
case character
case translation
case created
case title
}
If I try to map the documentID to my id attribute with
#DocumentID #objc dynamic var id: String? = NSUUID().uuidString
If get the following error:
'Primary key property 'id' does not exist on object 'Flashcard'
How can I solve this problem?
EDIT: To make it more understandable here is a screenshot of my Firestore database:
The collection "PredifinedDecks" will store many decks. For example the id = DF59B1B3-BD22-47CE-81A6-04E7A274B98F represents one deck. Each deck will store an array/List with cards in it.
Not sure I fully understand the question but let me address this at a high level.
It appears there is a PredefinedDecks (a collection) that contains documents. Each document has a field (an array) of cards and some other field data. If the goal is to read in all of the documents (the decks) and their child data and store them as Realm objects, here's one solution. Start with a Realm object to hold the data from Firestore
class DeckClass: Object {
#objc dynamic var deck_id = ""
#objc dynamic var created = ""
#objc dynamic var title = ""
let cardList = List<CardClass>()
convenience init(withDoc: QueryDocumentSnapshot) {
self.init()
self.deck_id = withDoc.documentID
self.title = withDoc.get("title") as? String ?? "no title"
self.created = withDoc.get("created") as? String ?? "no date"
let cardArray = withDoc.get("cards") as? [String]
for card in cardArray {
let card = CardClass(withCard: card) {
self.cardList.append(card)
}
}
}
}
With this, you simply pass the documentSnapshot from Firestore for each document and the class will populate its properties accordingly.
and the code to read Firestore
func readDecks() {
let decksCollection = self.db.collection("PredefinedDecks")
decksCollection.getDocuments(completion: { documentSnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
for doc in documentSnapshot!.documents {
let deck = DeckClass(withDoc: doc)
self.decksList.append(deck) //a Realm List class object? Something else?
}
})
}

Merge two arrays into one by key

I have two Arrays. I want to use them, to create the new one. Then, the new list will be use in the List View. So how it looks like? I have two models that are the basis for lists:
Photo:
class Photo: Object, Identifiable, Decodable {
#objc dynamic var albumId: Int = 0
#objc dynamic var id: Int = 0
#objc dynamic var title: String = ""
#objc dynamic var url: String = ""
#objc dynamic var thumbnailUrl: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
Album:
class Album: Object, Decodable {
#objc dynamic var id: Int = 0
#objc dynamic var userId: Int = 0
#objc dynamic var title: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
The child of both is ListItem. So the vars of the ItemLists are using vars from the Photo and Album classes. I hope I explained it well. If not, please ask.
class ListItem {
var id: Int = 0 Album id
var title: String = "" Photo title
var albumTitle: String = "" Album title
var thumbnailUrl: String = "" Photo thumbnailUrl
}
You can use zip to merge the 2 arrays together, then call map to create ListItems from each element.
let list = zip(photos, albums).map { photo, album in ListItem(id: album.id, title: photo.title, albumTitle: album.title, thumbnailUrl: photo.thumbnailUrl)}
If I understand your question directly you have a one to many relationship between Album and Photo so for each Photo we should look up the correct Album and create a new ListItem
This can be done using this code
let list: [ListItem] = photoArray.compactMap { photo in
guard let album = albumArray.first(where: { $0.id == photo.id }) else {
return nil
}
return ListItem(id: album.id, title: photo.title, albumTitle: album.title, thumbnailUrl: photo.url)
}

How to save and load GKGameModelPlayer from Realm in Swift?

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.

how to initialise variables in #NSManaged object class as a JSON value?

I want to initialise the variables in the 'Item' class like the variables 'Item2' class
this is the 'Item' class :-
import UIKit
import CoreData
import SwiftyJSON
class Item: NSManagedObject {
#NSManaged var name = String()
#NSManaged var symbol = String()
#NSManaged var checked : Bool = false
#NSManaged var buyPrice = String()
#NSManaged var rank = String()
}
this is 'Item2' class :-
import Foundation
import SwiftyJSON
class Item2 : Codable {
var name = String()
var symbol = String()
var checked : Bool = false
var buyPrice = String()
var rank = String()
init(bitJSON: JSON) {
self.name = bitJSON["name"].stringValue
self.symbol = bitJSON["symbol"].stringValue
self.buyPrice = bitJSON["price_usd"].stringValue
self.rank = bitJSON["rank"].stringValue
}
}
ManagedObject are not normal object. They are an object-oriented way to assess a database. Managed objects use a context to get all their properties. A managedObject must have context - they simply don't work without one.
extension Item {
static func insert(json:JSON, in context:NSManagedObjectContext) -> Item{
let newItem = Item.init(entity: Item.entity(), insertInto:context)
newItem.name = json["name"].stringValue
newItem.symbol = json["symbol"].stringValue
newItem.buyPrice = json["price_usd"].stringValue
newItem.rank = json["rank"].stringValue
}
}
Notice two things about this method: 1) it does not assume that it knows the context - that is a job for a higher level - for some apps it is simply inserted direclty into the viewContext, for other apps a temporary background context is created. and 2) it does not save the context - that is also not it's job - other object may be edited or created at the same time and they should all be save transactionally.
It would be used for example like this:
persistentContainer.performBackgroundTask { (context) in
let newItem = Item.insert(json: json, in: context)
do {
try context.save()
} catch {
// log to you analytic provider
}
}
Hope this helps.

RealmSwift replacement for RLMobject

Whats the correct way todo this with RealmSwift, it used to be RLMobject
var stream:Results<streams>
stream = Realm().objects(streams)
this first one lives on my class as a global the second line in my viewdidload
this is what i try todo: https://dpaste.de/AKKJ
class tabelviewcontroller has no initializers
the model
class streams: Object {
dynamic var br = ""
dynamic var categorie = 0
dynamic var ct = ""
dynamic var lc = ""
dynamic var ml = ""
dynamic var mt = ""
dynamic var name = ""
dynamic var shoutcatid = 0
dynamic var stationid = 0
override static func primaryKey() -> String? {
return "stationid"
}
}
A few quick notes, its good to keep class names singular, my model would look like this:
class Stream: Object {
...
}
If you want to get all objects from streams you can just do this:
let results = Realm().objects(Stream)
var stream:Results<streams>!
solved it