Custom UTI Not opening in App - objective-c

I am trying to create a new document UTI for my App so that people can share points of interest with others. From what I can understand on SO, Tutorials, and from Apple's documentation, you need to do the following:
Create a Document Type in the .plist
Create an Exported UTI that corresponds with it
Use the method: -(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
From what I understand, as long as you did those right, you should be able to open the file through Mail without any problems. Unfortunately it isn't working for my own custom UTIs. I DO see my App in the list of "Open with..." in Mail, but when I select it, my App doesn't open at all. It just dosen't do anything at all not only when the App isn't open, but when the App is open. Mail stays up and nothing happens at all. I also checked the console using "Organizer" and there is absolutely nothing that happens.
Originally I thought my plist was wrong, so I tested opening a public UTI (I added the com.adobe.pdf document type) and my app launched just fine (though it promptly crashed because I don't actually support PDFs ;)). But the point was that it launched without any problem.
The only thing I can think of that might be a problem is HOW I am creating the file. I am creating the file in an email by the use of this method (also in the App to export):
MFMailComposeViewController *picker = [[[MFMailComposeViewController alloc] init] autorelease];
[picker setSubject:[NSString stringWithFormat:#"My place: %#",POIName]];
[picker addAttachmentData:customPOIData mimeType:#"application/customPOI" fileName:[NSString stringWithFormat:#"%#.icp",POIName]];
[picker setMessageBody:#"Check out this great place I found!" isHTML:NO];
[picker setMailComposeDelegate:self];
[self presentModalViewController:picker animated:YES];
Is there anything wrong with that?
Also, here is my plist code:
CFBundleDocumentTypes:
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeIconFiles</key>
<array/>
<key>CFBundleTypeName</key>
<string>Custom POI</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>com.imadev.icp</string>
</array>
</dict>
</array>
UTExportedTypeDeclarations:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Custom POI</string>
<key>UTTypeIdentifier</key>
<string>com.imadev.icp</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>icp</string>
</array>
<key>public.mime-type</key>
<string>application/customPOI</string>
</dict>
</dict>
</array>
Thanks a lot for any help!!
-Mark

I finally figured out what was wrong by pulling apart all the code.
When I change "public.filename-extension" to a string and not an array of strings it works. Don't ask me why... I think it's strange we can't use an array of file extensions. But apparently that was it.
Any ideas as to why that happened?

In my case, I could keep filename-extension as an array without problems. Instead, it was the 'public.data' that was the issue. I switched to 'public.image' and then had jpg, jpeg, and png as file extensions, and it suddenly worked.

Related

Unable to select file when using UIDocumentPickerViewController initForOpeningContentTypes

I'm trying to use the iOS 14 initForOpeningContentTypes: rather than the deprecated initWithDocumentTypes:inMode: and I'm unable to get access to files with the extension p8 (they are greyed out).
I'm trying the following code:
NSArray<UTType*> * contentTypes = #[[UTType typeWithFilenameExtension: #"p8"
conformingToType: UTTypeData]];
UIDocumentPickerViewController *documentPicker = nil;
documentPicker = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes: contentTypes];
The picker launches, but the file "myfile.p8" is greyed out and unselectable. What am I doing wrong here?
Once I added the following to my application’s Info.plist I was able to select the file:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>com.my-company.my-identifier</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>My Description</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>p8</string>
</array>
</dict>
</dict>
</array>
Apparently it tells the system about the type. See this answer for info about UTExportedTypeDeclarations.

Application black screen on iOS 13 on launch

I recently tried converting an Objective-C project to Swift using Swiftify.
The project has been successfully converted and there are no errors/warning but on running the project the application shows a black screen.
The errors in the console are:
Info.plist configuration "Default Configuration" for UIWindowSceneSessionRoleApplication contained UISceneDelegateClassName key, but could not load class with name "SceneDelegate".
and
There is no scene delegate set. A scene delegate class must be specified to use the main storyboard file.
But as it can be seen from my Project hierarchy there is a SceneDelegate class present.
Also the contents of my Info.plist file is as follows:
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
Also please review the screen recording here.
Also here is the link of the project.
The problem is this pair of lines in the Info.plist:
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
The issue is that you think "SceneDelegate" is the name of the SceneDelegate class, but Objective-C does not think so! You have to write:
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
Try to configure your window scene like this:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.makeKeyAndVisible()
let controller = YoursatrtController() // your start controller class name
window?.rootViewController = controller
}

iOS - (kCFStreamErrorDomainSSL, -9813)

I'm getting (kCFStreamErrorDomainSSL, -9813) when connecting to a client's server.
The client's server has a self signed certificate that I can not change. The app is using AFNetworking 3.x. I've tried the following but nothing seems to work.
If someone could help me, it will be much appreciated.
Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key> *** CLIENT HOSTNAME *** </key>
<dict>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSTemporaryExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSTemporaryExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>
AFNetworking connection manager:
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
#ifdef USE_SELF_SIGNED_CERT_RULES
manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
manager.securityPolicy.allowInvalidCertificates = YES;
manager.securityPolicy.validatesDomainName = YES;
#endif
I had the same problem. I tried your solution but it did not work. Settings allowInvalidCertificates , validatesDomainName and AFSSLPinningModeCertificate did not solved my problem. After going through lots of googling I saw the structure of AFSecurityPolicy this class.
There is a function in this class
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(nullable NSString *)domain;
You have to subclass the AFSecurityPolicy and return YES in its implementation. Then you will be able to connect to your server.
My client server was also self-signed.
Settings properties on AFSecurityPolicy does not solve the problem, I wonder why.

NSService not calling its NSMessage

I'm trying to add a Finder service and all looks fine until I want the service to do its job.
This is the method in my AppDelegate.m:
-(void)uploadFromPasteboard:(NSPasteboard *)pboard userData:(NSString *)udata error:(NSString **)err
{
NSString *filename = [pboard stringForType:NSURLPboardType];
dbg(#"file: %#", filename);
}
The plist configuration:
<key>NSServices</key>
<array>
<dict>
<key>NSRequiredContext</key>
<dict/>
<key>NSMenuItem</key>
<dict>
<key>default</key>
<string>Upload File</string>
</dict>
<key>NSMessage</key>
<string>uploadFromPasteboard</string>
<key>NSPortName</key>
<string>Finder</string>
<key>NSSendTypes</key>
<array>
<string>NSURLPboardType</string>
</array>
<key>NSReturnTypes</key>
<array/>
</dict>
</array>
All seems fine, the service is displayed in the service menu, but when I click it, nothing happens, no logs or anything else, like its not called at all.
Could someone point me whats wrong cos I'm starting to pull my hair hardly :(
Are you setting your service provider instance? Like this (from: Providing a Service):
EncryptoClass* encryptor = [[EncryptoClass alloc] init];
[NSApp setServicesProvider:encryptor];
Merely having this method in your app delegate class is not enough. In the standard application set-up, having this in your app delegate might be sufficient:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[NSApp setServicesProvider: self];
}
Also you're specifying "Finder" for NSPortName. This is probably not correct. From Services Properties (emphasis mine):
NSPortName is the name of the port on which the application should
listen for service requests. Its value depends on how the service
provider application is registered. In most cases, this is the
application name. This property is ignored for Automator workflows
being used as services.
My reading of the documentation is that the application whose name is in NSPortName is the application that will be used to handle the service request. If the name of your app isn't "Finder" (and it shouldn't be, for obvious reasons) then your app will never be called by the service.

Assigning UTI to file type

I'm trying to add UTI for a pair of file types, matching by extension. I think I've got it setup properly - at the very least, the file extension -> UTI mapping is recognized (I declared it as an imported type in info.plist). However, when I try to get the UTI off of my test file, I get back a dynamic UTI. Here's the sample code I'm using to test these two cases:
NSString *previewTypeUTI = nil;
NSURL *previewFileURL = [NSURL fileURLWithPath:#"/Users/eblair/Desktop/EWF/thetestfile.myext1"];
[previewFileURL getResourceValue:&previewTypeUTI forKey:NSURLTypeIdentifierKey error:nil];
NSLog(#"%#", previewTypeUTI); // #1
NSString *testUTI = (NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[previewFileURL pathExtension], NULL);
NSLog(#"%#", testUTI); // #2
[testUTI release];
NSLog #1 prints out "dyn.########" and NSLog #2 prints out "com.mycompany.filetype1". I would've expected them to print the same thing. Is there some extra step to get the file->UTI mapping working or is it a case of Launch Services needing to catch up?
Just using the extension->UTI mapping isn't an option because I want to use some APIs that take advantage of UTIs and those APIs seem to be using the file->UTI mapping.
For the sake of completeness, here's the plist entry for the imported types:
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.disk-image</string>
</array>
<key>UTTypeDescription</key>
<string>File Type 1</string>
<key>UTTypeIdentifier</key>
<string>com.mycompany.filetype1</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>myext1</string>
</array>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.disk-image</string>
</array>
<key>UTTypeDescription</key>
<string>File Type 2</string>
<key>UTTypeIdentifier</key>
<string>com.mycompany.filetype2</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>myext2</string>
</array>
</dict>
</dict>
</array>
Well, it's been close to a year, but I had to re-address this today and I think I stumbled across what I did wrong. It looks like you need to include public.data as a type you conform to, even though public.disk-image already conforms to public.data. So, the UTTypeConformsTo array should look like this:
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
<string>public.disk-image</string>
</array>
After making this change, my above comparison of the file->UTI and extension->UTI now yield the same result.