Cannot share with UICloudSharingController; vanishes with "uploading" message - cloudkit

while presenting the UICloudSharingController on top of a view, it presents the screen and when I select the messages option to send a message to a person whom I want to share with, it gives a spinning wheel with "uploading" message and vanishes - attached.
However when I go to cloudkit dashboard the root record has been shared. But I cannot share it with specific person. Is it because it has shared global? How can I fix it?
self.shareInfraRecord(zoneID: appDelegate.privateContactZoneID, completion: { (status) in
if ( status == false) {
return
}
})
func shareInfraRecord(zoneID: CKRecordZone.ID, completion: #escaping(Bool) -> Void) {
if let rootRecord = self.rootRecord {
if self.rootRecord?.share == nil {
let sharingController = UICloudSharingController { (controller, preparationHandler: #escaping (CKShare?, CKContainer?, Error?) -> Void) in
let shareID = CKRecord.ID(recordName: UUID().uuidString, zoneID: zoneID)
var share = CKShare(rootRecord: rootRecord, shareID: shareID)
share[CKShare.SystemFieldKey.title] = Cloud.ShareInfrastructure.ContactShareTitleKey as CKRecordValue?
share[CKShare.SystemFieldKey.shareType] = Cloud.ShareInfrastructure.ContactShareTypeKey as CKRecordValue?
let modifyRecZoneOp = CKModifyRecordsOperation(recordsToSave:[rootRecord, share], recordIDsToDelete: nil)
modifyRecZoneOp.modifyRecordsCompletionBlock = { (records, recordID, error) in
if error != nil {
if let ckerror = error as? CKError {
if let serverVersion = ckerror.serverRecord as? CKShare {
share = serverVersion
}
completion(false)
}
}
preparationHandler(share, self.defaultContainer, error)
}
self.privateDB?.add(modifyRecZoneOp)
}
sharingController.availablePermissions = [.allowReadOnly, .allowPrivate]
sharingController.delegate = self
sharingController.popoverPresentationController?.sourceView = self.view
self.present(sharingController, animated:true, completion:nil)
} else {
let shareRecordID = rootRecord.share!.recordID
let fetchRecordsOp = CKFetchRecordsOperation(recordIDs: [shareRecordID])
fetchRecordsOp.fetchRecordsCompletionBlock = { recordsByRecordID, error in
guard error == nil, let share = recordsByRecordID?[shareRecordID] as? CKShare else {
if let ckerror = error as? CKError {
self.aErrorHandler.handleCkError(ckerror: ckerror)
//self.saveToCloudKitStatus(recordName: myRecordName, success: false)
}
completion(false)
return
}
DispatchQueue.main.async {
let sharingController = UICloudSharingController(share: share, container: self.defaultContainer!)
completion(true)
//completionHandler(sharingController)
}
}
self.privateDB?.add(fetchRecordsOp)
}
}
}

This might be a bit late but I was running into this issue too, while using NSPersistentCloudKitContainer and it seems the issue was just making sure that my iCloud container name in the Capabilities section of the settings matched my app bundle name ie iCloud.com.goddamnyouryan.MyApp

Related

Images not saving in Custom Folder in Gallery in iOS swift

I am trying to save my images in custom folder in iPhone gallery but instead of saving original image it saves only white blank image on real device. but when I run same code on simulator its working fine.
the code I am trying is :
import Photos
class CustomPhotoAlbum: NSObject {
static let albumName = "App Name" // here put your album name
static let sharedInstance = CustomPhotoAlbum()
var assetCollection: PHAssetCollection!
private var collection: PHFetchResult<PHAssetCollection>!
override init() {
super.init()
if let assetCollection = fetchAssetCollectionForAlbum() {
self.assetCollection = assetCollection
return
}
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized {
PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in
()
})
}
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
self.createAlbum()
} else {
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus) {
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
// ideally this ensures the creation of the photo album even if authorization wasn't prompted till after init was done
print("trying again to create the album")
self.createAlbum()
} else {
print("should really prompt the user to let them know it's failed")
}
}
func createAlbum() {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an asset collection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
} else {
print("error \(error)")
}
}
}
func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
var firstObject: PHAssetCollection?
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", CustomPhotoAlbum.albumName)
DispatchQueue.main.async {
self.collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _: AnyObject = self.collection.firstObject {
firstObject = self.collection.firstObject
}
}
if firstObject != nil {
return firstObject
} else {
return nil
}
}
func save(image: UIImage) {
if assetCollection == nil {
return // if there was an error upstream, skip the save
}
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
}, completionHandler: nil)
}
}
and when I use this code to save the image is:
func saveImage() {
let snapShot:UIView = backgroundImage.snapshotView(afterScreenUpdates: true)!
UIGraphicsBeginImageContext(backgroundImage.bounds.size)
snapShot.drawHierarchy(in: backgroundImage.bounds, afterScreenUpdates: true)
let image:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
CustomPhotoAlbum.sharedInstance.save(image: image)
self.view.snapshotView(afterScreenUpdates: true)
}
Also I add permissions in my info.plist.
but the code is running fine on simulator but not on real device. On real device its also log an error in console:
"Error returned from daemon: Error Domain=com.apple.accounts Code=7
"(null)"" 2022-08-30 17:25:21.043516+0500 App Name[19316:1400881]
[PAAccessLogger] Failed to log access with error: access=<PATCCAccess
0x28247e5b0> accessor:<<PAApplication 0x2809d98b0
identifierType:auditToken identifier:{pid:19316, version:56046}>>
identifier:08A3A297-406E-45A2-8D0D-4C443A3F2835 kind:intervalEnd
timestampAdjustment:0 tccService:kTCCServicePhotos, error=Error
Domain=PAErrorDomain Code=10 "Possibly incomplete access interval
automatically ended by daemon"
I am using Xcode 13.3, Almost same question here asked, I tried every answer but nothing helped.
can someone please help me to fix this issue Thanks.

Nested completion handlers that do not return items to be sent to TableViewController

I want to get some data from firebaseFirestore and download an image URL from firebaseStorage, while preparing for my segue that will bring the user to the TableViewController, where they will be displayed. Even when using some nested completion handlers (perhaps I made the code too longs), I'm still not able to perform my asyncronous tasks in order, thus rushing too early to the segue. For simplicity I'm using the single segue (no identifiers). In the ViewControllerForTable I have stated a variable var cells : [Cella] = [] globally.
let firestoreUsersReference = Firestore.firestore().collection("users")
let storageReference = Storage.storage()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! ViewControllerForTable
prepareDataForSegue(firestoreReference: firestoreUsersReference) { (cella) in
destinationVC.cells = cella
print(destinationVC.cells)
}
}
func getImagesDownloaded(reference: StorageReference, completion: #escaping (UIImage?,Error?)->()) {
reference.getData(maxSize: 10*1024*1024) { (data, error) in
guard error == nil, let data = data else {
completion(nil,error)
return
}
guard let image = UIImage(data: data) else {
completion(nil, FirebaseErrors.expectedImage)
return
}
completion(image,nil)
}
}
enum FirebaseErrors: Error {
case expectedImage
}
func prepareDataForSegue (firestoreReference: CollectionReference, completion : #escaping ([Cella])->()) {
var cellaArray : [Cella] = []
firestoreUsersReference.getDocuments { (querySnapshot, err) in
if err != nil {
print("There has been an error \(String(describing: err?.localizedDescription))")
}
else {
self.getDocumentsFromFirestore(querySnapshot: querySnapshot, completion: { (title, description, image) in
let newCell = Cella(image: image, title: title, bodyMessage: description)
print("NEW CELL : \(newCell)")
cellaArray.append(newCell)
})
}
}
completion(cellaArray)
}
func getDocumentsFromFirestore (querySnapshot: QuerySnapshot?, completion: #escaping (String,String,UIImage)->()) {
for documents in querySnapshot!.documents {
print("\(documents.documentID) => \(documents.data())")
let data = documents.data()
let title = data["userTitle"] as? String
let description = data["userDescription"] as? String
let imageURL = data["userImageURL"] as! String
print("Title: \(String(describing: title)), Description: \(String(describing: description)), imageURL: \(imageURL)")
let storagePath = Storage.storage().reference(forURL: imageURL)
self.getImagesDownloaded(reference: storagePath, completion: { (image, error) in
guard let image = image, error == nil else {
print(String(describing : error?.localizedDescription))
return
}
print("TITLE: \(String(describing: title)), IMAGE: \(image)")
completion(title!, description!, image)
})
}
}
If I understand your question correctly, here what you need to do:
Disconnect the segue for the button in your storyboard.
In the IBAction function for the button, do your prepareDataForSegue work
Once the completion handler is called, call performSegue, which will call the prepareSegue, where you can assign the downloaded cella.
This should load the tableVC only when the data is available.

Use of Unresolved Identifier firebase function

I've defined the function sendDataToDatabase but for some reason it's not resolving photoUrl?
I've been trying to figure out what might be causing this for 6 hours now and can't seem to find a solution, if anyone could provide some help it would be appreciated.
#IBAction func shareButton_TouchUpInside(_ sender: Any) {
ProgressHUD.show("Waiting...", interaction: false)
if let profileImg = self.selectedImage, let imageData = profileImg.jpegData(compressionQuality: 0.1) {
let photoId = NSUUID().uuidString
let storageRef = Storage.storage().reference(forURL: "manifest-bit-233115.appspot.com").child("posts").child(photoId)
storageRef.putData(imageData, metadata: nil, completion: { (metadata, Error) in
if Error != nil {
return
}
storageRef.downloadURL(completion: { (URL, Error) -> Void in
if (Error != nil) {
//handle any errors
} else {
//get download url
let photoUrl = URL?.absoluteString
}
self.sendDataToDatabase(photoUrl: photoUrl!)
})
}
)}
func sendDataToDatabase(photoUrl: photoUrl!) {
let ref = Database.database().reference()
//let uid = Auth.auth().currentUser!.uid
let postsRef = ref.child("posts")
let newPostId = postsRef.childByAutoId().key
let newPostRef = postsRef.child(newPostId!)
newPostRef.setValue(["photoUrl": photoUrl])
}
There are many issues.
You have to call sendDataToDatabase only in the else branch and declare the parameters with starting lowercase letters.
The parameters are not types.
storageRef.downloadURL(completion: { (url, error) -> Void in
if let error = error {
//handle any errors
} else {
//get download url
let photoUrl = url!.absoluteString
self.sendDataToDatabase(photoUrl: photoUrl)
}
})
and you have to declare the type in the function
func sendDataToDatabase(photoUrl: String) { ...
This won't work:
storageRef.downloadURL(completion: { (URL, Error) -> Void in
if (Error != nil) {
//handle any errors
} else {
//get download url
let photoUrl = URL?.absoluteString
}
self.sendDataToDatabase(photoUrl: photoUrl!)
})
photoUrl will only be available within the else clause, since that's where it's defined, and you can not use it outside of that scope.
Also, this:
func sendDataToDatabase(photoUrl: photoUrl!)
should probably be:
func sendDataToDatabase(photoUrl: String)
It's also a good idea to not name variables URL and Error, since they are identical to the URL and Error classes. Name them url and error instead.

Where to put Firebase Performance trace

I am trying to determine what the best location would be for putting a firebase Performance trace. I want to see how long it is taking my app to pull data.
In my VC I have the following
func pullAllUsersCards() {
// 1 Start Here?
FirebaseUtility.shared.getCards { (cards, errMessage) in
if let theCards = cards {
if theCards.count < 1 {
if let addVC = self.storyboard?.instantiateViewController(withIdentifier: StoryboardKeys.addCardViewControllerStoryboardID) as? AddCardViewController {
let addNavigation = UINavigationController(rootViewController: addVC)
if UIDevice.current.userInterfaceIdiom == .pad {
self.splitViewController?.present(addNavigation, animated: true, completion: nil)
} else {
self.present(addNavigation, animated: true, completion: nil)
}
}
} else {
// 2 Start Here?
MBProgressHUD.showAdded(to: self.view, animated: true)
self.cardArray = theCards
self.tableView.reloadData()
MBProgressHUD.hide(for: self.view, animated: true)
}
}
}
}
Originally I wanted to put the trace on my singleton class FirebaseUtility where the getCards method is.
func getCards(completion: #escaping (_ cards: [Card]?, _ errorMessage: String?) -> Void) {
// let testTrace = Performance.startTrace(name: "Test")
guard let userID = user?.uid else {
let error = "Unknown error occured! User is not logged in."
completion(nil, error)
return
}
let userCardRef = ref.child(FirebaseKeys.newCards).child(userID)
userCardRef.observe(.value, with: { (snapshot) in // changed from Single Event
let enumerator = snapshot.children
var cards = [Card]()
while let cardSnapshot = enumerator.nextObject() as? DataSnapshot {
if let cardDict = cardSnapshot.value as? [String : Any] {
let card = Card(id: cardSnapshot.key, cardDict: cardDict)
cards.append(card)
}
}
completion(cards, nil)
})
// testTrace?.stop()
}
however when I try to use it there I get an error saying Firebase Performance does not support Extensions at this time
are you using Firebase Performance in the context of an App Extension (e.g. Watch, keyboard, today, etc.)? That message is triggered by this line in the FirebasePerformance.h file:
NS_EXTENSION_UNAVAILABLE("FirebasePerformance does not support app extensions at this time.")
Firebase Performance currently only supports normal applications on iOS.

whose view is not in the windows heirarchy

I know this question has been asked before but none of the answers in the other questions worked for me. Here is my code:
```
var values = [String: AnyObject]()
func loginUserToFirebase(_ completion: () -> Void) {
let accessToken = FBSDKAccessToken.current()
guard let accessTokenString = accessToken?.tokenString else {fatalError()}
let credentials = FIRFacebookAuthProvider.credential(withAccessToken: accessTokenString)
FIRAuth.auth()?.signIn(with: credentials, completion: { (user, error) in
if error != nil {
print(error ?? "Something went wrong")
return
}
self.fbGraphRequest()
})
}
internal func fbGraphRequest(){
FBSDKGraphRequest(graphPath: "/me", parameters: ["fields": "id, name, email"]).start { (connection, result, error) in
if error != nil {
print(error ?? "error unknown")
return
} else {
print(result ?? "no result")
self.values = result as! [String: AnyObject]
print(self.values)
weak var rootViewModel = RootViewModel()
rootViewModel?.values = self.values
self.presentRootViewController()
}
}
}
internal func presentRootViewController() {
let loginController = LoginController()
let rootViewController = RootViewController()
loginController.present(rootViewController, animated: true,
completion: nil)
}
```
and here is my error:
Attempt to present <Art_Cache.RootViewController: 0x7fa6a1c2aab0> on <Art_Cache.LoginController: 0x7fa6a1c840b0> whose view is not in the window hierarchy!
This snippet worked when I had this in my LoginViewController and I used self.present(rootViewController, animation: true, completion: nil). Im trying to convert my project to MVVM and this is what's happening. The problem seems to be around the self.presentRootViewController(). These functions are fired upon pressing the facebook login button. Please help and cheers!
This happens when you current view controller's is not the part of window, could you change
func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
let moreNavigationController = tab.moreNavigationController
if let top = moreNavigationController.topViewController, top.view.window != nil {
return topViewController(top)
} else if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}
topViewController()?.present(rootViewController, animated: true,
completion: nil)