SwiftUI CKShare Set Default Permission to Read Only - ios

I have a couple of apps that include CloudKit record sharing and it now seems to work ok in iOS 16.
I only include Read Write and Read Only permission options and require the share recipient be in the user contacts.
This does indeed seem to work as expected, however the default choice in the share sheet options is Read Write.
The vast majority of the time I want Read Only. I have not been able to find a way to change
the default permission from "Can make changes" to "View only"
My CloudSharingController is pretty straightforward:
struct CloudSharingView: UIViewControllerRepresentable {
let share: CKShare
let container: CKContainer
let recipe: Recipe
func makeCoordinator() -> CloudSharingCoordinator {
CloudSharingCoordinator(recipe: recipe)
}
func makeUIViewController(context: Context) -> UICloudSharingController {
share[CKShare.SystemFieldKey.title] = recipe.rName
let controller = UICloudSharingController(share: share, container: container)
controller.modalPresentationStyle = .formSheet
controller.delegate = context.coordinator
controller.availablePermissions = [.allowReadWrite, .allowReadOnly]
return controller
}
func updateUIViewController(_ uiViewController: UICloudSharingController, context: Context) {
}
}//cloud sharing view
final class CloudSharingCoordinator: NSObject, UICloudSharingControllerDelegate {
let stack = CoreDataStack.shared
let recipe: Recipe
init(recipe: Recipe) {
self.recipe = recipe
}
func itemTitle(for csc: UICloudSharingController) -> String? {
recipe.rName
}
func cloudSharingController(_ csc: UICloudSharingController, failedToSaveShareWithError error: Error) {
print("Failed to save share: \(error)")
}
func cloudSharingControllerDidSaveShare(_ csc: UICloudSharingController) {
print("Saved the share")
}
func cloudSharingControllerDidStopSharing(_ csc: UICloudSharingController) {
if !stack.isOwner(object: recipe) {
stack.delete(recipe)
}
}
}//cloud sharing coordinator
The screen:
Any guidance would be appreciated. Xcode 14.0, iOS 16.0

The availablePermissions seems to have two pairs of options:
allowPrivate
allowPublic
and
allowReadOnly
allowReadWrite
You need to specify one from each pair, so in your case it looks like you need
controller.availablePermissions = [.allowPrivate, .allowReadWrite]
If you don't specify one from a pair, it looks like it takes the most permissive option.

Related

Value of type 'CapturedImage' has no member 'append'

I'm trying to evolve my app to save an array of images in Core Data instead of pre-defined attributes of an Entity. Today, my Entity has 4 image attributes, and the user saves 4 images as 4 separate actions within the app. It works.
Now I'm trying to implement VisionKit so the user can just auto scan and crop the images one after another, and then save one time to get multiple images saved. I have the scan piece working:
import SwiftUI
import VisionKit
struct ScannerView: UIViewControllerRepresentable {
#Binding var images: [Data]
var didFinishScanning: ((_ result: Result<[UIImage], Error>) -> Void)
var didCancelScanning: () -> Void
func makeUIViewController(context: Context) -> VNDocumentCameraViewController {
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = context.coordinator
return scannerViewController
}
func updateUIViewController(_ uiViewController: VNDocumentCameraViewController, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(with: self)
}
class Coordinator: NSObject, VNDocumentCameraViewControllerDelegate {
let scannerView: ScannerView
init(with scannerView: ScannerView) {
self.scannerView = scannerView
}
// MARK: - VNDocumentCameraViewControllerDelegate
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
var scannedPages = [UIImage]()
for i in 0..<scan.pageCount {
scannedPages.append(scan.imageOfPage(at: i))
let image = scan.imageOfPage(at: i)
scannerView.images.append( image.jpegData(compressionQuality: 0.55)! )
}
scannerView.didFinishScanning(.success(scannedPages))
controller.dismiss(animated: true, completion: nil)
}
func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
scannerView.didCancelScanning()
}
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
scannerView.didFinishScanning(.failure(error))
}
}
}
And can immediately view the images in the View
LazyHStack {
ForEach(0..<images.count, id: \.self) { imageIdx in
Image(uiImage: UIImage(data: self.images[imageIdx])!)
.resizable()
.cornerRadius(6)
.scaledToFit()
.frame(width: self.width, height: self.height)
}
}
Here's a quick video showing everything to this point: https://imgur.com/a/FJqHtW5
I'm stuck trying to properly save the array of images. I get error Value of type 'CapturedImage' has no member 'append'
for image in images {
let capImage = CapturedImage(context: viewContext)
capImage.image = image
capImage.timestamp = Date()
project.capturedimage?.append(capImage) --->>> error
}
My CapturedImage+CoreDataProperties.swift looks like this:
import Foundation
import CoreData
extension CapturedImage {
#nonobjc public class func fetchRequest() -> NSFetchRequest<CapturedImage> {
return NSFetchRequest<CapturedImage>(entityName: "CapturedImage")
}
#NSManaged public var image: Data?
#NSManaged public var timestamp: Date?
#NSManaged public var project: Project?
}
extension CapturedImage : Identifiable {
}
And here is the relationship between Project and CapturedImage entities (you'll also notice the image1 image2 image3 image4 attributes that currently work for single image saves...
What do I need to do in order to overcome that append error above?
Am I on the right track for saving the array to CD properly?
Apologies for the length of this post! I wanted to provide as much info as I could while trying to keep it brief. Thanks for looking
According to your model capturedimage in Project is a To Many relationship, the type is NSSet or Set<CapturedImage>. And the naming is misleading, it should be named in plural form capturedImages.
Sets are unordered, you have to insert the object. Depending on your settings Core Data might create a convenient setter addToCapturedImages on your behalf
project.addToCapturedImages(capImage)
Please see Generating Code in the documentation.

PHPickerViewController tapping on Search gets error... "Unable to load photos"

I'm trying to implement a PHPickerViewController using SwiftUI and The Composable Architecture. (Not that I think that's particularly relevant but it might explain why some of my code is like it is).
Sample project
I've been playing around with this to try and work it out. I created a little sample Project on GitHub which removes The Composable Architecture and keeps the UI super simple.
https://github.com/oliverfoggin/BrokenImagePickers/tree/main
It looks like iOS 15 is breaking on both the UIImagePickerViewController and the PHPickerViewController. (Which makes sense as they both use the same UI under the hood).
I guess the nest step is to determine if the same error occurs when using them in a UIKit app.
My code
My code is fairly straight forward. It's pretty much just a reimplementation of the same feature that uses UIImagePickerViewController but I wanted to try with the newer APIs.
My code looks like this...
public struct ImagePicker: UIViewControllerRepresentable {
// Vars and setup stuff...
#Environment(\.presentationMode) var presentationMode
let viewStore: ViewStore<ImagePickerState, ImagePickerAction>
public init(store: Store<ImagePickerState, ImagePickerAction>) {
self.viewStore = ViewStore(store)
}
// UIViewControllerRepresentable required functions
public func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> some UIViewController {
// Configuring the PHPickerViewController
var config = PHPickerConfiguration()
config.filter = PHPickerFilter.images
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// This is the coordinator that acts as the delegate
public class Coordinator: PHPickerViewControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let itemProvider = results.first?.itemProvider,
itemProvider.canLoadObject(ofClass: UIImage.self) else {
return
}
itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
if let image = image as? UIImage {
DispatchQueue.main.async {
self?.parent.viewStore.send(.imagePicked(image: image))
}
}
}
}
}
}
All this works in the simple case
I can present the ImagePicker view and select a photo and it's all fine. I can cancel out of it ok. I can even scroll down the huge collection view of images that I have. I can even see the new image appear in my state object and display it within my app. (Note... this is still WIP and so the code is a bit clunky but that's only to get it working initially).
The problem case
The problem is that when I tap on the search bar in the PHPickerView (which is a search bar provided by Apple in the control, I didn't create it or code it). It seems to start to slide up the keyboard and then the view goes blank with a single message in the middle...
Unable to Load Photos
[Try Again]
I also get a strange looking error log. (I removed the time stamps to shorten the lines).
// These happen on immediately presenting the ImagePicker
AppName[587:30596] [Picker] Showing picker unavailable UI (reason: still loading) with error: (null)
AppName[587:30596] Writing analzed variants.
// These happen when tapping the search bar
AppName[587:30867] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin interrupted while in use.
AppName[587:31002] [lifecycle] [u A95D90FC-C77B-43CC-8FC6-C8E7C81DD22A:m (null)] [com.apple.mobileslideshow.photospicker(1.0)] Connection to plugin invalidated while in use.
AppName[587:30596] [Picker] Showing picker unavailable UI (reason: crashed) with error: (null)
AppName[587:30596] viewServiceDidTerminateWithError:: Error Domain=_UIViewServiceInterfaceErrorDomain Code=3 "(null)" UserInfo={Message=Service Connection Interrupted}
Tapping the "Try Again" button reloads the initial scroll screen and I can carry on using it. But tapping the search bar again just shows the same error.
I'm usually the first one to point out that the error is almost definitely not with the Apple APIs but I'm stumped on this one. I'm not sure what it is that I'm doing that is causing this to happen?
Is it the fact that it's in a SwiftUI view?
Recreated the project in UIKit
I remade the same project using UIKit... https://github.com/oliverfoggin/UIKit-Image-Pickers
And I couldn't replicate the crash at all.
Also... if you are taking any sort of screen recording of the device the crash will not happen. I tried taking a recording on the device itself and couldn't replicate it. I also tried doing a movie recording from my Mac using the iPhone screen and couldn't replicate the crash. But... the instant I stopped the recording on QuickTime the crash was replicable again.
This fixed it for me .ignoreSafeArea(.keyboard) like #Frustrated_Student mentions.
To elaborate on #Frustrated_Student this issue has to do with the UIViewControllerRepresentable treating the view like many SwiftUI views to automatically avoid the keyboard. If you are presenting the picker using a sheet as I am then you can simply add the .ignoreSafeArea(.keyboard) to the UIViewControllerRepresentable view in my case I called it ImagePicker here is a better example.
Where to add it the .ignoreSafeArea(.keyboard)
.sheet(isPresented: $imagePicker) {
ImagePicker(store: store)
.ignoresSafeArea(.keyboard)
}
This is #Fogmeister code:
public struct ImagePicker: UIViewControllerRepresentable {
// Vars and setup stuff...
#Environment(\.presentationMode) var presentationMode
let viewStore: ViewStore<ImagePickerState, ImagePickerAction>
public init(store: Store<ImagePickerState, ImagePickerAction>) {
self.viewStore = ViewStore(store)
}
// UIViewControllerRepresentable required functions
public func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> some UIViewController {
// Configuring the PHPickerViewController
var config = PHPickerConfiguration()
config.filter = PHPickerFilter.images
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// This is the coordinator that acts as the delegate
public class Coordinator: PHPickerViewControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let itemProvider = results.first?.itemProvider,
itemProvider.canLoadObject(ofClass: UIImage.self) else {
return
}
itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
if let image = image as? UIImage {
DispatchQueue.main.async {
self?.parent.viewStore.send(.imagePicked(image: image))
}
}
}
}
}
}
Well.. this seems to be an iOS bug.
I have cerated a sample project here that shows the bug... https://github.com/oliverfoggin/BrokenImagePickers
And a replica project here written with UIKit that does not... https://github.com/oliverfoggin/UIKit-Image-Pickers
I tried to take a screen recording of this happening but it appears that if any screen recording is happening (whether on device or via QuickTime on the Mac) this suppresses the bug from happening.
I have filed a radar with Apple and sent them both projects to have a look at and LOTS of detail around what's happening. I'll keep this updated with any progress on that.
Hacky workaround
After a bit of further investigation I found that you can start with SwiftUI and then present a PHPickerViewController without this crash happening.
From SwiftUI if you present a UIViewControllerRepresentable... and then from there if you present the PHPickerViewController it will not crash.
So I came up with a (very tacky) workaround that avoids this crash.
I first create a UIViewController subclass that I use like a wrapper.
class WrappedPhotoPicker: UIViewController {
var picker: PHPickerViewController?
override func viewDidLoad() {
super.viewDidLoad()
if let picker = picker {
present(picker, animated: false)
}
}
}
Then in the SwiftUI View I create this wrapper and set the picker in it.
struct WrappedPickerView: UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentationMode
#Binding var photoPickerResult: PHPickerResult?
let wrappedPicker = WrappedPhotoPicker()
func makeUIViewController(context: Context) -> WrappedPhotoPicker {
var config = PHPickerConfiguration()
config.filter = .images
config.selectionLimit = 1
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
wrappedPicker.picker = picker
return wrappedPicker
}
func updateUIViewController(_ uiViewController: WrappedPhotoPicker, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: PHPickerViewControllerDelegate {
let parent: WrappedPickerView
init(_ parent: WrappedPickerView) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.presentationMode.wrappedValue.dismiss()
parent.wrappedPicker.dismiss(animated: false)
parent.photoPickerResult = results.first
}
}
}
This is far from ideal as I'm presenting at the wrong time and stuff. But it works until Apple provide a permanent fix for this.
I started getting a weird UI bug after the PHPickerViewController crashed where the keyboard was not visible but my views were still being squashed. So I suspected a keyboard / avoidance issue. I disabled keyboard avoidance in a parent view and managed to stop it from crashing.
.ignoresSafeArea(.keyboard)
.... still a iOS bug in 15.0. I've modified Fogmeister's class Coordinator to return the image in addition to the PHPickerResult.
struct WrappedPickerView: UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentationMode
#Binding var photoPickerResult: PHPickerResult?
#Binding var image: UIImage?
let wrappedPicker = WrappedPhotoPicker()
func makeUIViewController(context: Context) -> WrappedPhotoPicker {
var config = PHPickerConfiguration()
config.filter = .images
config.selectionLimit = 1
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
wrappedPicker.picker = picker
return wrappedPicker
}
func updateUIViewController(_ uiViewController: WrappedPhotoPicker, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: PHPickerViewControllerDelegate {
let parent: WrappedPickerView
init(_ parent: WrappedPickerView) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
self.parent.presentationMode.wrappedValue.dismiss()
self.parent.wrappedPicker.dismiss(animated: false)
self.parent.photoPickerResult = results.first
print(results)
guard let result = results.first else {
return
}
self.parent.image = nil
DispatchQueue.global().async {
result.itemProvider.loadObject(ofClass: UIImage.self) { (object, error) in
guard let imageLoaded = object as? UIImage else {
return
}
DispatchQueue.main.async {
self.parent.image = imageLoaded
}
}
}
}
}
}

I am getting firebase permission error. Even though I did it according to the firebase rules, it doesn't work

Hi firebase I wanted to upload pdf doc style data from icloud by connecting to simulator. but I am getting a user access error. I don't want to have a user already in my application. What I did as in the firebase documentation is still not working.
also allow read, write: if true; I haven't tried it still does not allow access
Firebase;
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
SwiftUI contentview;
import SwiftUI
import MobileCoreServices
import Firebase
struct ContentView: View {
#State var show = false
#State var alert = false
var body: some View {
Button {
self.show.toggle()
} label: {
Text("Document Picker")
}
.sheet(isPresented: $show)
{
DocumentPicker(alert: self.$alert)
}
.alert(isPresented: $alert) {
Alert(title: Text("Opps Wonderful"), message: Text("OKey Upload"), dismissButton: .default(Text("OK")))
} }
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct DocumentPicker : UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return DocumentPicker.Coordinator(parent1: self)
}
#Binding var alert: Bool
func makeUIViewController(context: UIViewControllerRepresentableContext <DocumentPicker> ) -> UIDocumentPickerViewController {
let picker = UIDocumentPickerViewController(documentTypes: [String(kUTTypeItem)] , in: .open)
picker.allowsMultipleSelection = false
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<DocumentPicker>) {
}
class Coordinator: NSObject, UIDocumentPickerDelegate {
var parent : DocumentPicker
init (parent1: DocumentPicker)
{
parent = parent1
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
let bucket = Storage.storage().reference()
bucket.child((urls.first?.deletingPathExtension().lastPathComponent)!).putFile(from: urls.first!,metadata: nil ){
(_, err) in
if err != nil {
print ((err?.localizedDescription)!)
return
}
print("succeess")
self.parent.alert.toggle()
}
}
}
}
The security rules you're showing are for Cloud Firestore (a document database), while your code is using Cloud Storage (file storage). While both are part of Firebase, they each have their own API and their own security rules, and the security rules of one don't apply to the other.
You'll want to study the documentation on securing Cloud Storage, and then set the security rules for Cloud Storage in your project.

SwiftUI and UICloudSharingController hate each other

I have a project using SwiftUI that requires CloudKit sharing, but I'm unable to get the UICloudSharingController to play nice in a SwiftUI environment.
First Problem
A straight-forward wrap of UICloudSharingController using UIViewControllerRepresentable yields an endless spinner (see this). As has been done for other system controllers like UIActivityViewController, I wrapped the UICloudSharingController in a containing UIViewController like this:
struct CloudSharingController: UIViewControllerRepresentable {
#EnvironmentObject var store: CloudStore
#Binding var isShowing: Bool
func makeUIViewController(context: Context) -> CloudControllerHost {
let host = CloudControllerHost()
host.rootRecord = store.noteRecord
host.container = store.container
return host
}
func updateUIViewController(_ host: CloudControllerHost, context: Context) {
if isShowing, host.isPresented == false {
host.share()
}
}
}
final class CloudControllerHost: UIViewController {
var rootRecord: CKRecord? = nil
var container: CKContainer = .default()
var isPresented = false
func share() {
let sharingController = shareController
isPresented = true
present(sharingController, animated: true, completion: nil)
}
lazy var shareController: UICloudSharingController = {
let controller = UICloudSharingController { [weak self] controller, completion in
guard let self = self else { return completion(nil, nil, CloudError.controllerInvalidated) }
guard let record = self.rootRecord else { return completion(nil, nil, CloudError.missingNoteRecord) }
let share = CKShare(rootRecord: record)
let operation = CKModifyRecordsOperation(recordsToSave: [record, share], recordIDsToDelete: [])
operation.modifyRecordsCompletionBlock = { saved, _, error in
if let error = error {
return completion(nil, nil, error)
}
completion(share, self.container, nil)
}
self.container.privateCloudDatabase.add(operation)
}
controller.delegate = self
controller.popoverPresentationController?.sourceView = self.view
return controller
}()
}
This allows the controller to come up normally, but...
Second Problem
Tap the close button or swipe to dismiss and the controller will disappear, but there's no notification that it's been dismissed. The SwiftUI view's #State property that initiated presenting the controller is still true. There's no obvious method to detect dismissal of the modal. After some experimenting, I discovered the presenting controller is the original UIHostingController created in the SceneDelegate. With some hackery, you can inject an object that is referenced in a UIHostingController subclass into the CloudSharingController. This will let you detect the dismissal and set the #State property to false. However, all nav bar buttons no longer function after dismissing so you could only ever tap this thing once. The rest of the scene is completely functional, but buttons in the nav bar don't respond.
Third Problem
Even if you could get the UICloudSharingController to present and dismiss normally, tapping on any of the sharing methods (Messages, Mail, etc) makes the controller disappear with no animation and the controller for the sharing URL doesn't come up. No crash or console messages--it just disappears.
Demo
I made a quick and dirty project on GitHub to demonstrate the issue: CloudKitSharing. It just creates a single String and a CKRecord to represent it using CloudKit. The interface displays the String (a UUID) with a single nav bar button to share it:
The Plea
Is there any way to use UICloudSharingController in SwiftUI? Don't have the time to rebuild the project in UIKit or a custom sharing controller (I know--the price of being on the bleeding edge 💩)
I got this working -- initially, I wrapped the UICloudSharingController in a UIViewControllerRepresentable, much like the link you provided (I referenced that while building it), and simply adding it to a SwiftUI .sheet() view. This worked on the iPhone, but it failed on the iPad, because it requires you to set the popoverPresentationController?.sourceView, and I didn't have one, given that I triggered the sheet with a SwiftUI Button.
Going back to the drawing board, I rebuilt the button itself as a UIViewRepresentable, and was able to present the view using the rootViewController trick that SeungUn Ham suggested here. All works, on both iPhone and iPad - at least in the simulator.
My button:
struct UIKitCloudKitSharingButton: UIViewRepresentable {
typealias UIViewType = UIButton
#ObservedObject
var toShare: ObjectToShare
#State
var share: CKShare?
func makeUIView(context: UIViewRepresentableContext<UIKitCloudKitSharingButton>) -> UIButton {
let button = UIButton()
button.setImage(UIImage(systemName: "person.crop.circle.badge.plus"), for: .normal)
button.addTarget(context.coordinator, action: #selector(context.coordinator.pressed(_:)), for: .touchUpInside)
context.coordinator.button = button
return button
}
func updateUIView(_ uiView: UIButton, context: UIViewRepresentableContext<UIKitCloudKitSharingButton>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UICloudSharingControllerDelegate {
var button: UIButton?
func cloudSharingController(_ csc: UICloudSharingController, failedToSaveShareWithError error: Error) {
//Handle some errors here.
}
func itemTitle(for csc: UICloudSharingController) -> String? {
return parent.toShare.name
}
var parent: UIKitCloudKitSharingButton
init(_ parent: UIKitCloudKitSharingButton) {
self.parent = parent
}
#objc func pressed(_ sender: UIButton) {
//Pre-Create the CKShare record here, and assign to parent.share...
let sharingController = UICloudSharingController(share: share, container: myContainer)
sharingController.delegate = self
sharingController.availablePermissions = [.allowReadWrite]
if let button = self.button {
sharingController.popoverPresentationController?.sourceView = button
}
UIApplication.shared.windows.first?.rootViewController?.present(sharingController, animated: true)
}
}
}
Maybe just use rootViewController.
let window = UIApplication.shared.windows.filter { type(of: $0) == UIWindow.self }.first
window?.rootViewController?.present(sharingController, animated: true)

How to print a UIWebView via UIActivityViewController?

I have a view controller containing a UIWebView and a toolbar with an action/share button. This initializes and presents a UIActivityViewController object. Depending on whether I supply the activityItems parameter with either the web view's URL or the URL's corresponding absoluteString, different actions are offered, but the Print option is never shown (nor offered in the "more" section).
I do know how to print the web view contents explicitly using UIPrintInfo and UIPrintInteractionController, but that would be a separate toolbar button whereas I want to simply include the system's Print option into the activity button row. I assume printing a web view does not need any explicit coding.
What can I do?
You can create Custom Activity For UIActivityCotnroller like this,
import UIKit
protocol CustomActivityDelegate : NSObjectProtocol
{
func performActionCompletion(actvity: CustomActivity)
}
class CustomActivity: UIActivity {
var delegate: CustomActivityDelegate?
override class var activityCategory: UIActivityCategory {
return .action
}
override var activityType: UIActivityType? {
guard let bundleId = Bundle.main.bundleIdentifier else {return nil}
return UIActivityType(rawValue: bundleId + "\(self.classForCoder)")
}
override var activityTitle: String? {
return "You title"
}
override var activityImage: UIImage? {
return <Your activity image >
}
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
return true
}
override func prepare(withActivityItems activityItems: [Any]) {
//
}
override func perform() {
self.delegate?.performActionCompletion(actvity: self)
activityDidFinish(true)
}
}
You can initialize this activity some thing like this
let customActivity = CustomActivity()
customActivity.delegate = self
And you can add this custom activity while preparing UIActivityController
let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [customActivity], applicationActivities: nil)
and you will also need to implement the call back method
func performActionCompletion(actvity: CustomActivity)
{
//Perform you task
}
Note : This is just pseudo code, might contain error or syntax problems

Resources