iOS 14: Create album with new "Selected Photos..." permission? and Fetch AssetCollection For Album? - photo

Step:1 In Gallery Permission i select "Selected Photos...".
Step:2 I create Custom Photo Album.
Step:3 And Try to Fetch AssetCollection from album.
Step:4 It give collection.firstObject = nil.
Step:5 It Work Good if i select Allow Access to All Photos.
For Create album i use :
#objc func createAlbumIfNeeded() {
if let assetCollection = fetchAssetCollectionForAlbum() {
// Album already exists
print("album Already exist")
self.assetCollection = assetCollection
} else {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an assetadsasdasddsa colsadasdasddfsfsdfsfsdfsfsfsdfsdfsdfsdfsdfsddsdf,nbmbmnbmnbmnsdasdasdlection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
print("Album Created")
} else {
print(error?.localizedDescription)
// Unable to create album
}
}
}
}
-> and For Fetch AssetCollection For Album: -
func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", CustomPhotoAlbum.albumName)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _: AnyObject = collection.firstObject {
return collection.firstObject as! PHAssetCollection
}
return nil
}

Selected Photos Option Which allow Selected Photos option in our application.
-> If you want to create album in Photo-library in iPhone So Allow Access to All Photos option is Compulsory but if you want to get photos from Photo-library Than Selected Photos option will work.

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.

Camera App - Create new album with PHPhotoLibrary with .addOnly PHAccessLevel

I have a camera app which takes pictures with iPhone cameras and saves it to the cameraRoll. It requires me to ask for PHPhotoLibrary PHAccessLevel as .addOnly.
An app should save pictures to specific Album - what forces me to check if album exists and to create one if not.
//Create variable that will store Album name
let albumName = "AppName"
var assetCollection: PHAssetCollection!
//Functions that checks if album with specific name exists and saves it to assetCollection variable
if let assetCollection = fetchAssetCollectionForAlbum() {
self.assetCollection = assetCollection
return
}
func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _: AnyObject = collection.firstObject {
return collection.firstObject
}
return nil
}
//Create album if doesn't exists
func createAlbum() {
if assetCollection == nil {
//If assetCollection is empty (album doesn't exist), create one
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: albumName) // create an asset collection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
} else {
//With PHAccessLevel as .addOnly error describes that .readWrite access required
print("error \(String(describing: error))")
}
}
}
}
The problem is that above functions requires full access to users photoLibrary. Is there a way to handle album creation without asking for full .readWrite PHAccessLevel?

swift5 how to only display limited photo library after user has granted limited access to select some photos

In WWDC2020, new PHPickerViewController and new PHPhotoLibrary.authorizationStatus(limited) were introduced. But I got below issue:
when user tapped a button to show the apple's multiple images picker and display the requestAuthorization as code:
let requiredAccessLevel: PHAccessLevel = .readWrite
PHPhotoLibrary.requestAuthorization(for: requiredAccessLevel) { (authorizationStatus) in
switch authorizationStatus {
case .authorized:
DispatchQueue.main.async {
self.presentImagePicker()
}
case .limited:
DispatchQueue.main.async {
self.presentImagePicker()
}
default:
break
}
}
self.presentImagePicker() functions:
func presentImagePicker() {
var configuration = PHPickerConfiguration(photoLibrary: .shared())
configuration.filter = .images
configuration.selectionLimit = self.imageCountMax - self.images.count
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
let accessLevel: PHAccessLevel = .readWrite
let authorizationStatus = PHPhotoLibrary.authorizationStatus(for: accessLevel)
switch authorizationStatus {
case .authorized:
DispatchQueue.main.async {
self.present(picker, animated: true)
}
case .limited:
DispatchQueue.main.async {
// Here I don't know how to display only limited photo library to users (after user has selected some photos through the limited access)
}
default:
break
}
}
my issue: please see code 2, case .limited: DispatchQueue.main.async { }, I think I should put the limited photo library in this block, but I don't know how to display only limited photo library to users.
You can use this PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self) method into .limited
DispatchQueue.main.async {
// Here I don't know how to display only limited photo library to users (after user has selected some photos through the limited access)
PHPhotoLibrary.shared().register(self)
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
}
and after that you have to use the delegate method for get the updated images.
func photoLibraryDidChange(_ changeInstance: PHChange) {
let fetchOptions = PHFetchOptions()
self.allPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
}

Swift 4 vCard on touch add to contacts

I have scoured the internet looking to find a way to save a persons contact information. Currently on my mobile website I have a vCard that when clicked on a phone asks the user if they would like to add the contact to their contacts. In swift however loading that same link generates a 404 error page. So how would I go about doing this? I want when the user clicks on the icon a pop up displays asking the user if they want to save the contact to their phone. The data is being pulled in through a json api. I assume I need to take this data and format it in a specific way. Any suggestions or pointers in a direction to take this is much appreciated.
Thanks
Update: Here is my attempt at some code for this. When this prints to the console I get the vcard output, but an error is thrown on the JSONSerialization. Maybe someone can point me in the right direction.
#IBAction func contactTapped(_ sender: Any) {
let contact = createContact()
do {
try shareContacts(contacts: [contact])
} catch {
print("Error printing contact")
}
}
func shareContacts(contacts: [CNContact]) throws {
guard let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
return
}
var filename = NSUUID().uuidString
if let contact = contacts.first, contacts.count == 1 {
if let fullname = CNContactFormatter().string(from: contact) {
filename = fullname.components(separatedBy: " ").joined(separator: "")
}
}
let fileURL = directoryURL
.appendingPathComponent(filename)
.appendingPathComponent("vcf")
let data = try CNContactVCardSerialization.data(with: contacts)
print("filename: \(filename)")
print("contact: \(String(describing: String(data: data, encoding: String.Encoding.utf8)))")
try JSONSerialization.data(withJSONObject: JSONEncoder(), options: JSONSerialization.WritingOptions.prettyPrinted)
let activityViewController = UIActivityViewController(
activityItems: [fileURL],
applicationActivities: nil
)
present(activityViewController, animated: true, completion: {})
}
func createContact() -> CNContact {
// Creating a mutable object to add to the contact
let contact = CNMutableContact()
contact.imageData = NSData() as Data // The profile picture as a NSData object
contact.givenName = fullNameLbl.text!
//contact.familyName = "Appleseed"
let workEmail = CNLabeledValue(label:CNLabelWork, value: emailLbl.text! as NSString)
contact.emailAddresses = [workEmail]
contact.phoneNumbers = [CNLabeledValue(
label:CNLabelPhoneNumberiPhone,
value:CNPhoneNumber(stringValue: phoneLabel.text!))]
let store = CNContactStore()
let saveRequest = CNSaveRequest()
saveRequest.add(contact, toContainerWithIdentifier: nil)
try! store.execute(saveRequest)
return contact
}

Move PHAsset from one Album to Another (Swift)

I want to move a PHAsset from one Album to another album. Here is what I am doing:
func saveImage(image: UIImage, album: PhotoAlbum, completion: (PHFetchResult?)->()) {
var placeholder: PHObjectPlaceholder?
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
// Request creating an asset from the image
let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(image)
// Request editing the album
guard let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: album) else {
assert(false, "Album change request failed")
return
}
// Get a placeholder for the new asset and add it to the album editing request
guard let photoPlaceholder = createAssetRequest.placeholderForCreatedAsset else {
assert(false, "Placeholder is nil")
return
}
placeholder = photoPlaceholder
albumChangeRequest.addAssets([photoPlaceholder])
}, completionHandler: { success, error in
guard let placeholder = placeholder else {
assert(false, "Placeholder is nil")
completion(nil)
return
}
if success {
completion(PHAsset.fetchAssetsWithLocalIdentifiers([placeholder.localIdentifier], options: nil))
}
else {
print(error)
completion(nil)
}
})
}
The problem is that it creates a copy of it to destination rather than moving. At last, I ended up getting same images in different Albums.