I have a dictionary of key-value pairs that I am trying to save to core data.
let children = userSnap.children.allObjects as! [DataSnapshot]
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let thisUser = ThisUser(context: context)
var data = [String: String]()
for child in children {
data[child.key] = child.value as? String
}
data["uid"] = currentUser!.uid
The child.key string matches the attribute in CoreData and I want to put child.value as? String in as the value for each CoreData attribute.
Is there a way I can do this without having to create a line of code for each attribute ... ex) thisUser.uid = data["uid]
Here is my data model:
You should be able to do this via Core Data introspection. You'd do something like:
Get the NSEntityDescription as thisUser.entity.
Get a list of the properties in the entity, by calling attributesByName on the entity description to get a dictionary of [String: NSAttributeDescription].
The keys for that dictionary are the names of your Core Data properties. Get values from your dictionary with that key. Assign values to your managed object with key-value coding, i.e. setValue(_:, forKey:).
If necessary you can also use the results of attributesByName to find out the type of the property. Use the attributeType property of NSAttributeDescription.
Basically, find out what attributes your Core Data object has, get their names, and then run through those names to copy data from one place to another.
Related
I'm reading data from the Firebase database which I then sorted in an alphabetical order.
I want to read the keys under the Scenario tree, but not the entire data under it.
Then I want to put the keys in the gradePickerValues1 variable.
Firebase Database Tree
2
Scenario
Yeni Senaryo
OffTime: "12:12"
OnTime: "12:12"
Yeni Senaryo2
OffTime: "12:12"
OnTime: "12:12"
MainActivity
var gradePickerValues1 : [String] = []
#objc func getSenaryo(){
let ref = Database
.database()
.reference()
.child(self.chipFieldText.text!)
.child("Scenario")
ref.observe(.value) { (snapshot) in
for group in snapshot.children {
self.gradePickerValues1.append((group as AnyObject).key)
}
}
}
Instead of
let userDict = userSnap.value as! String
do
let userDict = userSnap.value as! [String: [String: String]]
as your "Scenario" holds a dictionary with a structure of [Name: [Time: String]].
If you want to get "12:12" of OffTime of Yeni Senaryo from this dictionary, you can do something like this:
let value = userDict["Yeni Senaryo"]!["OffTime"]!
If the data base structure is more complex, you should use [String: Any] instead and use if let syntax to perform optional typecast.
EDIT: If you only want to read "Yeni Senaryo" and not the values under, it is simply not possible with Firebase.
See Best practices for data structure:
Avoid nesting data
Because the Firebase Realtime Database allows nesting data up to 32 levels deep, you might be tempted to think that this should be the default structure. However, when you fetch data at a location in your database, you also retrieve all of its child nodes. In addition, when you grant someone read or write access at a node in your database, you also grant them access to all data under that node. Therefore, in practice, it's best to keep your data structure as flat as possible.
Anyway, if you don't need the nested values and only the names, you can do
let userDict = userSnap.value as! [String: Any]
let userNames = Array(userDict.keys)
and the array of names as Strings will be stored in userNames.
EDIT2:
Don't typecast group as AnyObject.
AnyObject.key is not the key you want.
for group in snapshot.children {
guard let value = value as? DataSnapshot else { continue }
self.gradePickerValues1.append(value.key)
}
How can I access the data of the children memberId or name and photoURL of the child "members"?
You can see the structure of my database in images.
I tried to use queryOrdered and queryEqual but I just can use it one time
I tried like that because I know the room.key who is the "key" on the database.
let refParticipants = refDatabase.child("markers").queryOrdered(byChild: "key").queryEqual(toValue: room.key)
refParticipants.observe(.childAdded, with: { snapshot in
...
}
I use Swift 3.1
I update my answer with that screenshot:
I think you are asking how to access the child nodes of
/markers/oHQ.../members/9oBKY...
Let's simply the structure for this answer
markers
marker_0
members
member_0
name: "J"
member)1
name: "K"
and then the code that will access each member within the members node and print their name
let markersRef = self.ref.child("markers")
let marker0Ref = markersRef.child("marker_0")
let membersRef = marker0Ref.child("members")
membersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let name = dict["name"] as! String
print(name)
}
})
and the output will be
J
K
Since you know the parent node (oHQa...), which contains the child node 'members', it doesn't really matter as to what each members key is since you are iterating over them.
However, if you are wanting to query for certain members or other member data, you may want to consider flattening the database a bit like this
markers
oHQa...
//marker data
marker_members
member_0
name: "J"
member_of: "oHQa..."
member_1
name: "K"
member_of: "oHQa..."
With this structure you can query for all the members of any marker or query for all members whose name is "J" etc.
As a side note, in the structure in the question you have the member_id as both the key as well as a child which is unnecessary. If it's the key then you can always directly access that node without a query.
I have a fetch to core data that returns a NSManagedObject like so
let results = try context.fetch(data);
let resultset = results as! [NSManagedObject];
I have a string array created like so:
var db: [String] = [];
My core data has a column called blogs.
How do I get that entire column into my db variable?
Blogs column is a string.
New to core data too.
Use the map function
let results = try context.fetch(data) as! [NSManagedObject]
db = results.map { $0.value(forKey: "blogs") as! String }
or – preferable – if your are using NSManagedObject subclass and generic entity
let results = try context.fetch(data)
db = results.map { $0.blogs }
And remove the semicolons in your code by the way...
I am inserting an Array into my database as a String and after fetching it I want it to convert it again to Array. So that I can fetch my values again and I can do next operation.
Here below is my array inserting into database(TestQuestion) as a String:
let testQuestionModel : TestQuestion = NSEntityDescription.insertNewObject(forEntityName: "TestQuestion", into: AppDelegate.getContext()) as! TestQuestion
testQuestionModel.optionsArray = "\(question["options"] as! NSArray)"
Example: String Array I am getting from Database
(\n \"Rahul Abhyankar\",\n \"Pinkesh Shah\",\n \"Ramanan
Ganesan\",\n \"Dr. Marya Wani\",\n \"\",\n \"\"\n)".
Here is 4 options you can see this is my string after fetching from Database.
1) Rahul Abhyankar.
2) Pinkesh Shah.
3) Ramanan Ganesan.
4) Dr. Marya Wani.
Now how can I convert it into array?
I tried some methods.
let arr = NSArray(object: quetion.optionsArray!).
But I am getting only one object. How can I get my array values same as previous from this string array?
I don't know about the actual type of the "option" in your code, so I set up a fake Elem struct to represent it. The remaining logic is independent of the type as long as you provide a conversion logic to and from String.
struct Elem {
// let's say this is your element type in your array
let foo: Int;
}
extension Elem: CustomStringConvertible {
var description: String {
// provide a logic to convert your element to string
return "\(foo)";
}
}
let arrayToSave = [
Elem(foo: 1),
Elem(foo: 2),
Elem(foo: 3)
]
extension Elem {
init(string: String) {
// provide a function to construct your element type from a string
self.init(foo: Int(string)!)
}
}
let stringToSave = arrayToSave.map { $0.description }.joined(separator: "|")
// save this string
// at some point retrieve it from database, which hopefully same as the saved one
let retrivedString = stringToSave;
let retrivedArray = retrivedString.split(separator: "|").map { Elem(string: String($0)) }
print(retrivedArray) // [1, 2, 3]
Here below is my array inserting into database (TestQuestion) as a
String :
let testQuestionModel : TestQuestion = NSEntityDescription.insertNewObject(forEntityName: "TestQuestion", into: AppDelegate.getContext()) as! TestQuestion
testQuestionModel.optionsArray = "\(question["options"] as! NSArray)"
No, and No.
You are using -description method of an array to save it. Clearly no.
What's wrong? Apple can't affirm that in next OS release, it won't add an extra character. In some more complex cases, it's added <NSArray <0x address> or stuff similar like that.
Suggestion 1:
Modify your entity to have an ARRAY (or usually a Set) of String.
Learn about Core-Data relationship (but that's clearly a DataBase basic knownledge). A relationship one to many should be the thing to do.You could even keep in memory what were the choices, by adding for creating the entity Options, with a String property name (name of the option), another one boolean isChecked, etc.
Suggestion 2:
If you have a limited number of options (like says one to 5), add 5 options string to your entity, and iterate to set them
testQuestionModel.option1 = question["option"][0]
testQuestionModel.option2 = question["option"][1] (if it's of course not out of range for the array)
...
Suggestion 3:
Not really recommended (in my opinion it's missing the whole advantage of the database, especially fetch and predicates, on previous sample you could fetched easily which options were checked), but if you still want to save them as a String, save them as JSON (ie. stringified).
In pseudo code (I'm not sure about the exact syntax, there are no fail safe like try/catch, optional/wrapping):
let options = questions["options"] as [String]
let jsonData = JSONSerialization.data(withJSONObject: (question["options"], options:[])
let jsonString = String.init(data:jsonData encoding:.utf8)
To retrieve them:
let options = JSONSerialization.jsonObject(with data: myJSONString.data(encoding:.utf8), options:[]) as [String]
done using Library SwiftyJSON.
if let dataFromString = yourString?.data(using: String.Encoding.utf8, allowLossyConversion: false) {
do{
let json = try JSON(data: dataFromString)
print(json)
let arrayValue = json.rawValue as! NSArray
print(arrayValue)
} catch{
print(error.localizedDescription)
}
}
Source: https://github.com/SwiftyJSON/SwiftyJSON
I will try to explain the scenario as best as I can.
Lets say if viewControllerA tells the model to hold some data(for example data from a json response) and then viewControllerD(or any other viewcontroller) needs the data, how do i access the data from the model. Creating an instance of the model in viewControllerD creates a fresh instance without any data.
Below code explains the scenario.
ViewControllerA
let userdetails = UserDetails(json: self.userDetailsList!)
userdetailsarray.append(userdetails) //a global array
//Model
class UserDetails: NSObject {
var name : String?
var profession : String?
var id: String?
init(json: NSDictionary) {
let name = json["name"] as? String
let profession = json["profession"] as? String
let id = json["id"] as? String
self.name = name
self.profession = profession
self.id = id
super.init()
}
}
Possible Solution I know: Creating a global variable
var userdetailsarray = [UserDetails]
and appending UserDetails(model) into this array and using this array across multiple viewControllers. An alternative solution could be the model class being singleton.
I am looking for a more optimistic solution. Thankyou
If the data is more make it encrypted using NSKeyedArchiever and store it in user defaults. Whenever you required the data you can fetch it from user defaults and un archive it using NSKyedUnarchiever.
Note: This is the best solution only if you want to persist the data.