I have the following struct where I use string, Int, and Bool. I fill these variable when I query my products from Firestore. Now I do not know how to treat an array here in my struct:
struct Product {
var price: Int
var name: String
var isActive: Bool
//var categories: how do I call out the array here?
init(
price: Int,
name: String,
isActive: Bool,
//categories: how do I call out the array here?
){
self.price = price
self.name = name
self.isActive = isActive
//self.categories: how do I call out the array here?
}
init(data: [String: Any]){
price = data[DatabaseRef.price] as? Int ?? 0
name = data[DatabaseRef.name] as? String ?? ""
isActive = data[DatabaseRef.isActive] as? Bool ?? false
//categories: how do I call out the array here?
}
static func modelToData(product: Product) -> [String: Any] {
let data : [String: Any] = [
DatabaseRef.price : product.price,
DatabaseRef.name : product.name,
DatabaseRef.isActive : product.isActive,
//categories: how do I call out the array here?
]
return data
}
}
When I query my categories from my database; it would look like this:
categories = ["Fruits", "Vegetables", "Frozen"]
Not sure how I can call the categories out in each part of the struct I mentioned up here. Newbie alert!
You can declare an array like this.
var categories: [String] = []
Related
I am trying to create a generic array with different models. I have a parser method like that. But it doesn't work because it returns [Any] and it's not typesafe. I need to access my Movie and CastMember objects after parse method. I will use this array in my tableviewcontroller delegate methods. How can I do that?
static func parseSearchResult(_ data:Dictionary<String, AnyObject>) -> [Any] {
var array = [Any]()
let jsonData = JSON(data)
if let resultData = jsonData["results"].arrayObject {
let result = resultData as! [[String:AnyObject]]
for element in result {
if((element["media_type"]?.isEqual("person"))!){
let person = CastMember(json: element)
array.append(person)
}
else if((element["media_type"]?.isEqual("movie"))!){
let movie = Movie(json: element)
array.append(movie)
}
}
}
return array
}
and these are my structs
struct CastMember{
var id : Int?
var originalName : String?
var castName : String?
var picturePath : String?
init(json: [String:Any]){
originalName = json["name"] as? String
id = json["id"] as? Int
castName = json["character"] as? String
picturePath = "https://image.tmdb.org/t/p/w200/"
picturePath?.append((json["profile_path"] as? String) ?? "")
}
}
struct Movie{
var id : Int?
var title : String?
var imagePath : String?
init(json: [String:Any]){
title = json["title"] as? String
id = json["id"] as? Int
imagePath = "https://image.tmdb.org/t/p/w200/"
imagePath?.append((json["poster_path"] as? String)!)
}
}
Make your Movie and CastMember classes confirm to the protocol Codable.
also you will have to write a struct or class which matches the response data , like it must have an array of results and any other key coming in response.
struct ResponseModel<T> : Codable {
let results : [T]
}
then decode it like this :
let response : ResponseModel = JSONDecoder.decode(T.self)
You should make a protocol
Example:
enum MediaType {
case movie, castMember
}
protocol SearchResult {
var title: String { get }
var mediaType: MediaType { get }
}
struct SearchResultViewModel: SearchResult {
let title: String
let mediaType: MediaType
init(title: String, mediaType: MediaType) {
self.title = title
self.mediaType = mediaType
}
}
Then your parseSearchResult should return an array of [SearchResult] objects that conforms to the protocol, in this case, an array of SearchResultViewModel
I can't find online how to store an array of objects so that the key "line_items" presents numbers for each menuItem with values for each menuItem corresponding to its own number. In other words, I need the numbers to come after line_items rather than the nested key so that each individual MenuItem object can be quickly referenced. I found online how to make it so each key has an array of values, but I need line_items to have an array of MenuItem objects. The following code crashes:
public func uploadTransactionData(_ menuItems: [MenuItem], balanceId: String, subTotal: Int, completion: #escaping (() -> ())) {
guard let userId = Auth.auth().currentUser?.uid else { completion(); return }
let utilitiesManager = UtilitiesManager()
let timestamp = utilitiesManager.timestamp()
let params: [String: Any] = ["date": "\(timestamp)",
"balance_id": "\(balanceId)",
"subtotal": "\(subTotal)",
"user_id": "\(userId)",
"line_items": menuItems
]
Firestore.firestore().document("transaction_history/\(timestamp)").setData(params)
{ err in
if let e = err {
print("$-- error creating user \(e)")
completion()
} else {
completion()
}
}
}
Here's the MenuItem model:
struct MenuItem {
let itemId: String
let name: String
var modifiers: [String]?
var photoName: String?
var photoUrl: String?
var quantity: Int
var price: Int
var sizeAddOnPrice: Int
var toppingsAddOnPrice: Int
let description: String
var size: String
let category: String
init(itemId: String, name: String, modifiers: [String]?, photoName: String?, photoUrl: String?, quantity: Int, price: Int, sizeAddOnPrice: Int, toppingsAddOnPrice: Int, description: String, size: String, category: String) {
self.itemId = itemId
self.name = name
self.modifiers = modifiers
self.photoName = photoName
self.photoUrl = photoUrl
self.quantity = quantity
self.price = price
self.sizeAddOnPrice = sizeAddOnPrice
self.toppingsAddOnPrice = toppingsAddOnPrice
self.description = description
self.size = size
self.category = category
}
Problem:
Your app is crashing because you are trying to save user defined object MenuItem to Firestore. Firestore doesn't allow it. Firestore only supports this datatypes.
Solution:
You can convert your custom object MenuItem to Firestore supported datatypes.
You can do this by making following changes to your code.
Make MenuItem confirm to Codable protocol.
struct MenuItem: Codable {
// Your code as it is.
}
Make following changes to your uploadTransactionData() function:
public func uploadTransactionData(_ menuItems: [MenuItem], balanceId: String, subTotal: Int, completion: #escaping (() -> ())) {
let userId = Auth.auth().currentUser?.uid else { completion(); return }
let utilitiesManager = UtilitiesManager()
let timestamp = utilitiesManager.timestamp()
var list_menuItem = [Any]()
for item in menuItems {
do {
let jsonData = try JSONEncoder().encode(item)
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
list_menuItem.append(jsonObject)
}
catch {
// handle error
}
}
let params: [String: Any] = ["date": "\(timestamp)",
"balance_id": "\(balanceId)",
"subtotal": "\(subTotal)",
"user_id": "\(userId)",
"line_items": list_menuItem
]
Firestore.firestore().document("transaction_history/\(timestamp)").setData(params)
{ err in
if let e = err {
print("$-- error creating user \(e)")
completion()
} else {
completion()
}
}
}
This is because Firestore doesn't know how to save the value: menuItems
you can map it like this: "objectExample": [
"a": 5,
"b": [
"nested": "foo"
]
]
or:
.setData([
"name": "Frank",
"favorites": [ "food": "Pizza", "color": "Blue", "subject": "recess" ],
"age": 12
])
How can make model class for this json data
{
total: 41,
totalPages: 4,
valueData: [
{
id: "23",
lastLogin: "0-Jul-2011 11:27:36 AM",
name: "varii"
},
{
id: "24",
lastLogin: "0-Jul-2015 11:27:36 AM",
name: "sarii"
},
{
id: "25",
lastLogin: "0-Jul-2018 11:27:36 AM",
name: "narii"
} ]
}
class OnResponse {
var total: Int?
var totalPages: Int?
init(response: [String: Any]) {
self.total = response["total"]
self.totalPages = response["totalPages"]
}
}
It's not working how can make it ready for work
and how to pass values to controller to model and how to get value from model
Follow the below class structure
class Response {
var total: Int?
var totalPages: Int?
var valueData: [LoginData]?
init(response: [String: Any]) {
self.total = response["total"]
self.totalPages = response["totalPages"]
var items:[LoginData] = ()
for (data in response["valueData"]) {
let login = LoginData(name: data["name"], lastLogin: data["lastLogin"])
items.append(login)
}
self.valueData = items
}
}
class LoginData {
var name: String?
var lastLogin: String?
init(name: String, lastLogin: String) {
self.name = name
self.lastLogin = lastLogin
}
}
you should use "reverseMatches" to retrieve the array, not the "data". May be you can use a third library to convert your json data to a model, such as Unbox, https://github.com/JohnSundell/Unbox.
Model for Your response :
struct ModelName {
var total: Int?
var totalPage: Int?
var reverseMatches: [LoginDetails]?
}
struct LoginDetails {
var id: String?
var lastLogin: String?
var name: String?
}
Parse the api response and assign the values on the appropriate fields. I have made, all the variables are optional.
Assign values like below.
var obj = Model()
obj.total = response["total"] as? Int
obj should be var, because you are going to mutate the struct values. because struct is value based, not reference based
class DataModel{
var total : Int?
var totalPages : Int?
var valueData : [UserModel]?
init(JSON: [String:Any?]){
self = parser.doParse(JSON: JSON)
}
}
class UserModel{
var id : String?
var lastLogin : String?
var name : String?
}
class parser : NSObject{
class func doParse(JSON: [String:Any?])->DataModel{
let dataModel = DataModel()
dataModel.total = JSON["total"] as? Int
dataModel.totalPages = JSON["totalPages"] as? Int
var userInfo : [UserModel] = []
let valueData : [String:String?]? = JSON["valueData"] as? [String:String?]
if let valueData = valueData{
for dataDict : [String:String?] in valueData{
let itemModel = UserModel()
itemModel.id = dataDict["id"] as? String
itemModel.lastLogin = dataDict["lastLogin"] as? String
itemModel.name = dataDict["name"] as? String
userInfo.append(itemModel)
}
dataModel.valueData = userInfo
}
return dataModel
}
}
class LoginData: NSObject {
let total: Int = 0
let totalPages: Int = 0
let valueData: Array<ValueData> = Array<ValueData>()
override init(total: Int!, totalPages: Int, valueData: Array<ValueData>!) {
self.total = total
self.totalPages = totalPages
self.valueData = valueData
}
}
struct ValueData {
let id: int?
let lastLogin: String?
let name: String?
init(id: int!, lastLogin: string, name: string!)
self.id = id ?? 0
self.lastLogin = lastLogin ?? ""
self.name = name ?? ""
}
}
you should use struct instead of class for creating model object...
advantages of struct over class refer
Why Choose Struct Over Class?
class/24232845
use two struct for holding your data one is for your single total count
and other is for last login detail
struct lastLogin {
let totalCount: (total: Int, totalPages: Int)
let valueArray: [lastLoginDetail]
}
struct lastLoginDetail {
let valueData: (id: String, lastLogin: String,name: String)
}
extension lastLoginDetail {
init(json: [String : String]) throws {
let id = json["id"]
let lastLogin = json["lastLogin"]
let name = json["name"]
let value = (id,lastLogin,name)
self.valueData = value as! (id: String, lastLogin: String, name: String)
}
}
extension lastLogin {
init(json: [String : Any]) throws {
let total = (json["total"] as! NSNumber).intValue
let totalPages = (json["totalPages"] as! NSNumber).intValue
let totalCounts = (total,totalPages)
var userInfo : [lastLoginDetail] = []
// Extract and validate valueData
let valueData = json["valueData"] as? NSArray
if let valueData = valueData{
for dataDict in valueData{
let dic : [String : String] = dataDict as! [String : String]
let lastLoginDeatails = try! lastLoginDetail(json: dic)
userInfo.append(lastLoginDeatails)
}
}
self.valueArray = userInfo
self.totalCount = totalCounts
}
}
func HowToUseModelClass(){
let jsonDic = NSDictionary()
// jsonDic // your json value
let dataValue = try! lastLogin(json: (jsonDic as! [String : Any])) // parsing the data
print(dataValue.totalCount.total)
print(dataValue.totalCount.totalPages)
print(dataValue.valueArray[0].valueData.id)
}
I have a struct like this:
struct list
{
var userId: Int = 0
var id: Int = 0
var title: String = ""
var body: String = ""
}
In another part of my program, I declare an array of struct and I enter information through a JSON files downloaded from the internet.
var array = [list]()
Now my problem is to save and recuperar this array of struct in CoreData. I do not know how to go forward, would you give me a hand?
I think you best choice is to go with Binary Data type.
1.Create a toDictionary() method on your struct model:
func toDictionary() -> [String: Any] {
return ["userId: self.userId, "id": self.id, ...]
}
2.Then add a NSData property on your core data model, which represents your struct list:
#NSManaged var list: NSData?
3.Access that list property with a class helper:
func listDictionary() -> [String: Any]? {
guard let listData = self.list as? Data else { return nil }
let dict = NSKeyedUnarchiver.unarchiveObject(with: listData) as? [String: Any]
return dict
}
4.Create a fromDictionary() -> list on your list object which will build a model from dictionary
Data Model
class DataCart {
var icon: UIImage?
var cartId: String
var price: Int
var productName: String
var quantity: Int
init(icon: UIImage?, cartId: String, price: Int, productName: String, quantity: Int){
self.icon = icon
self.cartId = cartId
self.price = price
self.productName = productName
self.quantity = quantity
}
}
Dictionary
var cart = [DataCart]()
"products": [[
"productName": "Photobook", //"dataCart.productName"
"quantity": 2, //"dataCart.quantity"
"price": "5000.00", //"dataCart.price"
"pages": 40
],[
"productName": "Photobook2",
"quantity": 5,
"price": "7000.00",
"pages": 30
]]
for dataCart in cart {
........
}
I've array from data model that i'd like to show it as dictionary, and i got confused to change it. How to convert cart's array (DataCart) to dictionary?
var allDictionaries : [[String : AnyObject]]//array of all dictionaries.
func convertArrayToDictionaries([DataCart]) {
for data in DataCart {
let dictionary = [
"icon" : data.icon,
"cartId" : data.cartId,
"price" : data.price,
"productName" : data.productName,
"quantity" : data.quantity
]
allDictionaries.append(dictionary)
}
}
It's good practice to make conversion at class level.
So in future if you have more keys, you just need to change at class level only instead of changing in all the places where you have used the same.
Add one calculated property dictionayDataCart in class as below.
Also add one method "convertDataCartArrayToProductsDictionary:" which will convert your array of objects to dictionary.
class DataCart {
var icon: UIImage?
var cartId: String
var price: Int
var productName: String
var quantity: Int
init(icon: UIImage?, cartId: String, price: Int, productName: String, quantity: Int){
self.icon = icon
self.cartId = cartId
self.price = price
self.productName = productName
self.quantity = quantity
}
var dictionaryDataCart : [String : AnyObject] {
var objDict : [String : AnyObject]!
if let icon = self.icon {
objDict["icon"] = icon;
}
objDict["cartId"] = self.cartId;
objDict["price"] = price;
objDict["productName"] = productName;
objDict["quantity"] = quantity;
return objDict;
}
class func convertDataCartArrayToProductsDictionary(arrayDataCart : [DataCart]) -> Dictionary<String,AnyObject> {
var arrayDataCartDictionaries : Array<Dictionary<String,AnyObject>> = Array()
for objDataCart in arrayDataCart {
arrayDataCartDictionaries.append(objDataCart.dictionary);
}
return ["products" : arrayDataCartDictionaries];
}
}
Use method as below.
let arrayDataCarts : [DataCart] = //value of your array
//pass it to class function and you will get your result
let productsDict = let productsDict = DataCart.convertDataCartArrayToProductsDictionary(arrayDataCarts);