ContactImageUpdater.swift 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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 predicate = CNContact.predicateForContacts(matchingName: "LoopFollowBG")
  25. let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactImageDataKey] as [CNKeyDescriptor]
  26. do {
  27. let contacts = try self.contactStore.unifiedContacts(matching: predicate, keysToFetch: keysToFetch)
  28. if let contact = contacts.first, let mutableContact = contact.mutableCopy() as? CNMutableContact {
  29. mutableContact.imageData = imageData
  30. let saveRequest = CNSaveRequest()
  31. saveRequest.update(mutableContact)
  32. try self.contactStore.execute(saveRequest)
  33. print("Contact image updated successfully.")
  34. } else {
  35. let newContact = CNMutableContact()
  36. newContact.givenName = "LoopFollowBG"
  37. newContact.imageData = imageData
  38. let saveRequest = CNSaveRequest()
  39. saveRequest.add(newContact, toContainerWithIdentifier: nil)
  40. try self.contactStore.execute(saveRequest)
  41. print("New contact created with updated image.")
  42. }
  43. } catch {
  44. print("Failed to update or create contact: \(error)")
  45. }
  46. }
  47. }
  48. private func generateContactImage(bgValue: String, extra: String, stale: Bool) -> UIImage? {
  49. let size = CGSize(width: 300, height: 300)
  50. UIGraphicsBeginImageContextWithOptions(size, false, 0)
  51. guard let context = UIGraphicsGetCurrentContext() else { return nil }
  52. UIColor.systemBackground.setFill()
  53. context.fill(CGRect(origin: .zero, size: size))
  54. let bgText = bgValue
  55. let paragraphStyle = NSMutableParagraphStyle()
  56. paragraphStyle.alignment = .center
  57. // Attributes for bgValue
  58. var bgAttributes: [NSAttributedString.Key: Any] = [
  59. .font: UIFont.boldSystemFont(ofSize: extra.isEmpty ? 140 : 100), // Larger font if no extra text
  60. .foregroundColor: UIColor.label,
  61. .paragraphStyle: paragraphStyle
  62. ]
  63. if stale {
  64. bgAttributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
  65. }
  66. let bgRect = extra.isEmpty
  67. ? CGRect(x: 0, y: 80, width: size.width, height: 140) // Center bgValue vertically if no extra
  68. : CGRect(x: 0, y: 60, width: size.width, height: 100)
  69. bgText.draw(in: bgRect, withAttributes: bgAttributes)
  70. // Draw extra text if not empty
  71. if !extra.isEmpty {
  72. let trendAttributes: [NSAttributedString.Key: Any] = [
  73. .font: UIFont.systemFont(ofSize: 50),
  74. .foregroundColor: UIColor.secondaryLabel,
  75. .paragraphStyle: paragraphStyle
  76. ]
  77. let extraRect = CGRect(x: 0, y: 170, width: size.width, height: 50)
  78. extra.draw(in: extraRect, withAttributes: trendAttributes)
  79. }
  80. let image = UIGraphicsGetImageFromCurrentImageContext()
  81. UIGraphicsEndImageContext()
  82. return image
  83. }
  84. }