Core Data - Iterating through the attributes of a NSManagedObject - ios

I am a noob at core data, so here goes.
I have a core data object, for example student.
The student has mane attributes, like name, age, dob, address, and so on.
Now I need to display all these attributes into a table view.
Is there any easy way to iterate through the object student? Or do I have to convert the data into some type of dictionary?
If I need to convert it to a dictionary is there any support for it? Or do I have to write the function my self.
Many thanks for your help,
Stephen

Here's a very simple way to iterate over an NSManagedObject:
NSEntityDescription *entity = [myManagedObject entity];
NSDictionary *attributes = [entity attributesByName];
for (NSString *attribute in attributes) {
id value = [myManagedObject valueForKey: attribute];
NSLog(#"attribute %# = %#", attribute, value);
}
The clues for how to do this (plus lots more) comes from Ole Bergmann's blog: http://oleb.net/blog/2011/05/inspecting-core-data-attributes/

If you are like me and are trying to solve this problem in Swift here is how I did it.
SWIFT 3:
let request:NSFetchRequest<ENTITY_NAME>
if #available(iOS 10.0, *){
request = ENTITY_NAME.fetchRequest() as! NSFetchRequest<ENTITY_NAME>
}else{
request = NSFetchRequest<ENTITY_NAME>(entityName: "ENTITY_NAME")
}
do{
let entities = try YOUR_MOC.fetch(request)
for item in entities{
for key in item.entity.attributesByName.keys{
let value: Any? = item.value(forKey: key)
print("\(key) = \(value)")
}
}
}catch{
}
Swift 2:
let request = NSFetchRequest(entityName: "ENTITY_NAME")
do{
let entities = try YOUR_MOC.executeFetchRequest(request) as! [YOUR_ENTITY]
for item in entities{
for key in item.entity.attributesByName.keys{
let value: AnyObject? = item.valueForKey(key)
print("\(key) = \(value)")
}
}
}catch{
}
If you want to get relationship entities key/values as well then you should use propertiesByName instead of attributesByName like so:
SWIFT 3:
for key in item.entity.propertiesByName.keys{
let value: Any? = item.value(forKey: key)
print("\(key) = \(value)")
}
SWIFT 2:
for key in item.entity.propertiesByName.keys{
let value: AnyObject? = item.valueForKey(key)
print("\(key) = \(value)")
}
Just remember to be careful with the value since it is an NSManagedObject.

Related

Converting objective-c codes to swift 4

it's been 1.5 years i've been writing in obj-c, its a good language.. so i saw a medium article today about swift, really looking onto it, right now i am trying to convert all my obj-c codes to swift, fortunately everything is done expect for this part..
NSString *path = [[NSBundle mainBundle] pathForResource:#"list" ofType:#"plist"];
NSArray *plistData = [NSArray arrayWithContentsOfFile:path];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"english ==[c] %#", self.userQuery.text]; // something like "Abbey"
NSArray *filtered = [plistData filteredArrayUsingPredicate:filter];
NSLog(#"found matches: %# : %#", filtered,[filtered valueForKey:#"kurdi"]);
if (filtered.count>0) {
NSDictionary *dic = filtered[0];
self.theMeaningLabel.text = dic[#"kurdi"];
} else {
self.theMeaningLabel.text = #"Yay!‌";
}
i am not being able to properly convert this into the new swift 4, it gives random errors
EDIT
after a few searches, i could just write two lines of code
here's my swift code
var path: String? = Bundle.main.path(forResource: "list", ofType: "plist")
var plistData = [Any](contentsOfFile: path!)
var filter = NSPredicate(format: "english ==[c] %#", searchText)
// something like "Abbey"
var filtered = plistData?.filter { filter.evaluate(with: $0) }
print("found matches: \(filtered) : \(filtered?.value(forKey: "kurdi"))")
if filtered?.count > 0 {
var dic = filtered[0]
// theMeaningLabel.text = dic["kurdi"]
}
else {
//theMeaningLabel.text = "Yay!‌"
}
but getting the
Argument labels '(contentsOfFile:)' do not match any available overloads
Final edit
var path = Bundle.main.path(forResource:"list", ofType: "plist")
var plistData = NSArray(contentsOfFile: path!)
let filter = NSPredicate(format: "english ==[c] %#", searchText)
var filtered = plistData?.filtered(using: filter)
// [error 9:10] no viable alternative at input 'NSLog(#"found matches: %# : %#"'
if filtered?.count > 0 {
var dic = filtered![0]
// theMeaningLabel.text = dic["kurdi"]
}
else {
// theMeaningLabel.text = "Yay!‌"
}
the code above is fine, but one error comes
if filtered?.count > 0 { // here
var dic = filtered![0]
// theMeaningLabel.text = dic["kurdi"]
}
else {
// theMeaningLabel.text = "Yay!‌"
}
getting
Binary operator '>' cannot be applied to operands of type 'Int?' and 'Int'
A literal translation from Objective-C to Swift is not recommended because in many cases it does not take advantage of the improved semantics of Swift.
As Apple removed all path manipulating API from String and added more URL related API to other classes use always URL if possible
let url = Bundle.main.url(forResource: "list", withExtension: "plist")!
The forced unwrapped url is safe since the file is required to be in the bundle. A crash will reveal a design error and must not happen at runtime.
Unlike the Foundation collection types the Swift collection types don't support the implicit property list conversion so the Swift way is to use Data and PropertyListSerialization
let data = try! Data(contentsOf: url)
let dataArray = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [Any]
The forced try! is as safe as forced unwrapping the url for the same reasons.
[Any] is the most unspecified Swift Array. If the array is supposed to be an array of dictionaries write [[String:Any]] or even [[String: String]] if all values are String. If it's supposed to be an array of Strings write [String]. Swift encourages the developer to be as type specific as possible.
An NSPredicate can be replaced with the filter function in Swift. For example an array of strings can be filtered case insensitively with
let filtered = dataArray.filter { $0.compare(searchText, options:.caseInsensitive) == .orderedSame }
If the array contains dictionaries you need to specify the keys (same as NSPredicate)
let filtered = dataArray.filter { ($0["foo"] as! String).compare(searchText, options:.caseInsensitive) == .orderedSame }
Finally the ObjC expression
if (filtered.count > 0) { ...
can be written much more descriptive in Swift
if filtered.isEmpty == false { ...
or
if !filtered.isEmpty { ...
If you are looking for only one item you can even write
if let foundObject = dataArray.first(where: { $0.compare(searchText, options:.caseInsensitive) == .orderedSame }) { ...

Save json dictionary to core data Swift 3

I am able to get the last guest dictionary value in the json array saved into core data as a dictionary using the transformable key value however the other guest dictionary values are not saving. Guest is also it's on entity for now but I was hoping to save the guest as a dictionary since this task doesn't require complex relationships. I'm sure I'm looking through the json, the value type for reservation.guest = [AnyHashable: Any]?Any suggestions would be helpful here is my json response https://pastebin.com/J28myW66, thanks
Note: using Alamofire for the HTTP Request. Also haven't included my entire class here as this is the main part of it. Reservation and Guest are both NSManagedObject classes
let managedObjectContext = CoreDataManager.shared.persistentContainer.viewContext
let reservationEntityDescription = NSEntityDescription.entity(forEntityName: "Reservation", in: managedObjectContext)
let guestEntityDescription = NSEntityDescription.entity(forEntityName: "Guest", in: managedObjectContext)
let reservation = Reservation(entity: reservationEntityDescription!, insertInto: managedObjectContext)
let guest = Guest(entity: guestEntityDescription!, insertInto: managedObjectContext)
let url = "\(serverEndpoint)\(path)"
manager?.request(
url
).responseJSON { responseData in
if(responseData.result.error != nil) {
print(responseData.response)
}
else if responseData.result.value != nil{
let json = JSON(responseData.result.value!)
let content = json["data"]
var reservationArray: [String] = []
if let dates = content.array {
for item in dates {
if let str = item["date_time"].string {
reservationArray.append(str)
print(reservationArray)
}
}
}
for (key,obj) in content {
let guestData = obj["guest"]
let guestDict = guestData.dictionaryObject!
reservation.guest = guestDict
reservation.id = obj["id"].stringValue
reservation.dateTime = obj["date_time"].date
reservation.startTime = obj["start_time"].time
reservation.numOfPeople = obj["number_of_people"].intValue as NSNumber?
reservation.status = obj["status"].stringValue
reservation.tables = obj["tables"].arrayObject as! [NSString]?
reservation.reservationCollections = reservationArray as [NSString]?
guest.id = guestData["id"].stringValue
guest.email = guestData["email"].stringValue
guest.name = guestData["full_name"].stringValue
guest.phone = guestData["phone"].stringValue
guest.notes = guestData["notes"].stringValue
}
print("Reservation to be saved\(reservation)")
print("Guest to be saved: \(guest)")
}
}
do {
try reservation.managedObjectContext?.save()
} catch let error as NSError {
fatalError(error.localizedDescription)
}
do {
try guest.managedObjectContext?.save()
} catch let error as NSError {
fatalError(error.localizedDescription)
}
When your code starts, you create one instance of Guest and one instance of Reservation:
let reservation = Reservation(entity: reservationEntityDescription!, insertInto: managedObjectContext)
let guest = Guest(entity: guestEntityDescription!, insertInto: managedObjectContext)
After that you never create any other instances. In your loop you assign values to this instance:
reservation.guest = guestDict
reservation.id = obj["id"].stringValue
...
guest.id = guestData["id"].stringValue
guest.email = guestData["email"].stringValue
...
But since there's only one instance, only the last pass through the loop gets saved. The first time through the loop you assign values to guest and reservation. Every other time, you overwrite the previous values with new ones.
If you want to save a new instance for every pass through the loop, you need to create new instances every time. Move the let guest = ... and let reservation = ... lines inside the loop.
Firstly you need to make design flow bit generic i.e The HTTP request/response, DataBase Part, Model Part and UI part.
Now create a generic model class for your response,
so that the values will bind in single object.
In you core data sub class your table i.e custom NSManagedObject Class.
First convert the dictionary objects [objects in content.array] into respective model class objects.
In that SBPlayer is a model class
Favourite+CoreDataProperties.swift & Favourite+CoreDataClass.swift are custom NSManagedObject class (auto-generated).
Now with every object, you have mapping respective properties in database table and in custom NSManagedObject class.
Map the values and Save it DataBase.
For example: https://github.com/Abhishek9634/ScoreBoard/blob/master/ScoreBoard/ScoreBoard/SBDBManager.swift
Reference : https://github.com/Abhishek9634/ScoreBoard

FetchRequst issue with data fault

When I was inserting data to one entity of CoreData, All the rows are inserted successfully(Saved).
But when I try to fetch the data using FetchRequest, Only one row of data is coming even if number of rows inserted are 3 or 4 or anything(more than 1).
Remaining rows are not getting fetched. And when I print fetch results,
It says - Error
0:<EquipmentDetails: 0x6000000bad60>
(entity: EquipmentDetails; id: 0xd000000000040000
coredata:/EquipmentDetails/p1> **data:fault>)**
I didn't get what was going in backend of core data?
code for Insertion
func insertEqipToLocalDb()
{
let mobileNo : String = UserDefaults.standard.string(forKey: "phoneNumber")!
let equipDetailsItem = NSEntityDescription.insertNewObject(forEntityName: "EquipmentDetails", into:managedObjContext) as! EquipmentDetails
for (index,item) in array_IDEquip.enumerated()
{
equipDetailsItem.mobileNumber = mobileNo
equipDetailsItem.type = array_typeEquip[index]
equipDetailsItem.name = array_nameEquip[index]
equipDetailsItem.startDate = array_sDateEquip[index]
equipDetailsItem.endDate = array_eDateEquip[index]
equipDetailsItem.equpID = Int16(item)
equipDetailsItem.serviceDatesStr = array_serviceDateEquip[index]
}
do
{
try managedObjContext.save()
UserDefaults.standard.set("AlreadyInstalled", forKey: "statusInstallation")
}
catch
{
Exception.insertExceptionDetails(errorMsg: error as NSError, context: managedObjContext)
}
}
//code for fetching
let request = NSFetchRequest<NSFetchRequestResult>()
let entity = NSEntityDescription.entity(forEntityName:"EquipmentDetails", in: managedObjContext)
request.entity = entity
do
{
let fetchResults = try managedObjContext.fetch(request)
for r in fetchResults
{
typeEquipArray.append((r as AnyObject).value(forKey: "type") as! String)
}
}
catch let error as NSError
{
Exception.insertExceptionDetails(errorMsg: error, context: managedObjContext)
}
On this line:
let equipDetailsItem = NSEntityDescription.insertNewObject(forEntityName: "EquipmentDetails", into:managedObjContext) as! EquipmentDetails
You create one instance. In the loop that follows, you set values for the type, name, etc properties over and over again on that same instance. Then you save changes, which include just that one object. If you want a difference instance of EquipmentDetails for each pass through the loop, you need to create the instance inside the loop.
The "fault" message is not an error unless you tried to access the property values and found that they were not present. It's part of how Core Data works. See the answer that Harshal Valanda linked in the comments for more detail.

JSON parsing and accessing data from Dictionary

{
ac = 0;
storeLocation = "<null>";
storeSizeSqFt = 1000;
tinNumber = testdummy4y58;
wirelessInternet = 0;
}
for the above written response I have written code
func StoreInfo(notification : NSNotification){
if let result = notification.userInfo{
print(result)
if let particularStoreInfo = result["data"] as? NSDictionary{
print(particularStoreInfo)
if let temp = particularStoreInfo["store"] as? NSDictionary{
print(temp)
}
}
}
}
I have successfully traversed inside the dictionary but Now guide me how to save and access details inside Store Dictionary.
To access data from inside an NSDictionary, you can use this method, assuming temp in your question is the dictionary you want to retrieve information from.
temp.objectForKey("ac") // Key can be of type AnyObject
The above will retrieve the information saved under the key "ac".
To store data in an NSDictionary, you must use NSMutableDictionary instead, which contains the method setObject.
Example of saving information:
var value = "This is the string I will save in the dictionary"
var dictionary = NSMutableDictionary()
dictionary.setObject(value, forKey: "Name of the Key")
UPDATED: Changed setValue example to setObject. See #vadian's comment for details.

Like objective-C , how to write "valueForKey" in swift?

NSDictionary dic;
NSMutableArray array;
//Objective-C Code.
array = [dic valueForKey:#"Table"]; //"Table" is key in dictionary
How to write same code in Swift?
There is no more valueForKey: in Swift, but there is a way to use map to achieve a similar, if not same, result.
If the objects in the collection are dictionaries:
let array = dic.map { $0["table"] as? String }
If the objects in the collection are objects:
let array = dic.map{ $0.table }
If you are using Objective-C collection types like NSDictionary in your example, then like others have said, you can still continue to use valueForKey:.
let array = dic.valueForKey("table")
You can try this
let array: AnyObject? = dic["Table"]
Simple eg.
(dicDetail.valueForKey("name") as! String)
You can try this
var dict=NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: NSErrorPointer.null()) as NSDictionary
var temp=dict["contactname"] as String
var tem=dict.valueForKey("contactname") as String
You will better get the idea of how you can deal with Dictionaries in Swift
let employees = json["employees"]! as [[String : AnyObject]]
Next step is to loop the employees array and print the firstName and lastName.
Keep in mind that we are looping an array of dictionaries. Meaning that in order to get the String values from firstName and lastName we need to unwrap the optionals first:
for employee in employees {
let firstName = employee["firstName"]! as String
let lastName = employee["lastName"]! as String
println("employee: \(firstName) \(lastName)")
}
Build, run and you should see:
employee: John Doe
employee: Anna Smith
employee: Peter Jones
Please check this code. It might help in what you need.
var dictData = Dictionary <String,Any>()
var arrDictData = Array<Any>()
// Will store value for key in dictionary.
func storeData ( strName: String , strAge: String ) {
dictData["name"]=strName
dictData["age"]=strAge
println("dictData: \(dictData)")
storeInArray(dictData)
}
// Will store value of dictionary in array.
func storeInArray ( dict : Any ) {
arrDictData.append(dict)
println("arrDictData: \(arrDictData)")
}
// Will return count of array.
func resultArray() -> Int {
println(arrDictData.count)
return arrDictData.count
}
// This function will return value in label.
func arrDictResult( dictKey: String, rowIndex: Int) -> String {
var dataTemp = arrDictData[rowIndex] as Dictionary <String,Any>
var val = dataTemp[dictKey] as String
return val
}

Resources