In the below code, self.theFiles prints the null. Because after calling [self.restClient loadMetadata:#"/"], getFiles method keeps working.
I want getFiles method to wait until [self.restClient loadMetadata:#"/"] finishes to get file list and put the data to self.theFiles object. But after [self.restClient loadMetadata:#"/"], getFiles keeps working.
#synthesize theFiles;
- (void)getFiles:(CDVInvokedUrlCommand*)command
{
if (![[DBSession sharedSession] isLinked]) {
[[DBSession sharedSession] linkFromController:self];
}
else
{
[self.restClient loadMetadata:#"/"];
}
NSLog(self.theFiles); //Prints null
NSLog(#"Finished");
}
#pragma mark DBRestClientDelegate methods
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata {
NSArray* validExtensions = [NSArray arrayWithObjects:#"txt", #"text", nil];
NSMutableArray* newPhotoPaths = [NSMutableArray new];
for (DBMetadata* child in metadata.contents) {
self.theFiles=child.path;
NSString* extension = [[child.path pathExtension] lowercaseString];
if (!child.isDirectory && [validExtensions indexOfObject:extension] != NSNotFound) {
[newPhotoPaths addObject:child.path];
}
}
NSLog(self.theFiles); //Prints the file list
}
Output:
null
Finished
/file1.txt
/file2.txt
..... the file list in my dropbox
Is it possible to work my getFiles method with delegate synchronously?
UPDATE
I updated my code. I want to lock the thread using NSLock but doesn't work. The same result.
#synthesize theFiles;
- (void)getFiles:(CDVInvokedUrlCommand*)command
{
if (![[DBSession sharedSession] isLinked]) {
[[DBSession sharedSession] linkFromController:self];
}
else
{
if ([self.theLock tryLock]) {
[self.restClient loadMetadata:#"/"];
NSLog(self.theFiles); //Prints null
NSLog(#"Finished");
}
}
}
#pragma mark DBRestClientDelegate methods
- (void)restClient:(DBRestClient*)client loadedMetadata:(DBMetadata*)metadata {
NSArray* validExtensions = [NSArray arrayWithObjects:#"txt", #"text", nil];
NSMutableArray* newPhotoPaths = [NSMutableArray new];
for (DBMetadata* child in metadata.contents) {
self.theFiles=child.path;
NSString* extension = [[child.path pathExtension] lowercaseString];
if (!child.isDirectory && [validExtensions indexOfObject:extension] != NSNotFound) {
[newPhotoPaths addObject:child.path];
}
}
NSLog(self.theFiles); //Prints the file list
[self.theLock unlock];
}
Related
I'm trying to implement an iCloudStorage in react-native (0.53.3) with native code.
Right now I have the following:
Header
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#interface iCloudStorage : RCTEventEmitter <RCTBridgeModule>
#end
Source
#import "iCloudStorage.h"
#import <React/RCTEventDispatcher.h>
static NSString* const ICLOUDSTORAGE_PREFIX = #"#com.manicakes.iCloudStorage/";
static NSString* const ICLOUD_STORE_CHANGED = #"ICLOUD_STORE_CHANGED";
static NSString* const kStoreChangedEvent = #"iCloudStoreDidChangeRemotely";
static NSString* const kChangedKeys = #"changedKeys";
#implementation iCloudStorage
RCT_EXPORT_MODULE()
+ (NSString*)appendPrefixToKey:(NSString*)key {
return [NSString stringWithFormat:#"%#%#", ICLOUDSTORAGE_PREFIX, key];
}
+ (NSString*)removePrefixFromKey:(NSString*)key {
if (![key hasPrefix:ICLOUDSTORAGE_PREFIX]) {
return nil;
}
return [key substringFromIndex:[ICLOUDSTORAGE_PREFIX length]];
}
+ (NSDictionary*)storeDictionary {
NSUbiquitousKeyValueStore* store = [NSUbiquitousKeyValueStore defaultStore];
return [store dictionaryRepresentation];
}
+ (NSArray*)allKeysInStore {
return [[iCloudStorage storeDictionary] allKeys];
}
+ (id) getObjectForKey:(NSString*)key {
return [[NSUbiquitousKeyValueStore defaultStore] objectForKey:[iCloudStorage appendPrefixToKey:key]];
}
+ (void) setValue:(NSString*)value forKey:(NSString*)key {
[[NSUbiquitousKeyValueStore defaultStore] setObject:value forKey:[iCloudStorage appendPrefixToKey:key]];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
}
+ (void) removeKey:(NSString*)key {
[[NSUbiquitousKeyValueStore defaultStore] removeObjectForKey:[iCloudStorage appendPrefixToKey:key]];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
}
+ (NSString*) getMergedItemWithKey:(NSString*)key value:(NSString*)value rejecter:(RCTPromiseRejectBlock)reject {
NSDictionary* storedItem = #{};
NSDictionary* newItem = #{};
NSString* storedString = [iCloudStorage getObjectForKey:key];
if (storedString != nil) {
NSError* error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[storedString dataUsingEncoding:NSUTF8StringEncoding]
options:0
error:&error];
if (error != nil) {
reject(#"json_decode_err", #"Error parsing stored value as JSON string.", error);
return nil;
}
if (![object isKindOfClass:[NSDictionary class]]) {
reject(#"json_not_object_err", #"The stored JSON string does not parse into an object.", nil);
return nil;
}
if (value != nil) {
id newObject = [NSJSONSerialization JSONObjectWithData:[value dataUsingEncoding:NSUTF8StringEncoding]
options:0
error:&error];
if (error != nil) {
reject(#"json_decode_err", #"The provided value is not valid JSON.", error);
return nil;
}
if (![newItem isKindOfClass:[NSDictionary class]]) {
reject(#"json_not_object_err", #"The provided JSON string does not parse into an object.", nil);
return nil;
}
newItem = newObject;
}
storedItem = object;
}
NSMutableDictionary* mergedItem = [NSMutableDictionary dictionaryWithDictionary:storedItem];
[mergedItem addEntriesFromDictionary:newItem];
NSError* error = nil;
NSData* data = [NSJSONSerialization dataWithJSONObject:mergedItem options:0 error:&error];
if (error != nil) {
reject(#"json_encode_err", #"Error encoding the merged JSON data to string.", error);
return nil;
}
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
- (instancetype)init {
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(ubiquitousStoreUpdated:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:nil];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
}
return self;
}
- (NSArray<NSString *> *)supportedEvents {
return #[ kStoreChangedEvent ];
}
- (void) ubiquitousStoreUpdated:(NSNotification*)notification {
// if this notification comes in before bridge has initialized,
// don't try to send the event (app crashes if you do).
if (!self.bridge) {
return;
}
NSArray* changedKeys = [[notification userInfo] objectForKey:NSUbiquitousKeyValueStoreChangedKeysKey];
NSMutableArray* reportedChangedKeys = [NSMutableArray array];
for (NSString* key in changedKeys) {
NSString* reportedKey = [iCloudStorage removePrefixFromKey:key];
if (reportedKey) {
[reportedChangedKeys addObject:reportedKey];
}
}
if ([reportedChangedKeys count]) {
NSDictionary* body = #{ kChangedKeys : reportedChangedKeys };
[self sendEventWithName:kStoreChangedEvent body:body];
}
}
RCT_EXPORT_METHOD(getItem: (NSString*)key resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
resolve([iCloudStorage getObjectForKey:key]);
}
RCT_EXPORT_METHOD(setItem: (NSString*)key value: (NSString*)value resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[iCloudStorage setValue:value forKey:key];
resolve(#{});
}
RCT_EXPORT_METHOD(removeItem: (NSString*)key resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
[iCloudStorage removeKey:key];
resolve(#{});
}
RCT_EXPORT_METHOD(mergeItem: (NSString*)key value: (NSString*)value resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSString* newValue = [iCloudStorage getMergedItemWithKey:key value:value rejecter:reject];
if (newValue == nil) {
// we failed and reject block was called.
return;
}
[iCloudStorage setValue:newValue forKey:key];
resolve(#{});
}
RCT_REMAP_METHOD(clear, clearResolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
for (NSString* key in [iCloudStorage allKeysInStore]) {
if ([key hasPrefix:ICLOUDSTORAGE_PREFIX]) {
[[NSUbiquitousKeyValueStore defaultStore] removeObjectForKey:key];
}
}
resolve(#{});
}
RCT_REMAP_METHOD(getAllKeys, getAllKeysResolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSMutableArray* allKeys = [NSMutableArray array];
for (NSString* storeKey in [iCloudStorage allKeysInStore]) {
NSString* key = [iCloudStorage removePrefixFromKey:storeKey];
if (key != nil) {
[allKeys addObject:key];
}
}
resolve(allKeys);
}
RCT_EXPORT_METHOD(multiGet: (NSArray*)keys resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:[keys count]];
for (NSString* key in keys) {
NSObject* object = [iCloudStorage getObjectForKey:key];
if (object != nil) {
[result addObject:object];
}
}
resolve(result);
}
RCT_EXPORT_METHOD(multiSet: (NSDictionary*)keyValuePairs resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
for (NSString* key in [keyValuePairs allKeys]) {
[iCloudStorage setValue:[keyValuePairs objectForKey:key] forKey:key];
}
resolve(#{});
}
RCT_EXPORT_METHOD(multiRemove: (NSArray*)keys resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
for (NSString* key in keys) {
[iCloudStorage removeKey:key];
}
resolve(#{});
}
RCT_EXPORT_METHOD(multiMerge: (NSDictionary*)keyValuePairs resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:[keyValuePairs count]];
BOOL failed = NO;
for (NSString* key in [keyValuePairs allKeys]) {
NSString* newValue = [iCloudStorage getMergedItemWithKey:key value:[keyValuePairs objectForKey:key] rejecter:reject];
if (newValue == nil) {
break;
}
}
if (failed) {
return;
}
for (NSString* key in [result allKeys]) {
[iCloudStorage setValue:[result objectForKey:key] forKey:key];
}
resolve(#{});
}
- (NSDictionary<NSString *,id> *)constantsToExport {
return #{ ICLOUD_STORE_CHANGED : kStoreChangedEvent };
}
+ (BOOL)requiresMainQueueSetup {
return TRUE;
}
#end
The problem is that it isn't shown in the NativeModules when I import it. It returns undefined.
What am I missing? I use RCT_EXPORT_MODULE() to export it and I have the requiresMainQueueSetup to suppress the warning Module RCTImageLoader requires main queue setup since it overridesinitbut doesn't implementrequiresMainQueueSetup. In a future release React Native will default to initializing all native modules on a background thread unless explicitly opted-out of.
I've been searching but couldn't find an answer anywhere.
I've added the files to folder that is named after the project:
<projectfolder>
--<projectName>
iCloudStorage.h
iCloudStorage.m
--libraries
--etc..
Thanks for the help!
Just make sure your files are in the iOS folder.
Try to make a simple exported module work and then modify it according to your needs.
Don't forget to re-build with xCode or console after modifying iOS native files.
A simple exported native module should look like this (according to RN docs):
// CalendarManager.h
#import <React/RCTBridgeModule.h>
#interface CalendarManager : NSObject <RCTBridgeModule>
#end
// CalendarManager.m
#import "CalendarManager.h"
#implementation CalendarManager
// To export a module named CalendarManager
RCT_EXPORT_MODULE();
// This would name the module AwesomeCalendarManager instead
// RCT_EXPORT_MODULE(AwesomeCalendarManager);
#end
Then try to import it in your RN code:
import {NativeModules} from 'react-native';
var CalendarManager = NativeModules.CalendarManager;
Source:
https://facebook.github.io/react-native/docs/native-modules-ios
Your project tree should probably look like:
<projectfolder>
--<projectName>
--<ios>
iCloudStorage.h
iCloudStorage.m
--<android>
--JS libraries
--JS files etc
About the requiresMainQueueSetup warning:
If it is in the native modules, usually updating those modules should work.
If the warnings are related to your code you will have to modify your module as below:
For Swift:
#objc(MyModule)
class MyModule: NSObject {
// ADD the 3 lines from below:
#objc static func requiresMainQueueSetup() -> Bool {
return false
}
For Objective C:
+ (BOOL)requiresMainQueueSetup
{
return NO;
}
Just set the return value according to what your module needs. Usually a NO should be enough if your module doesn't interact with UI.
i am facing issue in dropbox upload.
When i am staying on the view on which i have written dropbox upload code then it works fine. But if in between upload process i leave the view and open another screen then upload stops or canceled.
I am using below code
- (void)didPressLink {
self.restClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
self.restClient.delegate = self;
if(![[DBSession sharedSession] isLinked]) {
[[DBSession sharedSession] linkFromController:self];
}
}
-(void)saveFileOnDropBox{
[self didPressLink];
NSString *filename = [soundOneNew lastPathComponent];
NSString *destDir = #"/";
[[self restClient] uploadFile:filename toPath:destDir withParentRev:nil fromPath:soundOneNew];
}
-(void)restClient:(DBRestClient*)client uploadedFile:(NSString*)destPath
from:(NSString*)srcPath metadata:(DBMetadata*)metadata {
NSLog(#"File uploaded successfully to path: %# %#", metadata.path, destPath);
[[self restClient] loadSharableLinkForFile: [NSString stringWithFormat:#"/%#",metadata.path]];
}
- (void)restClient:(DBRestClient*)client uploadFileFailedWithError:(NSError*)error {
NSLog(#"File upload failed with error - %#", error);
}
- (void)restClient:(DBRestClient*)restClient loadedSharableLink:(NSString*)link
forFile:(NSString*)path
{
NSLog(#"Sharable link %#",link);
NSLog(#"File Path %# ",path);
}
-(void) restClient:(DBRestClient *)client uploadProgress:(CGFloat)progress forFile:(NSString *)destPath from:(NSString *)srcPath {
static NSDate* date = nil;
static double oldUploadedFileSize = 0;
if (!date) {
date = [NSDate date] ;
} else {
NSTimeInterval sec = -[date timeIntervalSinceNow];
date = [NSDate date] ;
NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:srcPath error:nil];
double uploadedFileSize = (double)[fileAttributes fileSize] * progress;
if (sec) {
NSLog(#"speed approx. %.2f KB/s", (uploadedFileSize - oldUploadedFileSize )/1024.0 / sec );
}
oldUploadedFileSize = uploadedFileSize;
self.uploadAudioProgress.progress = progress;
}
}
How to keep upload process continue even if i leave the view
You call
self.restClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
to create a new REST client and then
[[self restClient] uploadFile:filename toPath:destDir withParentRev:nil fromPath:soundOneNew];
to do the upload. So, this client is owned by the view controller and is destroyed when the view controller is destroyed - i.e. when you navigate away from it.
You want some other class and an instance of it (probably a singleton or an instance owned by the app delegate and passed to instances that need it) which owns all of the REST clients created and retains them until they are complete.
I have a class called APICalls that manages the calls to the API. Every View Controller calls the appropriate method (createUsername, getStates...) and pass the parameters required. When the data is received and parsed, it calls back the viewcontroller to update the UI with the info downloaded. The following code is working but I would like to know if there is an easier or more flexible/appropriate way of doing this, specially when I update the UI in the viewcontroller. Perhaps with protocols and delegates? Any suggestion is welcomed.
-(void) getObjects:(id)returnObject ofClass:(Class)returnClass fromUrl:(NSString *)urlString withPost:(NSString *)post orPut:(NSString *)put token:(NSString *)token callName:(NSString *)call andAlertTitle:(NSString *)alertTitle
{
// NSString *className = NSStringFromClass([object class]);
__block NSObject *object = returnObject;
__block Class class = returnClass;
__block NSMutableArray *array = [[NSMutableArray alloc]init] ;
__block BOOL dataReceived = NO;
[SVProgressHUD showWithStatus:#"Connecting to the server"];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
ServerConnection *sc = [[ServerConnection alloc] init]; //post/get/put
NSDictionary *jsonDict;
if ( ([post isEqualToString:#""] || !post ) && ([put isEqualToString:#""] || !put ) )
jsonDict = [sc getFromUrl:urlString withToken:token];
else if ([put isEqualToString:#""] || !put)
jsonDict = [sc postToUrl:urlString withPost:post andToken:token];
else
jsonDict = [sc putToUrl:urlString withPut:put andToken:token];
if (jsonDict)
{
NSLog(#"API: json received");
//parse the received json
NSObject *data = [self parseJson:jsonDict alertTitle:alertTitle];
if ([data isKindOfClass:[NSArray class]]) {
NSLog(#"API: Array");
dataReceived = YES;
// Iterate through the array of dictionaries
for(NSDictionary *dict in (NSArray *) data) {
object = [[class alloc] initWithJSONDictionary:dict];
[array addObject:object];
}
}
else if ([data isKindOfClass:[NSDictionary class]]){
NSLog(#"API: Dictionary");
dataReceived = YES;
object = [[class alloc] initWithJSONDictionary:(NSDictionary *)data];
if ([array count]> 0)
[array addObject:object];
}
else
NSLog(#"API: Error from API"); //alertview is shown from HandleError class
}
else{
NSLog(#"no json received");
dispatch_async(dispatch_get_main_queue(), ^(void){
[self alertStatus:#"Error when connecting to the server, please try it again" :alertTitle];
});
}
if (dataReceived)
{
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
[SVProgressHUD dismiss];
if ([call isEqualToString:#"getStates"])
{
if ([self.currentViewController isKindOfClass:[SignUpViewController class]])
{
SignUpViewController *signup = (SignUpViewController *) self.currentViewController;
[signup updateStatesList:array];
}
else if ([self.currentViewController isKindOfClass:[MyProfileViewController class]])
{
MyProfileViewController *profileVC = (MyProfileViewController *) self.currentViewController;
[profileVC updateStatesList:array];
}
}
else if ([call isEqualToString:#"getPoints"])
{
PromotionSelectionViewController *promotionVC = (PromotionSelectionViewController *) self.currentViewController;
[promotionVC updatePoints:object];
}
else if ([call isEqualToString:#"getPromotions"])
{
PromotionSelectionViewController *promotionVC = (PromotionSelectionViewController *) self.currentViewController;
[promotionVC updatePromotionsList:array];
}
});
}
});
}
//CreateUser: creates an user when this sign up
-(void)createUserWithUsername:(NSString *)username name:(NSString *)name surname:(NSString *)surname birthdate:(NSString *)birthdate address:(NSString*) address city:(NSString *)city state:(int)state country:(int)country zipCode:(int)zipCode email:(NSString *)email password:(NSString *)password fromViewController:(UIViewController *)currentViewController
{
self.currentViewController = currentViewController;
//Create the post with the username and password
NSString *post =[[NSString alloc] initWithFormat:#"username=%#&name=%#&surname=%#&address=%#&city=%#&state=%d&country=%d&zipcode=%d&birthdate=%#&email=%#&password=%#&",username, name, surname, address, city, state, country, zipCode, birthdate,email,password];
NSLog(#"post: %#", post);
User *user;
[self getObjects:user ofClass:NSClassFromString(#"User") fromUrl:signupURL withPost:post orPut:nil token:nil callName:#"createUser" andAlertTitle:#"SignUp Failed"];
}
-(void) getPointsWithToken:(NSString *)token fromViewController:(UIViewController *)currentViewController{
self.currentViewController = currentViewController;
[self getObjects:nil ofClass:nil fromUrl:getPointsURL withPost:nil orPut:nil token:token callName:#"getPoints" andAlertTitle:#"Get Proints Number Failed"];
}
-(void)getStatesforCounry:(int)idCountry fromViewController:(UIViewController *) currentViewController
{
self.currentViewController = currentViewController;
NSString *url = [NSString stringWithFormat:#"%#%d", getStatesURL, idCountry];
// NSLog(#"url: %#", url);
State *state;
[self getObjects:state ofClass:NSClassFromString(#"State") fromUrl:url withPost:nil orPut:nil token:nil callName:#"getStates" andAlertTitle:#"States not loaded"];
}
...
Using a delegate protocol pattern might help, but in this situation, I think my preference would be to pass a completion-handling block into the method, then call that completion handler block on the main thread to handle the results of the API call—it feels like there's a bit too much view-controller logic going on in the API method and using a completion-handling block (or a delegate callback method) would help move that logic back to the view controller.
Also, though it doesn't really change anything, you can replace the calls
dispatch_async(dispatch_get_main_queue(), ^(void){
...
});
with
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
...
}];
(It is generally preferable to use higher-level APIs, such as NSOperationQueue, over lower-level APIs, like dispatch_async, when they are equivalent.)
I am trying to load a csv file in core data when the application is ran for the first time. On another post on stackoverflow found here (What is the fastest way to load a large CSV file into core data), I found out how to do that.
I am using the same code form the provided function: populateDB, in my controller and calling the function if the data has never been loaded before (first run). However, xcode is giving me an error:
No visible #interface for ...Controller declares the selector persistentStoreCoordinator.
The function is:
-(void)populateDB{
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
NSManagedObjectContext *context;
if (coordinator != nil) {
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:coordinator];
}
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"input" ofType:#"txt"];
if (filePath) {
NSString * myText = [[NSString alloc]
initWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:nil];
if (myText) {
__block int count = 0;
[myText enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) {
line=[line stringByReplacingOccurrencesOfString:#"\t" withString:#" "];
NSArray *lineComponents=[line componentsSeparatedByString:#" "];
if(lineComponents){
if([lineComponents count]==3){
float f=[[lineComponents objectAtIndex:0] floatValue];
NSNumber *number=[NSNumber numberWithFloat:f];
NSString *string1=[lineComponents objectAtIndex:1];
NSString *string2=[lineComponents objectAtIndex:2];
NSManagedObject *object=[NSEntityDescription insertNewObjectForEntityForName:#"Bigram" inManagedObjectContext:context];
[object setValue:number forKey:#"number"];
[object setValue:string1 forKey:#"string1"];
[object setValue:string2 forKey:#"string2"];
NSError *error;
count++;
if(count>=1000){
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
count=0;
}
}
}
}];
NSLog(#"done importing");
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
}
}
I am picking up iOS again after 3 years of absence and I have never dived into this part of the SDK before. I would greatly appreciate any help with this issue...
The code below shows you an example to load csv file in Core Data using the class CSVParser.hof Matt Gallagher and supposing an entity MyEntity
// CSV File input.csv and not input.txt separate by space #" "
CSVParser *csvParser = [[CSVParser alloc] initWithContentOfFile:[NSBundle mainBundle] pathForResource:#"input" ofType:#"csv" separator:#" "];
// Array with all your data from CSV
NSArray *data = [csvParser parseFile];
// Your entity from Core Data
MyEntity *myEntity = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
for (NSDictionary *dic in data)
{
fetchRequest.entity = [NSEntityDescription entityForName:#"MyEntity" inManagedObjectContext:context];
if (!entity)
entity = [NSEntityDescription insertNewObjectForEntityForName:#"MyEntity"
inManagedObjectContext:context];
[entity setValue:number forKey:#"number"];
[entity setValue:string1 forKey:#"string1"];
[entity setValue:string2 forKey:#"string2"];
}
// Save the context
[managedObjectContext save:nil];
CVSParser.h using ARC :
//
// CSVParser.h
// CSVImporter
//
// Created by Matt Gallagher on 2009/11/30.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file, free of charge, in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
// Source : http://cocoawithlove.com/2009/11/writing-parser-using-nsscanner-csv.html
#interface CSVParser : NSObject
{
NSString *csvString;
NSString *separator;
NSScanner *scanner;
BOOL hasHeader;
NSMutableArray *fieldNames;
id receiver;
SEL receiverSelector;
NSCharacterSet *endTextCharacterSet;
BOOL separatorIsSingleChar;
}
- (id)initWithString:(NSString *)aCSVString
separator:(NSString *)aSeparatorString;
- (id)initWithContentOfFile:(NSString *)aPath
separator:(NSString *)aSeparatorString;
- (id)initWithString:(NSString *)aCSVString
separator:(NSString *)aSeparatorString
hasHeader:(BOOL)header
fieldNames:(NSArray *)names;
- (id)initWithContentOfFile:(NSString *)aPath
separator:(NSString *)aSeparatorString
hasHeader:(BOOL)header
fieldNames:(NSArray *)names;
- (NSArray *)arrayOfParsedRows;
- (void)parseRowsForReceiver:(id)aReceiver selector:(SEL)aSelector;
- (NSArray *)parseFile;
- (NSMutableArray *)parseHeader;
- (NSDictionary *)parseRecord;
- (NSString *)parseName;
- (NSString *)parseField;
- (NSString *)parseEscaped;
- (NSString *)parseNonEscaped;
- (NSString *)parseDoubleQuote;
- (NSString *)parseSeparator;
- (NSString *)parseLineSeparator;
- (NSString *)parseTwoDoubleQuotes;
- (NSString *)parseTextData;
#end
CVSParser.m using ARC :
//
// CSVParser.m
// CSVImporter
//
// Created by Matt Gallagher on 2009/11/30.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file, free of charge, in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#import "CSVParser.h"
#implementation CSVParser
//
// initWithString:separator:hasHeader:fieldNames:
//
// Parameters:
// aCSVString - the string that will be parsed
// aSeparatorString - the separator (normally "," or "\t")
// header - if YES, treats the first row as a list of field names
// names - a list of field names (will have no effect if header is YES)
//
// returns the initialized object (nil on failure)
//
- (id)initWithString:(NSString *)aCSVString
separator:(NSString *)aSeparatorString
hasHeader:(BOOL)header
fieldNames:(NSArray *)names
{
self = [super init];
if (self)
{
csvString = [aCSVString retain];
separator = [aSeparatorString retain];
NSAssert([separator length] > 0 &&
[separator rangeOfString:#"\""].location == NSNotFound &&
[separator rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location == NSNotFound,
#"CSV separator string must not be empty and must not contain the double quote character or newline characters.");
NSMutableCharacterSet *endTextMutableCharacterSet =
[[NSCharacterSet newlineCharacterSet] mutableCopy];
[endTextMutableCharacterSet addCharactersInString:#"\""];
[endTextMutableCharacterSet addCharactersInString:[separator substringToIndex:1]];
endTextCharacterSet = endTextMutableCharacterSet;
if ([separator length] == 1)
{
separatorIsSingleChar = YES;
}
hasHeader = header;
fieldNames = [names mutableCopy];
}
return self;
}
- (id)initWithString:(NSString *)aCSVString
separator:(NSString *)aSeparatorString
{
return [self initWithString:aCSVString
separator:aSeparatorString
hasHeader:YES
fieldNames:nil];
}
- (id)initWithContentOfFile:(NSString *)aPath
separator:(NSString *)aSeparatorString
{
return [self initWithString:[NSString stringWithContentsOfFile:aPath
encoding:NSUTF8StringEncoding
error:nil]
separator:aSeparatorString];
}
- (id)initWithContentOfFile:(NSString *)aPath
separator:(NSString *)aSeparatorString
hasHeader:(BOOL)header
fieldNames:(NSArray *)names
{
return [self initWithString:[NSString stringWithContentsOfFile:aPath
encoding:NSUTF8StringEncoding
error:nil]
separator:aSeparatorString
hasHeader:header
fieldNames:names];
}
//
// dealloc
//
// Releases instance memory.
//
- (void)dealloc
{
[csvString release];
[separator release];
[fieldNames release];
[endTextCharacterSet release];
[super dealloc];
}
//
// arrayOfParsedRows
//
// Performs a parsing of the csvString, returning the entire result.
//
// returns the array of all parsed row records
//
- (NSArray *)arrayOfParsedRows
{
scanner = [[NSScanner alloc] initWithString:csvString];
[scanner setCharactersToBeSkipped:[[[NSCharacterSet alloc] init] autorelease]];
NSArray *result = [self parseFile];
[scanner release];
scanner = nil;
return result;
}
//
// parseRowsForReceiver:selector:
//
// Performs a parsing of the csvString, sending the entries, 1 row at a time,
// to the receiver.
//
// Parameters:
// aReceiver - the target that will receive each row as it is parsed
// aSelector - the selector that will receive each row as it is parsed
// (should be a method that takes a single NSDictionary argument)
//
- (void)parseRowsForReceiver:(id)aReceiver selector:(SEL)aSelector
{
scanner = [[NSScanner alloc] initWithString:csvString];
[scanner setCharactersToBeSkipped:[[[NSCharacterSet alloc] init] autorelease]];
receiver = [aReceiver retain];
receiverSelector = aSelector;
[self parseFile];
[scanner release];
scanner = nil;
[receiver release];
receiver = nil;
}
//
// parseFile
//
// Attempts to parse a file from the current scan location.
//
// returns the parsed results if successful and receiver is nil, otherwise
// returns nil when done or on failure.
//
- (NSArray *)parseFile
{
scanner = [[NSScanner alloc] initWithString:csvString];
[scanner setCharactersToBeSkipped:[[[NSCharacterSet alloc] init] autorelease]];
if (hasHeader)
{
if (fieldNames)
{
[fieldNames release];
}
fieldNames = [[self parseHeader] retain];
if (!fieldNames || ![self parseLineSeparator])
{
return nil;
}
}
NSMutableArray *records = nil;
if (!receiver)
{
records = [NSMutableArray array];
}
NSDictionary *record = [[self parseRecord] retain];
if (!record)
{
return nil;
}
while (record)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (receiver)
{
[receiver performSelector:receiverSelector withObject:record];
}
else
{
[records addObject:record];
}
[record release];
if (![self parseLineSeparator])
{
break;
}
record = [[self parseRecord] retain];
[pool drain];
}
[scanner release];
scanner = nil;
return records;
}
//
// parseHeader
//
// Attempts to parse a header row from the current scan location.
//
// returns the array of parsed field names or nil on parse failure.
//
- (NSMutableArray *)parseHeader
{
NSString *name = [self parseName];
if (!name)
{
return nil;
}
NSMutableArray *names = [NSMutableArray array];
while (name)
{
[names addObject:name];
if (![self parseSeparator])
{
break;
}
name = [self parseName];
}
return names;
}
//
// parseRecord
//
// Attempts to parse a record from the current scan location. The record
// dictionary will use the fieldNames as keys, or FIELD_X for each column
// X-1 if no fieldName exists for a given column.
//
// returns the parsed record as a dictionary, or nil on failure.
//
- (NSDictionary *)parseRecord
{
//
// Special case: return nil if the line is blank. Without this special case,
// it would parse as a single blank field.
//
if ([self parseLineSeparator] || [scanner isAtEnd])
{
return nil;
}
NSString *field = [self parseField];
if (!field)
{
return nil;
}
NSInteger fieldNamesCount = [fieldNames count];
NSInteger fieldCount = 0;
NSMutableDictionary *record =
[NSMutableDictionary dictionaryWithCapacity:[fieldNames count]];
while (field)
{
NSString *fieldName;
if (fieldNamesCount > fieldCount)
{
fieldName = [fieldNames objectAtIndex:fieldCount];
}
else
{
fieldName = [NSString stringWithFormat:#"FIELD_%d", fieldCount + 1];
[fieldNames addObject:fieldName];
fieldNamesCount++;
}
[record setObject:field forKey:fieldName];
fieldCount++;
if (![self parseSeparator])
{
break;
}
field = [self parseField];
}
return record;
}
//
// parseName
//
// Attempts to parse a name from the current scan location.
//
// returns the name or nil.
//
- (NSString *)parseName
{
return [self parseField];
}
//
// parseField
//
// Attempts to parse a field from the current scan location.
//
// returns the field or nil
//
- (NSString *)parseField
{
NSString *escapedString = [self parseEscaped];
if (escapedString)
{
return escapedString;
}
NSString *nonEscapedString = [self parseNonEscaped];
if (nonEscapedString)
{
return nonEscapedString;
}
//
// Special case: if the current location is immediately
// followed by a separator, then the field is a valid, empty string.
//
NSInteger currentLocation = [scanner scanLocation];
if ([self parseSeparator] || [self parseLineSeparator] || [scanner isAtEnd])
{
[scanner setScanLocation:currentLocation];
return #"";
}
return nil;
}
//
// parseEscaped
//
// Attempts to parse an escaped field value from the current scan location.
//
// returns the field value or nil.
//
- (NSString *)parseEscaped
{
if (![self parseDoubleQuote])
{
return nil;
}
NSString *accumulatedData = [NSString string];
while (YES)
{
NSString *fragment = [self parseTextData];
if (!fragment)
{
fragment = [self parseSeparator];
if (!fragment)
{
fragment = [self parseLineSeparator];
if (!fragment)
{
if ([self parseTwoDoubleQuotes])
{
fragment = #"\"";
}
else
{
break;
}
}
}
}
accumulatedData = [accumulatedData stringByAppendingString:fragment];
}
if (![self parseDoubleQuote])
{
return nil;
}
return accumulatedData;
}
//
// parseNonEscaped
//
// Attempts to parse a non-escaped field value from the current scan location.
//
// returns the field value or nil.
//
- (NSString *)parseNonEscaped
{
return [self parseTextData];
}
//
// parseTwoDoubleQuotes
//
// Attempts to parse two double quotes from the current scan location.
//
// returns a string containing two double quotes or nil.
//
- (NSString *)parseTwoDoubleQuotes
{
if ([scanner scanString:#"\"\"" intoString:NULL])
{
return #"\"\"";
}
return nil;
}
//
// parseDoubleQuote
//
// Attempts to parse a double quote from the current scan location.
//
// returns #"\"" or nil.
//
- (NSString *)parseDoubleQuote
{
if ([scanner scanString:#"\"" intoString:NULL])
{
return #"\"";
}
return nil;
}
//
// parseSeparator
//
// Attempts to parse the separator string from the current scan location.
//
// returns the separator string or nil.
//
- (NSString *)parseSeparator
{
if ([scanner scanString:separator intoString:NULL])
{
return separator;
}
return nil;
}
//
// parseLineSeparator
//
// Attempts to parse newline characters from the current scan location.
//
// returns a string containing one or more newline characters or nil.
//
- (NSString *)parseLineSeparator
{
NSString *matchedNewlines = nil;
[scanner
scanCharactersFromSet:[NSCharacterSet newlineCharacterSet]
intoString:&matchedNewlines];
return matchedNewlines;
}
//
// parseTextData
//
// Attempts to parse text data from the current scan location.
//
// returns a non-zero length string or nil.
//
- (NSString *)parseTextData
{
NSString *accumulatedData = [NSString string];
while (YES)
{
NSString *fragment;
if ([scanner scanUpToCharactersFromSet:endTextCharacterSet intoString:&fragment])
{
accumulatedData = [accumulatedData stringByAppendingString:fragment];
}
//
// If the separator is just a single character (common case) then
// we know we've reached the end of parseable text
//
if (separatorIsSingleChar)
{
break;
}
//
// Otherwise, we need to consider the case where the first character
// of the separator is matched but we don't have the full separator.
//
NSUInteger location = [scanner scanLocation];
NSString *firstCharOfSeparator;
if ([scanner scanString:[separator substringToIndex:1] intoString:&firstCharOfSeparator])
{
if ([scanner scanString:[separator substringFromIndex:1] intoString:NULL])
{
[scanner setScanLocation:location];
break;
}
//
// We have the first char of the separator but not the whole
// separator, so just append the char and continue
//
accumulatedData = [accumulatedData stringByAppendingString:firstCharOfSeparator];
continue;
}
else
{
break;
}
}
if ([accumulatedData length] > 0)
{
return accumulatedData;
}
return nil;
}
#end
Thanks for everyone's assistance. I found the answer on stackoverflow, a 2-3 years old forum (Adding Core Data to existing iPhone project)...
So the issue it seems is that when I first created the project I didn't request using core data and only did that later on. Following the post I posted above:
Add the following to supporting files/projectName-Prefix.pch
#import <CoreData/CoreData.h>
Once I did, the persistenceCoordinator error disappeared...
WOW, big lesson learned there...
-(void)populateDB {
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
NSManagedObjectContext *context;
[....]
}
The Problem might be [self persistentStoreCoordinator]... Do you have a function called "persistentStoreCoordinator" ? If not, you have to write the function.
[EDIT]
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
#synchronized (self)
{
if (__persistentStoreCoordinator != nil)
return __persistentStoreCoordinator;
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"myProject" ofType:#"sqlite"];
NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent: #"myProject.sqlite"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
NSLog(#"Copied starting data to %#", storePath);
else
NSLog(#"Error copying default DB to %# (%#)", storePath, error);
}
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
}
Is this Code in another File ? Maybe you forgot to import the .h-file, where persistentStoreCoordinator is declared in.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have this model in one of my iPhone apps:
//
// CatapultAccount.m
// Catapult
//
// Created by Aziz Light on 4/9/13.
// Copyright (c) 2013 Catapult Technology Ltd. All rights reserved.
//
#import "CatapultAccount.h"
#interface CatapultAccount ()
- (BOOL)createAccountsTable;
- (BOOL)accountWithNameIsAlreadyAdded:(NSString *)accountName;
- (NSDictionary *)getAccountLogosForClient:(NSDictionary *)client;
- (NSString *)getAccountLogoFromURL:(NSURL *)url;
- (NSString *)saveImage:(UIImage *)image
withFileName:(NSString *)imageName
ofType:(NSString *)extension
inDirectory:(NSString *)directoryPath;
#end
#implementation CatapultAccount
- (BOOL)createAccountWithAccountID:(NSString *)accountID
{
__block BOOL operationSuccessfull;
NXOAuth2Account *account = [[NXOAuth2AccountStore sharedStore] accountWithIdentifier:accountID];
if (account == nil) {
operationSuccessfull = NO;
} else {
if ([self openDatabaseConnection]) {
if ([self createAccountsTable]) {
[NXOAuth2Request performMethod:#"GET"
onResource:[NSURL URLWithString:[NSString stringWithFormat:#"%#/users/me", kCatapultHost]]
usingParameters:nil
withAccount:account
sendProgressHandler:nil
responseHandler:^(NSURLResponse *response, NSData *responseData, NSError *error) {
if (error != nil) {
operationSuccessfull = NO;
#if DEBUG
NSLog(#"ERROR: %#", error);
#endif
_lastError = error;
} else {
NSError *jsonError;
NSDictionary *serializedResponse = [NSJSONSerialization JSONObjectWithData:responseData
options:kNilOptions
error:&jsonError];
if (jsonError != nil) {
operationSuccessfull = NO;
#if DEBUG
NSLog(#"ERROR: %#", jsonError);
#endif
_lastError = jsonError;
} else {
NSDictionary *user = [serializedResponse objectForKey:#"user"];
NSDictionary *client = [serializedResponse objectForKey:#"client"];
NSString *forename = [user objectForKey:#"forename"];
NSString *surname = [user objectForKey:#"surname"];
NSString *accountName = [client objectForKey:#"account_name"];
NSString *clientName = [client objectForKey:#"client_name"];
if ([self accountWithNameIsAlreadyAdded:accountName]) {
operationSuccessfull = NO;
_lastError = [NSError errorWithDomain:kCatapultAccountErrorDomain
code:kCatapultDuplicateAccountErrorCode
userInfo:#{#"message": #"You have already added this account"}];
#if DEBUG
NSLog(#"ERROR: %#", _lastError);
#endif
} else {
NSDictionary *logos = [self getAccountLogosForClient:client];
operationSuccessfull = [self.db executeUpdate:#"insert into accounts(account_id, forename, surname, account_name, client_name, smallest_logo, thumb_logo) values(?,?,?,?,?,?,?)",
accountID, forename, surname, accountName, clientName, logos[#"smallest_logo"], logos[#"thumb_logo"]];
}
}
}
}];
} else {
operationSuccessfull = NO;
_lastError = [NSError errorWithDomain:kCatapultDatabaseErrorDomain
code:kCatapultUnableToCreateTableErrorCode
userInfo:#{#"message": #"Unable to create the accounts table"}];
#if DEBUG
NSLog(#"ERROR: %#", _lastError);
#endif
}
[self closeDatabaseConnection];
} else {
operationSuccessfull = NO;
_lastError = [NSError errorWithDomain:kCatapultDatabaseErrorDomain
code:kCatapultUnableToOpenDatabaseConnectionErrorCode
userInfo:#{#"message": #"Unable to open database connection"}];
#if DEBUG
NSLog(#"ERROR: %#", _lastError);
#endif
}
}
return operationSuccessfull;
}
- (BOOL)createAccountsTable
{
// Accounts table schema
// id integer primary key autoincrement
// account_id varchar(36) not null - unique
// forename varchar(255) not null
// surname varchar(255) not null
// account_name varchar(255) not null - unique
// client_name varchar(255) not null
// smallest_account_logo text
// thumb_account_logo text
BOOL tableCreationWasSuccessfull = [self.db executeUpdate:#"create table if not exists accounts(id integer primary key autoincrement, account_id varchar(36) not null, forename varchar(255) not null, surname varchar(255) not null, account_name varchar(255) not null, client_name varchar(255) not null, smallest_logo text, thumb_logo text, unique(account_id, account_name) on conflict abort)"];
if (tableCreationWasSuccessfull) {
_lastError = nil;
} else {
_lastError = [self.db lastError];
#if DEBUG
NSLog(#"Failed to create users table: %#", _lastError);
#endif
}
return tableCreationWasSuccessfull;
}
- (BOOL)accountWithNameIsAlreadyAdded:(NSString *)accountName
{
FMResultSet *account = [self.db executeQuery:#"select count(*) from accounts"];
return [account next];
}
- (NSDictionary *)getAccountLogosForClient:(NSDictionary *)client
{
NSString *smallestLogoURLString = [[[client objectForKey:#"logo"] objectForKey:#"smallest"] objectForKey:#"url"];
NSString *smallestLogoPath = [self getAccountLogoFromURL:[NSURL URLWithString:smallestLogoURLString]];
NSString *thumbLogoURLString = [[[client objectForKey:#"logo"] objectForKey:#"thumb"] objectForKey:#"url"];
NSString *thumbLogoPath = [self getAccountLogoFromURL:[NSURL URLWithString:thumbLogoURLString]];
return #{#"smallest_logo": smallestLogoPath, #"thumb_logo": thumbLogoPath};
}
- (NSString *)getAccountLogoFromURL:(NSURL *)url
{
NSString *urlWithoutGETParams = [[[url absoluteString] componentsSeparatedByString:#"?"] objectAtIndex:0];
NSString *lastSegmentOfURL = [[urlWithoutGETParams componentsSeparatedByString:#"/"] lastObject];
NSString *logoName = [[lastSegmentOfURL componentsSeparatedByString:#"."] objectAtIndex:0];
NSString *logoExtension = [[lastSegmentOfURL componentsSeparatedByString:#"."] lastObject];
NSString * documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *logoPath = [documentsDirectoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", logoName, logoExtension]];
BOOL logoExists = [[NSFileManager defaultManager] fileExistsAtPath:logoPath];
if (logoExists) {
return logoPath;
} else {
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *logo = [UIImage imageWithData:data];
logoPath = [self saveImage:logo withFileName:logoName ofType:logoExtension inDirectory:documentsDirectoryPath];
return (logoPath == nil) ? nil : logoPath;
}
}
- (NSString *)saveImage:(UIImage *)image
withFileName:(NSString *)imageName
ofType:(NSString *)extension
inDirectory:(NSString *)directoryPath
{
NSData *imageRepresentation;
if ([[extension lowercaseString] isEqualToString:#"png"] || [[extension lowercaseString] isEqualToString:#"gif"]) {
imageRepresentation = UIImagePNGRepresentation(image);
} else if ([[extension lowercaseString] isEqualToString:#"jpg"] || [[extension lowercaseString] isEqualToString:#"jpeg"]) {
imageRepresentation = UIImageJPEGRepresentation(image, 1.0);
} else {
#if DEBUG
NSLog(#"Image Save Failed\nExtension: (%#) is not recognized, use (PNG/JPG/GIF)", extension);
#endif
return nil;
}
NSString *imagePath = [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", imageName, extension]];
NSError *error;
BOOL imageDidSave = [imageRepresentation writeToFile:imagePath
options:NSAtomicWrite
error:&error];
if (error != nil) {
#if DEBUG
NSLog(#"Error saving the file: %#", error);
#endif
}
return (imageDidSave) ? imagePath : nil;
}
#end
And this method in one of my view controllers:
- (void)createAccount:(NSNotification *)notification
{
NXOAuth2Account *account = [notification.userInfo objectForKey:#"NXOAuth2AccountStoreNewAccountUserInfoKey"];
if ([_accountModel createAccountWithAccountID:account.identifier]) {
// Do something
NSLog(#"Yay");
} else {
// Delete the newly created account
[[NXOAuth2AccountStore sharedStore] removeAccount:account];
UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:#"Account Error"
message:#"Unable to add new account"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[errorMessage show];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
The problem is that, following my (naive) observations, most of the methods that use the database in the model execute too slowly and the createAccountWithAccountID: method doesn't wait for the method it calls to finish. The result of that is that the record is not saved to the database but for some reason the createAccountWithAccountID method returns YES… here are the logs that illustrate what I am saying:
2013-04-09 20:07:46.261 Catapult[21004:c07] Yay
2013-04-09 20:07:46.276 Catapult[21004:c07] The FMDatabase <FMDatabase: 0x7288300> is not open.
2013-04-09 20:07:46.606 Catapult[21004:c07] The FMDatabase <FMDatabase: 0x7288300> is not open.
The record is not saved to the database because the database connection gets closed too fast…
Does anybody know how I can solve my problem please?
Most methods which use a completion block execute asynchronously. The method will return immediately and the block will be executed when the request actually completes. You need to handle it accordingly in your code. Don't release anything after you send the request, but do anything that depends on the result in the response handler block and then release it.