Swiftui User Multiple Selection To Array of Object - arrays

I have the following response from API
"features": [
{
"name": "Safety",
"_id": "636a638959d10a2603b8d645",
"values": [
Array of String
]
},
{
"name": "Entertainment",
"_id": "636a64312bbe0cd292a1ffc6",
"values": [
Array of String
]
Which I decode it with :
struct Feature : Codable , Hashable{
var name : String = ""
var values : [Value] = []
}
struct Value : Codable, Hashable{
var value : String = ""
var unit : String = ""
}
And in the view is render it like :
var body: some View {
VStack{
HStack{
Text("Choose Your Features").font(Font.body.bold())
Spacer()
}.padding(.leading, 15)
ScrollView(.vertical, showsIndicators: false){
VStack{
ForEach(Array(features.enumerated()), id: \.offset) { featureIndex, feature in
HStack{
Text(feature.name).font(Font.body.bold())
Spacer()
}.padding(.bottom , 10)
ScrollView(.horizontal, showsIndicators: false){
HStack(spacing : 10){
ForEach(Array(feature.values.enumerated()), id: \.offset) { valueIndex, value in
FeatureCell(isSelected: $isSelected, value: value).onTapGesture{
// here
}
}
}
Divider().padding(10)
}
}.padding(15)
}
}
}
}
The user may select multiple item from each feature values list, Now Im really confused about how to store these selections in an array of features object again, I tried almost every thing like Array, Set and Dictionaries but could not reach any solution.
Update : This is the json object I should send back
{
"features": [
{
"Safety": [
"value1",
"value9",
"value3"
]
},
{
"Entertainment": [
"value7",
"value2",
"value8"
]
}
]
}
Any help or ideas will be much appreciated

You usually want to use a Set to store which items are selected. This set should be a State variable instantiated in the parent view. The onTapGesture closure will add or remove the value to the set. If the FeatureCell needs to know whether the value is selected, simply use the .contains method.
struct FeatureValueSelectionView: View {
// The feature whose values we are displaying
let feature: Feature
// Note: You may have to manually conform Value to the Hashable protocol
#State private var selectedValues = Set<Value>()
var body: some View {
ForEach(feature.values) { value in
FeatureCell(selected: selectedValues.contains(value), value: value)
.onTapGesture { selectedValues.toggle(value) }
}
}
}
For toggling a value in a set, I like to use this simple extension:
extension Set {
public mutating func toggle(_ element: Element) {
if self.contains(element) {
self.subtract([element])
} else {
self.insert(element)
}
}
}

Related

Creating a series of master detail lists from a single JSON file in SwiftUI

I'm trying to work through understanding how I can make data flow nicely through an app I'm building. I just want a basic master detail view where it starts with a list of all the top level objects(users), tapping one of them lets you see all the second level objects related to that top level (userX -> cities), and tapping one of them lets you see all the third level objects (userX -> cityX -> towns).
This is my JSON file:
[
{
"id": 1001,
"first_name": "Jimmy",
"last_name": "Simms",
"cities": [{
"name": "New York City",
"towns": [{
"name": "Brooklyn"
},
{
"name": "Manhatten"
}
]
},
{
"name": "Tokyo",
"towns": [{
"name": "Churo"
},
{
"name": "Riponggi"
}
]
}
]
}
...
]
I have a model that I think will work well for this:
import SwiftUI
struct UserModel: Codable, Identifiable {
let id: Int
let firstName: String
let lastName: String
let cities: [CityModel]
enum CodingKeys: String, CodingKey {
case id
case firstName = "first_name"
case lastName = "last_name"
case cities
}
}
struct CityModel: Codable {
let name: String
let towns: [TownModel]
}
struct TownModel: Codable {
let name: String
}
However, what I'm struggling to do is to build this all into a series of list views that are connected to each other. I have the top level one, UserList.swift at least showing a list of the users.
import SwiftUI
struct UserList: View {
var body: some View {
NavigationView {
List(userData) { user in
NavigationLink(destination: UserRow(user: user)) {
UserRow(user: user)
}
}
.navigationBarTitle(Text("Users"))
}
}
}
struct UserList_Previews: PreviewProvider {
static var previews: some View {
UserList()
}
}
And it's assistant view, UserRow:
import SwiftUI
struct UserRow: View {
var user: UserModel
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(user.firstName)
.font(.headline)
Text(user.lastName)
.font(.body)
.foregroundColor(Color.gray)
}
Spacer()
}
}
}
struct UserRow_Previews: PreviewProvider {
static var previews: some View {
UserRow(user: userData[0])
}
}
UserList.swift Preview:
What I can't figure out is how to write CityList/CityRow and TownList/TownRow such that I can drill down from the main screen and get a list related to the objected I tapped into.
Your CityModel and TownModel need to conform to Identifiable, just add an id to them like you did in UserModel.
Than you need to edit your UserList NavigationLink:
NavigationLink(destination: CityList(cities: user.cities)) {
Text(user.firstName)
}
The Navigation is now like this: UserList -> CityList -> TownList
CityList:
struct CityList: View {
var cities: [CityModel]
var body: some View {
List (cities) { city in
NavigationLink(destination: TownList(towns: city.towns)) {
Text(city.name)
}
}
}
}
TownList:
struct TownList: View {
var towns: [TownModel]
var body: some View {
List (towns) { town in
Text(town.name)
}
}
}
I hope that helps, in my test project it works!
first you have to create CityListView and CityRow, like you did for users:
struct CityListView: View {
var user: UserModel
var body: some View {
// don't forget to make CityModel Identifiable
List(user.cities) { city in
CityRowView(city: city)
}
.navigationBarTitle(Text("Cities"))
}
}
}
struct CityRowView: View {
var city: CityModel
var body: some View {
HStack {
Text(city. name)
.font(.headline)
Spacer()
}
}
}
after that you need to change destination in NavigationLink (not UserRow, but new CityListView)
...
//NavigationLink(destination: UserRow(user: user)) {
NavigationLink(destination: CityListView(user: user)) {
UserRow(user: user)
}
...
Another way is to declare variable "cities" as an array of CityModel and receive it from user:
struct CityListView: View {
var cities: [UserModel]
// list for array of cities
}
// in UserList
NavigationLink(destination: CityListView(cities: user.cities)) {
UserRow(user: user)
}
P.S. Apple made excellent tutorial for navigations in SwiftUI: https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation

How to use key and value from json response in tableview in iOS swift?

In tableview, each cell with key and value labels, i can able to pass static key value to tableview but now trying to get data from Json response to show in tableview.
here is my json response:
{
"d": {
"results": [
{
"__metadata": {
"id": "http://192.168.41.27:8009/sap/opu/odata/sap/Z_MM_PO_01_SRV/POItemSetSet('4500022401')",
"uri": "http://192.168.41.27:8009/sap/opu/odata/sap/Z_MM_PO_01_SRV/POItemSetSet('4500022401')",
"type": "Z_MM_PO_01_SRV.POItemSet"
},
"PoDocNo": "4500022401",
"Item": "00010",
"Material": "RMECC_MOB1",
"StorageLocation": "3001",
"MatGroup": "00107",
"Quantity": "2.000",
"OrderUnit": "KG",
"NetPrice": "1000.000",
"UnitofPrice": "1.000",
"ItemCat": "0",
"Requistor": ""
},
{
"__metadata": {
"id": "http://192.168.41.27:8009/sap/opu/odata/sap/Z_MM_PO_01_SRV/POItemSetSet('4500022401')",
"uri": "http://192.168.41.27:8009/sap/opu/odata/sap/Z_MM_PO_01_SRV/POItemSetSet('4500022401')",
"type": "Z_MM_PO_01_SRV.POItemSet"
},
"PoDocNo": "4500022401",
"Item": "00020",
"Material": "RMECC_MOB1",
"StorageLocation": "3001",
"MatGroup": "00107",
"Quantity": "2.000",
"OrderUnit": "KG",
"NetPrice": "1000.000",
"UnitofPrice": "1.000",
"ItemCat": "0",
"Requistor": ""
}
]
}
}
here i'm getting json response:
extension PoItemDetailsViewController {
func GetPoItemCount() {
if orderNo != nil {
// Call API
print("orderni::\(orderNo!)")
PoVendorListApiManager.sharedInstance.getPoListWithModel(orderString: orderNo!){ (json:JSON) in
// return json from API
if let categories = json["d"]["results"].dictionary {
print("catefory::\(self.categories)")
for (key, value) : (String, JSON) in categories {
self.dict[key] = value.object as AnyObject
}
print("dict:::\(self.dict)")
// for key in categories.keys {
// if let category =
categories[key]?.stringValue {
//
self.categories.updateValue(category, forKey: key)
// }
//
// }
}
print("catefory::\(self.categories)")
}
}
}
}//extension
here is my model:
import SwiftyJSON
struct poItems {
var key: String?
var value: String?
}
here is my static value i have passed to table view:
private var PoItems: [poItems]?
private var poVendorItemArray = [PoVendorModel]()
private func loadPoItems() -> [poItems] {
var tempItems = [poItems]()
let item1 = poItems.init(key: "Material#", value: "")
let item2 = poItems.init(key: "Quantity", value: "Bottles")
let item3 = poItems.init(key: "StorageLocation", value: "KP04")
let item4 = poItems.init(key: "PoDocNo", value: "KP Suppliers")
let item5 = poItems.init(key: "NetPrice", value: "1000")
return tempItems
}
how can i pass json reponse with key and value dynamically into tableview?
Any help much appreciates pls...
Please reread my answer in your earlier question (or read the JSON)
The value for results is an array
if let categories = json["d"]["results"].array {
print("category::\(self.categories)")
for category in categories {
for (key, value) in category {
print(key, value)
}
}
}
And – as suggested in many of your previous questions and in the comments – you are encouraged to drop SwiftyJSON and use Codable.

check if one array contains all values of another array (do all the users have jobs assigned?)

Total noob question, but I've done my best and can't figure out how to iterate over my array and determine if it contains all the values of another array.
Here is my array class (dailyJobs):
class JobsAndHabits {
var name: String
var multiplier: Double
var assigned: String
var order: Int
init(jobName: String, jobMultiplier: Double, jobAssign: String, jobOrder: Int) {
self.name = jobName
self.multiplier = jobMultiplier
self.assigned = jobAssign
self.order = jobOrder
}
And here is the class I want to check (users):
class UserClass {
var imageURL: String
var firstName: String
var birthday: Int
var passcode: Int
var gender: String
var childParent: String
init(userProfileImageURL: String, userFirstName: String, userBirthday: Int, userPasscode: Int, userGender: String, isUserChildOrParent: String) {
self.imageURL = userProfileImageURL
self.firstName = userFirstName
self.birthday = userBirthday
self.passcode = userPasscode
self.gender = userGender
self.childParent = isUserChildOrParent
}
Here is my JSON tree if that helps:
{
"-Kw2SkmAqo3G_cVFP26Y" : {
"assigned" : "Aiden",
"multiplier" : 1,
"name" : "table",
"order" : 2
},
"-Kw2SkmAqo3G_cVFP26Z" : {
"assigned" : "Mom",
"multiplier" : 1,
"name" : "dishes",
"order" : 3
},
"-Kw2SyhJNkjKNQ-sKBeT" : {
"assigned" : "Sophie",
"multiplier" : 1,
"name" : "floors",
"order" : 0
},
"-Kw2SyhJNkjKNQ-sKBeU" : {
"assigned" : "Savannah",
"multiplier" : 1,
"name" : "laundry",
"order" : 1
},
"-Kw7H3pY2L5PzLLdd6qD" : {
"assigned" : "Dad",
"multiplier" : 1,
"name" : "another daily test",
"order" : 4
},
"-Kw7HAWwAvv5ZjYuNOrg" : {
"assigned" : "Sophie",
"multiplier" : 1,
"name" : "daily job testsaroo!",
"order" : 5 }
And here is what I've tried:
Attempt #1:
var tempArray = [String]()
for user in self.users {
for job in self.dailyJobs {
if job.assigned == user.firstName {
print(user.firstName,"has a job",job.name)
tempArray.append(user.firstName)
}
}
}
for user in self.users {
if tempArray.contains(user.firstName) {
print("true")
} else {
print("false")
}
}
Attempt #2: (this didn't even compile)
for job in self.dailyJobs {
if self.users.contains(where: job) {
print(true)
}
}
Attempt #3: prints "Failed" repeatedly.
for job in self.dailyJobs {
for user in self.users {
if job.assigned != user.firstName {
print("FAILED")
}
}
}
How do I check if each of the users in my users array has at least one job assigned? In other words, how do I make sure that inside the dailyJobs array every user.firstName is represented at least once?
I'm thinking I have to do some sort of loop, but I'm not sure how to proceed. Can someone point me in the right direction? Right now I'm just going in circles (if you'll pardon the pun).
Attempt #1 is close. You can only say it's true after checking all users:
func areAllAssigned(users: [UserClass], tempArray: [String]) -> Bool {
for user in users {
if !tempArray.contains(user.firstName) {
return false
}
}
return true
}
In fact, you don't need the outer for loop:
for job in self.dailyJobs {
print(job.assigned, "has a job", job.name)
tempArray.append(job.assigned)
}
areAllAssigned(users: self.users, tempArray: tempArray)
Here's how to do it with sets (without making the classes Hashable).
if Set(users.map{firstName}).isSubset(of:dailyJobs.map{$0.assigned})
{
// all users are assigned to at least one job
}
else
{
let employed = Set(dailyJobs.map{$0.assigned})
let jobless = users.filter{ !employed.contains($0.firstName) }
}
Make the class to conform Hashable and use Set. You don't need that much code.
Updated
You don't need to use Hashable here. I misunderstood your question. I simplified the code but basically it has what you need.
struct JobsAndHabits {
let assigned: String
}
struct UserClass {
let firstName: String
}
let users = [UserClass(firstName: "person_a"),
UserClass(firstName: "person_b"),
UserClass(firstName: "person_c"),
UserClass(firstName: "person_d")]
let jobs = [JobsAndHabits(assigned: "person_a"),
JobsAndHabits(assigned: "person_c")]
let names = Set(users.map({ return $0.firstName }))
let assigned = Set(jobs.map({ return $0.assigned }))
print(names.subtracting(assigned)) // users who doesn't have jobs
After finding this answer, I got it to work.
My code:
func checkIfAllUsersHaveDailyJobs() -> Bool {
var tempArray = [String]()
for job in self.dailyJobs {
for user in self.users {
if job.assigned == user.firstName {
print(user.firstName,"has the job:",job.assigned)
tempArray.append(user.firstName)
}
}
}
print(tempArray.count)
for user in self.users {
if !tempArray.contains(user.firstName) {
return false
}
}
return true
}

Objectmapper get array of one item within JSON

So I have the following JSON, which I am using together with ObjectMapper and Realm.
{
"result": [
{
"id": 20,
"types": [
"now"
],
"url": "/nl/whereto/ezrhgerigerg",
"categories": [
{
"id": 39,
"name": "Food "
},
{
"id": 21,
"name": "Varia"
}
]
},
My problem is getting the data from "types", which for some items in the array says "now" or "later", and is empty for other items (hence, no types item is given).
I tried to do the following in my mapping:
class Publication: Object, Mappable {
dynamic var id:Int = 0
var typez = List<getType>()
dynamic var url:String?
required convenience init?(_ map: Map) {
self.init()
}
override static func primaryKey() -> String? {
return "id"
}
func mapping(map: Map) {
id <- map["id"]
typez <- map["types"]
url <- map["url"]
}
}
class getType: Object, Mappable {
dynamic var text: String = ""
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
text <- map[""]
}
}
When I check the Realm database, you can see that typez, an array of [getType] was made, but it's empty for all items (even the ones where types is "now"). The other two items (id and url) are filled in in the database.
What am I doing wrong that it won't save to the database?
Because Realm cannot detect assigning List properties since List property is not Objective-C type. So List properties should be declared as let, and should not be nil. You should use append/remove.../insert...method to modifying theList`.
So your code
typez <- map["types"]
doesn't work, since you assign values to the typez property directly.
The workaround is like the following:
func mapping(map: Map) {
...
var typez: [String]? = nil
typez <- map["types"]
typez?.forEach { t in
let obj = getType()
obj.text = t
self.typez.append(obj)
}
...
First, store the mapped value to the local variable (it is string array). Then convert the string array to objects. Then append the objects to the List property.

SwiftyJSON Append new data to existing JSON array

I am working on a way to add new JSON data to my existing JSON array:
var resources: JSON = [
"resources": []
]
override func viewDidLoad() {
super.viewDidLoad()
getApiResourceA() { responseObject, error in
var resourceA: JSON = [
"resourceA": []
]
let resourceAResponseObject = JSON(responseObject!)
resourceA["resourceA"] = resourceAResponseObject
self.resources["resources"] = resourceA
}
getApiResourceB() { responseObject, error in
var resourceB: JSON = [
"resourceB": []
]
let resourceBResponseObject = JSON(responseObject!)
resourceB["resourceB"] = resourceBResponseObject
self.resources["resources"] = resourceB
}
}
The structure I am trying to get is:
{
"resources": {
"resourceA": {
"id": 1
"name": "Name1"
}
"resourceB": {
"id": 2
"name": "Name2"
}
}
}
But in my code there are two different "resources"-array created...
Anyone know how to deal with this?
First it is important to understand that JSON is Struct means it is duplicated every time you pass it or use it.
Another issue, you declared resources as Array and not as Dictionary means you can use resource as key.
Declare extensions:
extension JSON{
mutating func appendIfArray(json:JSON){
if var arr = self.array{
arr.append(json)
self = JSON(arr);
}
}
mutating func appendIfDictionary(key:String,json:JSON){
if var dict = self.dictionary{
dict[key] = json;
self = JSON(dict);
}
}
}
use:
//notice the change [String:AnyObject]
var resources: JSON = [
"resources": [String:AnyObject](),
]
resources["resources"].appendIfDictionary("resourceA", json: JSON(["key1":"value1"]))
resources["resources"].appendIfDictionary("resourceB", json: JSON(["key2":"value2"]))
result:
{
"resources" : {
"resourceB" : {
"key2" : "value2"
},
"resourceA" : {
"key1" : "value1"
}
}
}
#daniel-krom has right, but is a little confusing implement the extension, so, we need only add at the end of the Swift controller (or class) that code that adds the "append" methods, nothing else.
Using the appendIfArray method, I could pass from this
[
{
"id_usuario" : 2
}
]
...to this
[
{
"id_usuario" : 2
},
{
"id_usuario" : 111
},
{
"id_usuario" : 112
},
{
"id_usuario" : 113
}
]
The complete code is below:
do{
try json2!["usuarios"][indice]["fotos"][0]["me_gusta"].appendIfArray(json: JSON( ["id_usuario": 111] ))
try json2!["usuarios"][indice]["fotos"][0]["me_gusta"].appendIfArray(json: JSON( ["id_usuario": 112] ))
try json2!["usuarios"][indice]["fotos"][0]["me_gusta"].appendIfArray(json: JSON( ["id_usuario": 113] ))
}catch {
print("Error")
}
The complete JSON structure can find in
http://jsoneditoronline.org/?id=56988c404dcd3c8b3065a583f9a41bba
I hope this can be useful

Resources