Choosing between strcuts, classes and enums - swift

I have some sample code I put together as I am trying to figure out how to model my first app. I'm not sure if the "shoe examples" should be enums or classes. I guess I could create one class that has all the features that each class has ( sole, string, color, fit) and then make subclasses of the main class and add the other features for classes that need them. I also thought about setting each up as enums because the data is fixed. Ultimately I want a user to be able to push a button and decide if a condition is a poor, fair, or excellent for each case in the enum or each stored property in the class or struct.
enum Conditions {
case poor
case fair
case excellent
}
// Shoe exmaples
enum Nike {
case sole
case string
case color
case fit
}
struct Rebook {
case sole
case string
case color
case fit
}
enum Jordan {
case sole
case string
case color
case fit
}
enum sketchers {
case sole
case string
case color
case fit
case (idk something else)
}
enum heels {
case sole
case string
case color
case fit
case height
case width
}
or

Your current approach is almost there. If I understand you correctly, you want each property of the shoe to be ranked with a Condition enum. You might have more luck making each shoe a generic class, like so:
class Shoe {
var sole: Condition?
var string: Condition?
var color: Condition?
var fit: Condition?
init(sole: Condition, string: Condition, color: Condition, fit: Condition) {
self.sole = sole
self.string = string
self.color = color
self.fit = fit
}
}
This will now allow you to create instances of Shoe for every type you want, instead of a different enum for each shoe. For example, if the user ranked a Nike, you might store their results like so:
var Nike = Shoe(sole: .poor, string: .excellent, color: .fair, fit: .excellent)
If you want to add different rankings for each shoe, such as "idk something else" or "height", then you can always add more properties to the Shoe class and make them optional, or you can subclass the Shoe class to add more properties.

Related

SwiftUI - using all but one values of Enum for different Pickers

In my app, I need to use a Picker in two different places. In one place, it's to set a property to one of three values: A, B, or C. But, in another place, I want to use the picker to filter a array of items to show either all items (no filter) or only the A, B, or C items.
So, my first attempt was to create an enum like so:
enum Category {
case all
case A
case B
case C
}
Using this, I can filter my list successfully. But, when I try to create a new item in the array, I don't want all to be an option. The user should only see a picker with A, B, and C.
I could try:
enum Category {
case A
case B
case C
}
but then how do I add the all option for the filter picker?
(To address some comments below, here are some more details on the specific thing I'm try to accomplish...
A tournament can be a men's, women's, or mixed tournament. On the screen where I list the tournaments, I want to be able to show all tournaments or just the men's, just the women's or just the mixed tournaments. So, I have a picker that spans the width of the iPhone. Looks and works great.
Obviously, when adding a new item to the list, I have to specify its category, but not "all". Also a picker.
So, in one case, I need three values, in the other case, I need the same three values with "All" added at the beginning.)
You should define your enum without the all case, because all is not a valid Category for an item. (This is a programming guideline known as “make illegal states unrepresentable”.)
enum Category: Hashable, CaseIterable {
case a
case b
case c
}
With that definition, the Picker for setting an item's property can look like this:
Picker("Category", selection: $category) {
ForEach(Category.allCases, id: \.self) { category in
Text(verbatim: "\(category)")
.tag(category)
}
}
Then you should recognize that your filter is optional. You can filter by item category, or you can perform no filtering. So your filter property should be declared optional:
#Binding var categoryFilter: Category?
The Picker for setting the filter then needs to be careful to use optional tags:
Picker("Category Filter", selection: $categoryFilter) {
Text("None")
.tag(Category?.none)
ForEach(Category.allCases, id: \.self) { category in
Text(verbatim: "\(category)")
.tag(Category?.some(category))
}
}
You can create enum in this way
enum Category {
case all
case A
case B
case C
static let categories: [Category] = [.A, .B, .C]
}
And then use the categories array when you need to choose among three.
You can make your enum CaseIterable and then you can use the allCases property when you want to display all enum values, and use a filter when you only want certain cases displayed.
NOTE: In my sample code, you'll need to replace selection: .constant(Category.a) with an #Binding
struct Test {
enum Category: String, CaseIterable {
case all = "All"
case a = "A"
case b = "B"
case c = "C"
}
struct TestView: View {
var body: some View {
VStack {
Picker("Picker Title", selection: .constant(Category.a)) {
ForEach(Category.allCases, id: \.self) { category in
Text(category.rawValue).tag(category)
}
}
Picker("Picker Title", selection: .constant(Category.a)) {
ForEach(Category.allCases.filter({ $0 != .all }), id: \.self) { category in
Text(category.rawValue).tag(category)
}
}
}
}
}
}
To answer your question if I understood you correctly.
Modify your enum to implement the CaseIterable take a look at it below.
enum Category:CaseIterable{
case all
case A
case B
case C
}
Filter out where your category matches the all case. Example below.
let preferredcategory = Category.allCases.filter{ return $0 != Category.all}
To test our result see the code below.
print(preferredcategory.count)
for catname in preferredcategory {
print("Hello, \(catname)!")
}
You can read more on this respective pages.
https://developer.apple.com/documentation/swift/caseiterable
https://developer.apple.com/documentation/swift/caseiterable/2994869-allcases
https://www.hackingwithswift.com/example-code/language/how-to-list-all-cases-in-an-enum-using-caseiterable
Thanks.
So you can use the preferredCategory variable to show the user your category for the one you do not want all to show. you can also use the same filter to show only the part of the enum that has all case as well.
Also, the good thing is you get to keep your enum and reuse it everywhere in your application without having to hardcode values. if you want to show only All you just have to filter out the remaining case.
Some people may want to downvote without a valid reasons but if you believe the approach is wrong feel free to edit or leave a comment on how to improve the solution.

How to get all values from nested enums?

Working with my key/value Strings file, I'm looking for a simpler way of pulling out values by passing an enum into a function, which does the lookup and returns that value.
This Strings file can be updated over the network, so my app ships with a Default version. Currently, to get those defaults into memory I iterate over my enums cases and add the strings to a dictionary.
It looks like this:
enum StringKeys: String, CaseIterable {
case string1
case string2
case string3
}
func setupDefaults() {
for key in StringKeys.allCases {
defaults[key] = stringFile.value(forKey: key)
}
}
func getValue(for: StringKeys) -> String {
// stuff here...
}
This has all been fine for a while now, however this enum is getting quite large and so autocomplete is less helpful.
What I would like to have is something more like this:
enum StringKeys: CaseIterable {
enum Feature1: String, CaseIterable {
case title1
case button1
}
enum Feature2: String, CaseIterable {
case title3
case toggle1
}
}
Then call my getValue function with:
titleLabel.text = Strings.getValue(for: StringKeys.Feature1.title1)
This is all totally fine too, but I can't work out how to iterate over these nested keys to build the defaults dictionary in a nice clean way.
I tried extending CaseIterable to add a little allCasesRecursive function. Which Swift wouldn't allow.
I know this can be done by adding a property inside each enum, returning allCases, and combining them together at each parent. But that's still a bit messy.
The other option is to not use the enums to set up defaults, and instead just parse the whole file and put that directly into defaults, but I'd much rather the enums decide what goes in the defaults. This also makes it easy to see if any are missing i.e, the default for that key won’t be found, and I can throw an error.
So, basically I want to turn a bunch of nested enums into a flat dictionary. Even just an array of keys would be enough to get me the rest of the way. Is it possible to recursively drill down through nested enums? Could I use Reflection to solve this problem?
I'm also open to other ideas, otherwise I'll just stick with the giant, flat enum.
I don't know if i understand your question correctly, maybe this will help
// Create a protocol
protocol StringKeyValue {}
enum StringKeys: CaseIterable {
enum Feature1: String, CaseIterable, StringKeyValue {
case title1
case button1
}
enum Feature2: String, CaseIterable, StringKeyValue {
case title3
case toggle1
}
}
func getValue(for: StringKeyValue) -> String {
return "hi there"
}
// Maybe this's what you want?
titleLabel.text = getValue(for: StringKeys.Feature1.title1)

Select random value from a enum

select random value from given enum
public class SwiftConfettiView: UIView {
public enum ConfettiType {
case confetti
case triangle
case star
case diamond
case image(UIImage)
}
// usage in other class
confettiView.type = .confetti
Would like to set randomly confettiView triangle, star,diamond,confetti
//This will not work
confetti.type = ConfettiType.allCases.randomElement()!
Type 'SwiftConfettiView.ConfettiType' has no member 'allCases'
//So, all all confetti to an array list and load in from there!
var confettiList = [SwiftConfettiView.ConfettiType.confetti
, SwiftConfettiView.ConfettiType.diamond
, SwiftConfettiView.ConfettiType.triangle
, SwiftConfettiView.ConfettiType.star
]
confettiView.type = confettiList.randomElement()!
It's working properly!
is this approach is correct or wrong?
Here your enum is associated type. So if case of type image you have to provide a image as parameter. I have considered a default image.
extension ConfettiType: CaseIterable {
static var allCases: [ConfettiType] {
let img: UIImage = UIImage(named: "default_image")! // change as your expectation
return [.confetti, .triangle, .star, .diamond, .image(img)]
}
}
let randomEnum = ConfettiType.allCases.randomElement()
Otherwise if your image type would be something like this image(UIImage?) then we can put nil as default value. In that case it would be more convenient.
#Alexander identified your issue in the comments: Your issue is that you need to conform to CaseIterable to get access to allCases, and that only works if your enum doesn't have associated values (because there's no way to enumerate all possible UIImages).
Your workaround is fine, and it can be made nicer by taking advantage of Swift's type inference.
You might add a randomChoice(from:) function to your enum class that lets the caller specify which items to choose from. Because of type inference, you can specify the cases without fully qualifying them (eg. .confetti and .triange is sufficient).
Here is a full example:
public class SwiftConfettiView: UIView {
public enum ConfettiType {
case confetti
case triangle
case star
case diamond
case image(UIImage)
static func randomChoice(from choices: ConfettiType...) -> ConfettiType {
return choices.randomElement()!
}
}
func test() {
for _ in 1...10 {
let choice = ConfettiType.randomChoice(from: .confetti, .triangle, .star, .diamond)
print(choice)
}
}
}
SwiftConfettiView().test()
triangle
triangle
diamond
diamond
triangle
star
confetti
confetti
star
triangle
Notes:
Alternatively, you could have randomChoice take [ConfettiType]. In that case, you need to decide how to handle an empty array (either return an optional so that you can return nil for an empty array, or provide a default value such as .confetti for that case).
return choices.randomElement() ?? .confetti
Taking an array gives you the ability to provide a default value, such as:
static func randomChoice(from choices: [ConfettiType] = [.confetti, .triangle, .star, .diamond]) -> ConfettiType {
return choices.randomElement() ?? .confetti
}
For example:
// take the default values
let choice = ConfettiType.randomChoice()
// specify specific choices
let choice = ConfettiType.randomChoice(from: [.star, .diamond])
If you always just want to choose from those 4 enum values, you could just have randomChoice take no arguments and just hard code the choice from those 4 values. In that case, randomChoice could be implemented as a computed property:
static var randomChoice: ConfettiType { return [.confetti, .triangle, .star, .diamond].randomElement()! }
and called like this:
let choice = ConfettiType.randomChoice

Enumeration of Enumerations and avoiding the use of Any type

I'm building a model for an app that tracks data related to wedding dress alterations. Our model diagram shows related enumerations, with "Section" being the outermost enumeration, and then each case connects to an additional, more detailed enumeration.
When the Alteration Specialist is doing the fitting, they will be entering which alterations they plan to do. I need an efficient way to track the relationships between these quantities.
See object model image
I have implemented a basic example, but I'm having trouble storing an array of these "Alteration" objects and defining the appropriate type.
Here is my Playground code:
struct Alteration {
var detail: Any
enum section {
case bodice
enum Bodice {
case addBraCups
case addOrRemoveBoning
}
case sides
enum Sides {
case bustTakeInOrOut
case hipOrWaistTakeInOrOut
}
}
}
var alterationOne = Alteration(detail: Alteration.section.Bodice.addBraCups)
var alterationTwo = Alteration(detail: Alteration.section.Sides.bustTakeInOrOut)
var alterations = [Any]()
alterations.append(alterationOne)
alterations.append(alterationTwo)
This code compiles but I would prefer to not use Any. I'm also wondering if it makes sense to nest enumerations in this manner and if these nested enumerations make sense inside of a struct or class.
Try using enums with associated values. Your structs could look like this:
enum BodiceVariation {
case addBraCups
case addOrRemoveBoning
}
enum SidesVariation {
case bustTakeInOrOut
case hipOrWaistTakeInOrOut
}
enum Alteration {
case bodice(BodiceVariation)
case sides(SidesVariation)
}
let alterationOne = Alteration.bodice(.addBraCups)
let alterationTwo = Alteration.sides(.bustTakeInOrOut)
var alterations = [Alteration]()
alterations.append(alterationOne)
alterations.append(alterationTwo)
(assuming that each of the alterations you've listed are mutually exclusive)
One problem with enums is that they are for things that are mutually exclusive. But yours are not.
I think I would use arrays of alteration types and use classes and subclass to specify what each one is. For example:
struct Dress {
var bodiceAlterations = [BodiceAlteration]()
var sideAlterations = [SideAlteration]()
}
enum InOrOut {
case `in`
case out
}
enum AddOrRemove {
case add
case remove
}
class BodiceAlteration {}
class BraCupAlteration : BodiceAlteration {
}
class BoningAlteration : BodiceAlteration {
var addOrRemove = AddOrRemove.add
}
class SideAlteration {}
class BustAlteration : SideAlteration {
var inOrOut = InOrOut.out
}
class HipAlteration : SideAlteration {
var inOrOut = InOrOut.out
}
An array can list multiple alterations of its type, and an array can happily be empty if there are no alterations of that type. Then the subclass tells you the more specific type. The enums are just for mutually exclusive choice: a hip alteration can be in or out but not both.
The classes give you room to grow, e.g. add measurements and other data as properties, as appropriate. If you have lots of classes that have inch measurements, dates, etc., you could use a protocol to unite them if you needed to (though if they all have that you can just inherit from the superclass).

Swift - Nested Enums default values

I have an enum as follows
enum AccountForm: String {
case Profile
enum Content: String {
case Feedback
case Likes
}
enum Actions: String {
case Redeem
case Help
}
}
This represents a form, where profile content and actions are sections and the cases are rows.
These resolve to strings and work as expected
AccountForm.Profile.rawValue returns "Profile"
AccountForm.Content.Feedback.rawValue returns "Feedback"
However, I'd like AccountForm.Content.rawValue to return "Content"
Is this possible? Or is there a better way besides enums to achieve this?
I'm guessing you've got an answer to this by now but just in case you didn't try this:
enum AccountForm : String {
case profile
enum Content: String {
static let rawValue = "Content"
case feedback = "Feedback"
case likes = "Likes"
}
enum Actions : String {
static let rawValue = "Actions"
case redeem = "Redeem"
case help = "Help"
}
}
Static properties on both the Content and Actions enumerations should achieve what you want. A word of warning though. By calling the properties rawValue you're obviously implying the returned values are raw values when technically they aren't. If you can I'd think of a better name (maybe sectionTitle?).
Couple of other things to note.
First, you have to define the properties as static as it sounds like you want to call them on the enumeration type (e.g. AccountForm.Content.rawValue) rather than on an individual enumeration case (e.g. AccountForm.Content.feedback.rawValue). I'll leave you to decide whether that makes sense in your context.
Secondly, when Swift 3.0 arrives, the recommendation for enumeration cases is going to be that case labels follow a lowerCamelCase naming convention rather than the UpperCamelCase convention that was recommended in Swift 2.2.
I've followed the Swift 3.0 recommendation here but the result is that explicit raw-value assignments is needed as you won't be able to rely on using the implicit raw-value assignment mechanism assigning a string with an UpperCamelCase representation which is kind of annoying but those are the implications.
Anyway, hope it helps.
enum Typo {
case Bold
case Normal
case Italic
case All
}
enum Format: CustomStringConvertible {
case Header(Typo)
case Text(Typo)
var description:String {
switch self {
case .Header(let value) where value != .All:
return "Header.\(value)"
case .Header(let value) where value == .All:
return "Header"
case .Text(let value) where value == .All:
return "Text"
case .Text(let value) where value != .All:
return "Text.\(value)"
default:
return ""
}
}
}
let a:Format = .Header(.Bold)
let b:Format = .Text(.Italic)
Format.Header(.All) // Header
Format.Text(.Bold) // Text.Bold
Format.Text(.All) // Text