0

I have an NSMutableArray self.certificates

This array is made up of saved strings and core data. I want to send this through AirDrop. I have checked out serialization and and im trying to send it with the folowing

- (void)send{

NSData *jsonData2 = [NSJSONSerialization dataWithJSONObject:self.certificates options:NSJSONWritingPrettyPrinted error:nil];

NSString *jsonString = [[NSString alloc] initWithData:jsonData2 encoding:NSUTF8StringEncoding];
NSLog(@"Electrical Certificates List:\n%@", jsonString);


UIActivityViewController *activityCtr = [[UIActivityViewController alloc] initWithActivityItems:@[jsonString]
                                                                          applicationActivities:nil];

NSMutableArray *excludedActivities = [self iOSActivities].mutableCopy;
[excludedActivities addObject:UIActivityTypeAddToReadingList];
[excludedActivities addObject:UIActivityTypePostToFlickr];
[excludedActivities addObject:UIActivityTypePostToTencentWeibo];
[excludedActivities addObject:UIActivityTypePostToVimeo];
[activityCtr setExcludedActivityTypes:excludedActivities];

[self presentViewController:activityCtr
                   animated:YES
                 completion:nil];
}

This gives me the following error
'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Certificate)'

I have converted to data to a string so not sure what im missing here

Ive researched NSInvalidArgumentException, reason: 'Invalid type in JSON write (__NSDate)' and How to send NSArray to web service

Community
  • 1
  • 1
JSA986
  • 5,870
  • 9
  • 45
  • 91

1 Answers1

4

The exception is thrown by JSONSerialization, before the array is converted to data.

To share a custom data type, you'll want to implement NSCoding and UIActivityItemSource on your model object:

@interface CertificateGroup : NSObject <NSCoding, UIActivityItemSource>

@property(copy, nonatomic) NSArray *certificates;

@end

@implementation CertificateGroup

- (void)encodeWithCoder:(NSCoder *)aCoder {
    // Save all your custom properties
    [aCoder encodeObject:self.certificates forKey:@"certificates"]l
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        // Read back properties
        self.certificates = [aDecoder decodeObjectForKey:@"certificates"];
    }
    return self;
}

- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
    //Let the activity view controller know NSData is being sent by passing this placeholder.
    return [NSData data];
}

- (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
    //Serialize this object for sending. NSCoding protocol must be implemented for the serialization to occur.
    return [NSKeyedArchiver archivedDataWithRootObject:self];
}

- (NSString *)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(NSString *)activityType {
    return @"com.mycompany.myapp.certificates";
}

@end

Then, when you create your activity view controller:

CertificatesGroup *group = [CertificatesGroup new];
group.certificates = self.certificates;
UIActivityViewController *activityCtr = [[UIActivityViewController alloc] initWithActivityItems:@[group]
                                                                          applicationActivities:nil];
...

You're app delegate should implement -application:openURL:sourceApplication:annotation: and decode the incoming certificates.

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
     NSData *groupData = [NSData dataWithContentsOfURL:url];
     CertificatesGroup *group = [NSKeyedUnarchiver unarchiveObjectWithData:groupData];
     NSLog(@"%@", group.certificates);
     return YES;
}

For more info, see Apple's AirDropSample project, especially APLProfile.h/.m, APLProfileViewController.h/.m and AppDelegate.m.

Austin
  • 5,625
  • 1
  • 29
  • 43
  • Thanks for reply, that gives me a nil string for `archivedString` – JSA986 Mar 31 '14 at 20:42
  • Ah, it's probably not a valid string. You might be able to pass the `NSData` object directly to the activity view controller. I assume, however, that you want to be able to open this data in your app on the receiving device, then decode it? – Austin Mar 31 '14 at 21:13
  • Yes thats its, the NSMutable array populates a table view, i want to send this to another device and receive an decode it. Just a way to manually keep devices in sync, tried iCloud but cant seem to get that functioning at all! – JSA986 Mar 31 '14 at 21:15
  • Are your certificates custom objects, or provided by the framework? – Austin Mar 31 '14 at 21:28
  • They are Custom objects – JSA986 Mar 31 '14 at 21:30
  • Updated my answer. You should probably read through that whole AirDropSample example - it will help a lot with understanding how AirDrop works. – Austin Mar 31 '14 at 21:47
  • Thnak you very much for providing such a detailed answer - plus 1, ive got the air drop sample code an have been studying it. Just one more question `self.certificates = [aDecoder objectForKey:@"certificates"];` gives me `no visibel@ interface error NSCoder declares the object` – JSA986 Mar 31 '14 at 22:08
  • Sorry, should be `decodeObjectForKey:`. Updated my answer. – Austin Apr 01 '14 at 13:11
  • Ah thanks for that, and should I return self in that method? or NSData? Presumably its NSData in the delegate method you provided? – JSA986 Apr 01 '14 at 17:34
  • Which delegate method? – Austin Apr 01 '14 at 18:47
  • `- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {` what should I be returning here? Im retuning self in `- (id)initWithCoder:(NSCoder *)aDecoder {` Correct? – JSA986 Apr 01 '14 at 21:39
  • You should return a `BOOL` indicating whether the request was handled successfully in app delegate method, and `self` in the init method. – Austin Apr 01 '14 at 21:47