Best practice for array as property in Swift - swift

I have a model class in Swift, whose primary purpose is to contain an array of custom objects, but also has other methods/properties etc.
public class Budget: NSObject, NSCoding {
var lineItems : [LineItem] = []
// Other methods
// Other properties
}
As I understand it, it's best practice to not make the property publicly settable, but I want it to be testable, so lineItems needs to be publicly gettable.
Reading the docs, I could do this:
private(set) public var lineItems : [LineItem] = []
But then I have to write a lot of boilerplate code to recreate array methods, such as insert, removeAtIndex etc.
What is best practice here? At the moment, I don't need to do anything else on insert/removal of items, but I guess I may need to do validation or similar in future, but even so it seems redundant to have to write code that just recreates Array methods.
Would it be better just to make lineItems publicly gettable and settable? Are their circumstances where this would or wouldn't make sense?
Thanks!

Swift's Array is a (immutable) value type, which means that
var a = ["object"]
var b = [String]()
b.append("object")
b == a // true
From this point of view it does not make sense to allow modifying an array and not allow setting it - modifying is basically creating new array and assigning it to variable.

Related

Hiding property setters by class in Swift

I would like to hide some property setters and initializers on my Swift model objects. These are reference data that the server provides, and under no circumstances should they be created or modified by the application. This is simple enough in Swift.
However, there is application in my project (a separate target) that needs to break this rule. It is a tool I use to populate the data in bulk, so of course needs to be able to initialize new model objects and set their properties.
What are my options for accomplishing this? I would rather not use a completely new project since it will mean a lot of code duplication. Is there some language-level way to keep this mutability hidden from one application but available to another?
If you declare a property with the let keyword. It can then only be set in the init of the type.
You can also declare a private setter to make the property readonly from the caller of the type but read/write inside the type
struct Foo {
private(set) var bar: Bool = true
func toggle() {
bar.toggle()
}
}
var foo = Foo()
let barState = foo.bar // This works
foo.toggle() // This works too
foo.bar.toggle() // This will make a compile time error

How to make a class-wide ID number

Perhaps I am simply reading old material, but I can't see a way to do something seemingly very simple.
I have a class called Robot, each instance of which needs a unique id. The id is simply an Int that should be 1,2,3... The normal solution would be to have a class var MaxId that you increment in the init() and then assign that to the instance's id.
Swift (4?) does not have class vars, but does have computed properties at the class level. However, I am a bit mystified about how one might use this to do a MaxId. Am I missing something blindingly obvious here?
a unique id. The id is simply an Int that should be 1,2,3.
You can certainly use a static property and increment it, but note that
those are two different requirements. If all you really want is a unique id, there is no need for the “least available integer” approach. Just use the built-in UUID struct and move on.
Static variables are essentially class variables. Try this in a playground:
class Numbered {
static var serial: Int = 1
let myID: Int
init() {
myID = Numbered.serial
Numbered.serial = Numbered.serial + 1
}
}
print(Numbered().myID)
print(Numbered().myID)

How to initialize an array of classes in kotlin?

I get an error when I put the type and size of an array of classes
I have tried:
fun main(args :Array<String>) {
class modul() {
var nommodul: String? = null
var coeff: Int? = null
var note: Int? = null
}
var releve
class notes() {
var releve: array<modul>(10){""} here the erreur
}
}
First of all, your code has several errors. This might be an MCVE and/or copy-paste issue, but I need to address these before I get started on the arrays.
var releve before the notes class isn't allowed. You don't assign it, you don't declare a type, and the compiler will complain if you copy-paste the code from your question.
Second, the array var itself: Array is upper-case, and initialization is separate. This would be more valid (note that this still does not work - the solution for that comes later in this answer):
var releve: Array<modul> = Array(10) {...}
// or
var releve = Array<modul>(10) {...}
And the last thing before I start on the array itself: please read the language conventions, especially the naming ones. Your classes should all start with an upper-case letter.
Kotlin arrays are quite different from Java arrays in many ways, but the most notable one being that direct array initialization also requires an initializer.
The brackets are expected to create a new instance, which you don't. You create a String, which isn't, in your case, a modul.
There are several ways to fix this depending on how you want to do this.
If you have instances you want to add to the array, you can use arrayOf:
arrayOf(modulInstance, modulInstance2, ...)
If you want to create them directly, you can use your approach:
var releve = Array(10) { modul() }
A note about both of these: because of the initialization, you get automatic type inference and don't need to explicitly declare <modul>
If you want Java-style arrays, you need an array of nulls.
There's two ways to do this:
var releve = arrayOfNulls<modul>(10)
// or
var releve = Array<modul?>(10) { null }
I highly recommend the first one, because it's cleaner. I'm not sure if there's a difference performance-wise though.
Note that this does infer a nullable type to the array, but it lets you work with arrays in a similar way to Java. Initialization from this point is just like Java: releve[i] = modul(). This approach is mostly useful if you have arguments you want to add to each of the classes and you need to do so manually. Using the manual initializers also provides you with an index (see the documentation) which you can use while initializing.
Note that if you're using a for loop to initialize, you can use Array(10) { YourClass() } as well, and use the supplied index if you need any index-sensitive information, such as function arguments. There's of course nothing wrong with using a for loop, but it can be cleaner.
Further reading
Array
Lambdas
here some example of kotlin array initialization:
array of Library Method
val strings = arrayOf("January", "February", "March")
Primitive Arrays
val numbers: IntArray = intArrayOf(10, 20, 30, 40, 50)
Late Initialization with Indices
val array = arrayOfNulls<Number>(5)
for (i in array.indices) {
array[i] = i * i
}
See Kotlin - Basic Types for details

Circular Reference between 2 classes

I have a problem trying to solve this circular reference.
First I have two NSManagedObject ACoreData and BCoreData
I want to separate the model from the DataBase layer to the UI Model.
So, I create A and B classes which those will be in the UI.
I have created a protocol (Convertible) that ACoreData and BCoreData will implement to convert to the ui objects.
So far so good, but now I have a problem. Each time I call convert from ACoreData it will create a new A and it will assign the BCoreData converted, but then the BCoreData object will call convert again for the A object. I will end up with a loop calling convert() each other.
This is the code:
protocol Convertible{
associatedtype T
func convert() -> T
}
class ACoreData: Convertible{
var b: BCoreData?
func convert() -> A {
var a = A()
a.b = self.b?.convert()
return a
}
}
class BCoreData: Convertible{
var a: ACoreData?
func convert() -> B {
var b = B()
b.a = self.a?.convert()
return b
}
}
class A{
var b: B?
}
class B{
var a: A?
}
Do you know how can I solve this problem to avoid the loop in this circle reference?
Thanks in advance.
I suggest that the best solution is don't do this. You're adding a lot of complexity to your code for no real advantage. Weigh the benefits you see for keeping managed objects away from the UI against the additional complexity of needing to convert to/from managed objects all the time, copying values from one to the other any time one of them changes, the memory hit of keeping duplicate copies of data around, and probably other stuff I haven't thought of yet. "Clean" architecture is going to cost you a lot in terms of maintainability and performance.
If you don't want the UI to know about managed objects, define a protocol that your managed objects adopt. Make the UI work with "things that implement the protocol" instead of managed objects. Keeping the UI unaware of the details of the data store does not require duplicate data models.

Declaring and using custom attributes in Swift

I would like to be able to annotate my types and methods with meta-data and read those at runtime.
The language reference explains how to declare attribute usages, but is it actually possible to declare your own attributes?
Reading would require some kind of reflection mechanism, which I was not able to find in the reference at all, so the second part of the question probably is - is there reflection possible. If these features are not available in Swift, can they be done with Objective-C code (but on Swift instances and types)?
A relatively unrelated note: The decision of what has been modelled as an attribute and what has been added to the core syntax strikes me as pretty arbitrary. It feels like two different teams worked on the syntax and on some attributes. E.g. they put weak and unowned into the language as modifiers, but made #final and #lazy attributes. I believe that once they actually add access modifiers, they will probably be attributes likes final. Is all of this somehow related to Objective-C interoperability?
If we take the iBook as definitive, there appears to be no developer-facing way of creating arbitrary new attributes in the way you can in Java and .NET. I hope this feature comes in later, but for now, it looks like we're out of luck. If you care about this feature, you should file an enhancement request with Apple (Component: Swift Version: X)
FWIW, there's really not a way to do this in Objective-C either.
You can now do something like this! Check out "Property Wrappers" - https://docs.swift.org/swift-book/LanguageGuide/Properties.html
Here's an example from that page:
#propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct SmallRectangle {
#TwelveOrLess var height: Int
#TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"
rectangle.height = 10
print(rectangle.height)
// Prints "10"
rectangle.height = 24
print(rectangle.height)
// Prints "12"