DBAccess: Get array of any one property value - ios

I have dbobject like:
#import <Foundation/Foundation.h>
#import <DBAccess/DBAccess.h>
#interface GroupMember : DBObject
#property (strong) NSString *firstname;
#property (strong) NSString *lastname;
#property (strong) NSString *_id;
#end
How can I get an array of all group member's firstname? Thanks in adv.

Because you are not dealing with SQL but with whole objects, this requires a little bit of working round the problem, but it is possible.
NSDictionary* resultsGroupedByFirstName = [[GroupMember query] groupBy:#"firstname"];
NSArray* names = resultsGroupedByFirstName.allKeys;
This is a fairly expensive call as it is having to do a fair amount of work in the background. Although it is optimised slightly by using an index to detect changes in the column.
This should do the trick.
NOTE:
DBAccess v1.6.7 now has a distinct call thanks to your question and feedback. http://www.db-access.org/downloads

Related

Why should I not use object_setIvar to set properties

I have a dictionary containing data for user from a REST endpoint. Here is my user class
#import <Foundation/Foundation.h>
#interface User : NSObject
#property (strong, nonatomic) NSString *uid;
#property (strong, nonatomic) NSString *email;
#property (strong, nonatomic) NSString *firstName;
#property (strong, nonatomic) NSString *lastName;
#property (assign, nonatomic) int status;
#end
I have a method to set all properties
/*
* set properties
*/
- (void)setPropertiesWithDictionary:(NSDictionary *)dictionary{
for(NSString *key in dictionary){
object_setIvar(self,
class_getInstanceVariable([self class], [[#"_" stringByAppendingString:key] UTF8String]),
dictionary[key]);
}
}
Instead of doing something like
[user setUid:#dictionary[#"uid"]];
I want to call my method like this
[user setPropertiesWithDictionary: dictionary];
Just wondering if implementing object_setIvar this way is fine. If not - Would be really great if you can explain why. Thanks in advance.
Do whatever you like, but why reinvent the wheel when key value coding (KVC) already exists? Just call this method:
https://developer.apple.com/documentation/objectivec/nsobject/1417515-setvaluesforkeyswithdictionary?language=objc
KVC does what you're trying to do, but it does it a lot better than you're likely to do it.
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/index.html
I think your problem might occur with "int status", because dictionary[# "status"] is not of type int.
In your code implementation,user.status = dictionary[# "status"],this result is unpredictable.
Unless you make a type judgment, user.status = [dictionary[# "status"]intValue];
I recommend a third-party framework on github called MJExtension that fulfills your needs.You can look at the source code.

ios/objective-c/core-data: How to show relationship as property in NSManagedObjects

I have two entities and have just created a 1:1 relationship between them. There are subclasses for the entities but they have a lot of code in them at this point so I don't want to use Xcode to automatically generate new NSManagedObject subclasses.
Instead, I thought I could just reference the relationship with a property in each one. I did that and it seemed to work for a while but now it is throwing mysterious errors and I can't seem to get rid of them. I have imported the reciprocal of each one but it is not helping. Can anyone recommend what I should do? Many thanks in advance.
Subclassed NSManagedObjects (simplified)
//Items.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Notes.h"
#interface Items : NSManagedObject
#property (nonatomic, retain) NSNumber *iid;
#property (nonatomic, retain) NSString *item;
//this is relationship
#property (nonatomic, retain) Notes *note;
//above throws error Unkown Type Name 'Notes'
#end
//Notes.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Items.h"
#interface Notes : NSManagedObject
#property (nonatomic, retain) NSNumber * nid;
#property (nonatomic, retain) NSString * note;
//this is relationship
#property (nonatomic, retain) Items *item;
//above throws error Unkown Type Name 'Item'
#end
This is similar to relationship
In Objective-C you can't use a type before it is declared. To fix this you can use a forward declaration of your classes by putting these lines below the #import statements and above the first #interface.
#class Items;
#class Notes;
If your posted code is not representative of your actual file structure (which I assume it isn't), you'll have to put the #class statement for Items in the Notes.h file and the #class statement for Notes in the Items.h file.

Relating two objects in DBAccess

I'm using dbaccess for my iOS project.
How can I pass an array to dbaccess object? For example:
I have dbobject like:
#interface Member : DBObject
#property (strong) NSString* firstname;
#property (strong) NSString* lastName;
#end
#interface Group : DBObject
#property (strong) NSString* groupName;
#property (strong) NSString* adminName;
#property (strong) Member* members;
#end
For this group, it has 4 member than How can I store all group members and group detail in one object and also how to retrieve them?
Thanx in adv.
To answer the question I have re-modelled and provided an example below of how you would go about creating a one-to-one relationship between these two objects.
This problem stems from the fact that there is no such thing as a typed array in Objective-c. When there is, we will look at re-implementing how the interface works.
I have moved the Group object into the member, as a member belongs to a group, and then added a members method to the Group object to look backwards to the child objects.
#interface Member : DBObject
#property (strong) NSString* firstname;
#property (strong) NSString* lastName;
#property (strong) Group* group;
#end
#interface Group : DBObject
#property (strong) NSString* groupName;
#property (strong) NSString* adminName;
- (DBResultSet*)members;
#end
And the implementations now look like this.
#implementation Member
#dynamic firstname,lastName, group;
#end
#implementation Group
#dynamic adminName, groupName;
- (DBResultSet *)members {
/* we achieve one-to-many relationships by querying the objects in the
manner a relational database would commonly be structured */
return [[[Member query] whereWithFormat:#"group = %#", self] fetch];
}
- (void)entityDidDelete {
/* when you create tight bonds, you may well wish to create 'trigger' methods
to clean data and keep the integrity in good order */
for (Member* member in [[[Member query] whereWithFormat:#"group = %#", self] fetch]) {
member.group = nil;
[member commit];
}
}
#end
Now there is nothing stopping you creating an array as a property type, and storing the Id values within it. But that is not as clean, and requires you to maintain it, whereas if you a looking for FK values, this requires no maintenance and you can create lovely logic to stop the deletion of objects if it is related to others, without having to hydrate lots of objects and then look inside arrays.
Plus you get the beautiful option of using the object dot notation to navigate the strongly typed relationships from the Person object.
NSString* admin = person.group.adminName;
Also, when you added the Group object into Member:
#property (strong) Group* group;
DBAccess automatically created an index in SQLite for the group property, and prioritises its importance within the cache, as objects which are linked this way are more likely to be accessed.
Hope this helps,
Adrian

Xcode is making me use temporary variable...?

Newbie to Objective-C....
I have a real simple .h file:
#interface IdentityManager : NSObject
#property (nonatomic, weak) NSString *username;
#property (nonatomic, weak) NSString *password;
#property (nonatomic, weak) NSString *connString;
#property (nonatomic, weak) NSString *token;
#end
And I want need to grab text from some text fields in another object to load into an Identity object:
self.identity.username = self.usernameTextField.text;
self.identity.password = self.passwordTextField.text;
Yep, it's a login page. Problem is that the username would not be set. After hours trying to find out why, I found that putting the value of self.usernameTextField.text into a local variable and passing the value of that to the Identity object worked:
NSString *tempUsername = self.usernameTextField.text;
self.identity.username = self.tempUsername;
self.identity.password = self.passwordTextField.text;
I have no idea why this would be. I can only guess that all my messing around has somehow left a trace of some old references in Xcode's cache somewhere.
More likely, I'm an idiot. Better, I'm still learning.
Should I be using NSMutableString?
I think something similar is happening again elsewhere. Use of a temp variable helping to achieve my goal.
Any thoughts anyone?
I don't really think your second solution actually fixes the issue. It's probably something else.
Though it's really wrong for a manager class to have a weak reference. In OOP, classes and especially managers should own the object they have as property.
So you should use strong references instead of weak:
#property (nonatomic, strong) NSString *username;
Additionally, you don't want any changes outside the class to modify the variable, so you should be passing a copy of the object:
NSString *username = self.usernameTextField.text;
self.identity.username = [username copy];
Alternatively, you can declare the property as copy instead of strong and you don't have to worry about copying the string every time you set it. (Credit to albertamg)
#property (nonatomic, copy) NSString *username;

Core Data EXC_BAD_ACCESS for non-zero integer values

I have two core data models with int64_t properties. One of them works fine while the other throws EXC_BAD_ACCESS when I try to assign a non-zero value to the integer field. I've read the answers that say to recreate the NSManagedObject child class and I have done with no success. The broken class looks like this:
#interface NoteObject : NSManagedObject
#property (nonatomic) int64_t remoteID;
#property (nonatomic) int64_t remoteArticleID;
#property (strong, nonatomic) ArticleObject *article;
#property (strong, nonatomic) NSString *status;
#property (strong, nonatomic) NSString *token;
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *noteContent;
#property (strong, nonatomic) NSDate *pubDate;
#property (strong, nonatomic) NSDate *modDate;
#end
#implementation NoteObject
#dynamic remoteID;
#dynamic remoteArticleID;
#dynamic article;
#dynamic status;
#dynamic token;
#dynamic title;
#dynamic noteContent;
#dynamic pubDate;
#dynamic modDate;
#end
The offending line is in this block:
_noteObject = [NSEntityDescription insertNewObjectForEntityForName:#"Note" inManagedObjectContext:self.managedObjectContext];
_noteObject.remoteArticleID = 0; // this works
_noteObject.remoteArticleID = 1; // this crashes
What really has me stumped is that in another model I have the same fields with the same types and they will accept non-zero values without any trouble:
bookmarkObject = [NSEntityDescription insertNewObjectForEntityForName:#"Bookmark" inManagedObjectContext:self.managedObjectContext];
bookmarkObject.remoteArticleID = 0; // this works
bookmarkObject.remoteArticleID = 1; // this works, too
Is there anything in my .xcdatamodeld file that could be causing this?
EDIT
My data models look like this:
I had exactly the same problem.
It appears that xcode (or perhaps the compiler, or perhaps the two between them) sometimes gets confused when you manually edit properties in the NSManagedObject - it ends up treating our integers as pointers and trying to access memory directly - hence the EXC_BAD_ACCESS.
Anyway, as this question explains: SO Question, the solution is to delete your old class (obviously copy out any custom code so you can paste it back again later) and then get xcode to regenerate it for you (select the entity in the data model and select "Editor / Create NSManagedObject subclass..."). In the dialogue that appears, make sure "Use scalar properties for primitive data types" is ticked.
You may have to manually edit the resulting class to turn some non scalar properties back into objects (I had a date object which it turned into something other than NSDate - I forget exactly what, but it accepted the manually made edit back to NSDate).
It worked for me. Hope it works for you.
Ali
Well, in case anyone else is having this issue, I never found a satisfactory answer for why one entity was working and the other wasn't. My workaround was to refactor the properties to use NSNumber wrappers instead of primitive int64_t values.
#property (strong, nonatomic) NSNumber *remoteID;
#property (strong, nonatomic) NSNumber *remoteArticleID;
Of course, that means boxing/unboxing the integer values.
_noteObject.remoteArticleID = [NSNumber numberWithInt:1];
int intVar = [_noteObject.remoteArticleID intValue];
In your model file, check that the entity's "Class" property is set to the appropriate class, and not the default NSManagedObject.
If you leave it as NSManagedObject, Core Data will create properties itself on a custom NSManagedObject subclass it generates itself, rather than using your own subclass. Most getters and setters will appear to work, but you may have issues with non-boxed primitive properties and custom getters and setters.

Resources