In my SwiftUI application I'm currently using a global struct that holds constants properties like colors or sizes.
E.g.
struct App {
struct Colors {
static let mainAppColor: UIColor = UIColor(red: 233/255, green: 230/255, blue: 231/255)
static let mainWhiteColorMiddleOpacity: UIColor = UIColor.white.opacity(0.5)
}
}
// usage
struct ContentView: View {
var body: some View {
Rectangle().foregroundColor(App.Colors.mainBlueColor)
}
}
I was debating whether this is a bad practice since I have LOTS of static variables, and was wondering if replacing them with static methods or computed properties would decrease memory usage (since body gets computed lots of times). E.g. replacement:
struct App {
struct Colors {
static var mainBlueColor: UIColor { UIColor.blue }
static var mainWhiteColorMiddleOpacity: UIColor { UIColor.white.opacity(0.5) }
}
}
// usage
struct ContentView: View {
var body: some View {
Rectangle().foregroundColor(App.Colors.mainBlueColor)
}
}
One would increase heap usage and the other stack usage, right?
What do you think would be the best practice?
Premature (and ill-conceived) optimization. You probably have no idea how little memory is actually taken up by even thousands of these values, in the grand scheme of things; it's trivial. And any decently written app will have a lot of them; it's what you do.
A struct is a great way to organize and namespace these sorts of things, although it happens that an enum is now Apple's preferred way (for reasons that I do not entirely understand).
Your "since body gets computed lots of times" is totally irrelevant: the name is a direct reference to the value with absolutely no overhead, since if you use let the whole thing is compiled away and replaced by the value right at the point of reference in your code. Lookup time at runtime is zero!
(A computed value or method, on the other hand, might require some serious overhead to run each time, potentially bringing the entire message dispatch mechanism into play, though it might in fact be inlined away perhaps. Serious backsliding there.)
Computed properties are only useful if there is something to compute so the variable makes sense.
You are definitely creating constants (as you mentioned), so use let
Regarding memory usage: How about declaring colors in the assets catalog?
I would say never use Computed properties for the things you already knows the answer or value, from the other side do you know how many times would SwiftUI run that Computed properties for using it? so many run code for getting the same thing!
you can use Computed properties for the things you need once or some times in your app just for reading value, for example you have a Rectangle that you know the width and height, then use Computed properties for getting the area, or something related like that, after reading value you would be finished with that as well, and there is no need to store it as well.
Related
I'm trying to simplify a huge file in Swift and I'm not sure what would be the best approach. It might be a very naive question as I'm still fairly new to Swift :)
For the sake of simplicity, let's say I have one User struct and in it I can call a ton of different setupModeA, setupModeB and so on, called depending on various conditions:
struct User {
var currentMode: String
mutating func setupModeA() {
currentMode = "A"
}
mutating func setupModeB() {
currentMode = "B"
}
// and so on for each mode
}
I'd like to be able to take the setupMode*() functions and their associated helper functions into a separate file to make it easier to read through, since most of the time I'd be focusing on only one mode.
I was thinking:
struct ModeA {
mutating func setup() {
// change the mode from there
}
}
However I can't figure out how to simply pass and mutate the User info to this new struct.
I thought about:
initializing a bunch of ModeX objects with a user variable in the User struct and passing the User on init, but it creates weird behaviours.
using static methods on the ModeX structs, but then I'm not quite sure how to mutate the User
taking a more functional approach & not mutating the User but instead cloning it and re-assigning it each time... this would probably work but since my code is very sequencial it feels like added complexity / memory usage for nothing.
switching from struct to classes and hoping it gives me other options... but I'm afraid everything would break
really complex things that feel too weird and unintuitive to be mentioned here :)
Any pointers would be much appreciated.
I'm finally making the switch from Objective-C to Swift. I'm creating a view layout system for my client to make their apps more flexible in layout, without using auto layout as they want to design their screens remotely and auto-layout would be too complex for them. I tried to do this using structs and protocols but I found it to be quite clumsy, so I'm suspecting I'm not thinking about it the right way.
With classes, the structure would be as follows:
class ViewModel {
var frame: CGRect = .zero
}
class ViewGroupModel: ViewModel {
var weight: Int = 1
var children:[ViewModel] = [ViewModel]()
}
class HorizontalViewGroupModel: ViewGroupModel {
}
class VerticalViewGroupModel: ViewGroupModel {
}
I tried to approach it with protocols by defining a ViewModel protocol, and a ViewGroupModel protocol, but I found it created a lot of duplication (properties). Is there a better approach? Would it be considered a good practice to use classes in this case?
EDIT: In case it would be better to not uses classes, I am looking for an answer that gives me a concrete solution in terms of structs and protocols.
If the concern was merely how to implement the protocol's properties, I wouldn't necessarily let that sway my choice between struct vs class. If you had a variety of properties that your struct types must implement, you have two basic options:
If you're talking about a few properties, just implement those few properties in your struct types that conform to that protocol. We do this all the time. E.g. when defining custom types that conform to MKAnnotation, we simply implement the three required properties.
Sure, if we're talking about a much larger set of properties, this gets tedious, but the compiler holds our hand through this process, ensuring that we don't miss anything. So the challenge is fairly modest.
While I'm not a fan of this approach, https://stackoverflow.com/a/38885813/1271826 shows that you could implement the shared properties as a component, where you have struct to wrap all of these properties, and then implement default computed properties for your protocol in an extension:
enum SubviewArrangement {
case none
case horizontal
case vertical
case flow
}
struct ViewComponent {
var frame = CGRect.zero
var weight = 1
var subviews = [ViewModel]()
var subviewArrangement = SubviewArrangement.none
}
protocol HasViewComponent {
var viewComponent: ViewComponent { get set }
}
protocol ViewModel: HasViewComponent { }
extension ViewModel {
var frame: CGRect {
get { return viewComponent.frame }
set { viewComponent.frame = newValue }
}
var weight: Int {
get { return viewComponent.weight }
set { viewComponent.weight = newValue }
}
var subviews: [ViewModel] {
get { return viewComponent.subviews }
set { viewComponent.subviews = newValue }
}
var subviewArrangement: SubviewArrangement {
get { return viewComponent.subviewArrangement }
set { viewComponent.subviewArrangement = newValue }
}
}
Where, you can then create an instance that conforms to ViewModel, like so:
struct LabelModel: ViewModel {
var viewComponent = ViewComponent()
}
var label = LabelModel()
label.weight = 2
print(label.weight)
I have to confess, this isn't the most elegant approach. (I hesitate to even present it.) But it avoids having to implement all of those properties individually in your types that conform to ViewModel.
So, let's set the property question aside. The real question is whether you should be using value type (struct) or reference type (class). I think it's illuminating to consider Apple's discussion of value vs reference semantics near the end (#42:15) the Protocol-Oriented Programming in Swift video. They touch upon those cases where you actually may still want to use classes. For example, they suggest you might want to use reference types when, "Copying or comparing instances doesn't make sense". They suggest this rule might apply when dealing with "Window" instances. The same applies here.
On top of that, it doesn't seem to me that there is much benefit to use value types to represent a view hierarchy, which is a collection of reference type objects. It only makes it more confusing. I would just stick with class types, as it will accurately mirror the view hierarchy it represents.
Don't get me wrong: We're so used to using reference types that I think it's always good to challenge our preconceived notions and take a long hard look at whether a value type could better address the situation. In this case, though, I simply wouldn't worry about it and just stick with a class hierarchy that mirrors the hierarchy of those objects you're modeling.
That having been said, the class hierarchy proposed in your question doesn't quite feel right, either. It feels strange that you can actually instantiate a ViewModel to which you can't later add subviews (whereas all UIView objects have subview property). Also, your horizontal and vertical group types don't feel correct either. For example, should it be a single type with some "axis" property, like UIStackView or some other "arrangement" property, to broaden the notion to capture UICollectionView layouts, too?. As you'll see in my ViewComponent example, above, I've flattened this a bit, with these two caveats in mind, but do whatever you see fit.
In general, use a class only if you need the special features of classes, which are:
A class can have a superclass and/or a subclass; a struct can't.
A class is a reference type, while a struct is a value type.
Objective-C can introspect a class (esp. if it derives from NSObject), whereas it cannot even see a struct declared in Swift.
Its always good to code to interface/protocol than to classes/structs. This your model wisely.
You can make use of generics as well beautifully for this purpose. I hope it would save a lot of variables and duplications for you.
For layout purpose in my opinion structs coupled with protocols and generics would make a great design. I don't see for you any need to use classes in your case.
Its always good to know in and out of a feature to better use it. The main difference between structs and classes in Swift are
Class objects are stored/passed as references where as struct instances are stored/passed as values
Reference counting allows more than one reference to a class instance.
For Class we have identity operators === and !== which are used to check whether two variables or constants refer to the same instance of Class. The question for identity operators does not arise for Struct because two different Struct variables or constants can not point to same instance. You can try applying identity operator to Struct types. You will get compile time error.
Inheritance enables one class to inherit the characteristics of another.
Type casting enables you to check and interpret the type of a class instance at runtime.
Deinitializers enable an instance of a class to free up any resources it has assigned.
For detailed disscussion you can through my post Struct Vs Classes in Swift.
I have two View Controllers accessing constants inside a single global structure, defines in a separate file. The structure is as follows:
struct Constants {
struct FAQ {
struct General {
static let QUESTIONS: [String] = [
//Some String questions here
]
static let ANSWERS: [String] = [
//Answers here
]
}
struct Specific {
static let QUESTIONS: [String] = [
//Questions
]
}
}
struct Tips {
struct General {
static let QUESTIONS: [String] = [
//Questions
]
static let ANSWERS: [String] = [
//Answers
]
}
}
}
Now, from one VC, controlling the FAQs of my app, I access Constants.FAQ and all the data inside it.
From another VC handling the Tips, I access Constants.Tips.
Now, to my question. What is the lifetime of the FAQ structure, once the FAQ View Controller has been removed from memory by the OS? Does it stay in memory for the app's lifetime?
To give a use case, take for example:
FAQ VC is created and starts execution. FAQ VC accesses Constants.FAQ.General.QUESTIONS and does its processing. Now, FAQ VC stops execution and is removed from memory.
After a while, FAQ Tips is created and starts execution. It accesses Constants.Tips.General.QUESTIONS and does the processing.
Now at this time, is the Constants.FAQ.General.QUESTIONS String array still in memory? Or was it's lifetime limited to it's access in the FAQ VC.
Another question, while I'm at it: Is using this kind of structure good practice?
Thanks.
This structure and all the sub structures and static methods will be defined at app startup, and will exist as long as the app is running.
To answer your second question, no this not generally considered good practice. Global data is generally error prone, resistant to change, and difficult to test. In your case the data is static, which avoids some of the issues with global shared data.
Usually data is kept in a separate data file and loaded at runtime. it is advantageous to pass the data to each view controller, by setting a property, or through a method call.
Consider loading json or plist into an array or dictionary, then parse the data into an instance of this structure.
It depends on how Constants() is created. If it's done at file scope in the same module that defines it, then it will never go away, since 'let constants = Constants()` retains a reference.
What is the difference between a Lazy or Optional property in Swift?
For example, if someone is building a navigation bar that comes in from the side, I think that should all be within one UIViewController. The user might never open the menu but sometimes they will.
var menu: NavigationBar?
lazy var menu: NavigationBar = NavigationBar.initialize()
Both of the optional I think are good code, because they don't create the view unless its needed. I understand Optional means there might be a value it might be nil. I also understand Lazy means don't worry about it until I need it.
Specific Question
My question is are their performance patterns (safety and speed) that say optionals are faster and safer or vise versa?
OK, this is an interesting question, and I don't want to imply that the existing answers aren't good, but I thought I'd offer my take on things.
lazy variables are great for things that need to be setup once, then never re-set. It's a variable, so you could change it to be something else, but that kind of defeats the purpose of a lazy variable (which is to set itself up upon demand).
Optionals are more for things that might go away (and might come back again). They need to be set up each time.
So let's look at two scenarios for your side menu: one where it stays around while it's not visible, and another for when it is deallocated.
lazy var sideMenu = SideMenu()
So the first time the sideMenu property is accessed, SideMenu() is called and it is assigned to the property. The instance stays around forever, even when you're not using it.
Now let's see another approach.
var _sideMenu: SideMenu?
var sideMenu: SideMenu! {
get {
if let sm = _sideMenu {
return sm
} else {
let sm = SideMenu()
_sideMenu = sm
return sm
}
}
set(newValue) {
_sideMenu = newValue
}
}
(Note this only works for classes, not structs.)
OK so what does this do? Well it behaves very similarly to the lazy var, but it let's you re-set it to nil. So if you try to access sideMenu, you are guaranteed to get an instance (either the one that was stored in _sideMenu or a new one). This is a similar pattern in that it lazily loads SideMenu() but this one can create many SideMenu() instances, where the previous example can only create one once.
Now, most view controllers are small enough that you should probably just use lazy from earlier.
So two different approaches to the same problem. Both have benefits and drawbacks, and work better or worse in different situations.
They're actually pretty different.
Optional means that the value could possibly be nil, and the user isn't guaranteeing that it won't be. In your example, var menu: NavigationBar? could be nil for the entire lifetime of the class, unless something explicitly assigns it.
Lazy on the other hand means that the assignment will not be called until it is first accessed, meaning that somewhere in code someone tries to use your object. Note however that it is STILL promised to not be nil if you declare it like you have here lazy var menu: NavigationBar = NavigationBar.initialize(), so no need to do optional chaining.
And actually, a variable can be BOTH Lazy AND Optional, which means that it's value will be loaded when it is first accessed, and that value might be nil at the point it's initialized or at any future point. For example:
lazy var menu: NavigationBar? = NavigationBar.initialize()
That NavigationBar.initialize() is now allowed to return nil, or someone in the future could set the menu to be nil without the compiler/runtime throwing errors!
Does that make the difference clear?
Edit:
As to which is BETTER that's really a case by case thing. Lazy variables take a performance hit on first initialization, so the first access will be a slow one if the initialization process is long. Otherwise, they're nearly identical in terms of safety/performance. Optional variables you have to unwrap before using and so there is a very minor performance cost with that (one machine instruction, not worth the time to think about)
Optional and lazy properties are not the same
An optional property is used when there are chances that the value might not be available(i.e can be nil). But in your scenario, the navigation bar will always be available, its just that the user might not open it.
So using a lazy property serves your purpose. The NavigationBar will only be initialised if the user taps on it.
I do not see any performance issues except that if you use an optional, there is an additional overhead of checking if the value is nil each time before accessing it.
I'm trying to build my first spritekit game in swift. I don't understand where to store constants so I can make them available to the whole project. I have some experience with C++ and I used to create a Constants.h file. Is there such thing in Swift? What's the recommended practice to do this?
Right now I'm using a struct with static constants but I'm not sure if it's the right way to do it:
struct Constants {
static let gravity : Int = 20
}
struct Constants {
static let buildName = "Orange-Pie"
struct FacebookConstants {
static let clientId ="asdasdsa"
}
struct TwitterConstants {
static let clientId ="asdasdsa"
}
}
Use :
Constants.FacebookConstants.clientId
If you have highly generic constants needed by every part of your program, this is indicating a design problem (whenever you are at a loss of where something should go, you probably have a design problem). Things like a gravitational constant shouldn't be necessary for the vast majority of the program, so they generally don't need to be global constants (that's true in C++, too).
Put your constants with the thing that needs that constant, or pass them into the thing that needs that constant. SpriteKit should do most gravity calculations for you, but if you're doing additional physics work, then there should be some object that represents the physics engine or "the world." That's where the gravity constant belongs. Alternately, put the gravity constant into a struct that you pass into the physics engine at launch.
Even if you do have very broad need of the gravitational constant, then you should put it into a struct like PhysicalConstants, not a generic Constants (which makes code reuse hard because it mixes unrelated things). A common case of this in my code are "style" constants like "the systemwide highlight color" (which are likely to be changed by the client, so I want a single place to modify them). These go into a header called Style.h in my apps, and now would go into a Style struct. But they're kept separate from non-style constants.
When I was at WWDC 2014, I asked an engineer the exact same thing. Their recommendation was to use your method to replace #define that we had in Objective-C. I agree that it's a suboptimal procedure and actual defining should be implemented in some way.
Also note that I don't think you need to explicitly state the type of your variable, as Swift has rather advanced type inference. So this should work:
struct Constants {
static let gravity = 20
}
struct Constants {
static let varName = "AnyValue"
}
Accessing to varName:
Constants.varName