Swift -如何正确地编码和解码CNMutableContact数组?

bvjveswy  于 2023-02-07  发布在  Swift
关注(0)|答案(2)|浏览(128)

我试图使CNMutableContact“可编码”。我已经构建了编码函数(见下文),但我得到了一些问题,以解码数组,如postalAddresses,emailAddresses等。
下面是我的encode函数:

public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    
    try container.encode(self.contact.contactType.rawValue, forKey: .contactType)
    
    try container.encode(self.contact.namePrefix, forKey: .namePrefix)
    try container.encode(self.contact.givenName, forKey: .givenName)
    try container.encode(self.contact.middleName, forKey: .middleName)
    try container.encode(self.contact.familyName, forKey: .familyName)
    try container.encode(self.contact.previousFamilyName, forKey: .previousFamilyName)
    try container.encode(self.contact.nameSuffix, forKey: .nameSuffix)
    try container.encode(self.contact.nickname, forKey: .nickname)
    
    try container.encode(self.contact.jobTitle, forKey: .jobTitle)
    try container.encode(self.contact.departmentName, forKey: .departmentName)
    try container.encode(self.contact.organizationName, forKey: .organizationName)
    
    var postalAddresses: [String:String] = [:]
    self.contact.postalAddresses.forEach { postalAddress in
        postalAddresses[postalAddress.label ?? "postal\(String(describing: index))"] = (CNPostalAddressFormatter.string(from: postalAddress.value, style: .mailingAddress))
    }
    try container.encode(postalAddresses, forKey: .postalAddresses)
    
    var emailAddresses: [String:String] = [:]
    self.contact.emailAddresses.forEach { emailAddress in
        emailAddresses[emailAddress.label ?? "email\(String(describing: index))"] = (emailAddress.value as String)
    }
    try container.encode(emailAddresses, forKey: .emailAddresses)
    
    var urlAddresses: [String:String] = [:]
    self.contact.urlAddresses.forEach { urlAddress in
        urlAddresses[urlAddress.label ?? "url\(String(describing: index))"] = (urlAddress.value as String)
    }
    try container.encode(urlAddresses, forKey: .urlAddresses)
    
    var phoneNumbers: [String:String] = [:]
    self.contact.phoneNumbers.forEach { phoneNumber in
        phoneNumbers[phoneNumber.label ?? "phone\(String(describing: index))"] = phoneNumber.value.stringValue
    }
    try container.encode(phoneNumbers, forKey: .phoneNumbers)
    
    var socialProfiles: [String:String] = [:]
    self.contact.socialProfiles.forEach { socialProfile in
        socialProfiles[socialProfile.label ?? "social\(String(describing: index))"] = socialProfile.value.urlString
    }
    try container.encode(socialProfiles, forKey: .socialProfiles)
    
    try container.encode(self.contact.birthday, forKey: .birthday)
    
    try container.encode(self.contact.note, forKey: .note)
}

如您所见,我以如下方式对postalAddresses进行编码:

var postalAddresses: [String:String] = [:]
self.contact.postalAddresses.forEach { postalAddress in
      postalAddresses[postalAddress.label ?? "postal\(String(describing: index))"] = (CNPostalAddressFormatter.string(from: postalAddress.value, style: .mailingAddress))
}
try container.encode(postalAddresses, forKey: .postalAddresses)

但是我很难理解如何解码,下面是我的解码函数(不完整):

init(from decoder: Decoder) throws {
    let decodedContact = try decoder.container(keyedBy: CodingKeys.self)
    
    id = try decodedContact.decode(UUID.self, forKey: .id)
    contactIdentifier = try decodedContact.decode(String.self, forKey: .contactIdentifier)
    contact = CNMutableContact()
    
    var intContactType = try decodedContact.decode(Int.self, forKey: .contactType)
    if intContactType == 0 {
        contact.contactType = CNContactType.person
    } else {
        contact.contactType = CNContactType.organization
    }
    
    contact.namePrefix = try decodedContact.decode(String.self, forKey: .namePrefix)
    contact.givenName = try decodedContact.decode(String.self, forKey: .givenName)
    contact.middleName = try decodedContact.decode(String.self, forKey: .middleName)
    contact.familyName = try decodedContact.decode(String.self, forKey: .familyName)
    contact.previousFamilyName = try decodedContact.decode(String.self, forKey: .previousFamilyName)
    contact.nameSuffix = try decodedContact.decode(String.self, forKey: .nameSuffix)
    contact.nickname = try decodedContact.decode(String.self, forKey: .nickname)
    
    contact.jobTitle = try decodedContact.decode(String.self, forKey: .jobTitle)
    contact.departmentName = try decodedContact.decode(String.self, forKey: .departmentName)
    contact.organizationName = try decodedContact.decode(String.self, forKey: .organizationName)
    
    // MISSING ARRAYS
    let postalAddresses = try decodedContact.decode([String:String], forKey: .postalAddresses)
    
    contact.birthday = try decodedContact.decode(DateComponents.self, forKey: .birthday)
    
    contact.note = try decodedContact.decode(String.self, forKey: .note)
}

注意:decode函数返回postalAddresses解码行的错误。
你能帮助我理解我的方法是否正确以及如何解码数组吗?
谢谢
我尝试了不同的方法来解码postalAddresses,但总是得到一个错误。

e4yzc0pl

e4yzc0pl1#

没有必要重新发明轮子。CN(Mutable)Contact符合NSSecureCoding,它可以序列化为Data
在Swift中,有一个PropertyWrapper模式,它公开了示例,并可以在幕后执行编码/解码操作

@propertyWrapper
struct CodableContact {
    var wrappedValue: CNMutableContact
}

extension CodableContact: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let data = try container.decode(Data.self)
        guard let contact = try NSKeyedUnarchiver.unarchivedObject(ofClass: CNMutableContact.self, from: data) else {
            throw DecodingError.dataCorruptedError(
                in: container,
                debugDescription: "Invalid contact"
            )
        }
        wrappedValue = contact
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        let data = try NSKeyedArchiver.archivedData(withRootObject: wrappedValue, requiringSecureCoding: true)
        try container.encode(data)
    }
}

在结构声明中

struct MyType: Codable {
    @CodableContact var contact: CNMutableContact
}
guz6ccqo

guz6ccqo2#

我终于改变了主意,因为更多的问题沿着了,我创建了一个结构,复制CNMutableContact和两个函数转换和CNMutableContact.你会发现完整的实现如下.
请随意评论,我在斯威夫特还是个新手

//
//  ContactDetails.swift
//  ShareContacts
//
//  Created by Philippe on 04.02.23.
//

import Foundation
import Contacts

struct ContactDetails: Codable {
    var identifier: String
    
    var contactType: Int
    
    var namePrefix: String
    var givenName: String
    var middleName: String
    var familyName: String
    var previousFamilyName: String
    var nameSuffix: String
    var nickname: String
    
    var jobTitle: String
    var departmentName: String
    var organizationName: String
    
    var birthday: DateComponents?
    
    var note: String
    
    var postalAddresses: [PostalAddress]
    var emailAddresses: [LabeledValue]
    var urlAddresses: [LabeledValue]
    var phoneNumbers: [LabeledValue]
    var socialProfiles: [SocialProfile]
    
    init(identifier: String = "", contactType: Int = 0, namePrefix: String  = "", givenName: String  = "", middleName: String  = "", familyName: String = "", previousFamilyName: String = "", nameSuffix: String = "", nickname: String = "", jobTitle: String = "", departmentName: String = "", organizationName: String = "", birthday: DateComponents = DateComponents(), note: String = "", postalAddresses: [PostalAddress] = [], emailAddresses: [LabeledValue] = [], urlAddresses: [LabeledValue] = [], phoneNumbers: [LabeledValue] = [], socialProfiles: [SocialProfile] = []) {
        self.identifier = identifier
        self.contactType = contactType
        self.namePrefix = namePrefix
        self.givenName = givenName
        self.middleName = middleName
        self.familyName = familyName
        self.previousFamilyName = previousFamilyName
        self.nameSuffix = nameSuffix
        self.nickname = nickname
        self.jobTitle = jobTitle
        self.departmentName = departmentName
        self.organizationName = organizationName
        self.birthday = birthday
        self.note = note
        self.postalAddresses = postalAddresses
        self.emailAddresses = emailAddresses
        self.urlAddresses = urlAddresses
        self.phoneNumbers = phoneNumbers
        self.socialProfiles = socialProfiles
    }
}

extension ContactDetails {
    func convertToCNMutableContact(contact: ContactDetails) -> CNMutableContact {
        let cnMutableContact = CNMutableContact()
        
        cnMutableContact.contactType = CNContactType(rawValue: contact.contactType) ?? CNContactType.person
        
        cnMutableContact.namePrefix = contact.namePrefix
        cnMutableContact.givenName = contact.givenName
        cnMutableContact.middleName = contact.middleName
        cnMutableContact.familyName = contact.familyName
        cnMutableContact.previousFamilyName = contact.previousFamilyName
        cnMutableContact.nameSuffix = contact.nameSuffix
        cnMutableContact.nickname = contact.nickname
        
        cnMutableContact.jobTitle = contact.jobTitle
        cnMutableContact.departmentName = contact.departmentName
        cnMutableContact.organizationName = contact.organizationName
        
        cnMutableContact.birthday = contact.birthday
        
        cnMutableContact.note = contact.note
        
        contact.postalAddresses.forEach { postalAddress in
            let cnPostalAddress = CNMutablePostalAddress()
            cnPostalAddress.street = postalAddress.street
            cnPostalAddress.city = postalAddress.city
            cnPostalAddress.postalCode = postalAddress.postalCode
            cnPostalAddress.state = postalAddress.state
            cnPostalAddress.country = postalAddress.country
            
            cnMutableContact.postalAddresses.append(CNLabeledValue<CNPostalAddress>(label: postalAddress.label, value: cnPostalAddress))
        }
        
        contact.emailAddresses.forEach { emailAddress in
            cnMutableContact.emailAddresses.append(CNLabeledValue<NSString>(label: emailAddress.label, value: emailAddress.value as NSString))
        }
        
        contact.urlAddresses.forEach { urlAddress in
            cnMutableContact.urlAddresses.append(CNLabeledValue<NSString>(label: urlAddress.label, value: urlAddress.value as NSString))
        }
        
        contact.phoneNumbers.forEach { phoneNumber in
            let cnPhoneNumber = CNPhoneNumber(stringValue: phoneNumber.value)
            
            cnMutableContact.phoneNumbers.append(CNLabeledValue<CNPhoneNumber>(label: phoneNumber.label, value: cnPhoneNumber))
        }
        
        contact.socialProfiles.forEach { socialProfile in
            let cnSocialProfile = CNSocialProfile(urlString: socialProfile.urlString, username: socialProfile.username, userIdentifier: socialProfile.userIdentifier, service: socialProfile.service)
            
            cnMutableContact.socialProfiles.append(CNLabeledValue<CNSocialProfile>(label: socialProfile.label, value: cnSocialProfile))
        }
        
        return cnMutableContact
    }
    
    func convertFromCNMutableContact(cnMutableContact: CNMutableContact) -> ContactDetails {
        var contact = ContactDetails()
        
        contact.identifier = cnMutableContact.identifier
        
        contact.contactType = cnMutableContact.contactType.rawValue
        
        contact.namePrefix = cnMutableContact.namePrefix
        contact.givenName = cnMutableContact.givenName
        contact.middleName = cnMutableContact.middleName
        contact.familyName = cnMutableContact.familyName
        contact.previousFamilyName = cnMutableContact.previousFamilyName
        contact.nameSuffix = cnMutableContact.nameSuffix
        contact.nickname = cnMutableContact.nickname
        
        contact.jobTitle = cnMutableContact.jobTitle
        contact.departmentName = cnMutableContact.departmentName
        contact.organizationName = cnMutableContact.organizationName
        
        contact.birthday = cnMutableContact.birthday
        
        contact.note = cnMutableContact.note
        
        cnMutableContact.postalAddresses.forEach { cnPostalAddress in
            let postalAddress = PostalAddress(label: cnPostalAddress.label ?? "", street: cnPostalAddress.value.street, city: cnPostalAddress.value.city, postalCode: cnPostalAddress.value.postalCode, state: cnPostalAddress.value.state, country: cnPostalAddress.value.country)
            
            contact.postalAddresses.append(postalAddress)
        }
        
        cnMutableContact.emailAddresses.forEach  { emailAddress in
            contact.emailAddresses.append(LabeledValue(label: (emailAddress.label ?? "") as String, value: emailAddress.value as String))
        }
        
        cnMutableContact.urlAddresses.forEach  { urlAddress in
            contact.urlAddresses.append(LabeledValue(label: (urlAddress.label ?? "") as String, value: urlAddress.value as String))
        }
        
        cnMutableContact.phoneNumbers.forEach  { phoneNumber in
            contact.phoneNumbers.append(LabeledValue(label: (phoneNumber.label ?? "") as String, value: phoneNumber.value.stringValue))
        }
        
        cnMutableContact.socialProfiles.forEach { cnSocialProfile in
            let socialProfile = SocialProfile(label: cnSocialProfile.label ?? "", username: cnSocialProfile.value.username, service: cnSocialProfile.value.service, urlString: cnSocialProfile.value.urlString, userIdentifier: cnSocialProfile.value.userIdentifier)
            
            contact.socialProfiles.append(socialProfile)
        }
        
        return contact
    }
}

相关问题