UIActivityViewController Attaching File - Wrong File Type - iphone

I am using UIActivityViewController to share the contents of my app in both a pure text format as well as a custom filetype that can be opened on other devices. The file attaches to the email created with no problem, however it is getting the wrong filetype associated with it. The custom file that is created is an NSDictionary that uses writeToURL.
NSMutableDictionary *theBinder = [NSMutableDictionary dictionaryWithObjects:#[#"test object"] forKeys:#[#"test key"]];
NSURL *binderURL = [NSURL fileURLWithPath:[[self cachesDirectory] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.mtgbinder", [[NSUserDefaults standardUserDefaults] valueForKey:#"Current Binder"]]]];
[theBinder writeToURL:binderURL atomically:YES];
NSArray *activityItems = [NSArray arrayWithObjects:binderContents, binderURL, nil];
UIActivityViewController *shareView = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
shareView.excludedActivityTypes = #[UIActivityTypePostToTwitter, UIActivityTypePostToWeibo];
[self presentViewController:shareView animated:YES completion:nil];
The file is correctly created as expected:
<?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>test key</key>
<string>test object</string>
</dict>
</plist>
However, when I then try to open that file from another app, the extension gets changed to csv. My app is able to open and export both my mtgbinder filetypes and also CSV. Am I doing something wrong with the UTExportedTypeDeclarations?
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.chichapps.MTG-Binder.csv</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>csv</string>
<key>public.mime-type</key>
<string>application/MTG-Binder</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.chichapps.MTG-Binder.mtgbinder</string>
<key>UTTypeDescription</key>
<string>MTG Binder Document</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.mime-type</key>
<string>application/MTG-Binder</string>
<key>public.filename-extension</key>
<string>mtgbinder</string>
</dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>MTG Binder Document</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>com.chichapps.MTG-Binder.csv</string>
<string>com.chichapps.MTG-Binder.mtgbinder</string>
</array>
</dict>
</array>
Thanks for any help and suggestions.

I figured out what to do. I had to change:
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.chichapps.MTG-Binder.csv</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>csv</string>
<key>public.mime-type</key>
<string>application/MTG-Binder</string>
</dict>
</dict>
To:
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.chichapps.MTG-Binder.csv</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>csv</string>
<key>public.mime-type</key>
<string>application/csv</string> //Changing this fixed the problem
</dict>
</dict>

Related

Can I have separate launch images for each of the four orientations?

I want my app to have different launch image when launched in portrait vs portrait upside down vs landscape left vs landscape right. Is that possible?
I'm willing to use any of the following:
Launch storyboard
Launch images in an asset catalog
Launch images as plain PNG files
To see why this is necessary, quit Camera.app and then launch it in landscape left and orientations. Notice the launch image, specifically that the shutter button is located near the home button in both landscape left and right orientations.
This is possible only if it has separate launch images / storyboards for landscape left and right. If it had only one, the shutter button would appear next to the home button in one orientation and next to the speaker (at the top of the screen) in the other orientation.
Camera.app seems to have four different launch images. How do I have four, as well?
EDIT:
Based on the accepted answer, I tried adding the following launch images (without adding anything in Info.plist):
On my iPhone 7 Plus, it works correctly in portrait, landscape left and landscape right. Not when the phone is upside down, in which case the portrait launch image is shown, followed by what looks like a 180-degree rotation when the real UI is shown. To confirm this, I drew a big red rectangle in the middle of the portrait upside down launch image, and I don't see this red rectangle when I run the app, so I can confirm that the portrait upside down launch image file isn't being used at all. Do you know why? Do you see anything else wrong with the filenames?
EDIT 2: I added the following to Info.plist:
<key>UILaunchImages</key>
<array>
<!-- 5.5-inch: -->
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{414, 736}</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{414, 736}</string>
<key>UILaunchImageOrientation</key>
<string>PortraitUpsideDown</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{414, 736}</string>
<key>UILaunchImageOrientation</key>
<string>LandscapeLeft</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{414, 736}</string>
<key>UILaunchImageOrientation</key>
<string>LandscapeRight</string>
</dict>
<!-- 4.7-inch: -->
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{375, 667}</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{375, 667}</string>
<key>UILaunchImageOrientation</key>
<string>PortraitUpsideDown</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{375, 667}</string>
<key>UILaunchImageOrientation</key>
<string>LandscapeLeft</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{375, 667}</string>
<key>UILaunchImageOrientation</key>
<string>LandscapeRight</string>
</dict>
<!-- 4-inch: -->
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
<key>UILaunchImageOrientation</key>
<string>PortraitUpsideDown</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
<key>UILaunchImageOrientation</key>
<string>LandscapeLeft</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
<key>UILaunchImageOrientation</key>
<string>LandscapeRight</string>
</dict>
<!-- 3.5-inch: -->
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
</dict>
<dict>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
</dict>
</array>
This made things worse — I see a stretched out portrait launch image being used when the phone is in landscape.
Note that all the UILaunchImageName keys are set to Default, relying on iOS to disambiguate them. I also tried renaming the PNGs to things like Default-portrait-upside-down#3x.png and setting the UILaunchImageName to Default-portrait-upside-down. It made no difference.
I believe you can specify a separate launch image for each orientation. iOS (since version 7.0) supports an Info.plist key UILaunchImages. The value is an array of dictionaries. Each dictionary describes one launch image. The dictionary can contain a key UILaunchImageOrientation, for which the value can be any of the four orientations.
Consult the Information Property List Key Reference for more details.

Exclusive iOS UTI

I'm trying to create / export an exclusive UTI type for my iOS app (very similar to how Instagram exclusively handles the UTI com.instagram.exclusivegram).
Basically, I want com.photoapp.photo to be what an app can use if they want to be able to open the photo in any app registered for taking photos (similar to Instagram's com.instagram.photo). Then I want com.photoapp.exclusive to only be able to open in my app (similar to com.instagram.exclusivegram).
What I'm running into on my device is that even when using com.photoapp.exclusive, the UIDocumentController prompts me to open it in either PhotoApp or DropBox where it should just be PhotoApp.
I have my app that registers the UTI as well as an example app I'm using to check on the opening ability. The code I'm using in the example app is below:
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:#"photoapp://"]]) {
NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:#"derp.png"], 1.0);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fullPathToFile = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"photoapp.pae"];
[imageData writeToFile:fullPathToFile atomically:NO];
interactionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:[NSString stringWithFormat:#"file://%#", fullPathToFile]]];
interactionController.UTI = #"com.photoapp.exclusive";
interactionController.delegate = self;
[interactionController presentOpenInMenuFromRect:self.view.frame inView:self.view animated:YES];
}
And here is what I have in my plist file for my app:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeDescription</key>
<string>Exclusive PhotoApp Photo</string>
<key>UTTypeConformsTo</key>
<array>
<string>com.photoapp.photo</string>
</array>
<key>UTTypeIdentifier</key>
<string>com.photoapp.exclusive</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>pae</string>
</dict>
</dict>
<dict>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>pa</string>
</dict>
<key>UTTypeIdentifier</key>
<string>com.photoapp.photo</string>
<key>UTTypeDescription</key>
<string>PhotoApp Photo</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.png</string>
<string>public.jpeg</string>
</array>
</dict>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>LSItemContentTypes</key>
<array>
<string>com.photoapp.exclusive</string>
<string>com.photoapp.photo</string>
</array>
<key>CFBundleTypeName</key>
<string>Photo</string>
<key>LSHandlerRank</key>
<string>Default</string>
</dict>
</array>
As soon as you specify a UTI such as public.png or public.jpeg, all apps that specify that they can open files of these types will be able to open your file. So, if you do not specify any type conformance at all in your com.photoapp.exclusive UTI, no apps will appear to support it.

Launch screen on iPad

I have an iPhone app. When I run it on the iPad everything looks great (but small) except for the launch screen that also is small (not full screen) and not really proportional.
I have read the documentation, but I'm not sure where I've gone wrong. I have added Default-Portrait.png and Default-Landscape.png, but it doesn't change anything.
I don't really mind about how it is, more than I don't want it to be rejected in App Store because of that.
Edit: My info.plist (if you see other weird things, please let me know. I'm submitting today)
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleDocumentTypes</key>
<array/>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIconFiles</key>
<array>
<string>Icon.png</string>
<string>Icon#2x.png</string>
<string>Icon-72.png</string>
<string>Icon-Small-50.png</string>
<string>Icon-Small.png</string>
<string>Icon-Small#2x.png</string>
<string>Icon-72.png</string>
<string>Icon-72.png</string>
</array>
<key>CFBundleIdentifier</key>
<string>com.xx.xxx</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>fb1234</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSMainNibFile</key>
<string>MainWindow</string>
<key>SHKFacebookKey</key>
<string>1234</string>
<key>SHKFacebookSecret</key>
<string>1234</string>
<key>SHKMyAppName</key>
<string>xxx</string>
<key>SHKMyAppURL</key>
<string>http://itunes.apple.com/xxx</string>
<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationPortrait</string>
<key>UIInterfaceOrientation~ipad</key>
<string>UIInterfaceOrientationPortrait</string>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UTExportedTypeDeclarations</key>
<array/>
<key>UTImportedTypeDeclarations</key>
<array/>
Make sure to add these into your Info.plist file
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
Even of your orientations work properly without, it's absolutely imperative to have these to select a proper launch screen.
BTW You won't be rejected. It's just a matter of good programming ethics.

iPhone: Can't access all value from .plist file

I have a pretty simple root.plist file that looks like this:
<?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>StringsTable</key>
<string>Root</string>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>User Info</string>
</dict>
<dict>
<key>Type</key>
<string>PSTextFieldSpecifier</string>
<key>Title</key>
<string>Username</string>
<key>Key</key>
<string>username_preference</string>
<key>DefaultValue</key>
<string></string>
<key>IsSecure</key>
<false/>
<key>KeyboardType</key>
<string>Alphabet</string>
<key>AutocapitalizationType</key>
<string>None</string>
<key>AutocorrectionType</key>
<string>No</string>
</dict>
<dict>
<key>Type</key>
<string>PSTextFieldSpecifier</string>
<key>Title</key>
<string>Password</string>
<key>Key</key>
<string>password_preference</string>
<key>DefaultValue</key>
<string></string>
<key>IsSecure</key>
<true/>
<key>KeyboardType</key>
<string>Alphabet</string>
<key>AutocapitalizationType</key>
<string>None</string>
<key>AutocorrectionType</key>
<string>No</string>
</dict>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>Application Info</string>
</dict>
<dict>
<key>Type</key>
<string>PSTitleValueSpecifier</string>
<key>Title</key>
<string>Version</string>
<key>Key</key>
<string>api_version_preference</string>
<key>DefaultValue</key>
<string>2.0</string>
<key>Value</key>
<string>2.0</string>
</dict>
</array>
</dict>
</plist>
All the fields is shown when navigating to the relevant settings page in the iphone's settings application, but i can't access api_version_preference from my application.
I've tried to print out the .plist file as a dictionary and I only see the values from the username/password fields, so i'm probably doing something wrong. Here is what I use when printing the data:
NSLog(#"defs: %#", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
Can anyone help? :)
You forgot to supply defaults to NSUserDefaults. NSUserDefaults doesn't store the setting if you haven't changed (ie saved) it.
create a NSDictionary of default values and set it with [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsDefaults];
like this:
NSDictionary *userdefaultsDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
#"2.0", #"api_version_preference",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:userdefaultsDefaults];
do this with all the settings you use from NSUserDefaults

Changing Data in a Plist

Hey, alright so I have a .plist that looks like;
<plist version="1.0">
<dict>
<key>Item 0</key>
<dict>
<key>Name</key>
<string>Jero</string>
<key>Initiative</key>
<integer>0</integer>
<key>EditThis</key>
<false/>
</dict>
</dict>
</plist>
In the app I have it so that when one of the rows (The data is put in a UITableView) is selected it pushes to a new view controller. I would like also to set the 'EditThis' boolean to yes, so the edit view controller can learn which to edit. However I can't seem to figure out how to change the value. Any help is much appreciated.
Load the plist into a mutable dictionary:
NSMutableDictionary *plist = [NSMutableDictionary dictionaryWithContentsOfFile:#"file.plist"];
[plist setObject:[NSNumber numberWithBool:YES] forKey:#"EditThis"];
[plist writeToFile:#"file.plist" atomically:YES];