Swift 3 build array (Don't know how to describe) - ios

First of all, sorry for the unclear title, but I don't know how to describe my problem or how to search for it. (Still a beginner)
So I have an array where I need to put values in.
let heliosDataArray:String = "[{\"timestamp\":\"\(timestamp)\",\"uv\":\"\(uvIndex!)\",\"light\":\"\(lightvalue!)\"}]"
So in this "template" I need to add 3 values: timestamp, uvIndex and lightValue. So far so good. Now I have a lot of values and for an API call I need to to chain this array multiple times in to 1 array, kind of like a master array. What is the most practical way of doing this? The amount of data is variable, and comes from CoreData. Probably going to put the values first in arrays. What should I be searching for? I was thinking a loop but like more advanced?
Thanks in advance

You can approach with object-oriented logic:
struct Data {
var timestamp: Double?
var lightValue: Double?
var uvIndex: Int?
}
let data1 = Data(timestamp: 13.4, lightValue: 3.4, uvIndex: 4)
let data2 = Data(timestamp: 12.4, lightValue: 2.4, uvIndex: 3)
let data3 = Data(timestamp: 11.4, lightValue: 1.4, uvIndex: 2)
var dataArray = Array<Data>() // or-> var data = [Data]()
dataArray.append(data1)
dataArray.append(data2)
dataArray.append(data3)

You can do this in many ways. One of them is shown below
let say you have three values from coredata timestamp, uvIndex and lightvalue
Seems what you are asking is a array of dictionaries, first you need is a dictionary of the values you got from CoreData, let call them dayItem
var dayItem = ["timestamp": timestamp,
"uv": uvIndex,
"light": lightValue]
Now create an array of values
var dayArray: [[String: Any]] = []
dayArray.append(dayItem)
Every time you want to append new items just use the append method of array

Related

iOS architecture: Object with many optionals

This is a fairly specific situation, so I'll try to explain as many details as possible.
I'm making an app that should fetch a list of reservations, where it's possible to either add new, or tap an existing reservation and have a "detailed" view about the reservation where the reservation details are editable, and then have an option to save it.
REST APIs have been done in C#, and there's no documentation on what can and can't be null (nil, in Swift case). So I'm ending up with:
struct Reservation: Codable {
var objectID: String?
var objectName: String?
var objectPrefix: String?
var reservationNumber: String?
var grownUPS: Int?
var teens: Int?
var children: Int?
var babies: Int?
var reservationDate: String?
var dateInserted: String?
var toDate:String?
var fromDate: String?
var price: Int?
var owner: String?
var note: String?
var agencyName: String?
var renterNote: String?
var reservationID: String?
// 20 more properties
init(objectID: String? = nil,
partnerID: String? = nil,
objectName: String? = nil,
// 20 more properties
)
{
self.objectID = objectID
self.objectName = objectName
// 20 more properties
}
So when I tap on an object, I pass a Reservation object, check every field, if not nil then set to TextField. On clicking save I update model from all TextFields, DatePickers, etc, and then do a network post or put request depending on whether it's a new reservation or editing existing.
If I tap on add, I pass an empty Reservation object, so all fields on "details" page are empty, and do a validation when clicking Save button.
It works so far, but all around it looks "Anti-Swift". Lot of optionals, lot of guards/unwrapping, tight coupling between "master" and "details" view, setting data retrieved from network in a closure (actual Alamofire call is hidden, but I'm not sure what will be nil, so I have to set each property to it's TextField with a nil-check/chaining).
Any arhitecture tips on how to improve this?
All tutorials on this make a simple, local, non-optional approach that makes everything look shiny.
Keep in mind I've no documentation what is allowed to be null (data previously entered via web, or internal desktop app).
One thing that I can think of from the top of my head would be to remove the optionality of some properties by defining default values eg var babies: Int = 0 or if you're using Swift's decodable you can do something like this
babies = (try? container.decode(Int.self, forKey: .babies)) ?? 0
so you don't have to make your babies variable an optional
edit based on comment: the ?? aka coalescing nil operator will try to unwrap the optional value on the left and if it is nil, it will return the value on the right which in this case is 0
I don't think you should be bothered by optionals and optionals unwrapping.
One of the powers of optionals is, that anybody who works with your code, knows, that this thing may take nil as its value.
Unwrapping logic, either you use guards , nil coalescing or any other unwrapping technique describes your business logic. The fact, that you have "big" model is, IMO, just a fact that should be accepted. It's ok until your code stays reliable, readable, testable and understandable, doesn't cause unnecessary side effects and so on.
You might "fix" this problem by adding another level of abstraction over unwrapping or so. But, IMO, it should be done very carefully and only for the case of real benefits.
SwiftyJson solves exactly what you are facing. Its great in handling optional chaining and unwrapping of a great no of objects very efficiently and in a very Swifty way.
If some type conversion fails it doesn't break but gives an empty value so that your app works without you checking every single variable.
Here is basic conversion example provided. For details please go through their documentation.
// Getting a double from a JSON Array
let name = json[0].double
// Getting an array of string from a JSON Array
let arrayNames = json["users"].arrayValue.map({$0["name"].stringValue})
// Getting a string from a JSON Dictionary
let name = json["name"].stringValue
// Getting a string using a path to the element
let path: [JSONSubscriptType] = [1,"list",2,"name"]
let name = json[path].string
// Just the same
let name = json[1]["list"][2]["name"].string
// Alternatively
let name = json[1,"list",2,"name"].string

Why <Element> is not needed when creating default value array in Swift?

The code below is excerpted from The Swift Programming Language (Swift 4.2) of Apple documentation.
Now consider this: there are 3 ways to create an empty array in Swift.
First:
var list1 = Array<String>()
Second:
var list2: [String] = []
Third:
var list3 = [String]()
In order to create a default value array, we make use of repeating: and count:
var list4 = Array(repeating: "Apples", count: 3)
The above array is created using the first form of array creation, however, why it is not needed to declare the <Element> next to Array initializer.
Trying to create a normal empty array without specifying which type inside the <Element> would trigger a runtime error.
var list5 = Array("Apples") //A,p,p,l,e,s
var list6 = Array("Apples", "Oranges") //return an error
Why this happens?
Because it knows the type of the first item. Since Arrays only contain objects of a single type, of it can get that information from the first item, then it is enough.
Following your comment:
I think you are getting confused with the first item. "Oranges" is an array of [Character] so when you use Array("Oranges") you are creating an array of characters. For your third item, rather than passing in two values, which an Array doesn't know how to deal with, pass in an array like this: let list6 = Array(["Apples", "Oranges"])

How to Make a Struct Version of the NSCountedSet Class?

I am developing an app in which I need to take two arrays, both holding the same custom data type, MenuItem.
I need to compare them and find the common elements of both arrays.
I compare the two arrays by converting them both to sets and then using the compare function of the set struct.
However, I then need to convert the array of common elements to a Counted Set so that I can find how many of each element exist in the group of common elements.
The problem I am having is with converting the the array of common elements to a Counted Set. In Swift, there is an NSCountedSet class but no CountedSet struct like there are for most built-in swift Data Types:
let groups = Array(self.menu.keys)
//menu and cart are dictionaries the structure [MenuGroup: [MenuItem]]
let cartGroups = Array(self.cart.keys)
let group = groups[indexPath.section]
if cartGroups.contains(group) {
let items = menu[group] as [MenuItem]!
let cartItems = cart[group]
let itemsSet = Set<MenuItem>(items!)
let cartItemsSet = Set<MenuItem>(cartItems!)
let similarSet = itemsSet.intersection(cartItemsSet)
let similarArray = NSMutableArray(Array(similarSet))
let similarCounterSet = NSCountedSet(array: similarArray)
}
I want to create a struct based off the NSCountedSet class so that I can convert the array of common elements to a counted set.
The if statement is for something else, it does not pertain to this question.
Any thoughts on how to do this? If anybody has a different approach to this problem feel free to mention that too.
Thanks,
Simon
Thanks to Martin R I Found An Example of How to do This:
There are various CountedSet implementation available, e.g. https://github.com/0x7fffffff/CountedSet.

Swift automatically creating temporary dictionaries

I'm working with a datasource for a UITableView where I need an array with dictionaries.
I tried making an array, and filling it with dictionaries in a for loop like this.
temporaryDataDict = [:]
temporaryDataDict = ["name":stockName, "ticker":ticker, "lastPrice":lastPrice, "purchasePrice":purchasePrice, "weight":weight, "daysHeld":daysHeld]
temporaryDataArray.append(temporaryDataDict)
But of course, when I start filling my tableView with the dataSource. I end up for 23 of the exact same dictionaries (the last one in the for loop).
This is of course, because it's changing the dictionary every time.
I havn't been able to find a way to keep the data in the dictionaries, or programatically make a new dictionary every time (since they need a new name otherwise it'll overwrite the data).
So how can I programatically make dictionaries everytime a for loop runs, then get the keys & values of these dictionaries?
or am I going about this completely wrong?
If it helps, here's the kind of data I'm working with.
I have a stock (or item) with 6 properties. So I think it makes the most sense to have an array where every item in the array is the "stock" as a dictionary that contains the 6 properties.
So is it possible to make swift automatically create these dictionaries for me? Since I don't know the amount of dictionaries needed.
PS.
I know this is what CoreData is for. I'm using these arrays and Dictionaries to later fill in my CoreData.
If the above isn't possible I am aware that I can probably create a new CoreData entity to accomplish what I want, but it doesn't seem like the best way to go about it.
Any help would be greatly appreciated! Thanks
I don't believe that a dictionary is the best sort of data structure to use in this case. Since typically a dictionary is composed of a unique key and a value.
I have edited this answer. Originally I suggested creating a class containing a property for each record field. Then I figured that this could be done using a tuple via a typealias for each record. Using a typealias gets around an issue related to creating arrays of tuples.
ETA: However please read the comments because Zaph who knows more about this than me reckons that using a Class is a stronger solution.
This is my tuple based solution. Most of what follows is just about quickly creating some dummy data:
typealias StockRecord = (stockName:String, ticker: String, lastPrice: Double, purchasePrice: Double, weight: Double, daysHeld: Int)
var temporaryDataArray = [StockRecord]()
// use a loop to create dummy records and add each to the array
// rough and ready - just to test the solution
var loopCounter: Int
for loopCounter = 0; loopCounter <= 23; loopCounter++ {
//some dummy field values for each record
var stockName = "stockName" + ("\(loopCounter)")
var ticker = "ticker" + ("\(loopCounter)")
var lastPrice = Double(loopCounter)
var purchasePrice = Double(loopCounter)
var weight = Double(loopCounter)
var daysHeld = loopCounter
var newRecord = (stockName, ticker, lastPrice, purchasePrice, weight, daysHeld)
temporaryDataArray.append(newRecord)
}
ETA: Iterate over the array - eg:
for recordEntry in temporaryDataArray {
var a = recordEntry.stockName
var b = recordEntry.ticker
// etc
}
Or with enumeration - eg:
for (count,recordEntry) in enumerate(temporaryDataArray) {
println("\(count) \(recordEntry)")
}
Output:
0 (stockName0, ticker0, 0.0, 0.0, 0.0, 0)
1 (stockName1, ticker1, 1.0, 1.0, 1.0, 1)
2 (stockName2, ticker2, 2.0, 2.0, 2.0, 2)
3 (stockName3, ticker3, 3.0, 3.0, 3.0, 3)
etc

How do I access an Array stored in a Dictionary in Swift?

This is how I created my Dictionary with the Array:
#IBAction func didTapSaveButton(sender: UIButton) {
var hooObject = Dictionary<String, [String]>()
hooObject["\(dayLabel.text)"] = ["\(fromTimeLabel.text) - \(toTimeLabel.text)", "\(costField.text)"]
NSLog("RESULT: \(hooObject)")
}
NSLog result:
RESULT: [Optional("Wednesday"): [Optional("9:00 PM") - Optional("3:00 PM"), 40.00]]
I've tried this:
#IBAction func didTapSaveButton(sender: UIButton) {
var hooObject = Dictionary<String, [String]>()
hooObject["\(dayLabel.text)"] = ["\(fromTimeLabel.text) - \(toTimeLabel.text)", "\(costField.text)"]
var res = hooObject["Wednesday"]
NSLog("RESULT: \(res)")
}
This returns nil which doesn't make sense to me. Wednesday is the stored key so I would have thought it would be enough to return the array.
What I'm trying to do:
Basically I have a table with 7 sections and each represent a day. In each day I'm storing time slots with a cost for booking an activity in that slot.
When I use numberOfRowsInSection I will be able to use the Dictionary key to get the number of rows (time slots) for a particular day that will be shown in the tableView. This was the easiest way I could think to do it with NOSql.
I store the dictionaries in a column in parse.com and access that dictionary in my tableView.
Anyway I don't understand why I can't seem to access the array with the key. I could easily go and use an NSMutableDictionary but I'm using Swift for all my projects as a learning experience.
Wondering if it would be better to use a Dictionary with Dictionaries e.g. ["fromTime":value, "toTime":value, "cost":value]
Will appreciate any help given.
Thanks for your time.
The problem comes from using string interpolation on dayLabel.text -- since that's a String? and not a String, you end up with "Optional(Wednesday)" as your key instead of just "Wednesday". You need to unwrap it, like this:
#IBAction func didTapSaveButton(sender: UIButton) {
var hooObject = Dictionary<String, [String]>()
if let day = dayLabel.text {
hooObject["\(day)"] = ["\(fromTimeLabel.text) - \(toTimeLabel.text)", "\(costField.text)"]
NSLog("RESULT: \(hooObject)")
}
}
You'll probably need to do something similar with the from and to time fields to get the values you want.
My answer:
#IBAction func didTapSaveButton(sender: UIButton) {
var hooObject = Dictionary<String, [String]>()
hooObject["\(dayLabel.text!)"] = ["\(fromTimeLabel.text) - \(toTimeLabel.text)", "\(costField.text)"]
var res = hooObject["Wednesday"]
NSLog("RESULT: \(res)")
}
I had to change dayLabel.text to dayLabel.text!
Those exclamation marks really baffle me. At first I thought they were like pointers because they can't be used on Int, Float etc (so I think).
But the fact I need one at the end of dayLabel.text and not my other labels baffles me even more. I guess in time I will have my ah hah moment. I did read a few posts on here but still don't quite get it.

Resources