How to get multidimensional array working in tableview - iphone

I am creating an iOS Swift 3 app and in this app I have a tableview with data coming from an API. I want to display this data in the tableview but I want to do it grouped by firstname. I have managed to first group the data (see below) but xCode is complaining that XXX.
This is the declaration of the sections:
var sections = [String: [User]]()
This is how I group the data:
self.sections = Dictionary(grouping: self.contacts.filter({ !$0.firstname!.isEmpty })) {
$0.firstname!.prefix(1).capitalized
}
This is my output:
["D": [motto.User(id: Optional(1), firstname: Optional("Dan"), lastname: Optional("Meeler"), email: Optional("time#example.com"))], "M": [coiqo.User(id: Optional(3), firstname: Optional("Mia"), lastname: Optional("Kallas"), email: Optional("mia#ka.com"))]]
This is the error I got:
Cannot subscript a value of type '[String : [User]]' with an index of type 'Int'
In this function for tableview:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
How can I get this array to work in a tableview?
Update
I just had to add this to dasblinkenlight answer and it worked!
if(groupKeys.count > 0) {
return sections[groupKeys[section]]!.count
} else {
return sections.count
}

Your self.sections is Dictionary, and dictionaries are unordered.
Once you've made user groups from API results, make a separate array composed of group keys:
let groupKeys : [String] = Array(self.sections.keys)
Sort that array in the way that you wish your sections to appear (alphabetical, reverse alphabetical, natural, etc.)
Now you can write your function like this:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[groupKeys[section]].count
}

Related

swift count elements in dictionary value for tableView

i have variable of type [Int: [CustomObject]], and i need to count [CustomObject].count by key in my numberOfRowsInSection ? how can i access this value ?
var tasks = [Int :[TaskEntity]]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("section: \(section), taskKey: \(tasks.keys.sorted())")
}
Like each key in dict has an array of elements as value, i need to count this elements in array for each key-value pair
UPD: I'he added print line as Joakim Danielson suggested. Print says:
When i add cell to section 0: "section: 0, taskKey: []
section: 0, taskKey: [0]"
When i add cell to section 1: "section: 1, taskKey: [0, 1]
section: 0, taskKey: [0, 1]"
Now i'm confused about output

How to make Swift NSComboBox return an array from a multi-dimensional array per line

I've got an NSComboBox in my app, and I'd like it to display the contents of an array per line from a multi-dimensional array, rather than an element from a one-dimensional array.
My ViewController conforms to NSComboBoxDataSource, has the two required methods (numberOfItems and objectValueForItemAt index), and within viewDidLoad() I've set:
myComboBox.usesDataSource = true
myComboBox.dataSource = self
Everything works perfectly with a one-dimensional array.
I've got the following array:
var testArray: [[String]] = [["Array 1", "Element"],["Array 2", "Element"]]
followed by the following functions later on:
func numberOfItems(in comboBox: NSComboBox) -> Int {
return testArray.count
}
func comboBox(_ comboBox: NSComboBox, objectValueForItemAt index: Int) -> Any? {
return testArray[index]
}
Please see the attached screengrab for what my NSComboBox returns (which is just '(' on each line). I had expected the first line to return ["Array 1", "Element"] and the second line to return ["Array 2", "Element"]. Ideally I'd want the NSComboBox to return only the contents of the arrays like so: Array 1, Element (i.e. without [ ] and ")
The closest I've got is by trimming characters as follows:
func comboBox(_ comboBox: NSComboBox, objectValueForItemAt index: Int) -> Any? {
return String(describing: testArray[index]).trimmingCharacters(in: CharacterSet(charactersIn: "[]\""))
}
But that returns Array 1", "Element, so if anyone has any suggestions for why it hasn't trimmed all the \" that would be appreciated too.
I'm new to Swift so apologies if I've missed something obvious!
Thanks in advance.
If you want to control the String representation, you should better not modify the output of String.init(describing:).
And trimmingCharacters(in:) trims the both ends of a String.
Try something like this:
func comboBox(_ comboBox: NSComboBox, objectValueForItemAt index: Int) -> Any? {
return testArray[index].joined(separator: ", ")
}

How to get a user attribute from an AWS Cognito user pool in Swift?

I'm using AWS Cognito user pools with Amazon's Swift sample app. I'm able to create users with a given_name attribute, but it's not obvious how to later retrieve the given_name.
The Amazon sample retrieves attributes as a AWSCognitoIdentityUserGetDetailsResponse and then dumps them to the screen. However, I can't find documentation for AWSCognitoIdentityUserGetDetailsResponse. It appears to be something of an array, but it's not obvious to me how to just pull out a given_name from the returned attributes. One would think that returning attributes as a dictionary would be a good idea, but it doesn't appear that Amazon did things that way.
Any pointers?
EDIT: To clarify, what's returned is an array of AttributeType objects. Here's code in the Cognito sample which displays all returned attributes:
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "attribute", for: indexPath)
let userAttribute = self.response?.userAttributes![indexPath.row]
cell.textLabel!.text = userAttribute?.name
cell.detailTextLabel!.text = userAttribute?.value
return cell
}
Here's the raw response:
Response body:
{"UserAttributes":[{"Name":"sub","Value":"XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX"},{"Name":"email_verified","Value":"true"},
{"Name":"given_name","Value":"Bob"},
{"Name":"email","Value":"bob#example.com"}],"Username":"AAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"}
It's just not obvious to me how to pull out given_name without iterating through the whole array.
Not an iOS expert here, but from what I can see in the SDK implementation, it looks like they copy the details from the AWSCognitoIdentityProviderGetUserResponse, which documentation shows it has the user attributes in the form of a map. Did you try to look for an userAttributes array in the response?
Also, the raw GetUser API says that the UserAttributes should be in the response.
Here's an example of using getDetails() to access userAttributes
self.user?.getDetails().continueOnSuccessWith { (task) -> AnyObject? in // handle all auth setup
DispatchQueue.main.async(execute: {
self.response = task.result // AWSCognitoIdentityUserGetDetailsResponse
if let attributes = task.result?.userAttributes { // https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html
for attribute in attributes {
print(attribute.name, attribute.value)
if attribute.name == "name" {
// ... do something with name
}
}
}
})
return task
}
}

Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?

I have the following model:
class Word: NSManagedObject {
#NSManaged var definitions: NSSet
}
Then, in one of my view controllers, I have the following property:
var word: Word?
However, if I try to do this:
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
{
return self.word?.definitions.count
}
I get an error:
Value of optional type 'Int?' not unwrapped; did you mean to use '!'
or '?'?
According to the documentation, the value of an optional chaining call is also optional, so .count should return an optional Int. So I tried unwrapping it:
return self.word?.definitions.count!
But that still results in the same error. What's the right way to do this?
The problem is the order of operations. The cleanest way to do this would be to stop optional chaining (if you really don't want to handle the nil case).
return self.word!.definitions.count
But I recommend doing this for safety:
if let actualWord = self.word {
return actualWord.definitions.count
}
else {
return 0
}
Or even shorter:
return self.word ? self.word!.definitions.count : 0
Otherwise, you might as well declare word as an implicitly unwrapped optional and not have to worry about it:
var word: Word!
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
{
return self.word.definitions.count
}

Apple Swift: what does the second argument in this function mean

In the following function:
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return 10
}
is the second argument the result returned by calling the function numberOfRowsInSection with section as the argument? If so, where is it getting section from?
This syntax allows to set a name for an argument that is different from the name of the local variable used to capture that arguments value.
numberOfRowsInSection is the name of the parameter that you use when calling this function.
section is the name of the local variable that holds the value that got passed.
So you would call this function like so:
Int rowCount = tableView(tableView: aTabelView, numberOfRowsInSection: 10);
And in that function, you would use the argument like so:
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return section;
}
numberOfRowsInSection is the argument name when calling the function. section is the argument name from within the function. So, for example, you would call this function as follows:
myObject.tableView(aView, numberOfRowsInSection:4)
but from within the function, you would refer to that 4 as follows:
let valueOfSectionArgument = section