How to prevent duplicate code when initializing variables? - swift

A class has some variables to be initialized in init() and at the same time, the class provides a function to restore these variables to their initial values in restoreInitValues(). Is there any way I can achieve this without setting these values twice (duplicate code) inside both init() and restoreInitValues()?
class Foo {
var varA: Int
var varB: Int
var varC: Int
init() {
//restoreInitValues() // error: call method before all stored proproties are initalized
//or I have to have duplicate code here as restoreInitValues below
varA = 10
varB = 20
varC = 30
}
func restoreInitValues() {
varA = 10
varB = 20
varC = 30
}
}

Personally I would assign the 3 default values to 3 class scope constants, then use those values to init and restore. You could also eliminate the assigning statements in the init if you want, and assign the value when you declare the var. In addition, by having your defaults defined in a class constant if you need to add any other functions to the class they'll be available for use.
class Foo {
let defaultA = 10
let defaultB = 20
let defaultC = 20
var varA: Int
var varB: Int
var varC: Int
init() {
varA = defaultA
varB = defaultB
varC = defaultC
}
func restoreInitValues() {
varA = defaultA
varB = defaultB
varC = defaultC
}
}
You could also define a struct, use it to assign your values, and then use your reset function to init.
struct values{
static let defaultA = 10
static let defaultB = 20
static let defaultC = 30
}
class test {
var a: Int = 0
var b: Int = 0
var c: Int = 0
init(){
resetValues()
}
func resetValues(){
(a, b, c) = (values.defaultA, values.defaultB, values.defaultC)
}
}

Use implicitly unwrapped optionals.
class Foo {
var varA: Int!
var varB: Int!
var varC: Int!
init() {
restoreInitValues()
}
func restoreInitValues() {
varA = 10
varB = 20
varC = 30
}
}

Update: Code's answer below about Implicitly Unwrapped Optionals is the answer. I'm not sure why I couldn't find it in the docs before. I'm leaving my answer for posterity, but you should accept Code's answer.
being new to swift, I gave it a try and found these two solutions, but I'm not sure if they're the best solutions.
The problem seems to be that swift tries hard to ensure that no class instance will ever have uninitialized properties (unless they're marked as optional) after initialization has been performed. It won't let you call non-static methods because you might use the instance before all properties are set. Also, it doesn't trust you to call another method which initializes all of the properties for you presumably because that would be really hard to verify.
For classes, use a private static method to return default values:
class Foo {
//...
init() {
(varA, varB, varC) = Foo.defaultValues()
}
func restoreInitValues() {
(varA, varB, varC) = Foo.defaultValues()
}
static private func defaultValues() -> ( Int, Int, Int ) {
return (10, 20, 30)
}
}
If you don't need a class, structs are copyable by value:
struct Foo {
//...
mutating func restoreInitValues() {
self = Foo()
}
}
or you could give up on restoreInitValues() and just do this:
var f = Foo()
f.varA = 10000
// instead of resetting the internal state of `f`, just replace it with
// a new `Foo` instance
f = Foo()
or you could use a static private method that modifies Foo instances, but to get around the compiler you have to make your properties be optional. This solution has a definite Ick factor:
class Foo {
var varA: Int?
var varB: Int?
var varC: Int?
init() {
Foo.resetValues(in: self)
}
func restoreInitValues() {
Foo.resetValues(in: self)
}
static private func resetValues(in foo: Foo) {
foo.varA = 10
foo.varB = 20
foo.varC = 30
}
}
This leads us back to the heart of the problem: swift requires that all properties be optional or initialized. Another solution would be to simply give all of the properties values (meaningless or not). The downside is that the property definitions may be misleading to someone reading the code for the first time.
class Foo {
var varA = -1
var varB = -1
var varC = -1
init() {
restoreInitValues()
}
func restoreInitValues() {
varA = 10
varB = 20
varC = 30
}
}
Last, check out the answers to this similar question: How to implement two inits with same content without code duplication

Related

Extension optional Array with Optional Element. Is it even possible?

I have a protocol FooProtocol. and a class Bar<Foo:FooProtocol>. Inside a class an Array var mess: [Foo?]? to keep [foo1, foo2, nil, foo3...] or nil
And I try to make extension for this array to count new Foo object. I prefer to have protocols, because Foos could be very different objects delivered from outer world.
protocol FooProtocol {
....
init(from heaven: Int)
}
extension Optional where
Wrapped: Collection,
Wrapped.Element == Optional,
Wrapped.Element.Wrapped: FooProtocol // 'Wrapped' is not a member type of 'Wrapped.Element'
{
var united: Wrapped.Element.Wrapped { // Nope
let i = ...
return Wrapped.Element.Wrapped(from: i) // Nope
}
}
class Bar<Foo:FooProtocol> {
var mess: [Foo?]?
init (with mess: [Foo?]?) {
self.mess = mess
}
var important: Foo {
return mess.united
}
}
Any ideas? I'm blocked.
Edit 1:
After Leo suggestions I changed some parts of my code. But still stucked. This time more code from Playgrounds.
Any object that could be converted into '[Double]'. Could be color (as RGBA), Bezier curve, square, whatever...
public protocol FooProtocol {
var atomized: () -> [Double] {get}
static var count: Int {get}
init(_ array:[Double])
init()
}
public extension Array where Element: FooProtocol {
var average: Element {
var resultAtoms: [Double] = []
let inputAtoms = self.map {$0.atomized()}
for i in 0..<Element.count {
let s = inputAtoms.reduce(into: 0.0, {$0 += $1[i]}) / Double (Element.count)
resultAtoms.append(s)
}
return Element(resultAtoms)
}
}
extension Optional where
Wrapped: Collection,
Wrapped.Element == Optional<FooProtocol>
{
typealias Foo = Wrapped.Element.Wrapped // Doesn't work. How to get class?
var average: Foo { // I cannot use Wrapped.Element, it's Optional
if let thatsList = self {
let withOptionals = Array(thatsList) // OK, its [Optional<FooProtocol>]
let withoutOptionals = thatsList.compactMap({$0}) // OK, its [FooProtocol]
// This is funny, called from class works and makes 'bingo'.
return withoutOptionals.average // Error: Value of protocol type 'FooProtocol' cannot conform to 'FooProtocol'; only struct/enum/class types can conform to protocols
} else {
return Foo() // Hello? init Wrapped? Foo? How to get Foo()?
}
}
}
class Bar<Foo:FooProtocol> {
var mess: [Foo?]?
init (with mess: [Foo?]?) {
self.mess = mess
}
func workOn() {
let z:Foo = mess.average // OK, I can make 'mess.average ?? Foo()' but prefer not do it
}
// Thats OK
func workHard() { // To prove 'Array extension where Element: FooProtocol' works
if let messExist = mess {
let withoutOptionals = messExist.compactMap({$0})
let bingo = withoutOptionals.average //It's OK
}
}
}
class SomeFoo : FooProtocol {
static var count = 3
required init() {
a = 0
b = 0
c = 0
}
required init(_ array: [Double]) {
self.a = Int(array[0])
self.b = Float(array[1])
self.c = array[2]
}
var atomized: () -> [Double] {
return {return [Double(self.a), Double(self.b), self.c]}
}
var a: Int
var b: Float
var c: Double
}
let aFoo = SomeFoo([1, 2, 3])
let bFoo = SomeFoo([7, 9, 1])
let cFoo = SomeFoo([2, 6, 5])
let barData = [nil, aFoo, nil, bFoo, cFoo]
let barWithData = Bar(with: barData)
let barWithoutData = Bar<SomeFoo>(with: nil)
Maybe I should forget about extending array and make some functions inside a class (I'm almost sure I will need those functions somewhere else)
Edit 2
Even if I try to simplify and to make extension for Array I found troubles.
extension Array where
Element == Optional<FooProtocol>
{
func averageNils <Foo: FooProtocol>() -> Foo {
let withOptionals = Array(self) // OK, its [Optional<FooProtocol>]
let withoutOptionals = self.compactMap({$0}) // OK, its [FooProtocol]
return withoutOptionals.average as! Foo // Error: Value of protocol type 'FooProtocol' cannot conform to 'FooProtocol'; only struct/enum/class types can conform to protocols
}
}
From my understanding, it should work as you did, but one never knows what happens in the swift compiler world (and especially it's error messages).
Anyway, you can circumvent digging deeper into Wrapped.Element.Wrapped by specifyig the Wrapped.Element more precisely to be an Optional<FooProtocol>:
protocol FooProtocol {}
class Foo : FooProtocol {}
extension Optional where
Wrapped: Collection, //OK
Wrapped.Element == Optional<FooProtocol> // still good
{
var unfied: Wrapped.Element // Should be 'Foo' if self is '[Foo?]?' {
{
return 1 == 0 ? nil : Foo()
}
}

Use Swift #propertyWrapper for dynamic default value?

I need a Swift property that -- if the value has not yet been set -- defaults to another value.
This can be implemented using backing-store private properties. For instance, for a property num that should default to a global defaultNum, it would work something like this:
var defaultNum = 1
class MyClass {
var num: Int {
get { _num ?? defaultNum }
set { _num = newValue }
}
private var _num: Int?
}
let c = MyClass()
print("initial \(c.num)") // == 1 ✅
// changing the default changes the value returned
defaultNum = 2
print("dynamic \(c.num)") // == 2 ✅
// once the property is set, returns the stored value
c.num = 5
print("base \(c.num)") // == 5 ✅
That works, but for a common pattern in our code, it's a lot of boilerplate for each such property.
Using Swift property wrappers, is it possible to do this more concisely?
What won't work
Note that, because we expect the default to be dynamic, static initializers will not work. For example:
var defaultNum = 1
class MyClass {
var num = defaultNum
}
var c = MyClass()
defaultNum = 2
print(c.num) // this == 1, we want the current value of defaultNum, which == 2
You can do this by creating a property wrapper like this:
#propertyWrapper
public struct Default<T> {
var baseValue: T?
var closure: () -> T
// this allows a nicer syntax for single variables...
public init(_ closure: #autoclosure #escaping () -> T) {
self.closure = closure
}
// ... and if we want to explicitly use a closure, we can.
public init(_ closure: #escaping () -> T) {
self.closure = closure
}
public var wrappedValue: T {
get { baseValue ?? closure() }
set { baseValue = newValue }
}
}
You then use the #Default property wrapper on a property like this:
var defaultNum = 1
class MyClass {
#Default(defaultNum)
var num: Int
}
You'd then see the following in practice:
let c = MyClass()
// if we haven't set the property yet, it uses the closure to return a default value
print("initial \(c.num)") // == 1 ✅
// because we are using a closure, changing the default changes the value returned
defaultNum = 2
print("dynamic \(c.num)") // == 2 ✅
// once the property is set, uses the stored base value
c.num = 5
print("base \(c.num)") // == 5 ✅

Cannot create class object or array of class objects

I'm working on a project thatand am simply trying to create a object instance of a simple custom class:
import Foundation
class Set {
private var gam1: Int!
private var gam2: Int!
init (gam1: Int, gam2: Int) {
self.gam1 = gam1
self.gam2 = gam2
}
//returns set info as array
func getStats () -> [Int] {
let arr = [gam1!, gam2!]
return arr
}
}
The class simply stores a few variables for use later and I want an array of such objects to store several values. However, when I try to create a n instance of the class in a different class I get errors:
import Foundation
class AnotherClass {
var mySet = Set(gam1: 6, gam2: 5) //error 1
//array of set objects
var setArray = [Set]() // error 2
//returns array of set objects
func getSets () -> [Set] { //error 3
return setArray
}
}
The errors state:
Cannot find an initializer for type 'Set' that accepts an argument list of type '(gam1: Int, gam2: Int)'
Cannot invoke initializer for type '[Set]' with no arguments
and
Reference to generic type 'Set' requires arguments in <...>
Any ideas of what the issue is here? could the 'Set' name of the class be conflicting with a reserved keyword?
Many thanks,
Kw
The issue that you are having is due to the naming conflict between Set in the Swift standard library and the one you defined.
This is never a good idea. Instead, give it a more descriptive name (and one that doesn't conflict with anything else). For instance, call it gamHolder and initialize it gamHolder(gam1: <an int>, gam2: <another int>).
Also, if you have defined variables inside the init function they do not need to be forced unwrapped optionals.
For example:
class myClass {
var myInt: Int
init(anInt: Int) {
myInt = anInt
}
}
You defined 2 parameters in your init method (since gam1 and gam2 are not optional). So, you have 2 solutions:
1 - You add parameters into your init call (like this):
var mySet = Set(gam1: 1, gam2: 2)
2 - You change gam1 and gam2 to optionals and you add a zero parameters init:
class Set {
private var gam1: Int?
private var gam2: Int?
init() {
}
init(gam1: Int, gam2: Int) {
self.gam1 = gam1
self.gam2 = gam2
}
// returns set info as array
func getStats() -> [Int] {
let arr = [gam1!, gam2!]
return arr
}
}
So, you will be able to call it like this: var mySet = Set()
Also: be careful: Set is a class used in the Swift standard library. It's never a good idea to use same class names than Swift Standard Library. A name like TenisSet would be better.
Edit:
Here is a final working example with a renamed class:
class TenisSet {
private var gam1: Int?
private var gam2: Int?
init() {
}
init(gam1: Int, gam2: Int) {
self.gam1 = gam1
self.gam2 = gam2
}
// returns set info as array
func getStats() -> [Int] {
let arr = [gam1!, gam2!]
return arr
}
}
class AnotherClass {
var mySet = TenisSet()
// array of set objects
var setArray = [TenisSet]()
// returns array of set objects
func getSets() -> [TenisSet] {
return setArray
}
}

swift set intersect, union, subtract

Does anyone know how to implement set operations with user defined classes, with its property as operated conditions. For instance:
class myClass {
var figure: Int!
}
let classObj1 = myClass()
classObj1.figure = 1
let classObj2 = myClass()
classObj2.figure = 2
let classObj3 = myClass()
classObj3.figure = 1
let set1: Set = [classObj1]
let set2: Set = [classObj2, classObj3]
Is there any method like: (pseudo code)
set2.substract(set1) {s2, s1 in s2.figure == s1.figure}
...so the expected result is [classObj2]?
Many thanks for any suggestion!
Certainly. Make your MyClass conform to Hashable and Equatable in such a way that == compares the figure values. Example:
class MyClass : Hashable {
var figure: Int
init(f:Int) {figure = f}
var hashValue : Int {return figure}
}
func ==(lhs:MyClass, rhs:MyClass) -> Bool {
return lhs.figure == rhs.figure
}

Change the value that is being set in variable's willSet block

I'm trying to sort the array that is being set before setting it but the argument of willSet is immutable and sort mutates the value. How can I overcome this limit?
var files:[File]! = [File]() {
willSet(newFiles) {
newFiles.sort { (a:File, b:File) -> Bool in
return a.created_at > b.created_at
}
}
}
To put this question out of my own project context, I made this gist:
class Person {
var name:String!
var age:Int!
init(name:String, age:Int) {
self.name = name
self.age = age
}
}
let scott = Person(name: "Scott", age: 28)
let will = Person(name: "Will", age: 27)
let john = Person(name: "John", age: 32)
let noah = Person(name: "Noah", age: 15)
var sample = [scott,will,john,noah]
var people:[Person] = [Person]() {
willSet(newPeople) {
newPeople.sort({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
people = sample
people[0]
I get the error stating that newPeople is not mutable and sort is trying to mutate it.
It's not possible to mutate the value inside willSet. If you implement a willSet observer, it is passed the new property value as a constant parameter.
What about modifying it to use didSet?
var people:[Person] = [Person]()
{
didSet
{
people.sort({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
willSet is called just before the value is stored.
didSet is called immediately after the new value is stored.
You can read more about property observers here
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
You can also write a custom getter and setter like below. But didSet seems more convenient.
var _people = [Person]()
var people: [Person] {
get {
return _people
}
set(newPeople) {
_people = newPeople.sorted({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
It is not possible to change value types (including arrays) before they are set inside of willSet. You will need to instead use a computed property and backing storage like so:
var _people = [Person]()
var people: [Person] {
get {
return _people
}
set(newPeople) {
_people = newPeople.sorted { $0.age > $1.age }
}
}
Another solution for people who like abstracting away behavior like this (especially those who are used to features like C#'s custom attributes) is to use a Property Wrapper, available since Swift 5.1 (Xcode 11.0).
First, create a new property wrapper struct that can sort Comparable elements:
#propertyWrapper
public struct Sorting<V : MutableCollection & RandomAccessCollection>
where V.Element : Comparable
{
var value: V
public init(wrappedValue: V) {
value = wrappedValue
value.sort()
}
public var wrappedValue: V {
get { value }
set {
value = newValue
value.sort()
}
}
}
and then assuming you implement Comparable-conformance for Person:
extension Person : Comparable {
static func < (lhs: Person, rhs: Person) -> Bool {
lhs.age < lhs.age
}
static func == (lhs: Person, rhs: Person) -> Bool {
lhs.age == lhs.age
}
}
you can declare your property like this and it will be auto-sorted on init or set:
struct SomeStructOrClass
{
#Sorting var people: [Person]
}
// … (given `someStructOrClass` is an instance of `SomeStructOrClass`)
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people.last
Caveat: Property wrappers are not allowed (as of time of writing, Swift 5.7.1) in top-level code— they need to be applied to a property var in a struct, class, or enum.
To more literally follow your sample code, you could easily also create a ReverseSorting property wrapper:
#propertyWrapper
public struct ReverseSorting<V : MutableCollection & RandomAccessCollection & BidirectionalCollection>
where V.Element : Comparable
{
// Implementation is almost the same, except you'll want to also call `value.reverse()`:
// value = …
// value.sort()
// value.reverse()
}
and then the oldest person will be at the first element:
// …
#Sorting var people: [Person]
// …
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people[0]
And even more directly, if your use-case demands using a comparison closure via sort(by:…) instead of implementing Comparable conformance, you can do that to:
#propertyWrapper
public struct SortingBy<V : MutableCollection & RandomAccessCollection>
{
var value: V
private var _areInIncreasingOrder: (V.Element, V.Element) -> Bool
public init(wrappedValue: V, by areInIncreasingOrder: #escaping (V.Element, V.Element) -> Bool) {
_areInIncreasingOrder = areInIncreasingOrder
value = wrappedValue
value.sort(by: _areInIncreasingOrder)
}
public var wrappedValue: V {
get { value }
set {
value = newValue
value.sort(by: _areInIncreasingOrder)
}
}
}
// …
#SortingBy(by: { a, b in a.age > b.age }) var people: [Person] = []
// …
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people[0]
Caveat: The way SortingBy's init currently works, you'll need to specify an initial value ([]). You can remove this requirement with an additional init (see Swift docs), but that approach is much less complicated when your property wrapper works on a concrete type (e.g. if you wrote a non-generic PersonArraySortingBy property wrapper), as opposed to a generic-on-protocols property wrapper.