Swift Realm Results Convert to Model - swift

Is there any simple way to map a Realm request to a Swift Model (struct) when it is just a single row?
When it is an array of data I can do something like this and work with the array. This is not working on a single row.
func toArray<T>(ofType: T.Type) -> [T] {
return compactMap { $0 as? T }
}
But what is best to do when just a single row of data?
my databases are big so doing it manually is just a pain and ugly.
It would also be nice when the Swift Model is not 100% the same as the Realm Model. Say one has 30 elements and the other only 20. Just match up the required data.
Thank you.

On my apps I m using this class to do all actions. I hope that's a solution for your situation. There is main actions for realm.
Usage
class Test: Object {
var name: String?
}
class ExampleViewController: UIViewController {
private let realm = CoreRealm()
override func viewDidLoad() {
super.viewDidLoad()
let data = realm.getArray(selectedType: Test.self)
}
import RealmSwift
class CoreRealm {
// Usage Example:
// let testObject = RealmExampleModel(value: ["age":1 , "name":"Name"])
// let testSubObject = TestObject(value: ["name": "FerhanSub", "surname": "AkkanSub"])
// testObject.obje.append(testSubObject)
let realm = try! Realm()
func deleteDatabase() {
try! realm.write {
realm.deleteAll()
}
}
func delete<T: Object>(selectedType: T.Type) {
try! realm.write {
let object = realm.objects(selectedType)
realm.delete(object)
}
}
func delete<T: Object>(selectedType: T.Type, index: Int) {
try! realm.write {
let object = realm.objects(selectedType)
realm.delete(object[index])
}
}
func add<T: Object>(_ selectedObject: T) {
do {
try realm.write {
realm.add(selectedObject)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
// return Diretly object
func getArray<T: Object>(selectedType: T.Type) -> [T]{
let object = realm.objects(selectedType)
var array = [T]()
for data in object {
array.append(data)
}
return array
}
func getObject<T: Object>(selectedType: T.Type, index: Int) -> T{
let object = realm.objects(selectedType)
var array = [T]()
for data in object {
array.append(data)
}
return array[index]
}
// return Result tyle
func getResults<T: Object>(selectedType: T.Type) -> Results<T> {
return realm.objects(selectedType)
}
func getResult<T: Object>(selectedType: T.Type) -> T? {
return realm.objects(selectedType).first
}
func createJsonToDB<T: Object>(jsonData data: Data, formatType: T.Type) {
// let data = "{\"name\": \"San Francisco\", \"cityId\": 123}".data(using: .utf8)!
let realm = try! Realm()
try! realm.write {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
realm.create(formatType, value: json, update: .modified)
} catch {
print("Json parsing error line 65")
}
}
}
}

Related

Generic delegate response handlers

I've got a class currently something like this
class Client {
var responseOneDelegate: ResponseOneDelegate?
var responseTwoDelegate: ResponseTwoDelegate?
...
func onData(forMessageType messageType: MessageType, data: Data) {
switch messageType {
case .responseOne:
let response = JSONDecoder().decode(Response<ResultForResponseOne>.self, from: data)
responseOneDelegate?.received(response: response)
case .responseTwo:
let response = JSONDecoder().decode(Response<ResultForResponseTwo>.self, from: data)
responseTwoDelegate?.received(response: response)
}
}
}
protocol ResponseOneDelegate {
func received(response: Response<ResultForResponseOne>)
}
protocol ResponseTwoDelegate {
func received(response: Response<ResultForResponseTwo>)
}
With the idea that a class can be one or multiple delegates
class Handler: ResponseOneDelegate, ResponseTwoDelegate {
func received(response: Response<ResultForResponseOne>) { }
func received(response: Response<ResultForResponseTwo>) { }
}
This seems to be screaming out to be generalised as there will be quite a lot of responses in this format, but I can't quite figure out how to do it
I've tried using a generic type to make just a single delegate
protocol ResponseDelegate: AnyObject {
associatedtype T
func received(response: Response<T>)
}
It doesn't seem possible to store the delegates in Client in [MessageType: ResponseDelegate] so with the idea of the generic delegate I'm not sure how I'd store the references of the delegates? Maybe I'd just have to cast them before calling?
How would you generalise this?
Functions may be helpful here, in cases where you have a protocol with just a single function in it, and few types that implement the protocol.
Here's an example of the idea:
class Client {
var handle: ((Data) -> Bool)
init(handle: #escaping ((Data) -> Bool)) {
self.handle = handle
}
func received(data: Data) {
handle(data)
}
}
let ints = { (data: Data) -> Bool in
guard let i = try? JSONDecoder().decode(Int.self, from: data) else {
return false
}
print("handle \(i)")
return true // if handled
}
let strings = { (data: Data) -> Bool in
guard let str = try? JSONDecoder().decode(String.self, from: data) else {
return false
}
// handle Strings
print("handle \(str)")
return true
}
let intOrString = { (data: Data) -> Bool in
ints(data) ||
strings(data)
}
func handleMany(handlers: ((Data) -> Bool)...) -> (Data) -> Bool {
return { data in
for handle in handlers {
if handle(data) {
return true
}
}
return false
}
}
let intsOrStrings = handleMany(handlers: ints, strings)
let aOrBClient = Client(handle: intsOrStrings)
let aClient = Client(handle: ints)

Firestore - Subcollections Swift

So I'm trying to learn some Firestore basic functionality and have watched "Kilo Locos" videos on YouTube explaining CRUD operations. I want to take his method of code and create subcollections from it. Basically, how can I add a collection and make the 'User' collection a sub collection from this new collection. Any help is greatly appreciated, many thanks!!
Here is a link to download the project:
https://kiloloco.com/courses/youtube/lectures/3944217
FireStore Service
import Foundation
import Firebase
import FirebaseFirestore
class FIRFirestoreService {
private init() {}
static let shared = FIRFirestoreService()
func configure() {
FirebaseApp.configure()
}
private func reference(to collectionReference: FIRCollectionReference) -> CollectionReference {
return Firestore.firestore().collection(collectionReference.rawValue)
}
func create<T: Encodable>(for encodableObject: T, in collectionReference: FIRCollectionReference) {
do {
let json = try encodableObject.toJson(excluding: ["id"])
reference(to: collectionReference).addDocument(data: json)
} catch {
print(error)
}
}
func read<T: Decodable>(from collectionReference: FIRCollectionReference, returning objectType: T.Type, completion: #escaping ([T]) -> Void) {
reference(to: collectionReference).addSnapshotListener { (snapshot, _) in
guard let snapshot = snapshot else { return }
do {
var objects = [T]()
for document in snapshot.documents {
let object = try document.decode(as: objectType.self)
objects.append(object)
}
completion(objects)
} catch {
print(error)
}
}
}
func update<T: Encodable & Identifiable>(for encodableObject: T, in collectionReference: FIRCollectionReference) {
do {
let json = try encodableObject.toJson(excluding: ["id"])
guard let id = encodableObject.id else { throw MyError.encodingError }
reference(to: collectionReference).document(id).setData(json)
} catch {
print(error)
}
}
func delete<T: Identifiable>(_ identifiableObject: T, in collectionReference: FIRCollectionReference) {
do {
guard let id = identifiableObject.id else { throw MyError.encodingError }
reference(to: collectionReference).document(id).delete()
} catch {
print(error)
}
}
}
FIRCollectionReference
import Foundation
enum FIRCollectionReference: String {
case users
}
User
import Foundation
protocol Identifiable {
var id: String? { get set }
}
struct User: Codable, Identifiable {
var id: String? = nil
let name: String
let details: String
init(name: String, details: String) {
self.name = name
self.details = details
}
}
Encodable Extensions
import Foundation
enum MyError: Error {
case encodingError
}
extension Encodable {
func toJson(excluding keys: [String] = [String]()) throws -> [String: Any] {
let objectData = try JSONEncoder().encode(self)
let jsonObject = try JSONSerialization.jsonObject(with: objectData, options: [])
guard var json = jsonObject as? [String: Any] else { throw MyError.encodingError }
for key in keys {
json[key] = nil
}
return json
}
}
Snapshot Extensions
import Foundation
import FirebaseFirestore
extension DocumentSnapshot {
func decode<T: Decodable>(as objectType: T.Type, includingId: Bool = true) throws -> T {
var documentJson = data()
if includingId {
documentJson!["id"] = documentID
}
let documentData = try JSONSerialization.data(withJSONObject: documentJson!, options: [])
let decodedObject = try JSONDecoder().decode(objectType, from: documentData)
return decodedObject
}
}
The Firestore structure cannot have collection as children of other collections.
The answer to your question (How can I add a collection and make the 'User' collection a sub collection from this new collection?) is you cannot. Instead you must put a document between those two collections.
Read this for more information.
It says: Notice the alternating pattern of collections and documents. Your collections and documents must always follow this pattern. You cannot reference a collection in a collection or a document in a document.

Hold reference to downloaded DynamoDB data

I have a class holding a DynamoDB model (I cut the # of variables for brevity, but they're all Optional Strings:
import AWSCore
import AWSDynamoDB
#objcMembers class Article: AWSDynamoDBObjectModel, AWSDynamoDBModeling {
var _articleSource: String?
class func dynamoDBTableName() -> String {
return "article"
}
class func hashKeyAttribute() -> String {
return "_articleId"
}
class func rangeKeyAttribute() -> String {
return "_articleUrl"
}
override class func jsonKeyPathsByPropertyKey() -> [AnyHashable: Any] {
return [
"_articleSource" : "articles.articleSource",
]
}
}
In my View Controller, I'm downloading data from the table and storing each article in an array like this:
let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
var allArticles = [AnyObject]()
func getArticles(completed: #escaping DownloadComplete) {
let scanExpression = AWSDynamoDBScanExpression()
scanExpression.limit = 50
self.dynamoDbObjectMapper.scan(Article.self, expression: scanExpression).continueWith(block: { (task:AWSTask<AWSDynamoDBPaginatedOutput>!) -> Any? in
if let error = task.error as NSError? {
print("The request failed. Error: \(error)")
} else if let paginatedOutput = task.result {
for article in paginatedOutput.items as! [Article] {
self.allArticles.append(article)
}
}
return(self.allArticles)
})
completed()
}
When I try to work with the data that should be stored in allArticles the array is empty. However, the array holds articles when I break execution in the download block where articles are being appended. How can I hold reference to the downloaded data? My use of a completion block was my attempt.
Edit: allArticles is of type [AnyObject] because I'm attempting to store objects from 3 different classes total in the same array to make it easier to work with in a TableView
The array wasn't empty after all, I just didn't realize this was all async (duh...)
I just needed:
DispatchQueue.main.async {
self.tableView.reloadData()
}
in place of completed() in the getArticles() func

How to save a struct with NSCoding

How can I save my struct with NSCoding so that it doesn´t change even if the user
closes the app? I would appreciate it if you could also show me how to implement the missing code correctly.
UPDATE with two new functions below:
Here is my code:
struct RandomItems: Codable
{
var items : [String]
var seen = 0
init(items:[String], seen: Int)
{
self.items = items
self.seen = seen
}
init(_ items:[String])
{ self.init(items: items, seen: 0) }
mutating func next() -> String
{
let index = Int(arc4random_uniform(UInt32(items.count - seen)))
let item = items.remove(at:index)
items.append(item)
seen = (seen + 1) % items.count
return item
}
func toPropertyList() -> [String: Any] {
return [
"items": items,
"seen": seen
]
}
}
override func viewWillDisappear(_ animated: Bool) {
UserDefaults.standard.set(try? PropertyListEncoder().encode(quotes), forKey:"quote2")
}
override func viewDidAppear(_ animated: Bool) {
if let data = UserDefaults.standard.value(forKey:"quote2") as? Data {
let quote3 = try? PropertyListDecoder().decode(Array<RandomItems>.self, from: data)
}
}
}
extension QuotesViewController.RandomItems {
init?(propertyList: [String: Any]) {
return nil
}
}
How can I make sure the whole Array is covered here?
For structs you should be using the new Codable protocol. It is available since swift 4 and is highly recommended.
struct RandomItems: Codable
{
var items: [String]
var seen = 0
}
extension RandomItems {
init?(propertyList: [String: Any]) {
...
}
}
// Example usage
let a = RandomItems(items: ["hello"], seen: 2)
let data: Data = try! JSONEncoder().encode(a)
UserDefaults.standard.set(data, forKey: "MyKey") // Save data to disk
// some time passes
let data2: Data = UserDefaults.standard.data(forKey: "MyKey")! // fetch data from disk
let b = try! JSONDecoder().decode(RandomItems.self, from: data2)
Update
It looks like the Original Poster is nesting the struct inside of another class. Here is another example where there struct is nested.
class QuotesViewController: UIViewController {
struct RandomItems: Codable
{
var items: [String]
var seen = 0
}
}
extension QuotesViewController.RandomItems {
init(_ items:[String])
{ self.items = items }
init?(propertyList: [String: Any]) {
guard let items = propertyList["items"] as? [String] else { return nil }
guard let seen = propertyList["seen"] as? Int else { return nil }
self.items = items
self.seen = seen
}
}
// example usage
let a = QuotesViewController.RandomItems(items: ["hello"], seen: 2)
let data: Data = try! JSONEncoder().encode(a)
UserDefaults.standard.set(data, forKey: "MyKey") // Save data to disk
// some time passes
let data2: Data = UserDefaults.standard.data(forKey: "MyKey")! // fetch data from disk
let b = try! JSONDecoder().decode(QuotesViewController.RandomItems.self, from: data2)

Get Core Data Entity relatives with a generic function

I'm designing a data manager for my Core Data model and I'd like to create a generic function to fetch relatives of a class.
I’ve created a protocol allowing to build managers for each data type. In this protocol I already defined two associated types T and K and several simple functions. Now I’m stuck with a class relatives fetching method — I need to indicate somehow that T has K relatives. I’ve tried in vain to create some protocol indicating this relationship thru mutual properties, so both classes could conform to this protocol. Any idea, is it even possible?
import Foundation
import CoreData
protocol DataManager {
associatedtype T: NSManagedObject, NSFetchRequestResult
associatedtype K: NSManagedObject, NSFetchRequestResult // Relative
static var sharedInstance: Self { get }
static func getAll(sorted: [NSSortDescriptor]?, context: NSManagedObjectContext) -> [T]?
static func insert(item: T)
static func update(item: T)
static func clean()
static func deleteById(id: String)
// Relatives
static func getRelatives(by: T) -> [K]?
static func get(byRelative: K) -> [T]?
}
extension DataManager {
static func getAll(sorted: [NSSortDescriptor]?, context: NSManagedObjectContext) -> [T]? {
guard let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else { return nil }
fetchRequest.sortDescriptors = sorted
var results: [T]? = nil
do {
results = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
return results
}
}
protocol Identifiable {
typealias Identity = String
var id: Identity? { get }
}
extension DataManager where Self.T: Identifiable {
static func get(by id: T.Identity, context: NSManagedObjectContext) -> T? {
guard let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else { return nil }
fetchRequest.predicate = NSPredicate(format: "%K == %#", "id", id)
var rawResults: [T]? = nil
do {
rawResults = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
if let result = rawResults?.first {
return result }
else { return nil }
}
}
Well, I've created one solution.
We can identify all relations with a particular class:
let relationships = T.entity().relationships(forDestination: K.entity())
It allows us to find all IDs of an item for each relationship (we can have many relationships for the same relative Entity):
let relativesIDs = item.objectIDs(forRelationshipNamed: relationship.name)
So, we can use these IDs to fetch records from another class.
static func getRelatives(of item: T, context:NSManagedObjectContext) -> [K]? {
guard let fetchRequest: NSFetchRequest<K> = K.fetchRequest() as? NSFetchRequest<K> else { return nil }
fetchRequest.fetchBatchSize = 100
var results: [K]? = nil
var resultSet: Set<K> = [] // doesn't allow duplicates
let relationships = T.entity().relationships(forDestination: K.entity())
for relationship in relationships {
let relativesIDs = item.objectIDs(forRelationshipNamed: relationship.name)
let predicate = NSPredicate(format: "self IN %#", relativesIDs)
fetchRequest.predicate = predicate
var batchResults: [K] = []
do {
batchResults = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
if batchResults.count > 0 { resultSet = resultSet.union(Set(batchResults)) }
}
if resultSet.count > 0 { results = Array(resultSet) }
return results
}
I'm not sure that this is the most elegant solution, but it works :-)