Swift: Set Address Property of ABRecord - ios

I took a look at the SE question here [Avinash's answer] and the Apple resource here [bottom of page 18 and top of page 19] in my attempt to set an address for an ABRecord. I did my best to translate them from Objective-C, but apparently I made an mistake somewhere since on the line let dict = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 5, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) I get the error Cannot assign to immutable value of type 'CFDictionaryValueCallBacks'.
Here's my code:
let information: ABRecord = ABPersonCreate().takeRetainedValue()
let address: ABMutableMultiValueRef = ABMultiValueCreateMutable(ABPropertyType(kABMultiDictionaryPropertyType)).takeUnretainedValue()
var keys = [CFStringRef]()
var values = [CFStringRef]()
keys.append(kABPersonAddressStreetKey)
keys.append(kABPersonAddressCityKey)
keys.append(kABPersonAddressStateKey)
keys.append(kABPersonAddressZIPKey)
keys.append(kABPersonAddressCountryKey)
keys.append(kABPersonAddressCountryCodeKey)
Note: country code left out
values.append(s["kABPersonAddressStreetKey"]!! as NSString)
values.append(s["kABPersonAddressCityKey"]!! as NSString)
values.append(s["kABPersonAddressStateKey"]!! as NSString)
values.append(s["kABPersonAddressZIPKey"]!! as NSString)
values.append(s["kABPersonAddressCountryKey"]!! as NSString)
values.append(s["kABPersonAddressCountryCodeKey"]!! as NSString)
let dict = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 5, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)
let scanned = ABUnknownPersonViewController()
let identifier = ABMultiValueIdentifier()
ABMultiValueAddValueAndLabel(address, dict, kABHomeLabel, &identifier)
ABRecordSetValue(information, kABPersonAddressProperty, address, nil)

I'm sure there's a much more concise way to do this, but here's the solution I've come up with.
var addressComponents = [String : String]()
if let value = s["kABPersonAddressStreetKey"] {
addressComponents[kABPersonAddressStreetKey as String] = value
}
if let value = s["kABPersonAddressCityKey"] {
addressComponents[kABPersonAddressCityKey as String] = value
}
if let value = s["kABPersonAddressStateKey"] {
addressComponents[kABPersonAddressStateKey as String] = value
}
if let value = s["kABPersonAddressZIPKey"] {
addressComponents[kABPersonAddressZIPKey as String] = value
}
if let value = s["kABPersonAddressCountryKey"] {
addressComponents[kABPersonAddressCountryKey as String] = value
}
if let value = s["kABPersonAddressCountryCodeKey"] {
addressComponents[kABPersonAddressCountryCodeKey as String] = value
}
let address: ABMutableMultiValue = ABMultiValueCreateMutable(ABPropertyType(kABMultiStringPropertyType)).takeRetainedValue()
ABMultiValueAddValueAndLabel(address, addressComponents, kABHomeLabel, nil)
ABRecordSetValue(information, kABPersonAddressProperty, address, nil)

Related

Values from nested array in Swift 3

I am using Swift 3.
I am getting the following response from a server and i need to parse and get values such as, Account No, Account Type, Address etc.
parsedData: (
{
key = "<null>";
offset = 1;
partition = 0;
topic = test;
value = {
"Account No" = 675;
"Account Type" = Saving;
Address = location;
User ID = 601;
User Name = Stella;
};
}
)
I have been trying to get value first, and then planning to get each value,
var temp: NSArray = parsedData["value"] as! NSArray
but this is giving error as cannot convert value of type String to expected argument type Int.'
How to retrieve values from the above mentioned array?
parsedData is an array which contains Dictionary at first index.
let dicData = parsedData[0] as! Dictionary
let valueDictionary = dicData["value"] as! Dictionary //dicData also contains dictionary for key `value`
let accountNumber = valueDictionary ["Account No"] //**Account number**
//////SECOND APPROACH IF YOU HAVE DICTIONARY IN RESPONSE
var valueDictionary : NSDictionary = parsedData["value"] as? NSDictionary
let accountNumber = valueDictionary ["Account No"] //**Account number**
You parse a dictionary as an array
var temp: NSDictionary = parsedData["value"] as? NSDictionary
Try this
let dicData = parsedData[0] as! Dictionary
var temp: NSDictionary = dicData["value"] as? NSDictionary
let accountNumber = temp.object(forKey: "Account No")
let accountType = temp.object(forKey: "Account Type")
let address = temp.object(forKey: "Address")
let userID = temp.object(forKey: "User ID")
let userName = temp.object(forKey: "User Name")

I am unable to pass data from model class to table view in swift 3?

In this I am getting data from server response after posting parameters and here I need to display it on table view and it should be displayed like shown below in the image 0 is the price for the particular shipping method
already i had written model class for server response data and here it is
struct ShippingMethod {
let carrierCode : String
let priceInclTax : Int
let priceExclTax : Int
let available : Any
let carrierTitle : String
let baseAmount : Int
let methodTitle : String
let amount : Int
let methodCode : String
let errorMessage : Any
init(dict : [String:Any]) {
self.carrierCode = dict["carrier_code"] as! String
self.priceInclTax = dict["price_incl_tax"]! as! Int
self.priceExclTax = dict["price_excl_tax"]! as! Int
self.available = dict["available"]!
self.carrierTitle = dict["carrier_title"] as! String
self.baseAmount = dict["base_amount"]! as! Int
self.methodTitle = dict["method_title"]! as! String
self.amount = dict["amount"]! as! Int
self.methodCode = dict["method_code"] as! String
self.errorMessage = (dict["error_message"] != nil)
}
}
by using this I had formed an array type like this by using code
var finalDict = [String: [String]]()
var responseData = [ShippingMethod]()
do
{
let array = try JSONSerialization.jsonObject(with: data, options: []) as? [[String : Any]]
for item in array! {
self.responseData.append(ShippingMethod.init(dict: item))
}
print(self.responseData)
}
catch let error
{
print("json error:", error)
}
print(self.responseData)
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [String]()
}
self.finalDict[carrierTitle]!.append(methodTitle)
}
print(self.finalDict)
the output of this finalDict is ["Flat Rate": ["Fixed"], "Best Way": ["Table Rate"]] in this carrier title key value pair should be displayed as section title and is Flat Rate and method title key value pair should be displayed as rows in section Fixed but the problem is I need amount key value pair with it also for corresponding method title can anyone help me how to get this ?
Why don't you create another struct for displaying row data:
struct CarrierInfo {
let name:String
let amount:Int
}
Change your finalDict to
var finalDict = [String: [CarrierInfo]]()
and create CarrierInfo instance and set it in finalDict
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
let amount = dict.amount
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [CarrierInfo]()
}
self.finalDict[carrierTitle]!.append(CarrierInfo(name: carrierTitle, amount: amount))
}
Likewise you can make other required changes. This would neatly wrap your row display data inside a structure.
PS: I have not tested the code in IDE so it may contain typos.
You can assign another dictionary with key as methodTitle and amount as value. i.e., ["fixed":"whatever_amount"]
OR
You can use finalDict differently, like ["Flat Rate": ["tilte":"Fixed","amount":"0"], "Best Way": ["title":"Table Rate","amount":"0"]]
If it is difficult for you to code this, you can revert back.
Edit
You can use the following code to create the array in the second solution I suggested above:
for item in self.responseData {
let dict = item
let carrierTitle = dict.carrierTitle
let methodTitle = dict.methodTitle
let amount = dict.amount
if self.finalDict[carrierTitle] == nil {
self.finalDict[carrierTitle] = [[String:String]]()
}
let innerDict = ["title":methodTitle,"amount":amount]
self.finalDict[carrierTitle]!.append(innerDict)
}

withUnsafeMutablePointer doesn't compile

I've been struggling on an issue for some time now. I'm wondering why this code :
private func generateIdentity (base64p12 : String, password : String?, url : NSURL) {
let p12KeyFileContent = NSData(base64EncodedString: base64p12, options: NSDataBase64DecodingOptions(rawValue: 0))
if (p12KeyFileContent == nil) {
NSLog("Cannot read PKCS12 data")
return
}
let options = [String(kSecImportExportPassphrase):password ?? ""]
var citems: CFArray? = nil
let resultPKCS12Import = withUnsafeMutablePointer(&citems) { citemsPtr in
SecPKCS12Import(p12KeyFileContent!, options, citemsPtr)
}
if (resultPKCS12Import != errSecSuccess) {
print(resultPKCS12Import)
return
}
let items = citems! as NSArray
let myIdentityAndTrust = items.objectAtIndex(0) as! NSDictionary
let identityKey = String(kSecImportItemIdentity)
identity = myIdentityAndTrust[identityKey] as! SecIdentityRef
hasCertificate = true
print("cert cre", identity)
}
compiles, whereas this other one not :
private func generateIdentity (base64p12 : NSData, password : String?) {
let p12KeyFileContent = NSData(data: base64p12)
let options = [String(kSecImportExportPassphrase):password ?? ""]
var citems: CFArray? = nil
let resultPKCS12Import = withUnsafeMutablePointer(&citems) { citemsPtr in // line with the error
SecPKCS12Import(p12KeyFileContent!, options, citemsPtr)
}
if (resultPKCS12Import != errSecSuccess) {
print(resultPKCS12Import)
return
}
let items = citems! as NSArray
let myIdentityAndTrust = items.objectAtIndex(0) as! NSDictionary
let identityKey = String(kSecImportItemIdentity)
identity = myIdentityAndTrust[identityKey] as! SecIdentityRef
hasCertificate = true
print("cert cre", identity)
}
XCode tells me that :
Cannot convert value of type 'inout CFArray?' (aka 'inout Optional') to expected argument type 'inout _'
I really don't see how the 2 codes are different for the citems variable because what I basically just did was to use a NSData argument for the function, thus bypassing base64 string conversion to NSData.
The error message is super-confusing, but the cause of the error resides here:
SecPKCS12Import(p12KeyFileContent!, options, citemsPtr)
In your second example, with declaring let p12KeyFileContent = NSData(data: base64p12), the type of p12KeyFileContent is NSData, not NSData?. So, you cannot use ! for p12KeyFileContent.
Try changing the line as:
SecPKCS12Import(p12KeyFileContent, options, citemsPtr)
(! removed.)
One more.
You usually have no need to use withUnsafeMutablePointer to call SecPKCS12Import.
Try replacing these 3 lines (in your second example):
let resultPKCS12Import = withUnsafeMutablePointer(&citems) { citemsPtr in // line with the error
SecPKCS12Import(p12KeyFileContent!, options, citemsPtr)
}
with this:
let resultPKCS12Import = SecPKCS12Import(p12KeyFileContent, options, &citems)
The first example code can be rewritten as well.

Cannot subscript a value of type '[Int : [String]]' with an index of type 'String!'

please, ask me, where is my mistake? I have Xcode error:
Cannot subscript a value of type '[Int : [String]]' with an index of
type 'String!'
in let keyExists = myDict[tmp.Hour] != nil, myDict[tmp.Hour] = Int and myDict[tmp.Hour].append(tmp.Minutes) of that part of code:
func array() -> Dictionary <Int,[String]>
{
let timeInfos = getTimeForEachBusStop()
var myDict: Dictionary = [Int:[String]]()
for tmp in timeInfos {
let keyExists = myDict[tmp.Hour] != nil
if (!keyExists) {
myDict[tmp.Hour] = [Int]()
}
myDict[tmp.Hour].append(tmp.Minutes)
}
return myDict
}
I understand, that problem is in optional type, but where is problem I don't understand
upd
func getTimeForEachBusStop() -> NSMutableArray {
sharedInstance.database!.open()
let lineId = getIdRoute
let position = getSelectedBusStop.row + 1
let getTimeBusStop: FMResultSet! = sharedInstance.database!.executeQuery("SELECT one.hour, one.minute FROM shedule AS one JOIN routetobusstop AS two ON one.busStop_id = (SELECT two.busStop_id WHERE two.line_id = ? AND two.position = ?) AND one.day = 1 AND one.line_id = ? ORDER BY one.position ASC ", withArgumentsInArray: [lineId, position, lineId])
let getBusStopInfo : NSMutableArray = NSMutableArray()
while getTimeBusStop.next() {
let stopInfo: TimeInfo = TimeInfo()
stopInfo.Hour = getTimeBusStop.stringForColumnIndex(0)
stopInfo.Minutes = getTimeBusStop.stringForColumnIndex(1)
getBusStopInfo.addObject(stopInfo)
}
sharedInstance.database!.close()
return getBusStopInfo
}
You are declaring your dictionary as a dictionary with keys of type Int and values of type [String]:
var myDict: Dictionary = [Int:[String]]()
(better written as: var myDict: [Int: [String]] = [:] because by casting it to Dictionary you are removing the types).
However, in
myDict[tmp.Hour] = [Int]()
You are using a value which is of [Int] type and tmp.Hour is probably a String.
So, your problem is a type mismatch.
The error states that you cannot subscribe your [Int:[String]] dictionary with a String key.
Therefore the type of tmp.Hour is obviously String rather than the expected Int
If tmp.Hour is guaranteed to be an integer string you can convert the value
let hour = Int(tmp.Hour)!
myDict[hour] = [Int]()
On the other hand since myDict is [Int:[String]] you might mean
let hour = Int(tmp.Hour)!
myDict[hour] = [String]()
Hour and Minutes are of type string (I guess - stringForColumnIndex) so your dictionary is wrong type. Should be:
func array() -> Dictionary <String,[String]>
{
let timeInfos = getTimeForEachBusStop()
var myDict: Dictionary = [String:[String]]()
for tmp in timeInfos {
let keyExists = myDict[tmp.Hour] != nil
if (!keyExists) {
myDict[tmp.Hour] = [String]()
}
myDict[tmp.Hour].append(tmp.Minutes)
}
return myDict
}

Variable is null when cast

I have a variable content from a NSArray : let content = application["content"]!
When I print content, I have a String :
print(content) -> My content
But when I want to cast my variable to String : let content = application["content"]! as! String
I can't print my variable because it's null :
print(content) -> Could not cast value of type 'NSNull' (0x1a0507768) to 'NSString' (0x1a0511798).
Why ?
UPDATE :
My array when value is not casted :
{
"application_title" = "Marina Kaye";
"application_type" = discussions;
"application_type_name" = Discussions;
content = (
{
content = "Le nouvel album de Marina Kaye";
link = "?message_id=118";
},
{
content = "Son album est num\U00e9ro 1 des";
link = "?message_id=131";
},
{
content = "Le nouvel album s'appel";
link = "?message_id=126";
}
);
"content_title" = "Messages utiles";
"content_type" = "useful_messages";
}
My array when value is casted :
{
"application_title" = "Marina Kaye";
"application_type" = discussions;
"application_type_name" = Discussions;
content = "<null>";
"content_title" = "<null>";
"content_type" = "usefull_messages";
}
I can't cast content to NSArray and content_title to String.
MY CODE :
let applicationsArray = result["applications"]! as! NSArray
for application in applicationsArray {
let applicationTitle = application["application_title"]! as! String
let applicationType = application["application_type"]! as! String
let applicationTypeName = application["application_type_name"]! as! String
let content = application["content"]! as! NSArray
let contentTitle = application["content_title"]! as! String
let contentType = application["content_type"]! as! String
self.listApplications.append(Application(applicationTitle: applicationTitle, applicationType: applicationType, applicationTypeName: applicationTypeName, content: content, contentTitle: contentTitle, contentType: contentType))
}
As you are coding in Swift, you do not need the legacy NSArray and NSDictionary types. Instead, these are now Array and Dictionary, but you do not even have to care about that.
To declare an array, you usually specify the type in square brackets, such as [String]. For a dictionary, you need this for both key and value, separated by a colon, e.g. [String: AnyObject].
From your log output, you have a Dictionary of type [String: AnyObject] with 6 keys; all of them point to String objects, except the "content" one.
The "content" key apparently points to an array of dictionaries. This is written like this: [[String: AnyObject]]. Thus it is not surprising that casting this to String is not successful.
Here is how you can parse the application dictionary's "content":
if let content = application["content"] as? [[String: AnyObject]] {
for item in content {
let text = content["content"] as? String
let link = content["link"] as? String
// do something with this data
}
}
I would recommend defining a class or struct to capture the application object. Your code will be much clearer and easier to maintain.

Resources