mercredi 22 juin 2016

TableView not retrieving/displaying all records from SQLite database

Recently modified my app to include a tab view controller. I'm now unable to load all of the categories that are in my SQLite database when the app launches and goes to the Categories table view nav controller on app load (CategoriesViewController). There are 6 category names that it should display; sometimes the app displays 0 rows, other times it displays 4. Modifying the XML file that this pulls data from, I got it to pull 6 categories (after a few times of 0 and fast execution speed). Leads me to think this has to do with timing of execution vs. time until the table view is loaded with data. Any ideas on what I need to change to get CategoriesViewController to display the table only after all data is read? Here's the code; please let me know if there's anything else that you'd need to see. Thanks!

CategoriesViewController.h

#import <UIKit/UIKit.h>

@interface CategoriesViewController : UITableViewController

@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; 

- (void)fetch; // Fetches categories from data store

@end

CategoriesViewController.m

#import "CategoriesViewController.h"
#import "ProductsViewController.h"
#import "Category.h"
#import "AppDelegate.h"

@interface CategoriesViewController () // Categories View Controller interface

@property (nonatomic, strong) ProductsViewController *productController;
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property (weak, nonatomic) IBOutlet UIImageView *logoImageView;

@end

#pragma mark -

@implementation CategoriesViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    _logoImageView.image = [UIImage imageNamed:@"company.jpg"];

    [self fetch]; // Fetch categories from our data store
}

- (void)fetch {
    NSError *error = nil;
    BOOL success = [self.fetchedResultsController performFetch:&error]; // Fetch results; if error, assign error code, output message
    NSAssert2(success, @"Unhandled error performing fetch at CategoriessViewController.m, line %d: %@", __LINE__, [error localizedDescription]);
}

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController == nil) {
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        [fetchRequest setEntity:[NSEntityDescription entityForName:@"Category" inManagedObjectContext:app.managedObjectContext]]; // Fetch categories
        NSArray *sortDescriptors = nil;
        NSString *sectionNameKeyPath = nil;
        sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"order" ascending:YES]];
        [fetchRequest setSortDescriptors:sortDescriptors];

        _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                        managedObjectContext:app.managedObjectContext
                                                                          sectionNameKeyPath:sectionNameKeyPath
                                                                                   cacheName:nil];
            }
    return _fetchedResultsController;
}

#pragma mark - UITableViewDataSource

- (NSInteger)numberOfSectionsInTableView:(UITableView *)table {
    return [[self.fetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *kCellIdentifier = @"CategoryCell";

    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier forIndexPath:indexPath];
    Category *category = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:NSLocalizedString(@"%@", @"%@"), category.name];
    return cell;
}

// Pass managed object to ProductsViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"showProducts"]) {
        ProductsViewController *detailsController = (ProductsViewController *)[segue destinationViewController];
        NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];

        detailsController.category = [self.fetchedResultsController objectAtIndexPath:selectedIndexPath];

        AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        [[segue destinationViewController] setManagedObjectContext:app.managedObjectContext];
    }
}

@end

AppDelegate.h

#import <UIKit/UIKit.h>
#import "companyXMLImporter.h"

@interface AppDelegate : NSObject <UIApplicationDelegate, companyXMLImporterDelegate>

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (NSDate *)lasModificationDateOfFileAtURL:(NSURL *)url;

@end

AppDelegate.m

#import "AppDelegate.h"
#import "ProductsViewController.h"
#import "CategoriesViewController.h"

@interface AppDelegate()

@property (nonatomic, strong) ProductsViewController *productsViewController;
@property (nonatomic, strong) CategoriesViewController *categoriesViewController;

// Properties for the importer and its background processing
@property (nonatomic, strong) companyXMLImporter *importer;
@property (nonatomic, strong) NSOperationQueue *operationQueue;

@property (nonatomic, strong) NSString *persistentStorePath;

@end

@implementation AppDelegate

static NSString * const kLastStoreUpdateKey = @"LastStoreUpdate"; // Identifies update object in user defaults storage

static NSTimeInterval const kRefreshTimeInterval = 3600;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    NSDate *lastUpdate = [[NSUserDefaults standardUserDefaults] objectForKey:kLastStoreUpdateKey];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:NSLocalizedString(@"Products XML", @"Products XML")]];
    NSDate *lastModifiedDate = [self lasModificationDateOfFileAtURL:url];
    if (lastUpdate == nil || lastModifiedDate >= lastUpdate ||  -[lastUpdate timeIntervalSinceNow] > kRefreshTimeInterval) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:self.persistentStorePath]) {
            NSError *error = nil;
            BOOL oldStoreRemovalSuccess = [[NSFileManager defaultManager] removeItemAtPath:self.persistentStorePath error:&error];
            NSAssert3(oldStoreRemovalSuccess, @"Unhandled error adding persistent store in %s at line %d: %@", __FUNCTION__, __LINE__, [error localizedDescription]);
        }

        // Object to retrieve, parse, and import into CoreData store
        self.importer = [[companyXMLImporter alloc] init];
        self.importer.delegate = self;

        // pass coordinator so importer can create its own managed object context
        self.importer.persistentStoreCoordinator = self.persistentStoreCoordinator;

        // URL for products XML file
        self.importer.companyURL = [NSURL URLWithString:[NSString stringWithFormat:NSLocalizedString(@"Products XML", @"Products XML")]];
        [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

        [self.operationQueue addOperation:self.importer];
    }
}

- (NSOperationQueue *)operationQueue {
    if (_operationQueue == nil) {
        _operationQueue = [[NSOperationQueue alloc] init];
    }
    return _operationQueue;
}

- (NSDate *)lasModificationDateOfFileAtURL:(NSURL *)url {
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];

    // Get only header
    request.HTTPMethod = @"HEAD";
    NSHTTPURLResponse *response = nil;
    NSError *error = nil;
    [NSURLConnection sendSynchronousRequest:request
                          returningResponse:&response
                                      error:&error];

    if (error) {
        NSLog(@"Error: %@", error.localizedDescription);
        return nil;
    } else if([response respondsToSelector:@selector(allHeaderFields)]) {
        NSDictionary *headerFields = [response allHeaderFields];
        NSString *lastModification = [headerFields objectForKey:@"Last-Modified"];

        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"];
        return [formatter dateFromString:lastModification];
    }
    return nil;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (_persistentStoreCoordinator == nil) {
        NSURL *storeUrl = [NSURL fileURLWithPath:self.persistentStorePath];
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
        NSError *error = nil;
        NSPersistentStore *persistentStore = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
        NSAssert3(persistentStore != nil, @"Unhandled error adding persistent store in %s at line %d: %@", __FUNCTION__, __LINE__, [error localizedDescription]);
    }
    return _persistentStoreCoordinator;
}

- (NSManagedObjectContext *)managedObjectContext {
    if (_managedObjectContext == nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [self.managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
    }
    return _managedObjectContext;
}

- (NSString *)persistentStorePath {
    if (_persistentStorePath == nil) {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths lastObject];
        _persistentStorePath = [documentsDirectory stringByAppendingPathComponent:@"Company.sqlite"];
    }
    return _persistentStorePath;
}

- (void)importerDidSave:(NSNotification *)saveNotification {
    if ([NSThread isMainThread]) {
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
//        [self.categoriesViewController fetch];
        [self.productsViewController fetch];
    } else {
        [self performSelectorOnMainThread:@selector(importerDidSave:) withObject:saveNotification waitUntilDone:NO];
    }
}

// Main-thread import completion processing
- (void)handleImportCompletion {
    [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kLastStoreUpdateKey];
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    self.importer = nil;
}

- (void)importerDidFinishParsingData:(companyXMLImporter *)importer {
    [self performSelectorOnMainThread:@selector(handleImportCompletion) withObject:nil waitUntilDone:NO];
}

// Process errors received in delegate callback
- (void)handleImportError:(NSError *)error {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    self.importer = nil;

    NSString *errorMessage = [error localizedDescription];
    NSString *alertTitle = NSLocalizedString(@"Error", @"Title for alert displayed when download or parse error occurs.");
    NSString *okTitle = NSLocalizedString(@"OK ", @"OK Title for alert displayed when download or parse error occurs.");

    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:alertTitle
                                                        message:errorMessage
                                                       delegate:nil
                                              cancelButtonTitle:okTitle
                                              otherButtonTitles:nil];
    [alertView show];
}

- (void)importer:(companyXMLImporter *)importer didFailWithError:(NSError *)error {
    [self performSelectorOnMainThread:@selector(handleImportError:) withObject:error waitUntilDone:NO];
}

@end

companyXMLImporter.h

#import <UIKit/UIKit.h>
#import <libxml/tree.h>

@class companyXMLImporter, Product, Category, CategoryCache;

@protocol companyXMLImporterDelegate <NSObject>

@optional

- (void)importerDidSave:(NSNotification *)saveNotification; // Posted when saved
- (void)importerDidFinishParsingData:(companyXMLImporter *)importer; // Called when parsing is finished
- (void)importer:(companyXMLImporter *)importer didFailWithError:(NSError *)error; // Called if error

@end

@interface companyXMLImporter : NSOperation {
@private
    id <companyXMLImporterDelegate> __unsafe_unretained delegate;

    // Reference to the libxml parser context
    xmlParserCtxtPtr context;
    NSURLConnection *xmlConnection;

    BOOL done;

    BOOL parsingAProduct;

    // Used for getting character data from XML elements
    BOOL storingCharacters;
    NSMutableData *characterBuffer;

    Product *currentProduct;

    NSUInteger countForCurrentBatch;
    NSManagedObjectContext *insertionContext;
    NSPersistentStoreCoordinator *persistentStoreCoordinator;
    NSEntityDescription *productEntityDescription;
    CategoryCache *theCache;
    NSURL *companyURL;
}

@property (nonatomic, retain) NSURL *companyURL;
@property (nonatomic, assign) id <companyXMLImporterDelegate> delegate;
@property (nonatomic, retain) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, retain, readonly) NSManagedObjectContext *insertionContext;
@property (nonatomic, retain, readonly) NSEntityDescription *productEntityDescription;
@property (nonatomic, retain, readonly) CategoryCache *theCache;

- (void)main;

@end

companyXMLImporter.m

#import "companyXMLImporter.h"
#import "Product.h"
#import "Category.h"
#import "CategoryCache.h"
#import <libxml/tree.h>

// Function prototypes for SAX callbacks
static void startElementSAX(void *context, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes);
static void endElementSAX(void *context, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI);
static void charactersFoundSAX(void *context, const xmlChar *characters, int length);
static void errorEncounteredSAX(void *context, const char *errorMessage, ...);

static xmlSAXHandler simpleSAXHandlerStruct; // Forward reference. Structure defined in full at end of file

@interface companyXMLImporter ()

@property BOOL storingCharacters;
@property (nonatomic, retain) NSMutableData *characterBuffer;
@property BOOL done;
@property BOOL parsingAProduct;
@property NSUInteger countForCurrentBatch;
@property (nonatomic, retain) Product *currentProduct;
@property (nonatomic, retain) NSURLConnection *xmlConnection;
@property (nonatomic, retain) NSDateFormatter *dateFormatter;

@end

static double lookuptime = 0;

@implementation companyXMLImporter

@synthesize companyURL, delegate, persistentStoreCoordinator;
@synthesize xmlConnection, done, parsingAProduct, storingCharacters, currentProduct, countForCurrentBatch, characterBuffer;

- (void)main {
    if (delegate && [delegate respondsToSelector:@selector(importerDidSave:)]) {
        [[NSNotificationCenter defaultCenter] addObserver:delegate selector:@selector(importerDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.insertionContext];
    }
    done = NO;
    self.characterBuffer = [NSMutableData data];
    NSURLRequest *theRequest = [NSURLRequest requestWithURL:companyURL];

    xmlConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; // create connection with request and start loading data

    context = xmlCreatePushParserCtxt(&simpleSAXHandlerStruct, (__bridge void *)(self), NULL, 0, NULL);
    if (xmlConnection != nil) {
        do {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        } while (!done);
    }

    // Release thread resources
    xmlFreeParserCtxt(context);
    self.characterBuffer = nil;
    self.dateFormatter = nil;
    self.xmlConnection = nil;
    self.currentProduct = nil;
    theCache = nil;

    NSError *saveError = nil;
    NSAssert1([insertionContext save:&saveError], @"Unhandled error saving managed object context in import thread: %@", [saveError localizedDescription]);
    if (delegate && [delegate respondsToSelector:@selector(importerDidSave:)]) {
        [[NSNotificationCenter defaultCenter] removeObserver:delegate name:NSManagedObjectContextDidSaveNotification object:self.insertionContext];
    }
    if (self.delegate != nil && [self.delegate respondsToSelector:@selector(importerDidFinishParsingData:)]) {
        [self.delegate importerDidFinishParsingData:self];
    }
}

- (NSManagedObjectContext *)insertionContext {
    if (insertionContext == nil) {
        insertionContext = [[NSManagedObjectContext alloc] init];
        [insertionContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
    }
    return insertionContext;
}

- (void)forwardError:(NSError *)error {
    if (self.delegate != nil && [self.delegate respondsToSelector:@selector(importer:didFailWithError:)]) {
        [self.delegate importer:self didFailWithError:error];
    }
}

- (NSEntityDescription *)productEntityDescription {
    if (productEntityDescription == nil) {
        productEntityDescription = [NSEntityDescription entityForName:@"Product" inManagedObjectContext:self.insertionContext];
    }
    return productEntityDescription;
}

- (CategoryCache *)theCache {
    if (theCache == nil) {
        theCache = [[CategoryCache alloc] init];
        theCache.managedObjectContext = self.insertionContext;
    }
    return theCache;
}

- (Product *)currentProduct {
    if (currentProduct == nil) {
        currentProduct = [[Product alloc] initWithEntity:self.productEntityDescription insertIntoManagedObjectContext:self.insertionContext];
    }
    return currentProduct;
}

// Forward errors to delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self performSelectorOnMainThread:@selector(forwardError:) withObject:error waitUntilDone:NO];
    done = YES; // End run loop
}

// Called when a chunk of data has been downloaded
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    // Process downloaded chunk of data
    xmlParseChunk(context, (const char *)[data bytes], [data length], 0);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // Signal the context that parsing is complete by passing "1" as last parameter
    xmlParseChunk(context, NULL, 0, 1);
    context = NULL;
    done = YES; // End the loop
}

static const NSUInteger kImportBatchSize = 20;

- (void)finishedCurrentProduct {
    parsingAProduct = NO;
    self.currentProduct = nil;
    countForCurrentBatch++;

    if (countForCurrentBatch == kImportBatchSize) {
        NSError *saveError = nil;
        NSAssert1([insertionContext save:&saveError], @"Unhandled error saving managed object context in import thread: %@", [saveError localizedDescription]);
        countForCurrentBatch = 0;
    }
}

// Character data appended to a buffer until current element ends.
- (void)appendCharacters:(const char *)charactersFound length:(NSInteger)length {
    [characterBuffer appendBytes:charactersFound length:length];
}

- (NSString *)currentString {
    // Create a string with character data using UTF-8 encoding
    NSString *currentString = [[NSString alloc] initWithData:characterBuffer encoding:NSUTF8StringEncoding];
    [characterBuffer setLength:0];
    return currentString;
}

@end

// XML element names, string lengths for parsing
static const char *kName_App = "App"; // Product container tag
static const NSUInteger kLength_App = 4;
static const char *kName_Sku = "prod_sku";
static const NSUInteger kLength_Sku = 9;
static const char *kName_Name = "prod_name";
static const NSUInteger kLength_Name = 10;
static const char *kName_Description = "prod_description";
static const NSUInteger kLength_Description = 17;
static const char *kName_Category = "prod_category";
static const NSUInteger kLength_Category = 14;
static const char *kName_Upc = "prod_upc";
static const NSUInteger kLength_Upc = 9;
static const char *kName_CountryCode = "prod_code_destination";
static const NSUInteger kLength_CountryCode = 22;
static const char *kName_Webpage = "prod_html_link";
static const NSUInteger kLength_Webpage = 15;
static const char *kName_Manual = "prod_manual";
static const NSUInteger kLength_Manual = 12;
static const char *kName_QuickStart = "prod_quick_start";
static const NSUInteger kLength_QuickStart = 17;
static const char *kName_Thumbnail = "prod_thumbnail";
static const NSUInteger kLength_Thumbnail = 15;
static const char *kName_MainImage = "prod_image_main";
static const NSUInteger kLength_MainImage = 16;
static const char *kName_SecondaryImage = "prod_image_secondary";
static const NSUInteger kLength_SecondaryImage = 21;

// Invoked when importer finds beginning of a node
static void startElementSAX(void *parsingContext, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI,
                            int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) {

    companyXMLImporter *importer = (__bridge companyXMLImporter *)parsingContext;

    if (!strncmp((const char *)localname, kName_App, kLength_App)) {
        importer.parsingAProduct = YES;
        } else if (importer.parsingAProduct && ((!strncmp((const char *)localname, kName_Sku, kLength_Sku) || !strncmp((const char *)localname, kName_Name, kLength_Name) || !strncmp((const char *)localname, kName_Description, kLength_Description) || !strncmp((const char *)localname, kName_Category, kLength_Category) || !strncmp((const char *)localname, kName_Upc, kLength_Upc) || !strncmp((const char *)localname, kName_CountryCode, kLength_CountryCode) || !strncmp((const char *)localname, kName_Webpage, kLength_Webpage) || !strncmp((const char *)localname, kName_Manual, kLength_Manual) || !strncmp((const char *)localname, kName_QuickStart, kLength_QuickStart) || !strncmp((const char *)localname, kName_Thumbnail, kLength_Thumbnail) || !strncmp((const char *)localname, kName_MainImage, kLength_MainImage) || !strncmp((const char *)localname, kName_SecondaryImage, kLength_SecondaryImage)))
                   ) {
        importer.storingCharacters = YES;
    }
}

// Invoked when parse reaches end of a node
static void endElementSAX(void *parsingContext, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI) {
    companyXMLImporter *importer = (__bridge companyXMLImporter *)parsingContext;

    if (importer.parsingAProduct == NO) return;
    if (!strncmp((const char *)localname, kName_App, kLength_App)) {
        [importer finishedCurrentProduct];
    } else if (!strncmp((const char *)localname, kName_Name, kLength_Name)) {
        importer.currentProduct.name = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_Category, kLength_Category)) {
        double before = [NSDate timeIntervalSinceReferenceDate];
        Category *category = [importer.theCache categoryWithName:importer.currentString];
        double delta = [NSDate timeIntervalSinceReferenceDate] - before;
        lookuptime += delta;
        importer.currentProduct.category = category;
    } else if (!strncmp((const char *)localname, kName_Sku, kLength_Sku)) {
        importer.currentProduct.sku = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_Description, kLength_Description)) {
        importer.currentProduct.prodDescription = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_Upc, kLength_Upc)) {
        importer.currentProduct.upc = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_CountryCode, kLength_CountryCode)) {
        importer.currentProduct.countryCode = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_Webpage, kLength_Webpage)) {
        importer.currentProduct.webpage = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_Manual, kLength_Manual)) {
        importer.currentProduct.manual = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_QuickStart, kLength_QuickStart)) {
        importer.currentProduct.quickStart = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_Thumbnail, kLength_Thumbnail)) {
        importer.currentProduct.thumbURLString = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_MainImage, kLength_MainImage)) {
        importer.currentProduct.mainImgURLString = importer.currentString;
    } else if (!strncmp((const char *)localname, kName_SecondaryImage, kLength_SecondaryImage)) {
        importer.currentProduct.secondaryImgURLString = importer.currentString;
    }
    importer.storingCharacters = NO;
}

// Invoked when parser encounters character data inside a node
static void charactersFoundSAX(void *parsingContext, const xmlChar *characterArray, int numberOfCharacters) {
    companyXMLImporter *importer = (__bridge companyXMLImporter *)parsingContext;

    // storingCharacters set when nodes of interest begin/end – determines whether character data handled/ignored
    if (importer.storingCharacters == NO) return;
    [importer appendCharacters:(const char *)characterArray length:numberOfCharacters];
}

// Error handling
static void errorEncounteredSAX(void *parsingContext, const char *errorMessage, ...) {
    // Handle errors as appropriate
    NSCAssert(NO, @"Unhandled error encountered during SAX parse.");
}

static xmlSAXHandler simpleSAXHandlerStruct = {
    NULL, /* internalSubset */
    NULL, /* isStandalone   */
    NULL, /* hasInternalSubset */
    NULL, /* hasExternalSubset */
    NULL, /* resolveEntity */
    NULL, /* getEntity */
    NULL, /* entityDecl */
    NULL, /* notationDecl */
    NULL, /* attributeDecl */
    NULL, /* elementDecl */
    NULL, /* unparsedEntityDecl */
    NULL, /* setDocumentLocator */
    NULL, /* startDocument */
    NULL, /* endDocument */
    NULL, /* startElement*/
    NULL, /* endElement */
    NULL, /* reference */
    charactersFoundSAX, /* characters */
    NULL, /* ignorableWhitespace */
    NULL, /* processingInstruction */
    NULL, /* comment */
    NULL, /* warning */
    errorEncounteredSAX, /* error */
    NULL,/* fatalError //: unused error() get all the errors */
    NULL, /* getParameterEntity */
    NULL, /* cdataBlock */
    NULL, /* externalSubset */
    XML_SAX2_MAGIC, //
    NULL,
    startElementSAX, /* startElementNs */
    endElementSAX, /* endElementNs */
    NULL, /* serror */
};

Aucun commentaire:

Enregistrer un commentaire