FlexCel iOS Guide
Introduction
The FlexCel code you have to write for iOS itself is very similar to normal FlexCel for Windows code.
This is for example the code needed to create a file in Windows:
static void CreateFile()
{
XlsFile xls = new XlsFile(1, true);
xls.SetCellValue(1, 1, "FlexCel says hello!");
xls.Save("result.xlsx");
}
And this is the code needed to create the same file in iOS:
static void CreateFile()
{
XlsFile xls = new XlsFile(1, true);
xls.SetCellValue(1, 1, "FlexCel says hello!");
xls.Save("result.xlsx");
}
There are really not many differences, and this applies to most FlexCel code you can write: It is exactly the same. So why are we covering iOS in a separate document? What else can we say that is not covered in the other documents?
Well, while most FlexCel code will be the same, there is a fundamental difference between iOS and Windows: Files in iOS are in a sandbox. You can’t just open a file in “My Documents” and save it in another place in the disk. Actually, you can’t access any file outside the folder where your application is installed. How to deal with the file sandbox is what we are going to cover on the rest of this file.
The document Sandbox
When working in Windows, applications can access almost any file in the hard drive. Which is a nice thing from a usability point of view, but a complete nightmare from a security point of view. Imagine you download an application from the internet, how do you prevent it from encrypting all the documents in your hard drive and then asking for some ransom money in order to decrypt them again?
For this reason, in iOS your application can only read and write to the folder where it is installed or its subfolders. This gives you the added advantage that when you uninstall the app it is gone completely, as it can’t leave garbage all over your hard disk. But on the other side, how do you work with a restriction like this? How do you create a file in Excel, open it with FlexCel, modify its values and give it back to Excel, if FlexCel and Excel can’t see each other at all?
The first way to share things is for special files: Apps can access certain other files such as address book data and photos, but only through APIs specifically designed for that purpose. But this isn’t a general solution, and while it might work for images, it won’t work for xls/x or PDF files.
The solution for more general files comes in 2 parts:
In order to do anything useful with the xls/x, pdf, or html files FlexCel can generate, you need to Export them to other apps.
To be able to read files from other apps like Dropbox or the email, you need to Import the files from the other apps.
With this Import/Export system, your application can’t open any file that wasn’t given to it. In order to open a file, the user needs to export it to your app.
A look at some of the available folders for your application
Before we continue, and having established that you can’t write to any folder in the device, let’s look at the folders where you can read or write:
<Application_Home>/Documents/ This is where you normally will put your files. Backed up by iTunes.
<Application_Home>/Documents/Inbox This is where other apps will put the files they want to share when exporting to your app. Read only. Backed up by iTunes.
<Application_Home>/Library/ This is for the files that ship with your app, but not for user files. You could for example put xls/x templates here.
<Application_Home>/tmp/ The files you write here might be deleted when your app is not running. Not backed up by iTunes.
Those are at a glance the most important folders you need to know about. You can get a more complete description of the available folders in the Apple documentation.
Importing files from other apps
Registering your app with iOS
In order to be able to interact with files from other applications, you need to register your application as a program that can handle the required file extensions. To do so, you need to modify the file Info.plist in your app bundle.
For handling xls or xlsx files, you would need to add the following to your Info.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Excel document</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.microsoft.excel.xls</string>
<string>com.tms.flexcel.xlsx</string>
<string>org.openxmlformats.spreadsheetml.sheet</string>
</array>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeDescription</key>
<string>Excel xlsx document</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>xlsx</string>
<key>public.mime-type</key>
<string>application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</string>
</dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.tms.flexcel.xlsx</string>
</dict>
</array>
</dict>
</plist>
You can either do this manually by entering the xml in a code editor (info.plist is just an xml file), or add this from Visual Studio, by clicking in info.plist:
You can find step-by-step information on how to register your app in the iOS tutorial.
When you configure everything, your app will appear in the “Open in” dialog from other applications on your phone:
Answering to an “Open in” event
Once you’ve registered your application as an app that can handle xls/x files, it will appear in the other application’s “Open in” dialogs. When the user clicks in your app icon, iOS will copy the file to <Your app folder>/Documents/Inbox (See “A look at some of the available folders for your application” above).
After that iOS will start your app, and send it an application:OpenUrl: message so you can actually open the file. So in order to do something useful, you will need to listen to application:OpenUrl: event.
Luckily this is straightforward; just open the file AppDelegate.cs in your project and add the following lines:
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
DoSomething(url);
}
Note
OpenURL would get the URL of the file (something like “file://localhost/folder...”) instead of a filename. The FlexCelView example shows how to convert the URL into a path, and how to handle an OpenUrl event.
Exporting files to other apps
Exporting files to other apps is the reverse of what we’ve seen in the previous section: Now we want to show a dialog where we show the user all the applications that can open the file we generated.
You can export a file with the following code:
partial void ShareClick(MonoTouch.UIKit.UIBarButtonItem sender)
{
if (PdfPath == null) return;
UIDocumentInteractionController docController =
new UIDocumentInteractionController();
docController.Url = NSUrl.FromFilename(PdfPath);
docController.PresentOptionsMenu(ShareButton, true);
}
Printing from iOS
To print an xls or xlsx file created by FlexCel, you need to export it to PDF first (using FlexCelPdfExport). Once the file is in pdf format and saved in your hard drive, just export it to the user as in the sample above.
And the “Print” button will appear among the other options inside the share sheet.
Backing up files
A note about the files you use with FlexCel. Not all of them might need to be backed up, and Apple considers it a reason for App Store rejection if your application is backing up static files (as this will increase backup times and sizes for all users).
If you are using xls or xlsx files as templates for your app, but they aren’t actual data and shouldn’t be backed up, you should use the NSURLIsExcludedFromBackupKey or kCFURLIsExcludedFromBackupKey properties to exclude them from backup.
You can find more information about this topic at: https://developer.apple.com/library/ios/#qa/qa1719/_index.html
Other ways to share files
Besides exporting and importing files, there are two other ways in how you can get files from and to your application:
iTunes file sharing
Your application can offer “Share in iTunes” functionality. To allow it, you need to add the key:
<key>UIFileSharingEnabled</key>
<true/>
To your Info.plist file. Once you add this entry, your app will appear in iTunes and the user will be able to read and write documents from the “Documents” folder of it. The interface is kind of primitive, but it gets the work done.
Note
Again as in the case of registering the application to consume some file types, the Delphi IDE doesn’t allow you to do it directly. You need to add a Boolean key, and Delphi will only add string keys. So, again you will have to create a different Info.plist and merge it, as we did in “Registering your app with iOS”. If you are already doing so to registering files to import, then you can use that same file to add this entry.
Warning
If you decide to enable iTunes sharing for your app, make sure that the documents in the “Documents” folder are actual documents the user cares about, and put the others somewhere else, like in “Library”. Failing to do so can result in a rejection from the App store. (as you can see here: http://stackoverflow.com/questions/10767517/rejected-by-uifilesharingenabled-key-set-to-true )
Copying files on startup
The last way to put files in your app is to copy them from your app bundle to the “Documents” folder when your app starts
Note that you can only put files inside your app bundle, you can’t put a file directly in the “Documents” folder. Because you will not be uploading the Documents folder to the app store; you will upload only the app bundle.
If your application is called “MyApp” and the folders look something like this:
Then you can only put files in the green folders in the diagram. This is because “MyApp.app” is what will be distributed to the App store, and what your users will download when they download your app.
So, how do we put a file on the blue folders in our distribution package? The usual technique is iOS is to copy them to some folder inside MyApp.app, and on startup of your application, copy those files to “/Documents” or “/Library”
A note about Encodings
When you create a Xamarin iOS or Android application, by default it will come with a limited number of encodings. This is to keep application size small. Those encodings include for example ASCII and Unicode, but no Win1252 (encoding used in western Windows machines) or IBM 437 (encoding used in zip files).
FlexCel will work in most cases with the reduced number of encodings, but there are some rare cases where we need the full list of encodings. An example is when reading an Excel 95 file, and there are a couple of other cases more.
So in order to not have problems with non-existing encodings, it might be a good idea to click in your project properties and add "west" encodings.
- In iOS: Go to Build->iOS Build and select the "Advanced" tab. There in the "Internationalization" section choose "west".
- In Android: Go to Build->Android Build and select the "Linker" tab. There in the "Internationalization" section choose "west".
If you are worried about the extra size you can skip this step. FlexCel will still work in most of the cases without the extra encodings.