I've an application when users can save their ECG. I want to split Realm usage by user. I'm developing a user.realm (referenced as _realmUsers) to save all the users that login on application, and a userID.realm (referenced as _realm and They will be once per user that login on application) where user saves his data. How to achieve that? Because if I write things like
Initialization of both Realms
+ (void)initializeRealmForUsers
{
// Open the encrypted Realm file
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
URLByAppendingPathComponent:USER_REALM]
URLByAppendingPathExtension:#"realm"];
NSError *err;
RLMRealm *realmUser = [RLMRealm realmWithConfiguration:config error:&err];
if(err == nil && realmUser != nil)
[sharedInstance setRealmUsers:realmUser];
}
+ (void)initializeDataRealmWithUserID:(NSString*)userUUID
{
// Open the encrypted Realm file
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.fileURL = [[[config.fileURL URLByDeletingLastPathComponent]
URLByAppendingPathComponent:userUUID]
URLByAppendingPathExtension:#"realm"];
NSError *err;
RLMRealm *realmUser = [RLMRealm realmWithConfiguration:config error:&err];
if(err == nil && realmUser != nil)
[sharedInstance setRealmUsers:realmUser];
[[RealmManager sharedInstance] deleteIncompleteExam];
}
Creation of User:
- (User *)createUserWithData:(NSDictionary *)data
{
[_realmUsers beginWriteTransaction];
User *newUser = [User new];
[newUser setEmail:[data objectForKey:USER_EMAIL]];
[newUser setPassword:[CryptoUtils MD5String:[data
objectForKey:USER_PASSWORD]]];
[newUser setLogged:#NO];
[_realmUsers addOrUpdateObject:newUser];
[_realmUsers commitWriteTransaction];
return newUser;
}
Creation of Patient, a record type of the second realm (the specific-user one):
- (Patient *)createPatientWithData:(NSDictionary *)data forUser:(BOOL)forUser
{
Patient *newPatient;
if([[RealmManager sharedInstance] getSelfPatient] == nil)
newPatient = [Patient new];
else
newPatient = [[RealmManager sharedInstance] getSelfPatient];
[_realm beginWriteTransaction];
[newPatient setFirstName:[data objectForKey:PATIENT_FIRSTNAME]];
[newPatient setLastName:[data objectForKey:PATIENT_LASTNAME]];
[newPatient setSelfPatient:forUser];
[_realm addOrUpdateObject:newPatient];
[_realm commitWriteTransaction];
return newPatient;
}
and Login:
- (User *)loginWithUsername:(NSString *)email password:(NSString *)password andData:(NSDictionary *)userData
{
User *user = [self userWithUsername:email];
if(user == nil){
NSMutableDictionary *mutableData = [NSMutableDictionary new];
[mutableData addEntriesFromDictionary:userData];
[mutableData setObject:password forKey:USER_PASSWORD];
user = [self createUserWithData:mutableData];
} else{
[_realmUsers beginWriteTransaction];
[user setPassword:[CryptoUtils MD5String:password]];
[_realmUsers commitWriteTransaction];
}
[_realmUsers beginWriteTransaction];
[user setLogged:#YES];
[_realmUsers commitWriteTransaction];
[RealmManager initializeDataRealmWithUserID:[user uuid]];
[self createPatientWithData:#{PATIENT_FIRSTNAME : [userData objectForKey:PATIENT_FIRSTNAME], PATIENT_LASTNAME : [userData objectForKey:PATIENT_LASTNAME]} forUser:YES];
return user;
}
After Login, my Calls Like [User allObjects] (user must be on user.realm) [Patient allObject](patients must be on userID.realm) gives me empty result.
You can access datas from different realm by
[User allObjectsInRealm: user.realm]
[User objectsInRealm:user.realm where:#""];
Related
I've added FMDB/SQLCipher (2.7.5) to my app by cocoapods.
use_frameworks!
pod 'FMDB/SQLCipher'
And perform the code bellow:
- (void)decryptDBAtPath:(NSString *)path withKey:(NSString *)key {
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
[queue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
BOOL result = [db setKey:key];
NSLog(#"decrypt db %#", result ? #"success" : #"failed");
//it always return YES.
NSString *sql = #"SELECT * FROM PTypeImage";
FMResultSet *rs = [db executeQuery:sql];
NSDictionary *dic = [rs resultDictionary];
NSLog(#"query test with result : %#", dic);
//query can not by executed. 'file is not a database'.
}];
}
It's always return decrypt success, but query failed.
Any wrong in my code?
I also tryed encryption by FMDB. It seems failed. Returns encrypt success, but can be open without key.
- (void)enryptDBAtPath:(NSString *)path withKey:(NSString *)key {
FMDatabase *db = [FMDatabase databaseWithPath:path];
if ([db open]) {
BOOL result = [db rekey:key];
//always return YES.
NSLog(#"encrypt db %#", result ? #"success" : #"failed");
[db close];
}
}
I am using REALM version 0.98.1 (For Objective C), and sometimes getting "Bad Realm file header" exception while compressing DB size on app launch.
Below is the method calling sequence in AppDelegate application didFinishLaunch....
[self setDefaultConfigrutaionForRealm];
[self vacuumRealm];
Below is the code to Configure Realm:
+(void)setDefaultConfigrutaionForRealm{
RLMRealmConfiguration * defCongfig = [RLMRealmConfiguration defaultConfiguration];
defCongfig.path = REALM_PATH(REALM_FILE_NAME);
defCongfig.schemaVersion = SCHEMA_VERSION;
[RLMRealmConfiguration setDefaultConfiguration:defCongfig];
}
And below is the code to VacuumRealm (Compress DB size):
+ (void)vacuumRealm {
#try{
#autoreleasepool {
RLMRealm *realm = [RLMRealm defaultRealm];
NSString *realmPath = [realm path];
NSLog(#"vacuumRealm realmPath = %#", realmPath);
long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:realmPath error:nil][NSFileSize] longLongValue];
NSLog(#"vacuumRealm ENTER filesize = %llu", fileSize);
//
NSError *err;
BOOL success;
NSDate *startDate = [NSDate date];
NSString *copyPath = [realmPath stringByAppendingString:#".copy"];
[[NSFileManager defaultManager] removeItemAtPath:copyPath error:&err];
success = [realm writeCopyToPath:copyPath error:&err];
if (success) {
success = [[NSFileManager defaultManager] removeItemAtPath:realmPath error:&err];
if (success) {
success = [[NSFileManager defaultManager] copyItemAtPath:copyPath toPath:realmPath error:&err];
if (success) {
[[NSFileManager defaultManager] removeItemAtPath:copyPath error:&err];
NSDate *endDate = [NSDate date];
NSTimeInterval executionTime = [endDate timeIntervalSinceDate:startDate];
NSLog(#"vacuumRealm cleanup took %f ms", executionTime);
}
}
}
//
fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:realmPath error:nil][NSFileSize] longLongValue];
NSLog(#"vacuumRealm EXIT filesize = %llu", fileSize);
}
}
#catch (NSException *exception) {
NSLog(#"Inside vacuumRealm exception = %#",exception.description);
}
#finally {
}
}
While debugging I observed realm path has been configured properly in "setDefaultConfigrutaionForRealm" method (Attached Screen shot for reference), but once "vacuumRealm" method is called there I am getting "Bad Realm file header (#1)" on below line :
RLMRealm *realm = [RLMRealm defaultRealm];
any help to resolve this Exception will really be helpful.
Thanks in advance.
You're creating a Realm instance (RLMRealm *realm = [RLMRealm defaultRealm];) and deleting the file from under it without releasing the instance. This will cause problems like the corruption you're seeing because you're modifying the file while Realm is still accessing it.
Here's an updated version of your method (omitting the debug logs and err since it wasn't being used):
__block BOOL copySuccess = NO;
NSString *realmPath = [[RLMRealmConfiguration defaultConfiguration] path];
NSString *copyPath = [realmPath stringByAppendingString:#".copy"];
#autoreleasepool {
[[NSFileManager defaultManager] removeItemAtPath:copyPath error:nil];
copySuccess = [[RLMRealm defaultRealm] writeCopyToPath:copyPath error:nil];
}
if (copySuccess && [[NSFileManager defaultManager] removeItemAtPath:realmPath error:nil]) {
[[NSFileManager defaultManager] moveItemAtPath:copyPath toPath:realmPath error:nil];
}
Also, whatever exceptions are being thrown in this process are not designed to be recoverable. So the only safe thing to do in the #catch block is to abort. Or not have a #try/#catch clause at all.
I am having an existing coredata ios 7 project. I want to encrypt my sqlLite file. While implementing sqlchipher via EncryptedStore I am facing crash when I want to delete an entity say E1 which has one to many and relationship with other entity say E2 with delete rule cascade.
Getting log
'NSInternalInconsistencyException', reason: '(null)'
If I delete E2 individually then its not crashing at save context.
With out sqlcipher is working fine.
Entity Delete Rule
code for persistence store and manage object context
+ (NSPersistentStoreCoordinator *)persistentStoreCoordinatorWithSqliteName: (NSString*)userFolderName fromSqliteFileName:(NSString*) fromSqliteFileNameWithOutExtension shouldCopySQLiteFromBundle:(BOOL)shouldCopyFromBundle withDataModelName:(NSString*)dataModelName
{
static NSPersistentStoreCoordinator *coordinator = nil;
static dispatch_once_t tokenGRC;
dispatch_once(&tokenGRC, ^{
// get the model
NSManagedObjectModel *model = [self managedObjectModelWithName:dataModelName];
// get the coordinator
coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSString *sqliteExtension = #"sqlite";
NSString *sqliteFileWithExtension = [[NSString alloc]initWithFormat:#"%#.%#",fromSqliteFileNameWithOutExtension,sqliteExtension];
if (shouldCopyFromBundle == YES) {
NSError *error = nil;
[self copySqliteFromFileName:fromSqliteFileNameWithOutExtension toUserFolder:userFolderName withError:&error];
}
else
{
NSFileManager *defaultManager = [NSFileManager defaultManager];
NSArray* appDocumentDirList= [defaultManager URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask];
// Append the bundle ID to the URL for the
// Application Support directory
NSURL *dirPath = [[appDocumentDirList objectAtIndex:0] URLByAppendingPathComponent:userFolderName];
NSError *error = nil;
// If the directory does not exist, this method creates it.
// This method is only available in OS X v10.7 and iOS 5.0 or later.
if (![defaultManager createDirectoryAtURL:dirPath withIntermediateDirectories:YES
attributes:nil error:&error])
{
NSAssert2(NO, #"%s Directory could not be created %#",__func__,[error userInfo]);
// Handle the error.
}
}
NSString *customUserDirectory = [[NSString alloc]initWithFormat:#"%#/%#",userFolderName,sqliteFileWithExtension];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:customUserDirectory];
NSError *error = nil;
// [[NSFileManager defaultManager] removeItemAtURL:databaseURL error:&error];
NSDictionary *options = #{
EncryptedStorePassphraseKey : SampleValue,
// EncryptedStoreDatabaseLocation : databaseURL,
NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES
};
NSPersistentStore *store = [coordinator
addPersistentStoreWithType:EncryptedStoreType
configuration:nil
URL:storeURL
options:options
error:&error];
// coordinator = [EncryptedStore makeStoreWithOptions:options managedObjectModel:model];
NSAssert(store, #"Unable to add persistent store!\n%#", error);
});
return coordinator;
}
+(A3ManagedObjectContext*)managedObjectContextFromCurrentUser:(NSString*)userName withConcurrencyType:(NSManagedObjectContextConcurrencyType)concurrencyType shouldCopySQLiteFromBundle:(BOOL)shouldCopyFromBundle withSqliteMode:(A3SqliteMode)sqliteMode
{
A3DataFetchMode dataFetchMode = [A3WebUtility currentDataFetchMode];
NSString *fromSqliteName = nil;
NSString *toFolderName = nil;
if (dataFetchMode == A3DataFetchModeOnline) {
fromSqliteName = #"DataBase";
toFolderName = userName;
}
else
{
fromSqliteName = #"DataBase-Demo";
toFolderName = #"DemoUser";
}
NSString *dataModelName = [self dataModelNameForSqliteMode:sqliteMode];
NSPersistentStoreCoordinator *coordinator = [A3ManagedObjectContext persistentStoreCoordinatorWithSqliteName:toFolderName fromSqliteFileName:fromSqliteName shouldCopySQLiteFromBundle:shouldCopyFromBundle withDataModelName:dataModelName];
if (coordinator != nil) {
A3ManagedObjectContext *tempManagedContext = [[A3ManagedObjectContext alloc]initWithConcurrencyType:concurrencyType];
[tempManagedContext setPersistentStoreCoordinator:coordinator];
tempManagedContext.sqliteMode = sqliteMode;
return tempManagedContext;
}
else
{
return nil;
}
}
I want to access my address book contacts in my app,i am successfully able to retrieve all contacts from my address book but if sync my i-cloud account in my iPad,then my address book get updated from my i-cloud contacts also,and if now i access my contacts it results in crash.
Kindly help me ,i am completely stuck don't know what to do.
I can easily access the address book contacts but once it get synced from i-cloudand after that i fetch the address book,it results in crash and give me bad excess error.
Here is the code that i used to fetch contacts.
+(NSArray *)getAllContacts
{
CFErrorRef *error = nil;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
__block BOOL accessGranted = NO;
if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
accessGranted = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
else { // we're on iOS 5 or older
accessGranted = YES;
}
if (accessGranted) {
#ifdef DEBUG
NSLog(#"Fetching contact info ----> ");
#endif
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
NSMutableArray* items = [NSMutableArray arrayWithCapacity:nPeople];
for (int i = 0; i < nPeople; i++)
{
MContact *contacts = [MContact new];
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
//get First Name and Last Name
contacts.firstName = (__bridge NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
contacts.lastName = (__bridge NSString*)ABRecordCopyValue(person, kABPersonLastNameProperty);
if (!contacts.firstName) {
contacts.firstName = #"";
}
if (!contacts.lastName) {
contacts.lastName = #"";
}
NSMutableArray *contactEmails = [NSMutableArray new];
ABMultiValueRef multiEmails = ABRecordCopyValue(person, kABPersonEmailProperty);
for (CFIndex i=0; i<ABMultiValueGetCount(multiEmails); i++) {
CFStringRef contactEmailRef = ABMultiValueCopyValueAtIndex(multiEmails, i);
NSString *contactEmail = (__bridge NSString *)contactEmailRef;
[contactEmails addObject:contactEmail];
// NSLog(#"All emails are:%#", contactEmails);
}
if([contactEmails count]==0){
}
else{
[contacts setemails:contactEmails];
[items addObject:contacts];
}
#ifdef DEBUG
#endif
}
return items;
} else {
#ifdef DEBUG
NSLog(#"Cannot fetch Contacts :( ");
#endif
return NO;
}
}
Did you found exactly where in your code the crash append ? If it is in your "for (int i = 0; i < nPeople; i++)" loop, may be you have an issue on 1 contact only...
PS 1, you should remove line 7 : ....ABAddressBookCreateWithOptions, as you need to check for authorization first....
PS 2, did you really need only contacts from default source ? What about if user have also contact from Lotus Notes or other gmail....
I see one issue but not sure it will solve your crash... You should replace your _bridge by _bridge_transfer :
contacts.firstName = (__bridge_transfer NSString*)ABRecordCopyValue(person,kABPersonFirstNameProperty);
Do the same for lastname....
If it did not solve the crash, I would suggest to try isolate the "person" in your AB witch crash, and try to skip it in your "for loop". This should help us to know if it is only a problem with this person or if it is related to the number of person to load. In my ABLocation App, I know some users have >500 contacts, and they have no PB...
I have the following code that I'm trying to use to set the AIM and Skype properties of a new contact I'm adding to Address Book:
ABRecordRef record = ABPersonCreate();
CFErrorRef an_error = NULL;
ABMutableMultiValueRef multi_aim = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(multi_aim, #"ExampleNick", kABPersonInstantMessageServiceAIM, NULL);
ABRecordSetValue(record, kABPersonInstantMessageProperty, multi_aim, &an_error);
ABMutableMultiValueRef multi_skype = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(multi_skype, #"example.nick", kABPersonInstantMessageServiceSkype, NULL);
ABRecordSetValue(record, kABPersonInstantMessageProperty, multi_skype, &an_error);
if (an_error != NULL) {
NSLog(#"Error while creating person");
}
CFErrorRef error = NULL;
ABAddressBookRef address_book = ABAddressBookCreateWithOptions(NULL, &error);
ABAddressBookRequestAccessWithCompletion(address_book, ^(bool granted, CFErrorRef error) {
if (!granted) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Unable to access address book"
delegate:self
cancelButtonTitle:nil
otherButtonTitles:nil];
[alert show];
} else {
BOOL is_added = ABAddressBookAddRecord(address_book, record, &error);
if (is_added) {
NSLog(#"Address book entry added");
} else {
NSLog(#"ERROR adding address book entry: %#", error);
}
error = NULL;
BOOL is_saved = ABAddressBookSave(address_book, &error);
if (is_saved) {
NSLog(#"Saved address book");
} else {
NSLog(#"ERROR saving address book: %#", error);
}
}
CFRelease(record);
CFRelease(multi_aim);
CFRelease(multi_skype);
CFRelease(address_book);
});
(I'm also adding first and last names but left that out of the example for brevity sake)
When the record gets added everything but the AIM and Skype fields get populated. What am I doing wrong?
Instants are multivalue properties of type kABMultiDictionaryPropertyType. Snippet inserting one skype id into ABRecordRef goes below. You can insert several instants into one ABRecordRef.
ABRecordRef record = ABPersonCreate();
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
(NSString*)kABPersonInstantMessageServiceSkype, (NSString*)kABPersonInstantMessageServiceKey,
#"YourSkypeIDGoesHere", (NSString*)kABPersonInstantMessageUsernameKey,
nil
];
CFStringRef label = NULL; // in this case 'IM' will be set. But you could use something like = CFSTR("Personal IM");
CFErrorRef error = NULL;
ABMutableMultiValueRef values = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
BOOL didAdd = ABMultiValueAddValueAndLabel(values, (__bridge CFTypeRef)(dictionary), label, NULL);
BOOL didSet = ABRecordSetValue(record, kABPersonInstantMessageProperty, values, &error);
if (!didAdd || !didSet) {
CFStringRef errorDescription = CFErrorCopyDescription(error);
NSLog(#"%s error %# while inserting multi dictionary property %# into ABRecordRef", __FUNCTION__, dictionary, errorDescription);
CFRelease(errorDescription);
}
CFRelease(values);