glennsep
glennsep

Reputation: 174

How to retrieve an image from an asset field in CloudKit using objective-c

I have a record type in CloudKit. It stores one string field and one asset field. Using the dashboard I uploaded an image to the asset field.

I can retrieve the string field. I now need to retrieve the contents of the asset field and store it to an image using objective-c. Can anyone help?

I am trying to display the record type in a table view. I have a class called TJKCategoriesViewController that inherits from UIViewController.

I have this code in my viewDidLoad method for the TJKCategoriesViewController class:

// get all categories
_jokeCategories = [[NSMutableArray alloc] init];
CKDatabase *jokePublicDatabase = [[CKContainer containerWithIdentifier:JOKE_CONTAINER] publicCloudDatabase];
NSPredicate *predicateCategory = [NSPredicate predicateWithFormat:@"CategoryName != %@", CATEGORY_TO_REMOVE_OTHER];
CKQuery *queryCategory = [[CKQuery alloc] initWithRecordType:CATEGORY_RECORD_TYPE predicate:predicateCategory];
NSSortDescriptor *sortCategory = [[NSSortDescriptor alloc] initWithKey:CATEGORY_FIELD_NAME ascending:YES];
queryCategory.sortDescriptors = [NSArray arrayWithObjects:sortCategory, nil];
[jokePublicDatabase performQuery:queryCategory inZoneWithID:nil completionHandler:^(NSArray<CKRecord*>* results, NSError * error)
 {
     if (!error)
     { 
         // add all categories to array
         for (CKRecord* jokeCategory in results)
         {
             TJKCategories *categories = [TJKCategories initWithCategory:[jokeCategory valueForKey:CATEGORY_FIELD_NAME] categoryImage:[jokeCategory valueForKey:@"CategoryImage"]];
             [_jokeCategories addObject:categories];
         }


         NSLog(@"Categories = %@",_jokeCategories);

         // setup table
         [self setupTableContents];
     }
     else
     {
         // display alert message and pop the view controller from the stack
         GHSAlerts *alert = [[GHSAlerts alloc] initWithViewController:self];
         errorActionBlock errorBlock = ^void(UIAlertAction *action) {[self closeCategories:self];};
         [alert displayErrorMessage:@"Oops!" errorMessage:@"The joke categories failed to load. This screen will close. Just try again!" errorAction:errorBlock];
     }
 }];

This is the method "setupTableContents"

// setup the array that will hold the table's contents
-(void)setupTableContents
{   
    // store to property and reload the table contents
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.categoriesTableView reloadData];
    });
}

TJKCategories is a class that stores the two fields in the record type. This is the TJKCategories.h file:

#import <UIKit/UIKit.h>

@interface TJKCategories : NSObject

#pragma Properties

@property (nonatomic, copy) UIImage *categoryImage;
@property (nonatomic, copy) NSString *categoryName;

#pragma Initializers
+(instancetype)initWithCategory:(NSString *)categoryName
                  categoryImage:(UIImage *)categoryImage;


-(instancetype)initWithCategory:(NSString *)categoryName
                  categoryImage:(UIImage *)categoryImage;

@end

This is the TJKCategories.m file:

// designated initializer
-(instancetype)initWithCategory:(NSString *)categoryName categoryImage:(UIImage *)categoryImage
{
    self = [super init];

    if (self)
    {
        self.categoryImage = categoryImage;
        self.categoryName = categoryName;
    }

    return self;
}

// class level initializer
+(instancetype)initWithCategory:(NSString *)categoryName categoryImage:(UIImage *)categoryImage
{
    TJKCategories *category = [[self alloc]initWithCategory:categoryName categoryImage:categoryImage];
    return category;
}

// override default initializer
-(instancetype)init
{
    return [self initWithCategory:@"" categoryImage:nil];
}

In the viewDidLoad method I am using: [jokePublicDatabase performQuery:queryCategory inZoneWithID:nil completionHandler:^(NSArray* results, NSError * error)

To retrieve the results of the record type. I am then looping through the results array, I can retrieve the string "CATEGORY_FOR_NAME" (which is a constant for "CategoryName", but when I try and retrieve the field for the image "CategoryImage" I get and "unrecognized selector sent to instance" error.

If you need to know what the names behind the constants are here is my contacts file:

// declare constants
extern NSString *const DEFAULT_CATEGORY;
extern NSString *const JOKE_CONTAINER;
extern NSString *const CATEGORY_RECORD_TYPE;
extern NSString *const CATEGORY_FIELD_NAME;
extern NSString *const CATEGORY_TO_REMOVE_OTHER;
extern NSString *const CATEGORY_TO_REMOVE_RANDOM;
extern NSString *const JOKE_RECORD_TYPE;
extern NSString *const JOKE_DESCR;
extern NSString *const JOKE_SUBMITTED_BY;
extern NSString *const JOKE_TITLE;

// assign values to constants
NSString *const DEFAULT_CATEGORY = @"None";
NSString *const JOKE_CONTAINER = @"iCloud.com.glennseplowitz.Jokes";
NSString *const CATEGORY_RECORD_TYPE = @"Categories";
NSString *const CATEGORY_FIELD_NAME = @"CategoryName";
NSString *const CATEGORY_TO_REMOVE_OTHER = @"Other";
NSString *const CATEGORY_TO_REMOVE_RANDOM = @"Random";
NSString *const JOKE_RECORD_TYPE = @"Jokes";
NSString *const JOKE_DESCR = @"jokeDescr";
NSString *const JOKE_SUBMITTED_BY = @"jokeSubmittedBy";
NSString *const JOKE_TITLE = @"jokeTitle";

So what I am trying to do is retrieve the asset type "CategoryImage" from the record type and store it to a UIImage. But like I said it gives me the error "unrecognized selector sent to instance" because I believe it cannot convert an asset field to a UIImage field directly.

Thanks, Glenn

Upvotes: 1

Views: 627

Answers (1)

glennsep
glennsep

Reputation: 174

Figured it out! I changed my TJKCategories class to accept a CKAsset instead of a UIImage. I then converted the CKAsset to a UIImage.

Here is the TJKCategories.h file:

#import <UIKit/UIKit.h>
#import <CloudKit/CloudKit.h>

@interface TJKCategories : NSObject

#pragma Properties

@property (nonatomic, copy) UIImage *categoryImage;
@property (nonatomic, copy) NSString *categoryName;

#pragma Initializers
+(instancetype)initWithCategory:(NSString *)categoryName
                  categoryImage:(CKAsset *)categoryImageAsset;


-(instancetype)initWithCategory:(NSString *)categoryName
                  categoryImage:(CKAsset *)categoryImageAsset;


@end

Here is the TJKCategories.m file:

#import "TJKCategories.h"

@interface TJKCategories()

#pragma Properities
@property (nonatomic, strong) CKAsset *categoryImageAsset;

@end

@implementation TJKCategories

#pragma initializers

// designated initializer
-(instancetype)initWithCategory:(NSString *)categoryName categoryImage:(CKAsset *)categoryImageAsset
{
    self = [super init];

    if (self)
    {
        self.categoryImageAsset = categoryImageAsset;
        self.categoryName = categoryName;
        UIImage *image = [[UIImage alloc] initWithContentsOfFile:self.categoryImageAsset.fileURL.path];
        self.categoryImage = image;       
    }

    return self;
}

// class level initializer
+(instancetype)initWithCategory:(NSString *)categoryName categoryImage:(CKAsset *)categoryImageAsset
{
    TJKCategories *category = [[self alloc]initWithCategory:categoryName categoryImage:categoryImageAsset];
    return category;
}

// override default initializer
-(instancetype)init
{
    return [self initWithCategory:@"" categoryImage:nil];
}

I also changed the line:

TJKCategories *categories = [TJKCategories initWithCategory:[jokeCategory valueForKey:CATEGORY_FIELD_NAME] categoryImage:[jokeCategory **objectForKey**:@"CategoryImage"]];

to

TJKCategories *categories = [TJKCategories initWithCategory:[jokeCategory valueForKey:CATEGORY_FIELD_NAME] categoryImage:[jokeCategory **valueForKey**:@"CategoryImage"]];

Note that CategoryImage is now using valueForKey and not objectForKey.

Upvotes: 1

Related Questions