Get Struct property using String variable after initialising Struct instance - swift

I hope the code and comments below illustrate what I'm trying to learn how to do.
Accessing Struct property named iphone, I can, it's valid to :
IconSizes().iphone
Accessing Struct property named iphone, I can't and want to access it using a variable containing a String value "iPhone" :
IconSizes().selectedIconType
In more context :
selectedIconType = "iphone" // already set as String
let sizesNamesArray = IconSizes().selectedIconType // obviously raises error.
The Struct :
struct IconSizes {
var typesList: Array<String>
var iphone: [Dictionary<String, Any>]
init() {
self.typesList = ["iPhone"]
self.iphone = [
["size":16,"name":"icon_small.png"],
["size":32,"name":"icon_small#2x.png"],
["size":32,"name":"icon_medium.png"],
["size":64,"name":"icon_medium#2x.png"],
["size":64,"name":"icon_large.png"],
["size":128,"name":"icon_large#2x.png"],
["size":128,"name":"icon.png"],
["size":256,"name":"icon#2x.png"]
]
}
}

Referencing names of variables at run time as if they were strings is usually easy in interpreted languages and less so as you move to compiled, static languages. Being explicit is likely to work better.
struct IconSizes {
var typesList: Array<String>
var iphone: [Dictionary<String, Any>]
var types = [String : [Dictionary<String, Any>]]()
init() {
self.typesList = ["iPhone"]
self.iphone = [
["size":16,"name":"icon_small.png"],
["size":32,"name":"icon_small#2x.png"],
["size":32,"name":"icon_medium.png"],
["size":64,"name":"icon_medium#2x.png"],
["size":64,"name":"icon_large.png"],
["size":128,"name":"icon_large#2x.png"],
["size":128,"name":"icon.png"],
["size":256,"name":"icon#2x.png"]
]
self.types["iPhone"] = self.iphone
}
}
let selectedIconType = "iPhone"
print(IconSizes().types[selectedIconType])

Related

How can I create data structure with multiple values in Swift?

I'm learning Swift and I'm wondering how can I create a data structure with multiple values and pass descriptions values from UITableViewController to another viewController? I have tried like this
struct faculty {
var name = String()
var descriptions = (String)[]
}
let faculties = [name: "Faculties", description: ["Study1", "Study2"]]
I have successfully managed to list an array ["Test1", "Test2"] in tableView.
There are a couple of issues
An empty string array is [String]().
description is not equal to descriptions.
An instance must be created with Type(parameter1:parameter2:).
And structs are supposed to be named with starting capital letter.
struct Faculty {
var name = String()
var descriptions = [String]()
}
let faculties = [Faculty(name: "Faculties", descriptions: ["Study1", "Study2"])]
However default values are not needed. This is also valid
struct Faculty {
let name : String
var descriptions : [String]
}

Swift & SwiftUI - Conditional global var

I want to make a global variable in Swift, so that its Data is accessible to any view that needs it. Eventually it will be a var so that I can mutate it, but while trying to get past this hurdle I'm just using it as let
I can do that by putting this as the top of a file (seemingly any file, Swift is weird):
let myData: [MyStruct] = load("myDataFile.json)
load() returns a JSONDecoder(). MyStruct is a :Hashable, Codable, Identifiable struct
That data is then available to any view that wants it, which is great. However, I want to be able to specify the file that is loaded based on a condition - I'm open to suggestions, but I've been using an #AppStorage variable to determine things when inside a View.
What I'd like to do, but can't, is do something like:
#AppStorage("appStorageVar") var appStorageVar: String = "Condition1"
if(appStorageVar == "Condition2") {
let myData: [MyStruct] = load("myDataFile2.json")
}
else {
let myData: [MyStruct] = load("myDataFile.json")
}
I can do this inside a View's body, but then it's only accessible to that View and then I have to repeat it constantly, which can't possibly the correct way to do it.
You could change just change the global in an onChange on the AppStorage variable. This is an answer to your question, but you have the problem that no view is going to be updating itself when the global changes.
var myData: [MyStruct] = load("myDataFile.json)
struct ContentView: View {
#AppStorage("appStorageVar") var appStorageVar: String = "Condition1"
var body: some View {
Button("Change value") {
appStorageVar = "Condition2"
}
.onChange(of: appStorageVar) { newValue in
myData = load(newValue == "Condition1" ? "myDataFile.json" : "myDataFile2.json")
}
}
}

Values of structs changing when appending to array with protocol type

I have a protocol, and some structs that conform to it, basically in the format shown below. I'm facing an issue where if I append different structs to an array of type [Protocol], the values of the structs are changing in a weird way. However, if I change the type of the array to [Struct1] or [Struct2], and only append the appropriate types, there's no problem.
protocol Protocol {
var id: String { get set }
var name: String { get set }
}
struct Struct1: Protocol {
var id: String = "1"
var name: String = "Struct1"
var uniqueProperty1: String = "uniqueProperty1"
}
struct Struct2: Protocol {
var id: String = "2"
var name: String = "Struct2"
var uniqueProperty2: String = "uniqueProperty2"
}
var structs: [Protocol] = []
let struct1 = Struct1()
let struct2 = Struct2()
structs.append(struct1)
structs.append(struct2)
And I should add, the above code works as expected. It's my project that has a protocol and some structs however that are behaving strangely. What could be causing this issue?
I discovered that if you look at the value of an element within an array of type [Protocol] in the Variables View within the Debug Area, it's possible that it won't reflect that element's actual values.
Here's an example:
You can see that itemsList in cards[2] is nil, but when I print out the same value in the Debugger Output of the Console, it's not nil (has a length of 4):
(lldb) po (cards[2] as? RBListCard)?.itemsList?.count
▿ Optional<Int>
- some : 4
I guess the moral of the story is don't trust the values that show up within the Variables View.

Swift - Declare nested variable names using dot

I'll keep it short. I'm trying to accomplish the following:
class Media {
var likes.count : Int? = 0
}
Obviously the complier throws me an error:
Consecutive declarations on a line must be separated by ';'
Is there a way to work around this? I know that i can eventually do some kind of String Replace using Mirror(reflecting:object) but i'd like to keep it efficient. Would love any help. Thanks.
UPDATE:
I wasn't clear enough, sorry. The issue is that the complier won't let me use . inside the variable declaration name.
The issue is that the complier won't let me use . inside the variable declaration name.
Exactly, a property name in Swift cannot contain the . character.
A possible approach
Now, if you want to be able to write something like this
let media = Media()
media.likes.count = 1
then you need to define your class like shown below
class Media {
class Likes {
var count = 0
}
var likes = Likes()
}
or
class Likes {
var count = 0
}
class Media {
var likes = Likes()
}
A few suggestions
PLEASE don't use implicitly unwrapped optionals like this one
var likes.count : Int! = 0
They are like a gun ready to fire and crash your entire app!
And finally the class keyword begins with a lowercase character: class not Class.
I recommend using a Struct. A Struct is basically the same as a class that is referenced by value. So you can have as many Structs as you want with their own nested variables and functions, just like a class! And the best part is, you never have to use the Struct as a functional piece of code, just as something to namespace your variables in. I do this frequently with a Constants swift file.
struct Constants {
struct MainMenu {
static var height:CGFloat = 420
static var width:CGFloat = 240
static var backgroundColor:UIColor = .red
}
struct MainViewController {
static var toolBarHeight:CGFloat = 49
static var backgroundColor:UIColor = .blue
}
}
Usage:
func getRemainingHeight() ->CGFloat {
let viewHeight = self.view.bounds.size.height
let menuHeight = Constants.MainMenu.height
let toolBarHeight = Constants.MainViewController.toolBarHeight
return viewHeight - (menuHeight + toolBarHeight)
}

how to create a singleton in swift with init variables

I am trying to create a singleton class in swift but I am getting an error
"cannot create a single-element tuple with an element label"
i am not getting it.
class GroupObject {
// we want the group object to be a singleton
var name: String
var id: Int
var groupJsonObject: JSON
init(groupJsonObject: JSON){
self.groupJsonObject = groupJsonObject
self.id = groupJsonObject["id"].int!
self.name = groupJsonObject["name"].string!
}
class var sharedInstance : GroupObject {
struct Static {
static let instance : GroupObject = GroupObject(groupJsonObject: JSON) // this is the problem line.
}
return Static.instance
}
}
The problem is that you cannot pass a parameter to the singleton. Your singleton implementation doesn't know to what JSON refers.
If you want this to be a singleton, you'd have to initialize the groupJsonObject separately from the initialization of the shared instance. For example:
class GroupObject {
var name: String!
var id: Int!
var groupJsonObject: JSON! {
didSet {
id = groupJsonObject["id"].int!
name = groupJsonObject["name"].string!
}
}
static let sharedInstance = GroupObject() // btw, this is a more concise syntax for declaring a singleton
}
And then, when you want to initialize those properties, you could do:
GroupObject.sharedInstance.groupJsonObject = json
If your "singleton" is supposed to hold some data passed to it on instantiation, how will it get that data? Where/when is it available?
I think you don't actually want a singleton at all; you want an instance created with your JSON data to be accessible from different points in your application. In that case, pick some "master controller", create it there, then pass it along to other controllers as needed.