changing json output when using serializer - ios

i want to ask, when i clicked the button and then println the json data appear like that picture..
i want to ask if it possible to change the output like this
{"telur" : "4" , "roti" : "4"}
{"softball" : "60" , "senam" : "60"}
in the other way, i want to make it one bracket and erase type and amount in it, I'm using jsonserializer though..
the code like this..
menu.swift
class Nutritiondata
{
var type = "type"
var amount = 0
//var date = "Date"
init (type: String, amount: Int)
{
self.type = type
self.amount = amount
//self.date = date
}
}
class Activitiesdata
{
var type = "type"
var amount = 0
//var date = "Date"
init (type: String, amount: Int)
{
self.type = type
self.amount = amount
//self.date = date
}
}
table view
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomCell = tableView.dequeueReusableCellWithIdentifier("cell") as! CustomCell
let menu = arrayOfMenu[indexPath.row]
cell.setCell(menu.type, rightlabeltext: menu.unit, imagename: menu.image)
var data = Nutritiondata(type: menu.type, amount: String(cell.value ).toInt()!)
var json = JSONSerializer.toJson(data)
JSONNutrisi.jsondata = json
return cell
}
var data = Nutritiondata(type: leftlabel.text!, amount: qtytext.text.toInt()!)
var temp:Nutritiondata
temp = data
var idx = 0
if(temp.amount-1 > 0) {
temp.amount -= 1
}
data = Nutritiondata(type: leftlabel.text!, amount: qtytext.text.toInt()!)
var json = JSONSerializer.toJson(data)
var tempJson = JSONSerializer.toJson(temp)
for(var i = 0; i < Nutritionmenu.data.count; i++){
if(Nutritionmenu.data[i] == tempJson){
self.check = true
self.idx = i
}
}

First of all make one struct function which have type and amount
struct Nutritiondata {
let type :String
let amount : Int
func Dict(age : Int) -> Nutritiondata
{
return Nutritiondata(type: type, amount: amount)
}
}
Now take one array and store the data in it. here I am store static data but you store the data from response
let alldata = [Nutritiondata(type: "telur", amount: 4),Nutritiondata(type: "roti", amount: 4),Nutritiondata(type: "softball", amount: 60),Nutritiondata(type: "seman", amount: 60)]
Now you can use the data as below.
alldata[0].type // telur
alldata[0].amount // 4
use this alldata in your cellForRowAtIndexPath delegate method.
let nutrition = alldata[indexPath.row]
you can display in tableview using below code
nutrition.type
nutrition.amount

Related

Group Array to introduce sections to UITableView in Swift

I have the following class:
ChatMessage: Codable {
var id: Int?
var sender: User?
var message: String?
var seen: Int?
var tsp: Date?
}
The tsp is formatted like this: YYYY-MM-DD hh:mm:ss
I would like to "group" messages sent on the same day to end up with something like in this example:
let groupedMessages = [ [ChatMessage, ChatMessage], [ChatMessage, ChatMessage, ChatMessage] ]
I ultimately want to use groupedMessages in a UITableViewController to introduce sections like so:
func numberOfSections(in tableView: UITableView) -> Int {
return groupedMessages.count
// would be 2 int the above
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return chatMessages[section].count
// would be 2 for the first and 3 for the second section
}
What would be the most performant way of getting the sorting done? - i.e. something that also works well once the number of chatMessages to be sorted increases
You could try:
let cal = Calendar.current
let groupedMessages = Dictionary(grouping: self.chatMessages, by: { cal.startOfDay($0.tsp) })
let keys = groupedMessages.keys.sorted(by: { $0 > $1 })
This however would give you a dictionary like:
[
SomeDateHere: [
ChatMessage(id: 0, sender: User?, message: "String", seen: 1),
ChatMessage(id: 0, sender: User?, message: "String", seen: 1)
],
AnotherDateHere: [
ChatMessage(id: 0, sender: User?, message: "String", seen: 1)
]
You could then use keys to return the section count:
return keys.count
And to get the array of messages for each dictionary item like so:
let key = keys[indexPath.section]
let messages = groupedMessages[key].sorted(by: { $0.tsp > $1.tsp })
Or to get just the count:
let key = keys[indexPath.section]
return groupedMessages[key].count
Grouping array by date you could find here How to group array of objects by date in swift?
How to connect this to UITableView you will find here https://www.ralfebert.de/ios-examples/uikit/uitableviewcontroller/grouping-sections/
I would probably start by introducing a new type:
public struct CalendarDay: Hashable {
public let date: Date
public init(date: Date = Date()) {
self.date = Calendar.current.startOfDay(for: date)
}
}
extension CalendarDay: Comparable {
public static func < (lhs: CalendarDay, rhs: CalendarDay) -> Bool {
return lhs.date < rhs.date
}
}
Then I would extend your message:
extension ChatMessage {
var day: CalendarDay {
return CalendarDay(date: tsp)
}
}
(I am assumming tsp is not an optional, there is no reason for it to be optional).
Now, let's define a section:
struct Section {
var messages: [ChatMessage] = []
let day: CalendarDay
init(day: CalendarDay) {
self.day = day
}
}
Let's group easily:
let messages: [ChatMessage] = ...
var sections: [CalendarDay: Section] = [:]
for message in messages {
let day = message.day
var section = sections[day] ?? Section(day: day)
section.messages.append(day)
sections[day] = section
}
let sectionList = Array(sections.values).sorted { $0.day < $1.day }
If your messages are not originally sorted, you might need to also sort messages in every section separately.
let testArray = ["2016-06-23 09:07:21", "2016-06-23 08:07:21", "2016-06-22 09:07:21", "2016-06-22 08:07:21"]
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYY-MM-DD hh:mm:ss"
let groupDic = Dictionary(grouping: testArray) { (pendingCamera) -> DateComponents in
let date = Calendar.current.dateComponents([.day, .year, .month], from: dateFormatter.date(from: pendingCamera)!)
return date
}
print(groupDic)
You can take groupDic and return in numberOfSections and titleForHeaderInSection.

Assign keys and values from [(key: String, value: [[String : Any]])] in UITableView

I have an array of dictionaries, [[String:AnyObject]], which is reduce+sorted as below successfully.
var arrUserList = [(key: String, value: [[String : Any]])]()
let result = self.arrJsonDict.reduce(into: [String: [[String:Any]]]()) { result, element in
let strName: String = (element as! NSDictionary).value(forKey: "name") as! String
if let firstLetter = strName.first {
let initial = String(describing: firstLetter).uppercased()
result[initial, default: [[String:Any]]() ].append(element as! [String : Any])
}}.sorted { return $0.key < $1.key }
self.arrUserList = result
Now I wanted to assign keys to table sections and values as table cell text from the array.
This is very cumbersome code.
You are highly encouraged to use a struct rather than a dictionary at least with a member name
struct Person {
let name : String
}
Declare and rename arrJsonDic (more descriptively) as
var people : [Person]()
and arrUserList as
var users = [String: [Person]]()
For the sections declare another array
var letters = [String]()
Group the array and populate letters simply with
users = Dictionary(grouping: people, by: { String($0.name.first!) })
letters = users.keys.sorted()
In the table view in numberOfSections return
return letters.count
and in numberOfRows return
let letter = letters[section]
return users[letter]!.count
In cellForRowAt assign a name to a label with
let letter = letters[indexPath.section]
let user = users[letter]![indexPath.row]
cell.nameLabel.text = user.name
------------------------------
To make it still swiftier declare a second struct Section
struct Section {
let index : String
let people : [Person]
}
delete
var letters = [String]()
and declare users
var users = [Section]()
The grouping is slightly different
let grouped = Dictionary(grouping: people, by: { String($0.name.first!) })
users = grouped.map({ Section(index: $0.0, people: $0.1) }).sorted{$0.index < $1.index}
The code in the three table view delegate methods are
return users.count
-
return users[section].people.count
-
let user = users[indexPath.section].people[indexPath.row]
cell.nameLabel.text = user.name

Attempting to get numberOfRowsInSection count to be equal to an array count from another file in Swift

Currently I am fetching data from firebase, turning the data into an array of objects and trying to have that array count be the count of the "numberOfRowsInSection".
So, I get the right count on the view controller where the array is declare but I keep getting 0's when I pass that data to the tableview controller.
Below I am showing the viewcontroller where I am fetching data and putting it in a filtered array:
func fetchAllData( completion: #escaping (_ call: [Restaurant]) -> Void) {
self.ref = Database.database().reference()
self.ref?.observe(.value, with: { (snap) in
guard let topArray = snap.value as? [[String:Any]] else {print(":(") ; return }
var restaurantArray = [Restaurant]()
for dictionary in topArray {
self.restaurantGroup.enter()
guard let address = dictionary["Address"] as? String,
let city = dictionary["City"] as? String,
let inspectionDate = dictionary["Inspection Date"] as? String,
let name = dictionary["Name"] as? String,
let major = dictionary["Number of Occurrences (Critical Violations)"] as? Int,
let minor = dictionary["Number of Occurrences (Noncritical Violations)"] as? Int,
let violationTitle = dictionary["Violation Title"] as? String else { continue }
print(2)
//MARK: - creates restaurants from the list above
let restaurant = Restaurant(address: address, city: city, inspectionDate: inspectionDate, name: name, major: major, minor: minor, violationTitle: violationTitle)
print(3)
//MARK: - Adds a restaurant to restaurant array instance
restaurantArray.append(restaurant)
}
self.restaurantList = restaurantArray
self.restaurantGroup.leave()
completion(self.restaurantList)
let dictionaryNew = Dictionary(grouping: self.restaurantList) { $0.name + " " + $0.address}
self.restaurantListModified = dictionaryNew
print(self.restaurantListModified.count)
})
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
var filteredArray = [[Restaurant]]()
guard let userSearch = searchBar.text?.uppercased() else { return }
pulleyViewController?.setDrawerPosition(position: .partiallyRevealed, animated: true)
var nameArray = [String]()
for (key, value) in restaurantListModified {
if value[0].name.hasPrefix(userSearch.uppercased()){
filteredArray.append(value)
}
}
for subarray in filteredArray {
let nameArrayForTBView = subarray[0].name
nameArray.append(nameArrayForTBView)
}
self.tableViewNameArray = nameArray
print("\(tableViewNameArray.count)😋😋😋😋😋😋😋😋😋😋😋😋")
print("this First")
}
}
The tableViewNameArray is the array I am trying to pass to this tableView Controller
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("This second")
print(MapViewController.sharedMapViewController.tableViewNameArray.count)
return MapViewController.sharedMapViewController.tableViewNameArray.count
}
I also have my array set to a Notification Center that is received in the tableView Controller as shown below:
var tableViewNameArray = [String]() {
didSet {
DispatchQueue.main.async {
DispatchQueue.main.async {
NotificationCenter.default.post(name: MapViewController.RestaurantNotification.notificationSet, object: self)
}
}
}
}
Points:
My array from viewcontroller is getting the right count on the view controller but the accurate count is not being passed to the tableView Controller.
the tableview count for the array being passed is 0
I found the problem that #Kamran pointed to. I was not assigning self.tableViewNameArray = nameArray. So I changed
var tableViewNameArray to
static var tableViewNameArray
If you're new to programming like I, make sure you read a bit more on local and global variables. Very helpful.

Dictionary of Dictionaries of Dictionaries of Dictionaries syntax understanding

I need to map the doors of buildings into a single map, from which afterward I need to populate three pickers using this data.
Each door contains the following data: building, level, range, door number and other information that is less relevant. So I created the following map:
public var doorsMap: [String : [String : [String : [String: Door]]]] = [:]
and a have a list of doors that I need to populate this map with, the problem is that I can't understand what should be the right syntax to perform this task, I tried:
doorsMap[door.building]?[door.level]?[door.range]?[door.number] = door
but this doesn't create the inner sets of dictionaries. when I tried to do:
doorsMap[door.building]![door.level]![door.range]![door.number] = door
Obviously, I get the:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
because I try to unwrap a nil value.
So what would be the correct syntax in swift to populate this map from a list of doors?
A single assignment won't create the multiple, intermediate directories. You need to do it explicitly.
You could use something like this:
func add(door: Door) {
var building = self.doorsMap[door.building] ?? [String : [String:[String: Door]]]()
var level = building[door.level] ?? [String : [String: Door]]()
var range = level[door.range] ?? [String:Door]
range[door.number] = door
level[door.range] = range
building[door.level] = level
self.doorsMap[door.building] = building
}
Personally, I would look for a better data structure, perhaps use a struct to hold the doorsMap. This struct could have functions to handle the insertion and retrieval of doors.
Perhaps something like this:
struct Door {
let building: String
let level: String
let range: String
let number: String
}
struct DoorMap {
private var buildingsSet = Set<String>()
private var levelsSet = Set<String>()
private var rangesSet = Set<String>()
private var numberSet = Set<String>()
private var doorsArray = [Door]()
var buildings: [String] {
get {
return Array(buildingsSet).sorted()
}
}
var levels: [String] {
get {
return Array(levelsSet).sorted()
}
}
var ranges: [String] {
get {
return Array(rangesSet).sorted()
}
}
var numbers: [String] {
get {
return Array(numberSet).sorted()
}
}
var doors: [Door] {
get {
return doorsArray
}
}
mutating func add(door: Door) {
buildingsSet.insert(door.building)
levelsSet.insert(door.level)
rangesSet.insert(door.range)
numberSet.insert(door.number)
doorsArray.append(door)
}
func doorsMatching(building: String? = nil, level: String? = nil, range: String? = nil, number: String? = nil) -> [Door]{
let matches = doorsArray.filter { (potentialDoor) -> Bool in
var included = true
if let b = building {
if potentialDoor.building != b {
included = false
}
}
if let l = level {
if potentialDoor.level != l {
included = false
}
}
if let r = range {
if potentialDoor.range != r {
included = false
}
}
if let n = number {
if potentialDoor.number != n {
included = false
}
}
return included
}
return matches
}
}
var map = DoorMap()
let d1 = Door(building: "b1", level: "1", range: "r1", number: "1")
let d2 = Door(building: "b1", level: "2", range: "r1", number: "2")
let d3 = Door(building: "b2", level: "2", range: "r1", number: "2")
map.add(door: d1)
map.add(door: d2)
map.add(door: d3)
let b1Doors = map.doorsMatching(building:"b1")
let level2Doors = map.doorsMatching(level:"2")
let allBuildings = map.buildings()
Now, maybe you have more information on buildings and levels etc, so they could be structs too instead of just strings.

How to Unwrap a Modal class object in Swift 3

Model Class:
class CountryModel: NSObject {
var name:NSString!
var countryId:NSString!
init(name: NSString, countryId: NSString) {
self.name = name
self.countryId = countryId
}
}
ViewController:
var nameArr = [CountryModel]() // Model Class object
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let arr = nameArr[indexPath.row] // How to do if let here
let str:String? = "\(arr.countryId) \(arr.name)"
if let sstr = str{
cell.textLabel?.text = sstr
}
return cell
}
How should one unwrap this because output is an optional, if I try to unwrap nameArr[indexPath.row] gives an error initializer for conditional binding must have optional type, not "country modal"
It works fine I am not concatenating arr.countryId with arr.name
This works fine
var nameArr = [CountryModal]()
nameArr.append(CountryModal.init(name: "harshal", countryId: "india"))
nameArr.append(CountryModal.init(name: "james", countryId: "australia"))
let arr = nameArr[0]
let str:String? = "\(arr.countryId!) \(arr.name!)"
if let sstr = str{
print(sstr)
}
let arr2 = nameArr[1]
let str2:String? = "\(arr2.countryId!) \(arr2.name!)"
if let sstr2 = str2{
print(sstr2)
}
You can try this library https://github.com/T-Pham/NoOptionalInterpolation. It does exactly that
import NoOptionalInterpolation
let n: Int? = 1
let t: String? = nil
let s: String? = "string1"
let o: String?? = "string2"
let i = "\(n) \(t) \(s) \(o)"
print(i) // 1 string1 string2
NoOptionalInterpolation gets rid of "Optional(...)" and "nil" in Swift's string interpolation. This library ensures that the text you set never ever includes that annoying additional "Optional(...)". You can also revert to the default behaviour when needed.
Also, please note that this does not affect the print function. Hence, print(o) (as opposed to print("(o)"), o as in the example above) would still print out Optional(Optional("string2"))
Avoid forced unwrapping as much as possible. i.e. using '!'. Whenever you see !, try and think of it as code smell.
For json parsing you would not know for sure if the values for countryId and name exists or not. So, it makes sense to keep them as optionals and gracefully try to unwrap them later.
class CountryModel {
var name: String?
var countryId: String?
init(name: String?, countryId: String?) {
self.name = name
self.countryId = countryId
}
}
var nameArr = [CountryModel]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {
fatalError("cell should always be initialised")
}
var str: String?
let arr = nameArr[indexPath.row] // How to do if let here
if let countryId = arr.countryId, let name = arr.name {
str = "\(countryId) \(name)"
}
if let unwrappedString = str {
cell.textLabel?.text = unwrappedString
}
return cell
}
Bonus:
You could simplify your json parsing a lot using the Object Mapper library. It simplifies a lot of your parsing logic.

Resources