realm. remove objects from ListBase - swift

I have different realm models. They have List properties. I want to make universal way for removing objects from List properties. So I did the following:
if let list = self[property.name] as? ListBase {
list._rlmArray.removeAllObjects()
}
but this just clear list property, without deleting objects from realm. The only way I've found is:
if let list = self[property.name] as? ListBase {
while list.count > 0 {
let object = list._rlmArray.firstObject()
let any = object as Any
if let theObject = any as? Object {
realm.delete(theObject)
}
}
}
Code above works and doesn't generate any warning. But it looks ugly.

You can use dynamicList(_ propertyName: String) to retrieve List property by name instead subscript.
if property.type == .array {
try! realm?.write {
realm?.delete(dynamicList(property.name))
}
}

Related

check if there much id inside dictionary than holds objects

I have a dictionary that holds objects, inside that objects I have an id, is there a way to check matches with this id and return the object?
example:
if I have id = 2 and I want to check if there exists in the object and get the particular object that matches with this id
Thanks for the help
my structs:
struct CoinsInfo:Codable {
let data:[String:CoinSpecificInfo]?
}
struct CoinSpecificInfo:Codable {
let urls:UrlsCoins?
let logo:String?
let id:Int?
let symbol:String?
}
You can try
// item is an instance of CoinsInfo
// get a new dictionary with all keys that has id matching in their value
guard let matches = item.data?.filter { $1.id == 2 } else { return }
// get an array of objects
let arr = Array(matches.values)

How can i extract the array of the objects in Swift?

i have json objects like and parsed in an array
let objects = [Object]()
struct Object {
name: String
id: Int
}
Suppose like
let objects [Object(name:oscar, id: 11), Object(name:sanchez, id: 12),Object(name:emily, id: 15),Object(name:clarck, id: 31) ... ]
How can i take the string array as below also with this name which object belongs to ? ( so i can use object easily)
let stringPropertyArray = [oscar, sanchez,emily,clarck ... ]
Thanks
how i will find the object ? if you have "emily" and i want to item.id which emily belongs to ?
Perhaps you want something like
if let ob = objects.first {$0.name == "emily"} {
print(ob.id)
}
But if your goal is to search quickly, it would be better to have a dictionary keyed by the value you will be searching on.
I think this is what you want
let stringPropertyArray: [String] = objects.map {$0.name}
There are 2 approaches you can use:
by looping (traditional approach)
var listName: [String] = []
for item in objects {
listName.append(item.name)
}
by using higher order function
let listName = objects.map{ $0.name }
There would be a case if your name property is optional and for some object, name property value is nil then we should use compactMap higher order function in order to avoid nil object in the list
let listName = objects.compactMap{ $0.name }
To find any specific object we can use filter like below:
let object = objects.filter{
$0.name == "sanchez" }.first
// OR
let object = objects.first { object -> Bool in
object.name == "emily" }

Trying to add objects to Class and save info to Firebase database

I want to save new objects via a view controller in my app. However, I want these new objects to load when the app is logged into. I am using firebase to save data into a database, but how can I save an object and have it return when the app is logged into again? I am new-ish to programming, sorry for any potential confusion.
Here is where the goal information is read when the app has been logged into.
for i in 0 ... clientList.count - 1 {
screenHandle = ref?.child(organizationCode).child(clientList[i].name).observe(.value, with: { (snapshot) in
let clientStuffLoad = snapshot.value as! [String:Any]
if clientStuffLoad["Goal 1 Description"] != nil {
clientList[i].goal1 = clientStuffLoad["Goal 1"] as! String
} else {
clientList[i].goal1 = ""
}
This is essentially what I have regarding adding a new member to the class Client:
#IBAction func addingClientSaveButton(_ sender: Any) {
var client7 = Client(name: addingClientName.text!,
goal1: addingClientGoal1.text!, goal2:
addingClientGoal2.text!,
goal3: addingClientGoal3.text!,
isSelected: false, s: 1,
ind: 1, targetBehavior1 : addingClientTB1.text!,
targetBehavior2 : addingClientTB2.text!,
targetBehavior3 : addingClientTB3.text!,
targetBehavior1Info : addingClientTB1Info.text!,
targetBehavior2Info : addingClientTB2Info.text!,
targetBehavior3Info : addingClientTB3Info.text!)
but I would like the object name to read the client name input as opposed to client7
The second part to this is that I want a way to write this to the database, and be able to read it at log in so that I can use the properties of the class and add to it when adding a new client.
This is a super broad question because it covers a lot of different aspects of working with Firebase; writing, reading, handling DataSnapshots etc. Also, I don't know what your data represents so I picked something for me to cover some of the aspects of working with Firebase.
There's no error checking but it works as is. I've commented along the way.
Firebase has no objects; just parent and child nodes. Everything can be thought of as key: value pairs like a dictionary. You cannot write an object or read an object. Only NSString, NSNumber, NSDictionary and the dreaded NSArray (or their Swift counterparts)
Let's start with a class - there's 100 ways to do this but I like classes to be responsible for their properties as well as accepting them and presenting them
class WineClass {
var wine_key = ""
var name = ""
var varietal = ""
//this is used when creating a new wine object before storing in firebase
init(withName: String, andVarietal: String) {
self.name = withName
self.varietal = andVarietal
}
//this is used when we are loading data from firebase to create the wineclass object
init(withSnapshot: DataSnapshot) {
let wineName = withSnapshot.childSnapshot(forPath: "wine_name").value as? String ?? "No Wine Name"
let wineDict = withSnapshot.value as! [String: Any]
let wineVarietal = wineDict["wine_varietal"] as? String ?? "No Wine Varietal"
self.wine_key = withSnapshot.key //when we read a wine, this will be it's reference in case we want to update or delete it
self.name = wineName
self.varietal = wineVarietal
}
//this is use to create a dictionary of key:value pairs to be written to firebase
func getWineDictForFirebase() -> [String: Any] {
let d = [
"wine_name": self.name,
"wine_varietal": self.varietal
]
return d
}
}
Then, we need a class var to store the WineClass's. This would be for example a dataSource for a tableView
var wineArray = [WineClass]() //a class var array to store my wines
Then I will give you two buttons, one that populates and writes some wine to Firebase and then a second that read them in and prints to console
func button0() {
self.writeWine(withName: "Scarecrow", andVarietal: "Red Blend")
self.writeWine(withName: "Ghost Horse", andVarietal: "Cabernet Sauvignon")
self.writeWine(withName: "Screaming Eagle", andVarietal: "Cabernet Sauvignon, Merlot, Cabernet Franc")
}
func button1() {
self.readWines()
}
And then the function that accepts some strings as properites for each wine and writes them to Firebase
func writeWine(withName: String, andVarietal: String) {
let newWine = WineClass(withName: withName, andVarietal: andVarietal) //create a new wine object
let wineListRef = self.ref.child("wine_list") //get a reference to my firebase wine_list
let thisWineRef = wineListRef.childByAutoId() //a new node for this wine
let d = newWine.getWineDictForFirebase() //get the wine properties as a dictionary
thisWineRef.setValue(d) //save it in firebase
}
and finally a function that reads in those wines, and prints their properties in console
func readWines() {
let wineRef = self.ref.child("wine_list")
wineRef.observeSingleEvent(of: .value, with: { snapshot in //we are reading in the entire wine node which will contain many child nodes
let allWines = snapshot.children.allObjects as! [DataSnapshot] //cast each child node as a DataSnapshot & store in array
for wineSnap in allWines { //iterate over each child node in the array
let wine = WineClass(withSnapshot: wineSnap) //create a new wine, ensuring we also keep track of it's key
self.wineArray.append(wine) //add to the array
}
for wine in self.wineArray {
print(wine.wine_key, wine.name, wine.varietal)
}
})
}
lastly, when button0 is clicked, our Firebase looks like this
wine_list
-LhbjhkEC8o9TUISCjdw
wine_name: "Scarecrow"
wine_varietal: "Red Blend"
-LhbjhkEC8o9TUISCjdx
wine_name: "Ghost Horse"
wine_varietal: "Cabernet Sauvignon"
-LhbjhkEC8o9TUISCjdy
wine_name: "Screaming Eagle"
wine_varietal: "Cabernet Sauvignon, Merlot, Cabernet Franc"
and then the output when button1 is clicked
-LhbjhkEC8o9TUISCjdw Scarecrow Red Blend
-LhbjhkEC8o9TUISCjdx Ghost Horse Cabernet Sauvignon
-LhbjhkEC8o9TUISCjdy Screaming Eagle Cabernet Sauvignon, Merlot, Cabernet Franc
Note that self.ref is a reference to the root node of my firebase yours will need to reference your firebase.

How to use NSSet created from Core Data

I have the following core data model:
where Person to Codes is a one-to-many relationship.
I have a function which returns a Person record and if the code person.codes returns an NSSet of all the codes associated with that Person. The issue that I am having is how to use the NSSet.
person.codes.allObjects.first returns this data:
<Codes: 0x60000213cb40> (entity: Codes; id: 0xb978dbf34ddb849 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Codes/p1> ; data: {
code = 4LQ;
number = 1;
whosAccount = "0xb978dbf34ddb869 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Person/p1>";
})
I thought if I made person.codes.allObjects.first of type Codes, I would be able to access the code and number elements but I get an error: error: value of type 'Any?' has no member 'number'
Also, how can I search this data set for a particular code or number.
I appreciate that this is proabably a simple question but have searched and read the documentation to no avail. I suspect that may base knowledge is not sufficient.
Update
I have a CoreDataHandler class which contains the following code:
class CoreDataHandler: NSObject {
//static let sharedInstance = CoreDataHandler()
private static func getContext() -> NSManagedObjectContext {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static func fetchPerson() -> [Person]? {
let context = getContext()
do {
let persons: [Person] = try context.fetch(Person.fetchRequest())
return persons
} catch {
return nil
}
}
I can fetch a person using:
let row = personTableView.selectedRow
let person = CoreDataHandler.fetchPerson()?[row]
Core Data supports widely native Swift types.
Declare codes as Set<Codes> in the Person class.
It's much more convenient than typeless NSSet.
You get a strong type and you can apply all native functions like filter, sort, etc. without type cast.
let codes = person.codes as! Set<Code>
Once that is done you can access the properties. Searching can be done by filtering for instance
let filteredCodes = codes.filter({ $0.code == "XYZ" })
will return all objects that has the code "XYZ". Or to get only one you can use
let code = codes.first(where: {$0.id == 1})
which will return the first object that has id = 1
A simple example getting all Person objects that has a given code
func findWithCode(_ code: String) -> [Person] {
guard let persons = CoreDataHandler.fetchPerson() else {
return []
}
var result = [Person]()
for person in persons {
let codes = person.codes as! Set<Code>
if codes.contains(where: { $0.code == code }) {
result.append(person)
}
}
return persons
}

Why can't I get the index of a filtered realm List?

I'm trying to find the index of an item in a List<> object after the item has been appended to it, so that I can insert into a tableview.
The tableview is sectioned with .filters so I have to apply the filter before looking for the indexPath. However, the filter appears to break the indexOf functionality.
I noticed that the function .map has the same effect.
import UIKit
import RealmSwift
class Model: Object {
#objc dynamic var title: String = ""
let items = List<Model>()
}
class ViewController: UIViewController {
var models: Results<Model>?
var parentModel: Model?
var items = List<Model>()
let realm = try! Realm()
override func viewDidLoad() {
super.viewDidLoad()
if !UserDefaults.standard.bool(forKey: "IsNotFirstTime") {
populateRealm()
UserDefaults.standard.set(true, forKey: "IsNotFirstTime")
}
models = realm.objects(Model.self)
parentModel = models!.first
items = parentModel!.items
let child = Model()
child.title = "Child"
try! realm.write {
parentModel!.items.append(child)
}
print(items.index(of: child)) // prints correct value
print(items.filter({ $0.title == "Child" }).index(of: child)) // prints nil
}
func populateRealm() {
let parent = Model()
parent.title = "Parent"
try! realm.write {
realm.add(parent)
}
}
}
The first print finds the object, but the second print doesn't, despite the mapping having no overall effect.
The strange thing is that the object IS in the filtered list, doing:
print(items.filter({ $0.title == "Child" }).first
Returns the object, so it is there.
Edit
On further inspection, it looks like it's not the filter but the conversion of array type that breaks the functionality, converting to array without a filter does the same thing.
print(Array(items).index(of: child)) // prints nil
When you want to use mapping you should add an attributes to map your objects according to it for example
print(items.map({ $0.id }).index(of: child.id))
if you use it on this way it will return what you expected
I figured out the solution. The filter syntax I used .filter({ $0.title == "Child" }) isn't the Realm filter, and converts the List to a LazyFilterCollection<List<Model>>, which doesn't seem to be compatible with searching for the index of a realm object.
The fix was to use the format .filter("title == %#", "Child"), which returns a realm Results object.