Empty initializer for swift structures - swift

Is it possible to initialize a swift struct without any values?
To summarize the idea, I'm creating a struct with two attributes, those attributes were filled when I called the init() method, as it should, but the problem is, I have a function inside this struct and I need to use it, in some cases, without filling the fields in the struct initializer.
I've tried adding the init(){} function, hoping that somehow it would work like the empty constructors in java, but I've got no success.
struct Quiz {
var pergunta: String
var resposta: Bool
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
//Something like that
init(){}
func popularQuiz() -> Array<Quiz> {
...
}
}
Is there a way to do it or swift doesn't have the option to create a empty structure?

The big question is why do you want to have an empty Quiz? What does an empty Quiz mean?
Those questions aside, you can only create an initializer with no parameters if all of the properties can be given default values or if the properties are optional.
struct Quiz {
var pergunta: String = ""
var resposta: Bool = false
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
init(){}
}
Now your empty init will work because all properties are fully initialized. But is that what you really want?
Another way to get the same result is to use default values on the parameters of the main initializer.
struct Quiz {
var pergunta: String
var resposta: Bool
init(pergunta: String = "", resposta: Bool = false) {
self.pergunta = pergunta
self.resposta = resposta
}
}
Now you can call:
Quiz()
Quiz(pergunta: "Some question", resposta: "Some Answer")
Quiz(pergunta: "Question with no answer")
Any of these still result in all properties being initialized.
The last option would be to make the properties optional instead of using special default values. But this adds more headache later.
struct Quiz {
var pergunta: String?
var resposta: Bool?
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
init(){}
}
The empty init will leave the properties as nil. But now you need to properly handle the optional values everywhere. Again, is that what you really want?

Related

Swift property wrappers. Assign by reference

I would like to share a string between classes, and keep modifications made on that string synchronised.
I thought of doing a property wrapper like:
#propertyWrapper class StringHolder {
var wrappedValue: String?
init(wrappedValue: String?) {
self.wrappedValue = wrappedValue
}
}
and then use it:
#StringHolder var sharedString: String = "testString"
let anotherClass = AnotherClass()
anotherClass.sharedString = sharedString
anotherClass.sharedString = "modified"
//sharedString is still "testString"
The problem is that when assigning the sharedString, only the wrapped value is re-set.
Is it possible to have the same wrapper assigned by reference to two properties?

Can I add associated object to Swift Struct?

I would like to add an additional property to the Swift String. I used this approach few times on objects, but it seems that it does not work on struct. Although, I don't get any error...
This is what I tried:
var str = "Hello, StackOverflow"
fileprivate struct AssociatedKeys {
static var myBool = "myBool"
}
extension String {
public var myBool: Bool {
get {
guard let myBoolObject = objc_getAssociatedObject(self, &AssociatedKeys.myBool) as? NSNumber else {
return false
}
return myBoolObject.boolValue // execution never reaches this line
}
set(value) {
let object = NSNumber(value: value)
objc_setAssociatedObject(self, &AssociatedKeys.myBool, object, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
str.myBool = true
print(str.myBool) // prints "false"
It prints out that it is false.
At first, I tried it without wrapping the Bool into NSNumber, but the result was the same.
Is this even possible to add an associated object to a struct at all? If not, can anyone tell me why?
Based on #Hamish's comment, I created the following solution to workaround the issue.
Preconditions:
Have a framework which proposes prefilled objects, the app works on these objects and the framework should know which of the properties are modified during the processing of this object later.
Not using looooong initializers to setup all properties of MyObject is a design decision.
In my example, the usage of the myObject is a dummy and shows what happens in the framework and what happens in the app.
// protocol is used, as we could handle more modifiable structs/classes in a common way
protocol PropertyState {
var isModified: Bool {get set}
}
internal struct ModifiableString : PropertyState {
var string: String
var isModified: Bool
}
class MyObject: PropertyState {
internal var _name = ModifiableString(string: "", isModified: false)
public var name: String {
get {
return _name.string
}
set(value) {
_name.string = value
_name.isModified = true
}
}
// + N similar properties (they can be other types than String, by implementing other structs, like ModifiableBool)
var isModified: Bool {
get {
return _name.isModified // || _myAnotherProperty.isModified
}
set(value) {
_name.isModified = value
// _myAnotherProperty.isModified = value
}
}
}
// internal filling of the object inside of the framework
let myObject = MyObject()
myObject.name = "originalValue"
print(myObject.isModified) // true
// filling the object with values ended, so we can set the state
myObject.isModified = false
print(myObject.isModified) // false
// the app can work with the object
// let myObject = Framework.getObject()
myObject.name = "modifiedValue"
// now the framework should now which properties are modified
print(myObject._name.isModified) // true

Read once and then set to nil

I'd like to implement such property, that it's value is available for reading only one time, and then the property should be set to nil.
I've implemented it in such way:
private var _readOnce: String?
var readOnce: String? {
get {
let value = _readOnce
_readOnce = nil
return value
}
set {
_readOnce = newValue
}
}
readOnce = "Andrej"
print("read once = \(readOnce)") // prints read once = Optional("Andrej")\n"
print("read once = \(readOnce)") // prints read once = nil\n"
But I'feel like using a separate property _readOnce is not the "swifty" / "most elegant" way to do it.
Does anyone know of a different way, that wouldn't require to use a separate property?
I can confirm that the above code works, it's only that I feel it could be more elegant with less lines to achieve the same behaviour.
I don't know that there's a way to avoid having a backing property, but what I'd probably do is to make a helper type to wrap up the behavior. Something like this:
struct OneTimeValue<T>
{
private var isUnread = true
private let value : T
init(_ value: T)
{
self.value = value
}
func get() -> T?
{
guard isUnread else {
return nil
}
self.isUnread = false
return self.value
}
}
You could also write this a little differently if you prefer, by nilling out value inside of get(), for example, but the general plan holds.
Then your class becomes:
class Miser
{
var readOnce : String?
{
return self._readOnce.get()
}
private let _readOnce = OneTimeValue("Can't touch this (twice)")
}
I've also used this pattern for a UserDefaultsValue (storage to/from user defaults) and a SynchronizedValue (read-write lock on a property) and I think it works well.
As far as I know it is not possible without a second variable. This is because computed properties do not store any data for the variable they represent:
In addition to stored properties, classes, structures, and
enumerations can define computed properties, which do not actually
store a value.
For non-computed properties, the only observers you can have are based upon the setting of the variable, not the getting (i.e. willSet and didSet)
Hope that helps!
EDIT:
It can be done with closures and property observers if you're careful:
This requires no other variables (instead the value is captured by the closure), but it is rather unclear — I wouldn't recommend it.
var readOnce: () -> String? = {nil} {
didSet{
readOnce = { [weak self, readOnce] in
self?.readOnce = {nil}
return readOnce()
}
}
}
readOnce() // returns nil
readOnce = {"Hi"}
readOnce() // returns optional wrapped "Hi"
readOnce() // returns nil
A more 'Swifty' answer for you :D
After Swift 5.1, We can use Property Wrapper
#propertyWrapper
struct ReturnAndFree<T> {
private var value: T?
init(wrappedValue: T?) {
value = wrappedValue
}
var wrappedValue: T? {
mutating get {
defer { value = nil }
return value
}
set {
value = newValue
}
}
}

Init a computed variable

I'm going to create a new class, and in this class there's a computed variable; so I'm looking for a way to init this variable:
import UIKit
class Squadra: NSCoder, NSCoding
{
var nomeSquadra: String
var numeroCoriSquadra: Int
var coloreSquadra: String
var immagineSquadra: String
var sottotitoloSquadra: String
{
get
{
return "I migliori cori: \(nomeSquadra)"
}
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
nomeSquadra = nome
coloreSquadra = colore
numeroCoriSquadra = numero
immagineSquadra = immagine
sottotitoloSquadra = sottotitolo
}
}
obviously with this line of code Xcode gives my a compile error (because the var is a get only property).
I think that i have to use a set to make the var writable, but I don't know how to operate because I don't know exactly how get and set work.
Either remove sottotitoloSquadra = sottotitolo or assign to a different variable. Even if the assignment worked, you never actually use the value that comes in as sottotitolo for anything.
I can't see useful behavior while you use independent property nomeSquadra and trying to have setter for sottotitoloSquadra at the same time. Maybe better to use hidden support property for computed variable in your case?
private var _sottotitoloSquadra: String
var sottotitoloSquadra: String
{
get
{
return "I migliori cori: \(_sottotitoloSquadra)"
}
set
{
_sottotitoloSquadra = newValue
}
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
//...
_sottotitoloSquadra = sottotitolo
}
If I understand you class correctly, you want to use the variable's default string when the init() did not provide a value (I'm just guessing though).
So if the team doesn't have a specific subTitle, you would make one up from the team's name.
I also understand that you don't want that property to be modifiable after the object is instantiated.
If that is the case, (I assume you would get an empty string for sottotitolo), you can define a private variable to hold the provided title and expose it using a computed variable. The default value (made up title) can be returned by that computed variable if a title was not supplied on the init().
class Squadra
{
var nomeSquadra: String
var numeroCoriSquadra: Int
var coloreSquadra: String
var immagineSquadra: String
private var _sottotitoloSquadra = ""
var sottotitoloSquadra: String
{
return _sottotitoloSquadra == ""
? "I migliori cori: \(nomeSquadra)"
: _sottotitoloSquadra
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
nomeSquadra = nome
coloreSquadra = colore
numeroCoriSquadra = numero
immagineSquadra = immagine
_sottotitoloSquadra = sottotitolo
}
}
Only your class, including its init() function, can modify the private variable holding the supplied sottotitolo. Outside of that source file, the private variable is not accessible at all.

Swift init with function to make clear separation

In ObjC I used to call in my init something like
- init() {
[self initView1]
[self initView2]
}
to separate the setup of the views
but in swift I can't call a function before any variable has been set, is there something I'm missing or it's not possible ?
You can assign an initial value on their declaration line:
class MyClass {
var prop1: String = ""
var prop2: String = ""
init() {
self.initView1()
self.initView2()
}
func initView1() {
self.prop1 = "Hello"
}
func initView2() {
self.prop2 = "World"
}
}
Or alternatively, make them implicitly unwrapped optionals by adding an exclamation mark (!):
var prop1: String!
var prop2: String!
This tell the compiler that the programmer has guaranteed that these properties will be properly initialized before use.
In Swift, every property must be initialized before you can call methods.
This is described in detail in the chapter Two-Phase Initialization of the Swift Programming Language Manual.
If you want to delegate initialization to a method, then you at least have to provide some dummy values first.
Alternatively, you can refactor your code to create your views from static methods (if that is possible); then you can use these objects for initialization:
class MyClass {
let prop1: String
let prop2: String
static func create1() -> String {
return "Hello"
}
static func create2() -> String {
return "World"
}
init() {
prop1 = Blah.create1()
prop2 = Blah.create2()
}
}