Array in object from data model comes nil - ios

I have a model class called Coupon and it has an array of objects called Array!
When I create an object from from Coupon class the array inside of this object comes nil and it gets an error. What am I doing wrong?
class Coupon {
private var _date: String!
private var _editor: String!
private var _predictions: Array<Prediction>?
var date: String {
get {
return _date
}
}
var editor: String {
get {
return _editor
}
}
var predictions: Array<Prediction>? {
get {
return _predictions
}
set {
self._predictions = predictions
}
}
}
And the controller is as follows: c.predictions![0] gives nil error
let ref = DataService.ds.REF_COUPONS.queryOrdered(byChild: "date")
ref.observe(.childAdded, with: { (snapshot) in
if let couponDict = snapshot.value as? Dictionary<String, AnyObject> {
let c_key = snapshot.key
let c = Coupon(couponKey: c_key, couponData: couponDict)
let childSnapShot = snapshot.childSnapshot(forPath: "predictions")
if let snapshots = childSnapShot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots{
let p_key = snap.key
let p = Prediction(predictionKey: p_key, predictionData: snap.value as! Dictionary<String, AnyObject>)
self.predictions.append(p)
c.predictions![0] = self.predictions[0] <--- ERROR LINE
}
}
self.coupons.append(c)
}
self.couponsTableView.reloadData()
})

Because it's value is nil by default. So predictions! will result in trying to unwrap an optional whose value is nil kind of error.
You should create new array there:
c.predictions = [self.predictions[0]]
Also there is no need for backing fields in swift. Your Coupon class can be reduced to:
class Coupon {
private(set) var date: String!
private(set) var editor: String!
var predictions: Array<Prediction>?
}
private(set) means that the value can only be set within this class.
The definition line of predictions var predictions: Array<Prediction>? can be replaced to have default value of empty array:
class Coupon {
private(set) var date: String!
private(set) var editor: String!
var predictions: [Prediction] = []
}
[Prediction] is same as Array<Prediction>
Once it has default value of empty array instead of creating new array you can safely append to it:
c.predictions.append(self.predictions[0])
Also note that your code c.predictions[0] = self.predictions[0] would work never even in this case because the array is defaulted to empty has not 0th element.

When you create the class Coupon with let c = Coupon(couponKey: c_key, couponData: couponDict) your array is not initialize, so that's normal to have an error when you are trying to use it in the line c.predictions![0] = self.predictions[0] <--- ERROR LINE, your object Coupon you could initialize the array predictions as an empty array like this
class Coupon {
private var _date: String!
private var _editor: String!
private var _predictions = [Prediction]()

Element in predictions array at index 0 doesn't exist, so you can't replace it with new element. I suggest you to replace _predictions array with empty predictions array
var predictions = [Prediction]()
then you can append new element to this array like this:
c.predictions.append(self.predictions[0])
Also in your case there is no need to use property with getter and setter and you can also use struct instead of class
struct Coupon {
var date: String
var editor: String
var predictions = [Prediction]()
// your convenience init
}

Related

Store array globally in struct or other method

I have an array of struct elements that I would like to store globally so that I can access it in different classes without having to run the query that populates it over and over.
I have a struct:
struct collectionStruct {
var name : String
var description : String
var title : String
var image : PFFile
var id: String
}
and a variable:
var collectionArray = [collectionStruct]()
and some code to build the array:
for object in items {
let arrayName = object.object(forKey: fromName) as! String
let arrayDescription = object.object(forKey: fromDescription) as! String
let arrayTitle = object.object(forKey: fromTitle) as! String
let arrayImage = object.object(forKey: fromImage) as! PFFile
let arrayID = object.objectId as String!
collectionArray.append(collectionStruct(name: arrayName,
description: arrayDescription,
title: arrayTitle,
image: arrayImage,
id: arrayID!))
}
I was thinking of creating another struct to hold the array itself bt am a bit lost here. this is what I was thinking:
struct globalArray {
var collectionArray = [collectionStruct]()
}
but a probably way off
You should consider naming your struct CollectionStruct rather than collectionStruct - as that indicates it is a type. To access the array anywhere you could create a singleton. With a singleton, you ensure there is only one instance available by giving it a private constructor. Here is an example:
class Global {
private init() { }
static let sharedInstance = Global()
var collection = [CollectionStruct]()
}
To use it you would use the following:
Global.sharedInstance.collection
You can use singleton for global class that's able to be accessing from anywhere:
class GlobalArray {
static let shared = GlobalArray()
var collectionArray = [collectionStruct]()
}
and accessing like this to assign or read value:
GlobalArray.shared.collectionArray
You can just declare
var collectionArray = [collectionStruct]()
at the top level in any file (outside any object), and it will be available globally
e.g.
var collectionArray = [collectionStruct]()
class MyClass {
func printArray() {
print(collectionArray)
}
}

Comparing the datatype of an inner class attributes to its corresponding datatype

I just want to know the attributes datatype of a class has inner class objects while iterating.
Find the code mentioned below.
class myClass1: NSObject {
var name:String?
var id:Int32?
}
class myClass2:NSObject {
var sessionId:String?
var classObj:[myClass1]?
var item:Int?
}
let mirroredObject = Mirror(reflecting: myClass2())
var dictionary = [String:Any]()
for(index,attr) in mirroredObject.children.enumerated() {
if let property_name = attr.label {
let submirroredObj = Mirror(reflecting: property_name)
dictionary["\(property_name)"] = type(of: (attr.value)) as Any?
}
}
for (index,item) in dictionary.enumerated() {
print(item.value)
}
In the above code it will display the list of attributes of a classObject. here I don't know how to compare the listed attributes of a class.
Use this to get the type of the property:
attr.value.dynamicType

Create an Array of Object from an Array of Dictionaries with Swift

I'm receiving a JSON dictionary from a web service and I need to map the return values to existing values. Here's essentially what I'm trying to do:
class Contract {
var contractID: String?
var ebState: String?
var ibState: String?
var importerState: String?
var exportersBankRefNo: String?
var importersBankRefNo: String?
}
let contract1 = Contract()
contract1.contractID = "001"
let contract2 = Contract()
contract2.contractID = "002"
// This is the JSON return dictionary
let exportAppnStatusList: [[String: String]] = [["contractID":"001",
"ExporterBankRefNo":"ExporterBankRefNo001",
"ExporterBankState":"ACCEPTED",
"ImporterBankRefNo":"",
"ImporterBankState":"UNKNOWN",
"ImporterState":"UNKNOWN" ],
["contractID":"002",
"ExporterBankRefNo":"ExporterBankRefNo002",
"ExporterBankState":"ACCEPTED",
"ImporterBankRefNo":"ImporterBankRefNo002",
"ImporterBankState":"ACCEPTED",
"ImporterState":"UNKNOWN" ]]
I need to take the exportAppnStatusList and fill in the associated values in the existing contract1 and contract2, mapping by the contractID
This fills the contracts with available information, it ignores contracts where the id could not be found:
for contract in [contract1, contract2] {
if let contractDict = exportAppnStatusList.filter({$0["contractID"] == contract.contractID}).first {
contract.exportersBankRefNo = contractDict["ExporterBankRefNo"]
contract.ebState = contractDict["ExporterBankState"]
contract.importersBankRefNo = contractDict["ImporterBankRefNo"]
contract.ibState = contractDict["ImporterBankState"]
contract.importerState = contractDict["ImporterState"]
}
}
Why not generate the contract object by mapping over the array of dictionaries like this? You'll need to write a custom initializer that takes all these params
exportAppnStatusList.map { (dict:[Stirng:String]) -> Contract in
return Contract(contractID:dict["contractID"],
ebState:dict["ExporterBankState"],
ibState:dict["ImporterBankState"],
importerState:dict["ImporterState"],
exportersBankRefNo:dict["ExporterBankRefNo"],
importersBankRefNo:dict["ImporterBankRefNo"]
}
Try using this init (your class must inherit from NSObject):
init(jsonDict: [String: String]) {
super.init()
for (key, value) in jsonDict {
if class_respondsToSelector(Contract.self, NSSelectorFromString(key)) {
setValue(value, forKey: key)
}
}
}
Then you can do this:
exportAppnStatusList.forEach {
print(Contract(jsonDict: $0))
}

Create dictionary from an array of objects using property of object as key for the dictionary?

With Swift is it possible to create a dictionary of [String:[Object]] from an array of objects [Object] using a property of those objects as the String key for the dictionary using swift's "map"?
class Contact:NSObject {
var id:String = ""
var name:String = ""
var phone:String = ""
init(id:String, name:String, phone:String){
self.id = id
self.name = name
self.phone = phone
}
}
var contactsArray:[Contact]
var contactsDict:[String:Contact]
contactsDict = (contactsArray as Array).map { ...WHAT GOES HERE... }
Let's say you want to use id as the key for the dictionary:
var contactsArray = [Contact]()
// add to contactsArray
var contactsDict = [String: Contact]()
contactsArray.forEach {
contactsDict[$0.id] = $0
}
The difference between map and forEach is that map returns an array. forEach doesn't return anything.
You can achieve this via reduce in a one-line functional-style code:
let contactsDict = contactsArray.reduce([String:Contact]()) { var d = $0; d[$1.id] = $1; return d; }
This also keeps contactsDict immutable, which is the preferred way to handle variables in Swift.
Or, if you want to get fancy, you can overload the + operator for dictionaries, and make use of that:
func +<K,V>(lhs: [K:V], rhs: Contact) -> [K:V] {
var result = lhs
result[rhs.0] = rhs.1
return result
}
let contactsDict = contacts.reduce([String:Contact]()) { $0 + ($1.id, $1) }
Swift 4
There's now a direct way to do this:
https://developer.apple.com/documentation/swift/dictionary/3127163-init
It's an initializer for Dictionary that lets you return a string key for each element in a Collection that specifies how it should be grouped in the resulting Dictionary.

Filter NSMutableArray data by value into object in Swift

I have this problem
I'm using Swift 2.0 in my project for iOS 9. I've created an object like this:
public class element_x: NSObject{
var name: String!
var description_element: String!
}
So, In a method I declare two NSMutableArray: 1) for all data and 2) for filter data, like this:
var original = NSMutableArray()
var filtered = NSMutableArray()
And during the process I populate this NSMutableArray like this:
let custom_object = element_x()
self.original.addObject(custom_object);
My question is: how can I filter original array by name value and saved in filtered array?
You don't have to use NSMutableArray. The native Array type in Swift is very capable. You can declare it mutable with var (equivalent to NSMutableArray) or constant with let (same as NSArray):
public class element_x: NSObject{
var name: String!
var description_element: String!
}
// Declare an array containing elements of element_x type
var original = [element_x]()
var filtered = [elememt_x]()
let custom_object = element_x()
self.original.append(custom_object)
// Find all elements with name == "david"
self.filtered = self.original.filter { $0.name == "david" }

Resources