Does anyone know how to disable the ability to “Save to Files?” I see that someone here, claims it can be done by following the instructions here. I attempted to follow the instructions, but they are not working…
I want to prevent users from saving a PDF to the phone locally. However, I want to allow them only the ability to print the PDF.
Here’s my custom UIActivityItemProvider class per the instructions instructions here (updated for Swift 5):
class ActivityItemProvider: UIActivityItemProvider {
override func activityViewController(
_ activityViewController: UIActivityViewController,
itemForActivityType activityType: UIActivity.ActivityType?
) -> Any? {
// Check that the ActivityType is not nil
if activityType != nil {
// in here we'll check activityType = "com.apple.CloudDocsUI.AddToiCloudDrive" (Save to Files),
// activityType = "com.apple.mobileslideshow.StreamShareService" (Shared Album)
if activityType!.rawValue.contains("com.apple.CloudDocsUI.AddToiCloudDrive") || activityType!.rawValue.contains("com.apple.mobileslideshow.StreamShareService") {
// dismiss activityViewController first
activityViewController.dismiss(animated: true, completion: nil)
// show alert controller, we can using UIApplication.shared.keyWindow?.rootViewController to present alert
return nil
}
return self.placeholderItem
}
// Else if nil, then return nil
else {
return nil
}
}
}
Here is the rest of the struct, which creates the UIKit Share view via the UIActivityViewController. Supposedly by using the above custom class it would have prevented the user from seeing the Save to Files option, but that’s not working:
import SwiftUI
struct ShareSheet: UIViewControllerRepresentable {
var activityItems: [Any]
var applicationActivities: [UIActivity]? = nil
// Used to dismiss the shared sheet
@Environment(\.presentationMode) var presentationMode
func makeUIViewController(context: UIViewControllerRepresentableContext<ShareSheet>) -> UIActivityViewController {
// Custom ActivityItemProvider code from the
let item = ActivityItemProvider.init(placeholderItem: activityItems[0])
// Create the UIActivityViewController, which displays shared information
// let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
let controller = UIActivityViewController.init(activityItems: [item], applicationActivities: applicationActivities)
// MARK: - Exclude Activities Besides Print
controller.excludedActivityTypes = [
.addToReadingList,
.assignToContact,
.saveToCameraRoll,
.copyToPasteboard,
.airDrop,
.postToFacebook,
.postToTwitter,
.message,
.markupAsPDF,
.mail,
.openInIBooks,
.postToFlickr,
.postToTencentWeibo,
.postToVimeo,
.postToWeibo
]
// Used to dismiss the share screen
controller.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
self.presentationMode.wrappedValue.dismiss()
}
return controller
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ShareSheet>) {
}
}
Right now, it still displays the “Save to Files” option:
Here’s my SwiftUI code that generates the PDF and calls the UIKit UIActivityViewController that shows the Share options:
import SwiftUI
struct ContentView: View {
// Holds the generated PDF data
@State var pdfData: Data = Data()
// Tracks whether/ not the sheet to share is popped-up
@State var isSharePresented: Bool = false
var body: some View {
VStack {
Button {
generatePdf()
} label: {
Text("Generate PDF")
}
// MARK: - Show or Hide PDF
// Only display this button, if the PDF is not empty
if pdfData.isEmpty == false {
// MARK: - Share and Print PDF
Button("Print PDF") {
// Set the State property to true, so the View gets displayed
self.isSharePresented = true
}
// The State variable isSharePresented triggers the view to pop-up the share options as a sheet from below
.sheet(isPresented: $isSharePresented, onDismiss: {
isSharePresented = false
}, content: {
// Display the UIKitRepresentable view
ShareSheet(activityItems: [self.pdfData])
})
}
}
}
/**
Method generates a PDF and assigns the resulting data to the @State pdfData property
It also saves the PDF to the document directory by the user.
*/
func generatePdf() {
let pageRect = CGRect(x: 0, y: 0, width: 612, height: 792)
let renderer = UIGraphicsPDFRenderer(bounds: pageRect)
let title = "All’s Well That Ends Well - The COUNT’s palace.\n"
let text = """
COUNTESS
In delivering my son from me, I bury a second husband.
BERTRAM
And I in going, madam, weep o’er my father’s death
anew: but I must attend his majesty’s command, to
whom I am now in ward, evermore in subjection.
LAFEU
You shall find of the king a husband, madam; you,
sir, a father: he that so generally is at all times
good must of necessity hold his virtue to you; whose
worthiness would stir it up where it wanted rather
than lack it where there is such abundance.
COUNTESS
What hope is there of his majesty’s amendment?
LAFEU
He hath abandoned his physicians, madam; under whose
practises he hath persecuted time with hope, and
finds no other advantage in the process but only the
losing of hope by time.
COUNTESS
This young gentlewoman had a father,–O, that
‘had’! how sad a passage ’tis!–whose skill was
almost as great as his honesty; had it stretched so
far, would have made nature immortal, and death
should have play for lack of work. Would, for the
king’s sake, he were living! I think it would be
the death of the king’s disease.
"""
let titleAttributes = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 36)]
let textAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12)]
let formattedTitle = NSMutableAttributedString(string: title, attributes: titleAttributes)
let formattedText = NSAttributedString(string: text, attributes: textAttributes)
formattedTitle.append(formattedText)
let data = renderer.pdfData { ctx in
ctx.beginPage()
formattedTitle.draw(in: pageRect.insetBy(dx: 50, dy: 50))
}
DispatchQueue.main.async {
// Set the pdfData @State variable of this view equal to this pdf data
self.pdfData = data
}
}
}