iOS print without allowing UIPrintInteractionController to appear - iphone

I am researching a potential app for a client, and I had a question I wasn't able to find the answer on. What I would like to do is print automatically, without having the UIPrintInteractionController get displayed. An important point I should make is this will not be a production app. I know that otherwise this could violate the user's privacy to just print without the user's consent, but again this will not be a public app on the App Store. One app I found that seems to be able to this is Printer Pro. The user is prompted to print a test page, and the page prints without the UIPrintInteractionController appearing - this app is on the store, so I'm assuming there's a legal way to do it. Anyone know how this is done?

As far as I know it is not possible to print in iOS7 without invoking the UIPrintInteractionController and displaying the system print dialog.
However, iOS8 now provides a provision for printing directly to a print without showing the dialog. The basic idea is that you obtain a UIPrinter object and use this in conjunction with the new - printToPrinter:completionHandler: method of the UIPrintInteractionController to print without showing the dialog.
Obtaining the UIPrinter object for your printer can be accomplished in a couple of ways.
The most straightforward is to use the new UIPrinterPickerController. But this would show a dialog and so doesn’t meet your needs.
The alternative is to create a printer via its URL using the UIPrinter method
+ printerWithURL:. I’m not entirely clear how to obtain this URL but it may be contained in the printer’s HTML admin pages. And I believe you can obtain it programmatically using the Bonjour API. More info here:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/NetServices/Introduction.html#//apple_ref/doc/uid/10000119i
Bonjour is a service discovery API which includes discovery of IPP printers, which the is the protocol used by AirPrint.
As for how the Printer Pro app is able to print without a dialog in iOS7, I would guess they are interacting with the printer at a very low level (e.g. raw HTTP posts, etc.).

Follow these steps to Print the Documents without prompting..
First Search for Devices Using the Below Code...
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) {
UIPrinterPickerController *printPicker = [UIPrinterPickerController printerPickerControllerWithInitiallySelectedPrinter:nil];
[printPicker presentAnimated:YES completionHandler:
^(UIPrinterPickerController *printerPicker, BOOL userDidSelect, NSError *error) {
if (userDidSelect) {
//User selected the item in the UIPrinterPickerController and got the printer details.
[UIPrinterPickerController printerPickerControllerWithInitiallySelectedPrinter:printerPicker.selectedPrinter];
// Here you will get the printer and printer details.ie,
// printerPicker.selectedPrinter, printerPicker.selectedPrinter.displayName, printerPicker.selectedPrinter.URL etc. So you can display the printer name in your label text or button title.
NSURL *printerURL = printerPicker.selectedPrinter.URL;
NSLog(#"printerURL--->%#",printerURL.absoluteString);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[printerURL absoluteString] forKey:#"printURL"];
[defaults synchronize];
}
}];
}
And Print the Documents by without prompting by using the below code...
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
UIPrinter *currentPrinterObj = [UIPrinter printerWithURL:[NSURL URLWithString:[defaults stringForKey:[defaults stringForKey:#"printURL"]]]];
UIPrintInteractionController *controller = [UIPrintInteractionController sharedPrintController];
if(currentPrinterObj) {
[controller printToPrinter:currentPrinterObj completionHandler:^(UIPrintInteractionController *printController, BOOL completed, NSError *error) {
if(completed) {
} else {
NSLog(#"FAILED! due to error in domain %# with error code %u", error.domain, error.code);
}
}];
}
}

Related

Detecting programmatically an installed app

I am trying to detect installed Instagram on my device , formerly I used this code to detect an app , but it seems it does not work with iOS 6 or non-JB devices :
NSString *filePath = #"/Applications/Instagram.app";
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
[self checkingInstalledApp];
}
else
{
NSLog(#"no instagram installed");
}
I check this question but his answer gives me a lot errors ! any solution ?
This is invalid code for two reasons.
1) It attempts to interact with an area outside of its sandbox.
2) It relies on an undocumented implementation detail (Perhaps the install location has moved or the application name has changed?)
What you need to do is use the canOpenURL: method on UIApplication to determine if the system can launch an application via its custom scheme (note: If the App has no custom scheme then you are out of luck)
Custom URL Scheme Opening instagram://, followed by one of the following parameters, will open our app and perform a custom action.
For example, for camera, you would direct users on the iPhone to the
custom URL instagram://camera.
NSURL *instagramURL = [NSURL URLWithString:#"instagram://location?id=1"];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {
[[UIApplication sharedApplication] openURL:instagramURL];
}
From Instagram iPhone Hooks

iCloud only seems to work on start up

I'm trying to create an iCloud app that uses Core Data. To do so, I use a single instance of UIManagedDocument. For persistentStoreOptions, I set "Database.data" for NSPersistentStoreUbiquitousContentNameKey and -iCloudURL-/CoreData_Logs for NSPersistentStoreUbiquitousContentURLKey. As by Apple suggested, I open the document like so:
if ([[NSFileManager defaultManager] fileExistsAtPath:[urlOfDatabase path]])
{
[_database openWithCompletionHandler:^(BOOL success) { ... }];
}
else
{
[_database saveToURL:urlOfDatabase
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) { ... }];
}
Also, I registered for NSPersistentStoreDidImportUbiquitousContentChangesNotifications, and merge the context from the notification in that method.
To test the app, I use an iPad and an iPhone. When I run it on one device, add some data, and open it up on the other device, the correct data is displayed. If I however add data while the app is running on both devices, it either doesn't update (I've waited for at least 10 minutes once) or it updates after a few minutes but only with a whole bunch of error messages: "The item failed to download.".
Did I miss something (hopefully obvious) in my set up?

Display encrypted file using QuickLook framework or UiDocumentInteractionController

I have an encrypted word/excel/pdf file locally stored which I need to preview in my iPad app. I understand that QLPreviewController or UiDocumentInteractionController could be used to preview these files. I can very well use this
- (id <QLPreviewItem>) previewController: (QLPreviewController *) controller previewItemAtIndex: (NSInteger) index {
return [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[documents objectAtIndex:index] ofType:nil]];
}
But the file is encrypted and when I decrypt it I would get hold of NSData object. How do I go about loading NSData in either of these.
Also I understand that I can very well store the NSData back as a local file and load it in Preview. But there is a constraint of not storing the unencrypted file locally.
If someone has already accomplished this and can help me out here it will be greatly appreciated.
Thanks
AJ
Since you are using Quick Look, your options are limited. You must give Quick Look an NSURL, which means it must be on the file system (or the Internet). Fortunately, this shouldn't be much of a problem. iOS devices use hardware-level encryption. When your file is encrypted, only your app has the key to decrypt it. So, your file will still be encrypted, but it will also be readable by your app and only your app.
Here's what you do:
Decrypt your file into an NSData object, which you've already done.
Write the file to a location that will not get uploaded to iCloud nor backed up by iTunes. The tmp directory is probably the best choice. The code looks something like this:
NSData * data = // Your decrypted file data.
NSString * fileName = // Whatever you want to name your file.
NSString * path = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName];
NSURL * url = [NSURL URLWithString:path];
NSError * error = nil;
BOOL success = [data writeToURL:url
options:NSDataWritingFileProtectionComplete
error:&error];
if (success) {
// Give the URL to Quick Look.
}
else {
// An error happened. See the 'error' object for the details.
}
At this point you have an NSURL which you can use with Quick Look. Don't forget to delete the decrypted file when you are done with it.
There are a few things to note about on-disk encryption:
It is only supported on iOS 4.0+.
It may not work on "older" devices.
The user must have an active passcode.
If you use NSDataWritingFileProtectionComplete, the file is not accessible while the device is locked. If you need to access the file while the app is locked, then you should use NSDataWritingFileProtectionCompleteUnlessOpen or NSFileProtectionCompleteUntilFirstUserAuthentication instead. This will still give you great protection, even if the device is stolen and jailbroken. Be aware, though, that these encryption options are only available on iOS 5.0+
For more details for on-disk encryption, check out the iOS App Programming Guide
After doing some digging, I found out that QLPreviewController is using UIWebView underneath, and calls the loadRequest: to load the requested file.
Another way to accomplish what you desire is to make a private Category on UIWebView,
and use method swizzling to override the loadRequest: method, and call instead the loadData:MIMEType:textEncodingName:baseURL: method.
Beware that:
1) In low-memory scenarios (i.e. large files) a black screen with
"Error to load the document" appears, if that concerns you. (The
unhacked QLPreviewController knows how to handle these scenarios
very well and present the document).
2) I'm not sure Apple are going
to approve this kind of hack, although no private APIs are used
here.
code:
#implementation UIWebView (QLHack)
- (void)MyloadRequest:(NSURLRequest *)request
{
// Check somehow that it's the call of your QLPreviewController
// If not, just call the original method.
if (!insideQLPreviewController)
{
// Call original implementation
[self MyloadRequest:request];
}
else
{
// Load the real data you want
[self loadData:data MIMEType:mimeType textEncodingName:nil baseURL:someURL];
}
}
+ (void)load
{
method_exchangeImplementations(class_getInstanceMethod(self, #selector(loadRequest:)), class_getInstanceMethod(self, #selector(MyloadRequest:)));
}
#end
Actually, writing a file to a tmp directory is still insecure. The other alternative is to use UIWebView with NSURLProtocol and allow decrypting this data on the fly.
One way could be.
use Temp Dir , Save File in Temp , Make NSURL From that Temp File and Display and then Delete that temp Dir after that.
Thanks.

URLForUbiquityContainerIdentifier returns nil even if configured correctly

I am having the problem, that URLForUbiquityContainerIdentifier is returning nil in some cases even if the user has set up everything correctly in the settings. My code:
dispatch_async(someQueue, ^{
if (![[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]) {
ErrLog(#"iCloud container not available.");
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
[...]
});
});
Does anybody came across the same problem? I am setting nil as the container identifier which should work according to the Apple docs, but I am not so convinced anymore about that. Also this code works fine for the majority of users, but somehow not for everybody.
Check that you have enabled iCloud containers in your entitlement. The containers are not added by default when you enable entitlements. Click the "+" sign in Target/Summary/Entitlements to add your appId.
iPad mini with iOS7. I just have experienced that URLForUbiquityContainerIdentifier suddenly started to return nil. I tried restarting the device, but it didn't help.
Inside the Settings app, under the 'iCloud' menu. I noticed that 'Documents & Data' was set to Off.
Solution was to change 'Documents & Data' to 'On'.
If anyone is having the same problem using the iOS8 Betas, there seems to be a bug with the way the Entitlements are generated in Xcode 6. I was banging my head against this for days until coming across this post in the Apple Developer Forums: https://devforums.apple.com/thread/229509?
Essentially, Xcode generates a key named com.apple.developer.icloud-container-identifiers when it should be com.apple.developer.ubiquity-container-identifiers (ubiquity, not icloud). It may also have the value set as iCloud.$(CFBundleIdentifier) when it should be $(TeamIdentifierPrefix).$(CFBundleIdentifier).
Of course this may get fixed in the next Beta but for anyone as confused as me I hope this helps.
I'd look at your entitlement.plist on one app I needed to change $(TEAMID) to profile id
the numbers and letter before the dns in the profile - here is some code that will work with UIManagedDocument
/// test iCloud Access
- (void)initializeiCloudAccess {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil] != nil)
NSLog(#"iCloud is available\n");
else
NSLog(#"This Application requires iCloud, but it is not available.\n");
});
}
- (NSURL *)iCloudURL
{
return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
}
- (NSURL *)iCloudDocumentsURL
{
return [[self iCloudURL] URLByAppendingPathComponent:#"Documents"];
}
- (NSURL *)iCloudCoreDataLogFilesURL
{
return [[self iCloudURL] URLByAppendingPathComponent:#"CoreDataLogs"];
}
URLForUbiquityContainerIdentifier alway return nil
NSURL *ubiquityURL = [fileManager URLForUbiquityContainerIdentifier:nil]; if (ubiquityURL == nil)
app build by xcode4.5
app is enable icloud
ios is loginned icloud account
ubiquityURL is normal in debug, but ubiquityURL is always nil after distribution to appstore.
Today I experienced this problem again.
This time it was because I had an invalid provisioning profile.
On the developer site, I had to generate a new provisioning profile. Afterward reload the provisioning profiles within Xcode5. Now URLForUbiquityContainerIdentifier again returns valid urls.
If you still having issue o make sure you are running on a device as simulator wont always return a valid value .
I tried you code on my app and Im not getting nil
Enable "iCloud Drive" in the iPhone's Settings

Detect and Customize UIWebView's "Unable to Read Document" error

I'm testing a UIWebview with a number of different document types - .xlsx, .jpg, etc. - and it opens most of them just fine. From time to time, I open a local file and this message appears right in the web view:
Unable to Read Document
An error occurred while reading the document
I'm not concerned with "why" this error occurs - it happens, for instance, when I feed the UIWebView a garbage file (intentionally). The problem is that I can't figure out how to detect "when" this happens. It doesn't trigger webView:didFailLoadWithError, it doesn't trigger an NSException (via #try & #catch), and when I inspect the document in webViewDidFinishLoad, webView.request.HTTPBody is null.
Anyone know how to detect when UIWebView can't display content?
just call uiwebview delegate method
- (void)webViewDidStartLoad:(UIWebView *)webView
{
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *html = [webView stringByEvaluatingJavaScriptFromString:
#"document.body.innerHTML"];
if ([html rangeOfString:#"Unable to Read Document."].location == NSNotFound) {
NSLog(#"NO Error");
}
else
{
// NSLog(#"File contains Error");
}
}
If inspecting the log does not show anything out of the ordinary, run it on the actual device. As a bit of help, my log says
Cannot find data converter callback for uti public.data
Failed to generate preview
which means that the simulator is failing. After being frustrated with this same problem, I went thru the whole final certification and installation and provisioning process, and too confirm that Word 97 and Pages / Pages.zip files containing text do indeed display just fine on the actual device. SOLVED. The simulator itself is broken, which is very...troubling, that this little note didn't seem to make it into the release notes, and also complicates development a tad bit. However, the work around is to select the Device in Xcode and deploy out, and it should work.