ContactImageUpdater.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. //
  2. // ContactImageUpdater.swift
  3. // LoopFollow
  4. //
  5. // Created by Jonas Björkert on 2024-12-10.
  6. // Copyright © 2024 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. import Contacts
  10. import UIKit
  11. class ContactImageUpdater {
  12. private let contactStore = CNContactStore()
  13. private let queue = DispatchQueue(label: "ContactImageUpdaterQueue")
  14. func updateContactImage(bgValue: String, extra: String, stale: Bool) {
  15. queue.async {
  16. guard CNContactStore.authorizationStatus(for: .contacts) == .authorized else {
  17. print("Access to contacts is not authorized.")
  18. return
  19. }
  20. guard let imageData = self.generateContactImage(bgValue: bgValue, extra: extra, stale: stale)?.pngData() else {
  21. print("Failed to generate contact image.")
  22. return
  23. }
  24. let bundleDisplayName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? "LoopFollow"
  25. let contactName = "\(bundleDisplayName) - BG"
  26. let predicate = CNContact.predicateForContacts(matchingName: contactName)
  27. let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactImageDataKey] as [CNKeyDescriptor]
  28. do {
  29. let contacts = try self.contactStore.unifiedContacts(matching: predicate, keysToFetch: keysToFetch)
  30. if let contact = contacts.first, let mutableContact = contact.mutableCopy() as? CNMutableContact {
  31. mutableContact.imageData = imageData
  32. let saveRequest = CNSaveRequest()
  33. saveRequest.update(mutableContact)
  34. try self.contactStore.execute(saveRequest)
  35. print("Contact image updated successfully.")
  36. } else {
  37. let newContact = CNMutableContact()
  38. newContact.givenName = contactName
  39. newContact.imageData = imageData
  40. let saveRequest = CNSaveRequest()
  41. saveRequest.add(newContact, toContainerWithIdentifier: nil)
  42. try self.contactStore.execute(saveRequest)
  43. print("New contact created with updated image.")
  44. }
  45. } catch {
  46. print("Failed to update or create contact: \(error)")
  47. }
  48. }
  49. }
  50. private func generateContactImage(bgValue: String, extra: String, stale: Bool) -> UIImage? {
  51. let size = CGSize(width: 300, height: 300)
  52. UIGraphicsBeginImageContextWithOptions(size, false, 0)
  53. guard let context = UIGraphicsGetCurrentContext() else { return nil }
  54. UIColor.black.setFill()
  55. context.fill(CGRect(origin: .zero, size: size))
  56. let paragraphStyle = NSMutableParagraphStyle()
  57. paragraphStyle.alignment = .center
  58. let maxFontSize: CGFloat = extra.isEmpty ? 200 : 160
  59. let fontSize = maxFontSize - CGFloat(bgValue.count * 15)
  60. var bgAttributes: [NSAttributedString.Key: Any] = [
  61. .font: UIFont.boldSystemFont(ofSize: fontSize),
  62. .foregroundColor: stale ? UIColor.gray : UIColor.white,
  63. .paragraphStyle: paragraphStyle
  64. ]
  65. if stale {
  66. bgAttributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
  67. }
  68. let extraAttributes: [NSAttributedString.Key: Any] = [
  69. .font: UIFont.systemFont(ofSize: 90),
  70. .foregroundColor: UIColor.white,
  71. .paragraphStyle: paragraphStyle
  72. ]
  73. let bgRect = extra.isEmpty
  74. ? CGRect(x: 0, y: 46, width: size.width, height: size.height - 80)
  75. : CGRect(x: 0, y: 26, width: size.width, height: size.height / 2)
  76. bgValue.draw(in: bgRect, withAttributes: bgAttributes)
  77. if !extra.isEmpty {
  78. let extraRect = CGRect(x: 0, y: size.height / 2 + 6, width: size.width, height: size.height / 2 - 20)
  79. extra.draw(in: extraRect, withAttributes: extraAttributes)
  80. }
  81. let image = UIGraphicsGetImageFromCurrentImageContext()
  82. UIGraphicsEndImageContext()
  83. return image
  84. }
  85. }