Change static variable - swift

im new in swift. I'd like to ask u if i go the right way.
I have something like this:
class ViewController: UIViewController {
struct myVars {
var static short_text = ""
}
override func viewDidLoad() {
super.viewDidLoad()
loadData()
println(short_text)
}
func loadData() {
myVars.short_text = "Hello world!"
}
}
This code works. I have "Hello world!" string in the variable and i can print it. But iam asking you if this is a good and clear way to redefine static var? I do it because i want to work with this variable across the code.
Thank you for your answers.
PS: The final methods are much more difficult. This is shorted code for example only.

If your intent is to make the variable for this instance of ViewController accessible to other classes (i.e. other view controllers), then you don't have to use static. You only need to use static if it is critical to make the property accessible across multiple instances of that class. I don't think that's what you intended here.
If the intent is to pass data between the view controllers, I'd suggest you refer to:
How do you share data between view controllers and other objects in Swift?
How do you pass data between view controllers in Swift?
If you search for "pass data between view controllers", you'll find lots of other similar links.
Bottom line, the use of static is possible, but probably not what you intended.
Two side notes:
You are using a static within a struct. Swift 1.2 obviates the need for that pattern. If you really need static (and I don't think you need it here), you can just declare your variable as static and eliminate the struct:
static var shortText = ""
I don't think your use of struct meant to open the "by-value vs by-reference" discussion, but if you did, I might refer you to WWDC 2015 video Building Better Apps with Value Types in Swift.

Related

SwiftUI MVVM tiny question from what I saw in a lecture

I was watching a lecture about MVVM in Swift and how it works, and I basically understood that the Model will contain data and logic, ViewModel passes that data to the View, maybe cleans it up a bit and the View can also call intent functions in the ViewModel to notify the Model of some things that need to be modified.
Now I know I don't really have much context but there are a bunch of lectures here and there's no way for me to really explain everything for now but basically we're making a memory card game and now we are now changing it to have a MVVM design pattern(It didn't have one before). The model currently contains a Card struct and a choose function to choose a card and stuff like that, but for some reason the lecturer puts an array of emojis(The content of the cards in this game) in the ViewModel and not the Model.
I thought that the Model should be the one that stores the data and not the ViewModel? Could anyone maybe try to explain why this was done?
ViewModel:
import SwiftUI
class EmojiMemoryGame //this is the ViewModel
{
static let emojis = ["floaf","taco","george","chicken","squeaky","cat","dollar","a","b","c","d","e","f","g","h"] // these are supposed to be the emojis I just used some words instead.
static func createMemoryGame() -> MemoryGame<String>
{
MemoryGame<String>(numbersOfPairsOfCards: 4) { pairIndex in emojis[pairIndex]}
}
private var model : MemoryGame<String> = createMemoryGame()
var cards: Array<MemoryGame<String>.Card>
{
model.cards
}
}
Model:
import Foundation
struct MemoryGame<CardContent> //MemoryGame is the model for the MVVM pattern
{
private(set) var cards : Array<Card>
func choose(_ card: Card)
{
}
init(numbersOfPairsOfCards: Int, createCardContent: (Int) -> CardContent)
{
cards = Array<Card>()
//add numbersOfPairsOfCards*2 to cards array.
for pairIndex in 0..<numbersOfPairsOfCards
{
let content = createCardContent(pairIndex)
cards.append(Card(content: content))
cards.append(Card(content: content))
}
}
struct Card
{
var isFaceUp: Bool = false
var isMatched: Bool = false
var content: CardContent
}
}
It's entirely subjective, MVVM is just a design pattern and does not need to be rigorously adhered to.
Note that in MVVM a given view is typically "backed" by a single ViewModel and that a VM might interact with many different models. If the data is to be shared across views, it probably belongs on some shared model. If the data just used on a single view, it's likely fine on the VM.
I knew and understood which lecture you are referring too.
Model is for storing the data, but VM is the middleman who can uses the Model to compute data or update the View without giving the View a direct access to the Model. ViewModel also updates the Model whenever it receives any specific interaction from the View or User. You do not process or compute any data inside a Model to update the View. You must use the ViewModel.
Also, in this case the "emojis" array is not really the main model. It is just an array of String not "Card". emojis is used to store the data for initializing the "Model". Think of it like an input from the user, you need this to pass to your Model, otherwise you don't need a Model if you don't have any data to store or initialize.
The main model is still "Card", and "Card" is still completely abstract in this context.
ViewModel's job is to compute the data like a middleman and then either passing it to the Model or Update the View. Also, it is fully possible for a ViewModel to have its own implemented variables for helping the process between VM and Model or VM and View.
All in all, Model does not do any process, compute, direct interact with the View. Model only stores the data. ViewModel does all the compute, process, initialize.
ViewModel is a common incorrect interpretation of the underlying concept, stemming from the lack of spaces that we have in programming languages obfuscating intent. What you’re actually working with is a View Model, expressible in Swift via View.Model or Model.View…
struct CardView: View {
struct Model: DynamicProperty {
extension MemoryGame.Card {
struct View: SwiftUI.View
…or, when it’s a model for several views instead of one, that’s called a Store:
final class Store: ObservableObject {
Even though MVVM is a cross-platform term that Apple has largely rejected, every view has a model, regardless of if you abstract it into a separate type. The view model is anything the view needs to draw itself. Anything outside of that is a model for something else and doesn’t belong in the type.

Should I put my UserDefaults saving process into ViewModel? (good architecture)

I'm creating a simple NewsApp. I want to create the best app architecture I can made. So my question is that if I want save really simple data like username and maybe 5-6 tags as strings, should I put userDefaults logic into my viewModel or should I create a layer between ViewModel and UserDefaultsAPI which will take care about saving data?
I mean I will create StoreData protocol which UserDefaultsAPI will implement. And if I should do it how I can achieve that? I am using RxSwift and I don't now how to subscribe changing data in UserDefaults by UserDefaultsAPI.
You should create a layer between, but given an Rx/functional architecture, it shouldn't be something heavy weight like a protocol.
Learn How to Control the World and do something like this instead:
struct Storage {
static var shared = Storage()
var saveProperty: (Property) -> Void = { property in
UserDefaults.standard.set(try? JSONEncoder().encode(property), forKey: "property")
}
var deleteProperty: () -> Void = {
UserDefaults.standard.removeObject(forKey: "property")
}
var currentProperty: () -> Observable<Property?> = {
UserDefaults.standard.rx.observe(Data.self, "property")
.map { $0.flatMap { try? JSONDecoder().decode(Property.self, from: $0) } }
.distinctUntilChanged()
}
}
struct Property: Codable, Equatable { }
It depends what your doing creating a seperate layer gives you, the opportunity to have different sources for data that implement the same protocol could be useful, and your data may be complex types than need to be encoded and decoded, so it makes sense to encapsulate that out, you may also want to provide some limit range to some of your values, but UserDefaults is just a service like NotificationCenter, you are not going to automatically wrap NotificationCenter in ever class, just do what is simplest, but doesn't run the risk of painting yourself in a corner later on. You are not going to get every issue decided at the get go right, the skill is to make sure you can quickly change if the need arises, and knowing about what possibility's you will need to take advantage of in the future and how can you avoid needing to make complex changes in the moment that don't add squat. There are lots of things you need to do, and being able to prioritise them is an important part of what you do, don't try make cleaver designs, be cleaver in making designs that can be easy for other to understand and modify.

Confused by the usage of a class or a struct as container for static variables (Swift)

In my app I currently use a separate class to store a bunch of static variables that I want to access from anywhere at anytime.
class Sounds {
static var soundInitial : Sound!
static var soundThunder : Sound!
static var soundWind : Sound!
// etc... (about 50 more)
}
This Sounds class is never being used as an instance, but figures as a container for all the sound-effects (which are of type Sound - which is a separate class)
I initialize all the sound-effects from the Main View Controller during viewDidLoad:
let urlPath1 = Bundle.main.url(forResource: "art.scnassets/sounds/my_sound_file", withExtension: "mp3")
Sounds.soundInitial = Sound(url: urlPath1!, volume: 1.0)
// etc...
Then I can use it at any given time like so:
Sounds.soundInitial.play(numberOfLoops: -1) // endless looping
My very basic question as a not-yet-so-well-experienced-programmer:
Is there a more common approach or better way to store all my sound variables centrally together? (I don't want to define them in View Controller, because this would give me too much variables there...)
I found out, that I could use a struct to achieve the same. But would this be a better approach?
Is there a special Container Object in Swift designed for such purposes?
What you're doing is clumping global constants into a namespace.
Is there a special Container Object in Swift designed for such purposes?
Yes, a caseless enum is the conventional way to do this, as it is the most lightweight and cannot be accidentally instantiated; it is "pure" namespace.
If you watch some Apple videos you'll see that's how they do it. Personally I used to recommend a struct but I've switched to using enums for the reason given.

Proper way of using Singletons (class with structs inside) in Swift

I would like to understand which is the proper way of structuring my code. I have created a Singleton class for video processing (detecting silences) and storing its processing output (the silence timestamps and other info) as a Struct. As this should be a single reference in the whole program, I am using a Singleton pattern, as I will run some multithreading tasks after this one and want to have a single source of truth for that class.
class SilenceDetector {
static let shared = SilenceDetector() // Singleton-pattern
// <-- should I init it?
func detectSilence(videoURL: URL) -> SilencesInfo { ... }
private struct Silence {...}
struct SilencesInfo { // <-- Should I use a Singleton-class?
// here I run different functions with Silence struct
...
I then use SilenceDetector.shared.detectSilence() but to my surprise I cannot access SilenceDetector.shared.SilenceInfo... but SilenceDetector.SilenceInfo.
Whats the proper way of doing it?
There is no proper way of using the singleton pattern. It was always a hack, but has been made completely obsolete by the SwiftUI environment.
Your SilenceDetector should be an EnvironmentObject, whether or not you're using SwiftUI Views.

Mocking a static class method in a swift unit test in a swifty way?

I'm a seasoned Objective-c programmer but I can't say the same for Swift, I'm having a hard time unit testing a class in swift without using frameworks like OCMock.
The Problem: I'm integrating Firebase into a mixed Objective-C/Swift project, and I need to configure it based on the build configuration of the app.
I've written a Swift class for that (that will be used by the obj-c app delegate), however since the firebase framework is configured trough a static class method, precisely FIRApp.configure(with: FIROptions), I need to mock this method somehow in order to unit test it.
My code, without any handle for Dependency Injection, looks like that:
#objc class FirebaseConfigurator: NSObject{
func configureFirebase(){
let config = configManager.buildConfiguration
var optionsPlistBaseName = getPlistName()
let optionsFile = Bundle.main.path(forResource: optionsPlistBaseName, ofType: "plist")
guard let opts = FIROptions(contentsOfFile: optionsFile) else{
assert(false, "fatal: unable to load \(optionsFile)")
return
}
FIRApp.configure(with: opts)
}
func getPlistName() -> String{
// retrieves correct plist name and returns it
}
}
I've done some research but so far I didn't find nothing that fits my solution, however I was thinking of one of the following:
I could pass a function that defaults to FIRApp.configure(with:) however I should do this from objective-c and the function also accepts a parameter, I was struggling with the syntax
I could use a wrapper around FIRApp, but I wanted to avoid it unless the only viable clean solution.
I could keep on playing with protocols and do dependency inversion, however being the method static I was struggling with the syntax again, I can't find an easy way to do DI with a mock class with a static method.
As a reference (both personal and for who might need it) these are some of the resources I found useful and upon which I will keep on digging:
Dealing with static cling in Swift
This Question
This article about generic unit testing
In the meanwhile, every help would be really appreciated.
As a sidenote, there are many ways I can solve this problem without struggling with mocking a static class method, but my aim here is to find out a way of mocking it in order to have a better understanding of the best practices when testing more complex situations.
You can indeed do any of those.
Closure Argument
You can have your configureFirebase function take an "applier" closure that defaults to what you originally used:
func configureFirebase(
using apply: (_ options: FIROptions) -> Void
= { opts in FIRApp.configure(opts) }
) {
// building |opts| as before
// Now replace this: FIRApp.configure(with: opts)
apply(opts)
}
Protocols
You need a Configurable protocol, and then to conform FIRApp to it for the default case:
protocol Configurable {
static func configure(with options: FIROptions)
}
extension FIRApp: Configurable {}
class FirebaseConfigurator {
var configurable: Configurable
init(configurable: Configurable = FIRApp) {
self.configurable = configurable
}
func configureFirebase() {
//load |opts|…
configurable.configure(with: opts)
}
}
If you're just going to use this in one method, though, it's merely transient state, and it should probably be a function argument rather than stored property.
(If it's unclear whether it's persistent or transient state because the whole point of the class is to call a single function, perhaps you don't even need a class, just a function.)