Is it possible to load a URL inside the body of MFMailComposeViewController? If not, what is the efficient way where I can load this URL (i.e. HTML) so I can load it in the mail body?
You can load HTML into a MFMailComposeViewController like this:
MFMailComposeViewController *composer = [MFMailComposeViewController new];
[composer setSubject:#"HTML test"];
[composer setMessageBody:#"<html><body><h1>Test</h1></body></html>" isHTML:YES];
However, you can't load an externally hosted website into it. You would have to fetch the HTML document and modify it, so that the links to the assets (images, scripts etc.) are absolute and include the protocol and hostname of the remote host (where you downloaded the website). You then could set this modified source code as the composer's messageBody.
Related
We have an iOS application that manages documents via Core Data. The actual files reside in the app's shared container so that the app's file provider extension can also access them for Files.app support. We want to give the user the option to open these files in third-party apps so that they can edit them in-place instead of sending a copy to the other app.
We provide a UIActivityViewController for sharing files with other apps. We also provide a UIActivity that shows a UIDocumentInteractionController which seems to work better in some cases. We give the UIActivityViewController the document's file URL, the raw text content, and printable data.
This works but all third-party editors are shown as Copy to … instead of Open in …
We've also set the UIFileSharingEnabled and LSSupportsOpeningDocumentsInPlace properties to YES in the app's info.plist but they seem to be only relevant for open-in-place when sharing files residing in the app's Documents folder.
Now we've stumbled upon the NSItemProviderFileOptionOpenInPlace option for NSItemProvider. As we're already supporting a file provider extension and from Apple's documentation this seemed like a great place to accomplish just what we want.
Adding a "pure" NSItemProvider works, in a way, but shows fewer options than when also sharing the file URL and text in addition (which is expected). However, when we use -[NSItemProvider registerFileRepresentationForTypeIdentifier:fileOptions:visibility:loadHandler:] with the said option (or just zero, same result) and return the file URL in the loadHandler's completionHandler() nothing is shared anymore. E.g., Mail no longer attaches the file, Messages doesn't show the document for sending.
These are the relevant bits of the code:
NSMutableArray *items = [NSMutableArray array];
NSMutableArray <UIActivity *> *activities = [NSMutableArray array];
NSURL *fileURL = self.record.metadata.fileURL;
NSString *fileUTI = self.record.metadata.uti;
NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithItem: fileURL typeIdentifier: fileUTI];
[itemProvider registerFileRepresentationForTypeIdentifier:fileUTI fileOptions:NSItemProviderFileOptionOpenInPlace visibility:YES loadHandler:^NSProgress * _Nullable(void (^ _Nonnull completionHandler)(NSURL * _Nullable, BOOL, NSError * _Nullable))
{
if (fileURL)
completionHandler(fileURL, YES, nil);
else
completionHandler(nil, YES, [NSError errorWithDomain:NSCocoaErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]);
return nil;
}];
[items addObject:itemProvider];
self.activityViewController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:activities];
[UIAppDelegate.splitViewController presentViewController:self.activityViewController animated:YES completion:nil];
The using the Share menu the item provider's load handler is correctly called and the file's actual URL returned.
Is that not how NSItemProviderFileOptionOpenInPlace is intended to be used? Or are we using it simply wrong? Apple's description is extremely sparse and we couldn't find any information elsewhere on the internet except for the official documentation.
I've found out what my problem was: Not deep enough understanding of the relationship between the activity view controller and file providers.
As all my files reside in the shared container and are published also through the file provider extension, what I need to share through the activity view controller is the exact same URL that is shared through the file provider extension. Technically then the app that opens the file accesses it through there file provider mechanism.
I'm trying to get WKWebView to display locally downloaded images in a WKWebView. The webview normally displays HTML, which is retrieved remotely. The contents of the HTML can sometimes contain remote links to images. My app parses the HTML and looks for these HTML tags, downloads the file it is referencing and subsequently replaces the remote link with a local one.
Normally speaking, this wouldn't be very difficult but the images aren't being displayed, presumably due to the images and the local HTML files for the webview being in two separate directories (the documents directory and the app bundle directory respectively).
I've seen people suggest moving the download destination of the images to the same directory as where the HTML files are but this isn't an option for me as I don't want to start mixing up files downloaded by the user with local assets.
What would be my best course of action here?
Well, I've found a workaround. Instead of locally storing the images and referencing them in the HTML files, I'm now instead converting the images to Base64 and then adding them to the HTML. It's not ideal but it gets the job done. I'm going to leave this question open in case someone ever manages to find an actual solution.
To display cached HTML referencing cached resources in a WKWebView:
For each of the resources within your HTML content string, cache it into the directory as provided by NSTemporaryDirectory(). So an image tag like:
...<img src='https://www.myimage.com/example_image.png'/>...
should be cached and replaced into something like this:
...<img src='/private/var/mobile/Containers/Data/Application/527CF4FC-9319-4DFF-AB55-9E276890F5DC/tmp/example_image.png'/>...
Now cache the HTML content string with the replaced resource URLs. It must also be cached in the directory provided by NSTemporaryDirectory(). One difference here is that it must be cached (and later referenced) using the file:// protocol as a restriction of caching the string using NSData (see sample code).
For example file:///private/var/mobile/Containers/Data/Application/527CF4FC-9319-4DFF-AB55-9E276890F5DC/tmp/my_html_content_string.html
A few things to point out:
You cannot load the HTML as a raw string (loadHTMLString:baseURL:).
You cannot reference the cached resource within your HTML string using the file:// protocol. That may work in a UIWebView, but will not work in the WKWebView.
Objective-C
// To cache the HTML string:
NSString *HTML = <HTML CONTENT WITH CACHED RESOURCES>;
NSData *data = [HTML dataUsingEncoding: NSUTF8StringEncoding];
[data writeToURL: cachedHTMLURL atomically: YES];
// To load the store HTML file:
[myWKWebView loadRequest: [NSURLRequest requestWithURL: cachedHTMLURL]]; // (file://.../tmp/my_html_content_string.html)
Swift
// To cache the HTML string:
let HTML = <HTML CONTENT WITH CACHED RESOURCES>
let data = HTML.data(using: String.Encoding.utf8)
do {
try data.write(to: cachedHTMLURL, options: .atomic)
} catch {
print(error)
}
// To load the store HTML file:
myWKWebView.load(URLRequest(url: cachedHTMLURL)) // (file://.../tmp/my_html_content_string.html)
I had the same problem with WKWebView as it can not load both html strings and images at the same time for security purposes. I switched to UIWebView, which is deprecated, but I was able to load both html strings and referenced images at the same time.
I developed a definitive solution for the company I work for. But it relies on the html / javascript side. Anywhere inside your html code where you will reference to a local image <img src="..."/> you should set this "src" dynamically, and it will work seamlessly.
function getLocalURL(path) {
let origin = window.location.origin
if (origin == "file://") {
return origin + window.location.pathname.replace("/index.html","") + path
}
return path
}
You should, clearly, rename index.html to whatever is your main .htm(l) filename :)
Usage:
getLocalURL("/local_images/location_icon.png")
Will return a WKWebView working path for the referenced local image path:
"file:///Users/arthurdapaz/Library/Developer/CoreSimulator/Devices/5073AF19-26A0-460E-BC82-E89100B8E1AB/data/Containers/Data/Application/2B099343-0BF5-4849-B1C2-2512377A9772/Documents/distDriver/local_images/location_icon.png"
I tried to load some files from Dropbox/G-Drive/iCloud using the UIDocumentMenuViewController. I tried,
UIDocumentMenuViewController *importMenu = [[UIDocumentMenuViewController alloc] initWithDocumentTypes:#[(__bridge NSString*)kUTTypeContent] inMode:UIDocumentPickerModeImport];
importMenu.delegate = self;
importMenu.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:importMenu animated:YES completion:nil];
with this to get this done. But I need to get only the doc and pdf types only from picker. But when I use kUTTypeContent, this allows for images and videos also. Is there a way to allow this picker only for doc and pdf(custom types) or is there a why to prevent media files? Please help me with this.
I found a solution for this from apple developer site. There's a type called kUTTypeCompositeContent which is support only for all document types like doc, pdf, xls etc.
public.composite-content:(kUTTypeCompositeContent)
Base type for mixed content. For example, a PDF file contains both text and special formatting data.
As per my requirement, want to access iOS device photos app file url (not file as data) for my GCDWebUploader. I want assets library url for my web server.
NSString* documentsPath =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory: documentsPath];
// this is working and document directory files shown on browser.
_webServer = [[GCDWebUploader alloc] initWithUploadDirectory:assetsUrl]; // this is not working.Nothing shown on browser. //assetsUrl= assets library url for file from photos app
_webServer.delegate = self;
_webServer.allowHiddenItems = YES;
[_webServer start];
my web-server display all the photos app images and videos on pc browser if document directory.this functionality already done using GCDWebUploader. but I can't find asset url behave like file path.
I don't want to copy the photos app files into document-directory and use.but directly access from assets library.
I want assets url work same like document directory filepath. please help me for that.
An asset URL looks like this:
assets-library://asset/asset.JPG?id=CD12228F-0E99-4ABD-999D-6A76F54024E7&ext=JPG
This is an internal URL to ALAssetsLibrary which means nothing outside of this context. You can't expect to pass this URL to GCDWebServer and expect the server to magically do something with it.
Furthermore, GCDWebServer by definition can only serve URLs with the HTTP scheme, with the hostname matching your iPhone/iPad network name, and with paths for which you have implemented handlers.
For instance if you have implemented a GET handler for the path /photos/index.html, then connecting to your iPhone/iPad using your web browser at http://my-device.local/photos/index.html will call the corresponding handler on GCDWebServer, which then can return some content (like an HTML web page or an image file).
Connecting however to assets-library://asset/asset.JPG from your web browser doesn't mean anything and will fail.
Connecting to http://my-device.local/asset.JPG?id=CD12228F-0E99-4ABD-999D-6A76F54024E7&ext=JPG will also fail if you don't have a GET handler in GCDWebServer for that path.
So in a nutshell, to serve photos from ALAssetsLibrary using GCDWebServer, you can do it as such:
Implement a default handler to catch all GET requests
Implement a handler for GET requests to /index.html (you must add it to the GCDWebServer instance after the default handler)
In the implementation of the /index.html handler, you return an HTML web page that lists the URLs of the photo assets from ALAssetsLibrary, each of them having a relative URL link like My photo link (the path portion of the asset URL).
In the implementation of the default handler, you retrieve the path of the GCDWebServerRequest, prepend assets-library://asset, and that gives you back the original asset URL: assets-library://asset/asset.JPG?id=CD12228F-0E99-4ABD-999D-6A76F54024E7&ext=JPG. With this URL, you can finally retrieve the asset data, i.e. the JPEG image, and return it using a GCDWebServerDataResponse (don't forget to set the MIME type to image/jpeg).
I'm working with Xcode.
In my app I save some UIdocuments at that location
[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
I'm searching for a way to share documents, my first option is by email.
Can I send those documents by email, as an attachment? Can I open then with another device with the same app?
You could do like the following.
Create a MFMailComposeViewController and use - (void)addAttachmentData:(NSData*)attachment mimeType:(NSString*)mimeType fileName:(NSString*)filename method to add your attachment.
For example.
MFMailComposeViewController *mailVC = [[MFMailComposeViewController alloc] init];
[mailVC setSubject:#"Shared documents"];
[mailVC setToRecipients:#[#"sample#example.com"]];
[mailVC setMessageBody:#"Here the docs I want to share" isHTML:NO];
[mailComposer addAttachmentData:pdfData mimeType:#"application/pdf" fileName:#"file.pdf"];
[mailVC setMailComposeDelegate:self];
[self presentViewController:mailVC animated:YES completion:nil];
where pdfData is of type NSData. So, you need to transform your document into a NSData.
From Apple doc.
addAttachmentData:mimeType:fileName:
This method attaches the specified data after the message body but
before the user’s signature. You may attach multiple files (using
different file names) but must do so prior to displaying the mail
composition interface. Do not call this method after presenting the
interface to the user.
About the second part of your question. Could you explain what type of document do you need to display?
In the meantime, take a look at Adding "Open In..." option to iOS app.
To send any attachment you need to get the contents into an NSData object. If the document is on disk then this is simple. You just need the path or file URL to the document. Then you can create the NSData object using the path or URL.
If the receiver of the email has the same app and the app is setup to appear in the "Open In" menu for documents of this type, then the user can open the app from the attachment. Your app then just needs to know what to do when it is asked to open a file of this type. There are plenty of existing documentation and questions here on SO that describe how to register an app to open certain file types.