3

I have an app with an iOS 10 iMessage app. When I attach my file URL to an MSMessage message.URL is (null). I really don't know what's causing this. When I check the logs, I see a proper url: URL: file:///thisuser/... etc. However, message.URL logs (null).

I've build an Exporter class, this saves the file to disk and then returns the path for it.

+ (NSString *) saveToDisk:(NSDictionary *)dictionary {
    // Figure out destination name (in public docs dir)
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *zippedName = [self getExportFileName:dictionary withExtension:YES];
    NSString *zippedPath = [documentsDirectory stringByAppendingPathComponent:zippedName];

    // Export to data buffer
    NSData *gzData = [NSKeyedArchiver archivedDataWithRootObject:dictionary];

    if (gzData == nil) return FALSE;

    // Write to disk
    [gzData writeToFile:zippedPath atomically:YES];

    return zippedPath;
}

This will return something like: /Users/thisuses/Library/Developer/CoreSimulator/Devices/.../Documents/new-save.rst, where .rst is a custom file extension just for my app. This, in turn, is added to the MSMessage.

MSConversation *conversation = [self activeConversation];

MSMessageTemplateLayout *layout = [[MSMessageTemplateLayout alloc] init];
layout.image = [UIImage imageNamed:@"test"];
layout.caption = url.host;

MSMessage *message = [[MSMessage alloc] init];
message.layout = layout;

NSLog(@"Converter: %@", [Converter toDictionary:array]);
NSLog(@"Exporter: %@", [Exporter saveToDisk:[Converter toDictionary:array]]);
NSLog(@"URL: %@", [NSURL fileURLWithPath:[Exporter saveToDisk:[Converter toDictionary:array]]]);

message.URL = [NSURL fileURLWithPath:[Exporter saveToDisk:[Converter toDictionary:array]]];

NSLog(@"Message URL 1: %@", message.URL);

[conversation insertMessage:message completionHandler:^(NSError * error) {
    NSLog(@"MSConvo error: %@",error);
}];

== Edit: I added a check to the code to see if the Exporter returns a valid file path and turns out, it does.

NSURL *fileURL = [NSURL fileURLWithPath:[Exporter saveRequestToDisk:[Converter databaseToRequest:history]]];

if ([fileURL isFileURL]) {
    NSLog(@"is File URL!");
    message.URL = fileURL;
}
user4992124
  • 1,474
  • 1
  • 12
  • 27
  • You call '[Exporter saveToDisk:[Converter toDictionary:array]]' three times in that snippet. This leads me to ask if the filepath that the data is written to stays the same for all three of these calls, i.e. could you show us or elaborate on the implementation of `[self getExportFileName: withExtension:]`? – Jan Nash Nov 01 '16 at 15:25
  • Two of them are `NSLog`s that have been added after noticing the problem. When I remove them - and thus call it only once-, I get the same result. – user4992124 Nov 01 '16 at 15:29
  • I have no idea yet, but for debugging purposes, you could add an NSLog to `if (gzData == nil) return FALSE;`, i.e. `if (gzData == nil) { NSLog(@"Archiving failed!"); return FALSE; }` and instead of leaving the result of `[gzData writeToFile:zippedPath atomically:YES];` unused, add a check there, too, like `if (![gzData writeToFile:zippedPath atomically:YES]) { NSLog(@"Failed writing to file-path %@", zippedPath) }`. As a side note, I think, it might be better practice to return nil instead of FALSE in the former call. If you desperately need a real Bool, you should use NO, not FALSE, I guess – Jan Nash Nov 01 '16 at 15:34
  • (There's a semicolon missing after the last NSLog in my comment, I was too slow to edit the comment again, sorry :D) – Jan Nash Nov 01 '16 at 15:40
  • Thanks, @JanNash. Both succeed though I added some more information to the question as well. – user4992124 Nov 01 '16 at 15:49

1 Answers1

3

After looking into the docs and skimming this article, I think that the url property is not supposed to point to a file. Instead it should

[...][encode] data to be transmitted with the message.

I guess the right way to go is to

Encode your application’s data in the URL. For example, you can encode data as key-value pairs in the URL’s query string, as shown below:

guard let components = NSURLComponents(string: myBaseURL) else {
    fatalError("Invalid base url")
}

let size = NSURLQueryItem(name: "Size", value: "Large")
let count = NSURLQueryItem(name: "Topping_Count", value: "2")
let cheese = NSURLQueryItem(name: "Topping_0", value: "Cheese")
let pepperoni = NSURLQueryItem(name: "Topping_1", value: "Pepperoni")
components.queryItems = [size, count, cheese, pepperoni]

guard let url = components.url  else {
    fatalError("Invalid URL components.")
}

message.url = url

(Code taken from the docs, you might want to convert it to ObjC...)

So instead of converting your dictionary to NSData and writing it to a file, you might want to encode it into queryItems, possibly like this:

NSMutableArray *queryItems = [[NSMutableArray alloc] init]; // Or initWithCapacity for the sake of performance...
NSDictionary *dict = [Converter toDictionary:array];

for (id key in dict) {
    id value = queryDictionary[key];
    NSURLQueryItem *queryItem = [NSURLQueryItem queryItemWithName:key value:value];
    [queryItems addObject:queryItem];
}

[url setQueryItems:queryItems];
Jan Nash
  • 1,621
  • 18
  • 24
  • Damn, that's some weird documentation. They clearly state that it can either contain `HTTP`, `HTTPS` of a file scheme. Anyway, too bad you can't send a file across. – user4992124 Nov 01 '16 at 15:58
  • They do state that this is the case on macOS only, though it's not obvious. Had to read it twice to get the subtle context-change... -.- – Jan Nash Nov 01 '16 at 16:09
  • (Also, could you accept my answer, since it's technically correct? That would be nice :)) – Jan Nash Nov 01 '16 at 16:11
  • Yeah, sure. Thanks a lot! – user4992124 Nov 01 '16 at 16:11
  • Thanks, that was my first accepted one :) Also, maybe you could change the title to something like `MSMessage url stays nil after setting it to url of a file`? That way, I guess, other people with the same problem might find it more easy. There will be some people, I guess, since the docs are really a bit confusing... – Jan Nash Nov 01 '16 at 16:15