I am inserting a new contact with Apple's Contacts framework. The image is a .png from Asset catalog. After the successful insertion the new contact is visible with the correct image in the Contacts app. However, when there is an incoming call from the newly inserted contact the image is NOT displayed.
I couldn't find any reliable information about the exact cases when does the iOS display the contact image. Is it true that images will only appear if the user sets it manually? Did I miss something? Could you please point me to a good documentation or provide me an explanation? (The only relevant information is in this article: https://support.apple.com/en-us/HT202158 but I fulfill every requirement and it is probably not closely related.)
Code for the contact insertion:
let newContact = CNMutableContact()
...
if let image = UIImage(named: "ContactImage") {
newContact.imageData = UIImagePNGRepresentation(image)
}
...
let saveRequest = CNSaveRequest()
saveRequest.addContact(newContact, toContainerWithIdentifier: nil)
do {
try contactStore.executeSaveRequest(saveRequest)
log.debug("New contact successfully saved!")
} catch let error as NSError {
log.error(error.localizedDescription)
} catch {
log.error("Unknown error happened during contact saving.")
}
I've contacted Apple Developer Technical Support and their engineers determined that this would be best handled as a bug report. The submitted bug's ID is 26033574.
UPDATE
Apple asked me the following question.
Can you please share the vcard and image you’re using on the contact card?
I shared the vCard with them and unfortunately there was no image in it, although on iOS (and macOS) the image was clearly visible.
I found that saving the contact two times in a row solved the problem.
Save the contact
Add the image to the contact
Resave the contact
Related
I'm using SDWebImageDownloader.shared().downloadImage to download images, then
if let i = image {
let key = SDWebImageManager.shared().cacheKey(for: product.imageURL)
SDImageCache.shared().store(i, forKey: key, completion: nil)
}
My code iterates on a product array, and downloads all the images for them (so it will appear while the user is only even if the product's page wasn't opened before)
My problem is, that it doesn't save the images at all, I see a bunch of these in the log:
CFNetwork internal error (0xc01a:/BuildRoot/Library/Caches/com.apple.xbs/Sources/CFNetwork/CFNetwork-811.5.4/Loading/URLConnectionLoader.cpp:304)
Could you please give me a hint where to start?
Thanks!
So, I have been coming across a problem where my Firebase app does not update user values when a user makes an update. To be more clear: Lets say user 1 has a photo of a dog and then changes it to a cat.
Once they change it to a cat, my node value in Firebase is successfully updated but the user themselves won't be able to see the change in other previously loaded areas in the app (other places with the dog picture) until they log out and then log back in.
For this reason I was wondering if there was any way to conduct a background app refresh that way all previous dog values in the app are changed to cat values without the user having to log out and then log back in. Please note that this same problem occurs not only with my user's profile picture but also any other user field I have setup.
Here is how I am updating a node value for my user in Firebase:
let storageRef = FIRStorage.storage().reference()
_ = FIRStorageMetadata()
let filePath = "\(FIRAuth.auth()?.currentUser?.uid)/\("userPhoto")"
let profileImageData = UIImageJPEGRepresentation(self.profilePicture.image!, 1.0)
if let data = profileImageData {
storageRef.child(filePath).put(data, metadata: nil){(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
} else {
let downloadURL = metaData!.downloadURL()!.absoluteString
let userPhotoUpdateRef = FIRDatabase.database().reference().child("users").child(self.currentUser).child("userPhoto")
userPhotoUpdateRef.setValue(downloadURL)
}
}
}
If you have any questions please ask! Any help would be appreciated!
The Firebase SDK for Cloud Storage provides an easy way to read file from and write files to cloud storage. It does not provide a way to monitor those files.
The easiest way to provide a monitoring approach is to write the metadata of the files to the Firebase Realtime Database. See this short section in the Storage docs for a brief mention of that: https://firebase.google.com/docs/storage/ios/file-metadata#custom_metadata
When you write data to a location in the Firebase Database, all apps that are actively monitoring that location will be instantly updated. When they get that update, you can reload the image from Cloud Storage for Firebase.
I need to show progress while uploading/downloading data from Firebase database.
Can I get it?. I saw in Firebase storage but I didn't saw in Firebase database.
Progress based on data:
I recommend looking over this post, as it will help you understand how to use some of the built in data references to determine your upload progress. While it is not for iOS, it explains the thought process of how you can measure upload progress.
You can always use a progress monitor like such:
let observer = uploadTask.observeStatus(.Progress) { snapshot in
print(snapshot.progress) // NSProgress object
}
Progress based on count:
As to what FrankvanPuffelen said, there is in fact no tool to give you what you are asking for. However, as Jay states, you can determine the "progress" of your task(s) based on how you are reading/writing.
Say for instance you are writing (uploading) 10 photos. For each photo that is successfully written (uploaded), you can simply increment some sort of progress meter by 1/10.
Example for some local file on the users device:
// Some image on the device
let localFile = URL(string: "<PATH>")!
// Make a reference to the file that needs to be uploaded
let riversRef = storageRef.child("chicken.jpg")
// Upload the file to the path you specified
let uploadTask = riversRef.putFile(from: localFile, metadata: nil) { metadata, error in
if let error = error {
// Whoops, something went wrong :(
}
else {
// Tada! Uploaded file
someCountVariable += 1
// If needed / applicable
// let downloadURL = metadata!.downloadURL()
}
}
Of course for one (1) item you will go from 0 to 100 (..real quick - shout out to Drake), but this can just be nested in some loop that iterates through some list of items to upload. Then per each item successfully uploaded increment.
This will be more expensive in terms of requests if you need to upload objects other than multiple images, but for the purpose this will give you a nice visual / numeric way of tracking at least how many items are left to be successfully uploaded.
If you really want to get down to the nitty gritty, you can always try checking the current connection strength to determine a relative data transfer speed, and cross that with the current task you are attempting. This could potentially give you at least some estimate the progress based on how long the process should take to how long it actually has taken.
Hope some of this helps / points you in the right direction. Happy coding!
val bytesTransferred=taskSnapshot.bytesTransferred.toFloat()
val totalByteCount=taskSnapshot.totalByteCount.toFloat()
val progress = bytesTransferred / totalByteCount
Log.d("progress", (progress*100).toString())
Log.d("progressDivide", (bytesTransferred / totalByteCount).toString())
Log.d("progressbytesTransferred", bytesTransferred.toString())
Log.d("progresstotalByteCount", totalByteCount.toString())
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am trying to programmatically remove contacts from address book on specific time. Is it even possible in Swift and do Apple allow it?. I'm already familiar with CNContactStorebecause I have got working adding contacts into phonebook. Granted access into Contacts etc.. But I do not know how to programmatically delete contacts from addressbook(forever) on specific time.
Any help is appreciated!
REFERENCE:
http://www.ios-blog.co.uk/tutorials/swift/contacts-framework-p2/
EXPLANATION (FROM THE LINK):
Delete Contact
"The iOS contacts framework gives us the function deleteContact(:) to help us delete contacts. Hopefully you’ve understood this tutorial enough so far to proceed as I’m only going to outline the process and let you have a try. Just like we have throughout this tutorial we are going to instantiate an object of type CNSaveRequest, Issue the deleteContact(:) function that I just mentioned and pass the mutable contact to it. Then, Like when we created contacts or updated contacts we are going to use the executeSaveRequest(_:).
Please note that Delete means Delete! Contacts that are deleted can not be obtained again. This shouldn’t matter too much on the simulator but you do need to make sure that you have safety protocols in place so that you don’t delete a users contacts.
So, Did you manage to get the delete working? Ok, Fine, I will post the full code so you can see."
SOLUTION (FROM THE LINK):
let predicate = CNContact.predicateForContactsMatchingName("John")
let toFetch = [CNContactEmailAddressesKey]
do{
let contacts = try store.unifiedContactsMatchingPredicate(predicate,keysToFetch: toFetch)
guard contacts.count > 0 else{
print("No contacts found")
return
}
guard let contact = contacts.first else{
return
}
let req = CNSaveRequest()
let mutableContact = contact.mutableCopy() as! CNMutableContact
req.deleteContact(mutableContact)
do{
try store.executeSaveRequest(req)
print("Success, You deleted the user")
} catch let e{
print("Error = \(e)")
}
} catch let err{
print(err)
}
What I am trying to do is to save videos to PHPhotoLibrary, and then remove them when upload to clients remote server in the application completes (basically, photo library serves as temporary storage to add additional layer of security in case anything at all fails (I already save my vides it in the applications directory).
Problem:
The problem is for that to work, everything has to work without input from the user. You can write video to photos library like this:
func storeVideoToLibraryForUpload(upload : SMUpload) {
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.Authorized {
// Don't write to library since this is disallowed by user
return
}
PHPhotoLibrary.sharedPhotoLibrary().performChanges({ () -> Void in
// Write asset
let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(NSURL(fileURLWithPath: upload.nonsecureFilePath!)!)
let assetPlaceholder = assetRequest.placeholderForCreatedAsset
let localIdentifier = assetPlaceholder.localIdentifier
// Store local identifier for later use
upload.localAssetIdentifier = localIdentifier
}, completionHandler: { (success, error) -> Void in
....
})
}
And that works flawlessly, I get local identifier, I store it for later use.. Unicorns and rainbows.
Now when I want to remove that video immediately after upload finishes, I call following:
func removeVideoFromLibraryForUpload(upload : SMUpload) {
// Only proceed if there is asset identifier (video previously stored)
if let assetIdentifier = upload.localAssetIdentifier {
// Find asset that we previously stored
let assets = PHAsset.fetchAssetsWithLocalIdentifiers([assetIdentifier], options: PHFetchOptions())
// Fetch asset, if found, delete it
if let fetchedAssets = assets.firstObject as? PHAsset {
PHPhotoLibrary.sharedPhotoLibrary().performChanges({ () -> Void in
// Delete asset
PHAssetChangeRequest.deleteAssets([fetchedAssets])
}, completionHandler: { (success, error) -> Void in
...
})
}
}
}
Which successfully deletes the video, BUT user have to confirm deletion first. That is a problem as that backing up won't work.
I obviously know why there is confirmation (so you don't clear entire user library for example, but the thing is, My app made the video - and so I thought there will be way around it, since as an "owner" I should not be doing that, or at least have option to disable confirmation.
Thanks in advance!
TLDR: How can I disable confirmation on delete request, if my application created that content? (I don't want to delete anything else).
Note: Somebody can probably say this is rather strange thing to do but the application is distributed internally and there is good reason to do it like this (the video content is too valuable to be lost, even if user deletes the application for some reason, or there is anything at all that goes wrong, we need to be able to preserve the videos), so please don't question that and just focus your attention on the question :)
I cannot see a way to avoid the delete confirmation. It is an implementation detail of the Photos framework, similar to the way you cannot prevent the device from asking the user's permission to use the microphone when your app tries to use it, and is a matter of security & trust. Once you have saved an asset to the device photo library your app is no longer the owner of that asset, so as you noted in your question the device must of course ensure the app has the user's permission before it goes about deleting such data.
You can never entirely safeguard your users' data against their own unpredictable behaviour - if they decide to remove your app, or delete a particular asset from within Photos, it is up to them. I think your best option is to either put up with the built-in delete confirmation, or to provide a guide to your users that makes it clear that they should be careful to protect this important data by backing up their device, and not deleting the app!
If you did decide to stick to this approach, perhaps the best thing you could do is to prepare the user for the fact that their device may ask them for confirmation to delete a file that is being uploaded to your own servers. For example, put up your own modal alert just before trying to delete the asset. I wouldn't normally suggest that kind of approach for a public shipping app, but since you're only distributing internally it may be acceptable for your team.