【问题标题】:JSON encode/decode Swift CNContact object generically [closed]JSON编码/解码Swift CNContact对象一般[关闭]
【发布时间】:2017-08-01 10:56:09
【问题描述】:

我一直在尝试以通用方式对 Swift 的 CNContact 对象进行 JSON 编码/解码(我的意思是我想确定该对象在运行时拥有哪些属性,我不想对其进行硬编码属性),但是我没有设法这样做。我的问题是:有没有办法在 Swift 4 中实现这种行为,如果有,怎么做?

【问题讨论】:

  • 你想从联系人中获取多个json字段?
  • 我想对每个 CNContact 对象拥有的所有属性进行序列化和反序列化...这能回答你的问题吗?

标签: json swift


【解决方案1】:

不,CNContact 没有根据official documentation 改变,所以它不符合Codable 协议,这只是Encodeable & Decodable 的类型别名。您可以看到当前符合EncodableDecodable 的类列表,并在此处看到CNContact 不在其中。

但是,您可以为CNContact 编写扩展名,使其符合上述协议。

这是一个如何使用JSONSerialization 框架在Swift3 中编码CNContact 对象的示例。请注意,这只是一个示例,因此我没有解析所有可能的字段,并且使用此实现,如果 CNContact 对象中不存在某个值,则 JSON 中也不存在该键。另外,解码器的功能还没有完全实现,但是如果你检查一下编码器是如何工作的,你可以很容易地实现它。

JSON 键的名称也是随结构任意选择的,因此您可以更改其中任何一个。

下面的代码是一个完整的、可以工作的 Playground 文件,如果你愿意的话,你可以自己测试一下。

import Contacts

let contact = CNMutableContact()
contact.birthday = DateComponents(calendar: Calendar.current,year: 1887, month: 1, day: 1)
contact.contactType = CNContactType.person
contact.givenName = "John"
contact.familyName = "Appleseed"

contact.imageData = Data() // The profile picture as a NSData object

let homeEmail = CNLabeledValue(label:CNLabelHome, value: NSString(string: "john@example.com"))
let workEmail = CNLabeledValue(label:CNLabelWork, value: NSString(string: "j.appleseed@icloud.com"))
contact.emailAddresses = [homeEmail, workEmail]

contact.phoneNumbers = [CNLabeledValue(label:CNLabelPhoneNumberiPhone, value:CNPhoneNumber(stringValue:"(408) 555-0126"))]

let homeAddress = CNMutablePostalAddress()
homeAddress.street = "1 Infinite Loop"
homeAddress.city = "Cupertino"
homeAddress.state = "CA"
homeAddress.postalCode = "95014"
contact.postalAddresses = [CNLabeledValue(label:CNLabelHome, value:homeAddress)]

func encodeContactToJson(contact: CNContact)->Data?{
    var contactDict = [String:Any]()
    if let birthday = contact.birthday?.date {
        let df = DateFormatter()
        df.dateFormat = "yyyy-MM-dd"
        contactDict["birthday"] = df.string(from: birthday)
    }
    contactDict["givenName"] = contact.givenName
    contactDict["familyName"] = contact.familyName
    if let imageData = contact.imageData {
        contactDict["image"] = imageData.base64EncodedString()
    }
    if contact.emailAddresses.count > 0 {
        var emailAddresses = [String:String]()
        for (index, emailAddress) in contact.emailAddresses.enumerated() {
            emailAddresses[emailAddress.label ?? "email\(index)"] = (emailAddress.value as String)
        }
        contactDict["emailAddresses"] = emailAddresses
    }
    if contact.phoneNumbers.count > 0 {
        var phoneNumbers = [String:String]()
        for (index, phoneNumber) in contact.phoneNumbers.enumerated() {
            phoneNumbers[phoneNumber.label ?? "phone\(index)"] = phoneNumber.value.stringValue
        }
        contactDict["phoneNumbers"] = phoneNumbers
    }
    if contact.postalAddresses.count > 0 {
        var postalAddresses = [String:String]()
        for (index, postalAddress) in contact.postalAddresses.enumerated() {
            postalAddresses[postalAddress.label ?? "postal\(index)"] = (CNPostalAddressFormatter.string(from: postalAddress.value, style: .mailingAddress))
        }
        contactDict["postalAddresses"] = postalAddresses
    }
    return try? JSONSerialization.data(withJSONObject: contactDict)
}

func decodeContactsJson(jsonData: Data)->CNContact?{
    if let jsonDict = (try? JSONSerialization.jsonObject(with: jsonData)) as? [String:Any] {
        let contact = CNMutableContact()
        print(jsonDict)
        return contact as CNContact
    } else {
        return nil
    }
}

if let jsonContact = encodeContactToJson(contact: contact) {
    print(decodeContactsJson(jsonData: jsonContact) ?? "Decoding failed")
} else {
    print("Encoding failed")
}

【讨论】:

  • 非常感谢。我对 Swift 开发完全没有经验,你能提供一个 CNContact 扩展的示例代码吗?
  • 我目前没有安装 Xcode9 测试版,所以我无法编译 Swift4 代码,我不想为您提供可能无法编译的代码示例。但是,如果您愿意,我可以为您提供一个小示例,说明如何使用 JSONSerialization 框架,该框架已在 Swift3 中提供。
  • 我真的很感激 :)
  • @mmarkus 查看我的更新答案,我已经包含了一个完整的工作示例
  • 谢谢。我会看看你的解决方案。
【解决方案2】:

尝试将 Simple 与联系人选择一起使用。

func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {

    var name = ""
    var email = ""
    var mobile =  ""
    var address = ""
    var companyname = ""
    var jobtitle = ""
    var organization = ""
    var workaddress = ""
    var worknumber = ""
    var personalurl = ""
    var companyurl = ""
    var otherurl = ""
    var othernumber = ""
    var otheraddress = ""
    var homeemail = ""
    var workemail = ""
    var otheremail = ""
    var dataAll = NSMutableArray()

    if contact != nil
    {
        dataAll = NSMutableArray()



        if (contact?.isKeyAvailable(CNContactGivenNameKey))! {
            let name2 = contact?.familyName
            name = (contact?.givenName)! + " " + name2!

            UserDefaults.standard.set(name, forKey: "name")


        }



        if (contact?.isKeyAvailable(CNContactOrganizationNameKey))!
        {
            companyname  = (contact?.organizationName)!


            //var dic : NSDictionary = [:]
            if companyname != "" {
                let dic : [String : AnyObject] = ["name":companyname as AnyObject,
                                                  "type":"companyname" as AnyObject,
                                                  "typeof":"companyname" as AnyObject]
                dataAll.add(dic)
            }



        }

        let designation = contact?.jobTitle

        if (designation?.isEmpty)!
        {

        }
        else
        {
            jobtitle = designation!
            //let dic : [String : AnyObject] = ["designation":jobtitle as AnyObject]

            let dic : [String : AnyObject] = ["name":jobtitle as AnyObject,
                                              "type":"designation" as AnyObject,
                                              "typeof":"designation" as AnyObject
            ]
            dataAll.add(dic)

        }
        if (contact?.isKeyAvailable(CNContactPhoneNumbersKey))!{

            if (contact?.phoneNumbers.count == 0)
            {

            }
            else
            {

                for phoneNumber:CNLabeledValue in (contact?.phoneNumbers)! {
                    let number  = phoneNumber.value

                    let number2 = number.stringValue


                    let lable :String  =  CNLabeledValue<NSString>.localizedString(forLabel: phoneNumber.label! )


                    let dic : [String : AnyObject] = ["name":number2 as AnyObject,
                                                      "type":lable as AnyObject,
                                                      "typeof":"phonenumber" as AnyObject]


                    dataAll.add(dic)


                    if number2 != ""
                    {

                    }


                    if lable == "home"
                    {
                        if number2 == ""
                        {

                        }
                        else
                        {
                            mobile = number.stringValue


                            let number2  = String(mobile.digitsOnly())!



                        }


                    }
                    if lable == "work"
                    {
                        if number2 == ""
                        {

                        }
                        else
                        {
                            worknumber = number.stringValue

                        }


                    }
                    if lable == "mobile"
                    {
                        if number2 == ""
                        {

                        }
                        else
                        {
                            othernumber = number.stringValue


                        }


                    }

                }

            }
        }


        if (contact?.isKeyAvailable(CNContactEmailAddressesKey))! {

            if (contact?.emailAddresses.count == 0)
            {

            }
            else
            {

                for emaiadd:CNLabeledValue in (contact?.emailAddresses)! {

                    let name2 = (emaiadd.value as String) as String

                    if name2 != ""
                    {
                        UserDefaults.standard.set(emaiadd.value as String, forKey: "mainemail")
                    }

                    let lable :String  =  CNLabeledValue<NSString>.localizedString(forLabel: emaiadd.label! )

                    //  let dic : [String : AnyObject] = [lable:emaiadd.value as String as AnyObject]
                    let dic : [String : AnyObject] = ["name":name2 as AnyObject,
                                                      "type":lable as AnyObject,
                                                      "typeof":"email" as AnyObject]

                    dataAll.add(dic)


                    if lable == "home"
                    {

                        if name2 == ""
                        {

                        }
                        else
                        {
                            email = emaiadd.value as String


                        }

                    }
                    if lable == "work"
                    {
                        if name2 == ""
                        {

                        }
                        else
                        {
                            workemail = emaiadd.value as String

                        }

                    }
                    if lable == "other"
                    {
                        if name2 == ""
                        {

                        }
                        else
                        {
                            otheremail = emaiadd.value as String

                        }

                    }

                }
            }

        }



        if (contact?.isKeyAvailable(CNContactPostalAddressesKey))! {

            for addres:CNLabeledValue in (contact?.postalAddresses)! {

                let addre = addres.value
                let addre2 = addre.street

                let lable :String  =  CNLabeledValue<NSString>.localizedString(forLabel: addres.label! )


                let dic : [String : AnyObject] = ["name":addre2 as AnyObject,
                                                  "type":lable as AnyObject,
                                                  "typeof":"address" as AnyObject]

                dataAll.add(dic)

                if lable == "home"
                {
                    if addre2 == ""
                    {

                    }
                    else
                    {
                        address = addre.street



                    }
                }

                if lable == "work"
                {
                    if addre2 == ""
                    {

                    }
                    else
                    {
                        workaddress = addre.street

                    }

                }
                if lable == "other"
                {
                    if addre2 == ""
                    {

                    }
                    else
                    {
                        otheraddress = addre.street


                    }
                }

                print(lable + (addre.street))
            }


        }

        if (contact?.isKeyAvailable(CNContactUrlAddressesKey))! {

            for urladd:CNLabeledValue in (contact?.urlAddresses)! {

                let url2 = (urladd.value as String) as String

                let lable :String  =  CNLabeledValue<NSString>.localizedString(forLabel: urladd.label! )


                let dic : [String : AnyObject] = ["name":url2 as AnyObject,
                                                  "type":lable as AnyObject,
                                                  "typeof":"url" as AnyObject]

                dataAll.add(dic)

                if lable == "home"
                {
                    if url2 == ""
                    {

                    }
                    else
                    {
                        personalurl = urladd.value as String



                    }
                }
                if lable == "work"
                {
                    if url2 == ""
                    {

                    }
                    else
                    {
                        companyurl = urladd.value as String


                    }

                }
                if lable == "other"
                {
                    if url2 == ""
                    {

                    }
                    else
                    {
                        otherurl = urladd.value as String



                    }

                }


            }

        }

        if (contact?.isKeyAvailable(CNContactImageDataKey))! {

            let image = contact?.imageData

            let utf8str = image?.base64EncodedString()


        }



        dataArr = [["code": "Mobile", "title": othernumber],["code": "Work Number", "title": worknumber],["code": "OtherMobile", "title": number2],["code": "Email", "title": email],["code": "WorkEmail", "title": workemail],["code": "OtherEmail", "title": otheremail],["code": "Address", "title": address],["code": "Work Address", "title": workaddress],["code": "OtherAddress", "title": otheraddress],["code": "Company Name", "title": companyname],["code": "Designation", "title": jobtitle],["code": "Personal Website", "title": personalurl],["code": "Company Website", "title": companyurl],["code": "Other Website", "title": otherurl]]
    }

}

在按钮操作中添加此代码

 let contactPickerViewController = CNContactPickerViewController()

        contactPickerViewController.delegate = self

        present(contactPickerViewController, animated: true, completion: nil)

【讨论】:

  • 尽可能使用 Swift 时,避免使用具有原生 Swift 替代方案的 Foundation 版本的数据结构(例如 NSMutableArray,您应该使用 Array&lt;Type&gt;)。此外,您应该重新格式化您的代码,因为所有不必要的空格都很难阅读。您的回答实际上并没有回答问题,因为您只返回字典,而不是实际的 JSON 对象,并且在问题中没有提到 CNContactPickerViewController
猜你喜欢
  • 1970-01-01
  • 2011-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-20
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多