Dictionary ObjectIdentifier: CustomClass Questions about Dictionary append in Swift - ios

Hi? I have created BananaMilkFactory, CiderFactory and CantataCoffeeFactory that inherit superclass called Beverage. And I made an Inventory that bundles Beverages into an array.
For example, if you create 3 Banana Milk, 2 Cider, 1 Cantata Coffee, Inventory will have a total of 6 Beverages.
The function I want is [ObjectIdentifier: Inventory] and I want to return the Beverages of the same ObjectIdentifier separately.
// [BananaMilk(), BananaMilk(), BananaMilk(), Cider(), Cider(), CantataCoffee()]
private var inventory: [Beverage]
func readInventores() -> [ObjectIdentifier : Inventory] {
var allInventores = [ObjectIdentifier : Inventory]()
self.inventory.forEach { beverage in
// Help~!!
}
return allInventores
}
What should I do to return it in [ObejctIdentifier: Inventory] format?

create an array which contains class name of object (BananaMilkFactory, CiderFactory ....). Iterate through the array,put objects with the same class name in a new array.I don't know if I understand your question correctly.

Related

Realm Relation how to implement

I am working on iOS and that is saving product. and this product has some more things inside its model
let suppose the following model
#objcMembers public class ProductModel : Object, Codable {
dynamic var Id : Int = 0
dynamic var Name : String = 0
dynamic var Price : Double = 0.0
}
and the other model (CustomerModel) that contains the ProductModel is as follow
#objcMembers public class CustomerModel : Object, Codable {
dynamic var Id : Int = 0
dynamic var Name : String = 0
var Product : ProductModel? = nil
}
Now when I save customer with the product inside it, I can see that in Realm it gets saved successfully. But if and only if that object is not in Realm already,
Let suppose this
let customer1 = CustomerModel()
customer1.Id = ...
customer1.Name = .....
customer1.Product = product1
Now this customer data is saved. But I am getting exception if I try to save following data
let customer2 = CustomerModel()
customer2.Id = ...
customer2.Name = .....
customer2.Product = product1
Just notice that customer2 also want to save product info that is already saved in Realm namely "product1".
So how to handle this sitution.
I am trying to save the data with the following generic function for realm objects
func save <T: Object> (_ obj : T){
do {
try realmObj.write{
realm.add(obj)
}
}catch{}
}
Question 2:
Also I want to get All Customer, I know how to do it, but problem is It never retrieves the Product inside the Customer. I can see in Realm DB Browser that the customer that get saved with the product, that customer table contains the reference of Product also. But when I try to get all customer then that customer have only customer details not Product detail. Whereas that must be there.
Just put dynamic keyword before your property
dynamic var Product : ProductModel? = nil

Will an array of a superclass contain subclasses as well?

I created a class Person. Person contain properties like name and email. Both are from type String.
Beside the Person class, I have a subclass Student that inherited from superclass Person. Subclass Student contain properties like student number (String) and isGraduated (Boolean).
I have an empty array of persons from the class Person, like:
var persons: [Person]()
After I created both Person and Student objects inside the array persons, I read them out using a UITableView. Both models will be print in the cells. But when I want to check the value of isGraduated from the selected row, auto-completion doesn't give me the value of the property: persons.isGraduated.
The first thought of this problem is, will my persons array contain also the subclass Student? My second thought would be, that I think I should not check the value isGraduated inside the TableView. My wish of this function is that it will do something, like call the native camera if the person is graduated.
Looking forward to the solution.
Tree options here
A: Change the array type to Student
var students: [Student]()
B: Cast each element from the persons array to a Student. Ideally inside a for each person.
guard let student = person as? Student else {
return // or continue if inside a for loop
}
Then u will have access to that student variable.
C: Or if they might be students and persons at the same time in the array then
for person in persons {
switch person{
case let student as Student: //student case
//do something
default: //person case
//do something
}
}
If you want to check what is the type of your person : Person or Student you can use is keyword like this :
if persons[0] is Person {
//if a person
} else if persons[0] is Student {
//if a student
}
If you want to use a function member for your specific type, you need to downcast the type by using the keyword as like this :
if let person = persons[0] as? Person {
//person is now a Person type
} else if let student = persons[0] as? Student {
//student is now a Student type
}

How can I properly copy objects from one Realm object to another object

Based on the following code I would like to be able to create a new ItemList from an existing one. In other words I have an ItemList called First List and I want to create a new ItemList, call it Second List and fill it with the Items from First List.
The way I have it right now is that it creates the Second List as expected, the Items from the First List show in Second List but what doesn't work is when I want to delete only the Items from First List, it deletes Items from both lists. I guess I'm not truly copying the items.
So the question is, how can I copy Items from First List to Second List?
Object Models:
class ItemList: Object {
dynamic var listName = ""
dynamic var createdAt = NSDate()
let items = List<Item>()
}
class Item:Object{
dynamic var productName:String = ""
dynamic var createdAt = NSDate()
}
Code to create Second List from First List
This Works fine, it creates Second List and adds the items from First List but I don't think I'm making copies just showing them in Second List.
let newList = ItemList()
newList.listName = "Second List"
if let selectedList = realm.objects(ItemList.self).filter("listName = %#", "First List").first{
let itemsFromFirstList = selectedList.items
newList.items.append(objectsIn:itemsFromFirstList)
}
try! realm.write {
realm.add(newList)
}
This code is supposed to delete only the items from First List
This actually deletes items from both First List and Second List
let listToDelete = realm.objects(ItemList.self).filter("listName = %#", "First List").first
try! realm.write {
for item in (listToDelete?.items)! {
realm.delete(realm.objects(Item.self).filter("productName = %#", item.productName).first!)
}
}
What you want to do is use:
for record in postsDB.objects(PostModel.self) {
if !combinedDB.objects(PostModel.self).filter("postId == \(record.parentId)").isEmpty {
combinedDB.create(PostModel.self, value: record, update: false)
}
}
The create method is inherited from Object. It tells the target to create a new object. Use true if you want it to look to see if there is already a record there, and update it if there is.
PostModel is the Object type, record is what you want copied.
Edit: I added the if statement to provide more context. You didn't show your class definitions, so I was guessing. This is a working example. I ask for a set of records from DatabaseA and copy it to DatabaseB (postsDB to combinedDB).
So if the type of the object you're trying to insert is a List, I'd recommend you define a subclass of Object, and have at least the list you need as a property.
class TagList: Object {
dynamic var tag = ""
var list = List<PostModel>()
override class func primaryKey() -> String? {
return "tag"
}
}
Full working example illustrating: creating new objects, copying all objects to a second list, deleting from second list after copying, adding to first list (which didn't get anything deleted from it.
import Foundation
import RealmSwift
class Letter: Object {
dynamic var letter = "a"
}
class Letters: Object {
var letterList = List<Letter>()
}
class ListExample {
let listRealmStore = try! Realm() // swiftlint:disable:this force_try
func testThis() {
print(Realm.Configuration.defaultConfiguration.fileURL!)
listRealmStore.beginWrite()
addSingleItems() // add 3 objects to the DB
let firstList = Letters()
let allObjects = listRealmStore.objects(Letter.self)
for item in allObjects {
firstList.letterList.append(item)
}
let secondList = Letters()
let itemsToCopy = firstList.letterList
for item in itemsToCopy {
let obj = listRealmStore.create(Letter.self)
obj.letter = item.letter
secondList.letterList.append(obj)
}
let third = Letter()
third.letter = "Z"
listRealmStore.add(third)
firstList.letterList.append(third)
secondList.letterList.removeLast()
do {
try listRealmStore.commitWrite()
} catch let error {
print("couldn't commit db writes: \(error.localizedDescription)")
}
print("list one:\n\(firstList)")
print("list two:\n\(secondList)")
}
func addSingleItems() {
for letter in ["a", "b", "c"] {
let objectToInsert = Letter()
objectToInsert.letter = letter
listRealmStore.add(objectToInsert)
}
}
}
Results in:
list one:
Letters {
letterList = List<Letter> (
[0] Letter {
letter = a;
},
[1] Letter {
letter = b;
},
[2] Letter {
letter = c;
},
[3] Letter {
letter = Z;
}
);
}
list two:
Letters {
letterList = List<Letter> (
[0] Letter {
letter = a;
},
[1] Letter {
letter = b;
}
);
}
Are you really trying to create copies of your items, or do you just want to be able to remove them from lists independently?
When you do:
newList.items.append(objectsIn: itemsFromFirstList)
you end up with the same objects being in both lists. List<T> just stores references to objects that live within the Realm. Appending an object to a List just references the existing object, it doesn't copy the object.
When you call Realm.delete(_:) you remove that object entirely from the Realm, not just from a single list that it is a member of. To remove an object from a List, you should instead use List.remove(objectAtIndex:).
One part the solution you are looking for could be like this, make copy objects in the list, or you can just use this idea to clone whole list it self:
Previously answered here
As of now, Dec 2020, there is not proper solution of 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 Note")
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.

how to print value of object in an array in swift, not its location

I have a class that contains data on certain entrepreneurs in a separate swift file that is within the same project.
It looks like this:
class Entrepreneur:NSObject {
var name:String?
var netWorth = (0.0, "")
var company:String?
var summary: [String]
var age: Int?
override init() {
name = ""
company = ""
summary = [""]
age = 1;
}
In the same file I have a function that returns an NSMutableArray which contain the instances of the entrepreneur class like this:
func populateArray() -> NSMutableArray {
var entreprenuersArray: NSMutableArray = []
//Mark Zuckerberg
let markZuckerBerg = Entrepreneur()
markZuckerBerg.name = "Mark Zuckerberg"
markZuckerBerg.age = 19
markZuckerBerg.company = "Facebook"
markZuckerBerg.netWorth = (35.7, "Billion")
// add mark zuckerberg object to array
entreprenuersArray.addObject(markZuckerBerg)
print (entreprenuersArray)
in my ViewController.swift file I create a constant called entrepreneurss and give it a type of the class "Entrepreneur" created above and initialize it like so:
let entrepreneuerss:Entrepreneur = Entrepreneur()
I then access one the class methods, the "populateArray" function and try to print the entrepreneurss array like so:
entrepreneuerss.populateArray()
print (entrepreneuerss)
The issue is the print function is printing the location of the object and not the value...something like this: .Entrepreneur: 0x7f88d0e3ecc0>"
What do I need to do so that I can return an array of the object values and not the location. I want to be able to access the array of object from my ViewController.swift file and randomly select an object and access its properties.
First, you have a instance method which returns an array of Entrepreneur objects (by the way, I don't see a return, maybe you forgot to copy it).
This method should be a class method, because you don't use any property of the Entrepreneur object which returns it :
class func populateArray() -> [Entrepreneur] {
var entreprenuersArray:[Entrepreneur] = []
//Mark Zuckerberg
let markZuckerBerg = Entrepreneur()
markZuckerBerg.name = "Mark Zuckerberg"
markZuckerBerg.age = 19
markZuckerBerg.company = "Facebook"
markZuckerBerg.netWorth = (35.7, "Billion")
// add mark zuckerberg object to array
entreprenuersArray.append(markZuckerBerg)
print (entreprenuersArray)
return entreprenuersArray
}
Then you can have your array by calling :
let array = Entrepreneur.populateArray()
Secondly, in this method, you create an array of Entrepreneur object and returns it, but in your example code, you never use this array :
// Creates a Entrepreneur object with default values
let entrepreneuerss:Entrepreneur = Entrepreneur()
// create an array of entrepreneurs objects, returns it, but never use it
entrepreneuerss.populateArray()
// print some information about the object with default values
print (entrepreneurs)
Instead, you should use the class method and try:
// create an array of entrepreneurs objects, returns it,
let arrayEntrepreneurs = Entrepreneur.populateArray()
// print some information about the array of Entrepreneurs objects
print (arrayEntrepreneurs)
In order to have a detailed description of your object, since it inherits from NSObject, just override the description read-only property in order to customize the text when you log your object :
override var description : String {
get {
return "Entrepreneur : name : `\(name) - company : `\(company)` - summary : `\(summary)` - age : `\(age)`"
}
}
Thus your print function will return :
[Entrepreneur : name : `Optional("Mark Zuckerberg") - company : `Optional("Facebook")` - summary : `[""]` - age : `Optional(19)`]
Maybe I am missing something here, but you can just use...
dump(arrayEntrepreneurs)
for Cocco
Swift.dump(arrayEntrepreneurs)
try
for element in array {
print(element)
}
What do I need to do so that I can return an array of the object values and not the location. I want to be able to access the array of object from my ViewController.swift file and randomly select an object and access its properties.
You already have the all the objects of entrepreneuers which are populated from your another swift file. you may use any object from the array and print it's any property.
Still you want to print the value of all the objects or all the properties of a object then you need to override the property called "description" into your model class.
add below code to your Entrepreneur class
override var description:String {
return "name :\(self.name) \n company \(self.company)"
}
The above code will print the name and company of any object of Entrepreneur

Is there a way to save an array within an array using swift?

Is it possible to save an array of names within an array? For example, Lets say we have 2 car companies, Toyota and Honda. And lets say we want to create an array within the car company array of the cars they make. For example...
var ArrayWithinArray = ["Toyota, "SIENNA", "CAMRY"", "Honda, "Odyssey", "Civic""]
How would i do this using swift?
In such situation, you can create an dictionary of arrays like this:
var listData = [
"Toyota": ["SIENNA", "CAMRY"],
"Honda": ["Odyssey", "Civic"]
]
To access a particular model, ("SIENNA" here)
let model = listData["Toyota"]?.first ?? "Car not found"
model will contain SIENNA
And if you want to iterate over all models
for model in listData["Toyota"] ?? [] {
println(model)
}

Resources