3 H3: Gegevens structureren
Tot nu toe hebben we losse variabelen gebruikt: één voor een naam, één voor een leeftijd. Maar wat als je meerdere dingen bij elkaar wilt bewaren die bij hetzelfde onderwerp horen? In dit hoofdstuk leer je hoe je gegevens samenvoegt in een struct — en hoe je die gegevens kunt lezen uit een JSON-bestand.
3.1 Wat gaan we bouwen?
We bouwen het fundament van de EnergyClock app: het EnergyPrice-model. Dit is de struct die één uurprijs beschrijft — welk uur het is, wat de prijs is, en op welke dag. Straks gebruikt de hele app dit model.
// Bron: EnergyClock/EnergyPrice.swift
struct EnergyPrice: Codable, Equatable, Identifiable {
var id: Date { date }
let hour: Int
let price: Double?
let date: Date
}3.2 Playground-voorbeeld
import Foundation
// Een struct beschrijft hoe iets eruitziet
struct Boek {
let titel: String
let auteur: String
var bladzijden: Int
}
// Een instantie maken van de struct
var mijnBoek = Boek(titel: "Swift op macOS", auteur: "Bas", bladzijden: 300)
// Eigenschappen uitlezen
print(mijnBoek.titel) // Swift op macOS
print(mijnBoek.bladzijden) // 300
// Een var-eigenschap aanpassen
mijnBoek.bladzijden = 320
print(mijnBoek.bladzijden) // 3203.3 Concept uitgelegd
3.3.1 Struct: een recept
Een struct is als een recept. Het recept voor een appeltaart beschrijft wat erin gaat: appels, bloem, suiker. Maar het recept zelf is geen taart — het is alleen de beschrijving.
Als je de taart daadwerkelijk maakt, maak je een instantie van het recept. Je kunt hetzelfde recept meerdere keren gebruiken om verschillende taarten te maken.
// Het recept (de struct)
struct Persoon {
let naam: String
var leeftijd: Int
}
// De taart (een instantie)
var emma = Persoon(naam: "Emma", leeftijd: 9)
var lotte = Persoon(naam: "Lotte", leeftijd: 11)
print(emma.naam) // Emma
print(lotte.naam) // LotteElke instantie heeft zijn eigen kopie van de gegevens. Als je emma.leeftijd verandert, verandert lotte.leeftijd niet mee.
3.3.2 Optionals: een doosje dat leeg kan zijn
In het echte leven zijn sommige gegevens soms gewoon niet beschikbaar. De stroomprijs van morgen weten we vanavond nog niet. In Swift gebruik je hiervoor een optional: een waarde die er kan zijn, maar ook nil kan zijn — leeg.
Je herkent een optional aan het vraagteken achter het type:
var prijsVanMorgen: Double? = nil // nog niet bekend
var prijsVanGisteren: Double? = 0.08 // wel bekendAls je een optional gebruikt, moet je altijd controleren of hij leeg is:
if let prijs = prijsVanMorgen {
print("Prijs: \(prijs)")
} else {
print("Prijs nog niet bekend")
}Dit heet optional binding: je “opent het doosje” en kijkt of er iets in zit.
3.4 Code schrijven
We maken nu het EnergyPrice-model voor de EnergyClock. Maak een nieuw Swift-bestand aan in Xcode: File > New > File > Swift File, noem het EnergyPrice.swift.
Stap 1: de basis struct
import Foundation
// Bron: EnergyClock/EnergyPrice.swift
// Model voor energieprijs per uur
struct EnergyPrice {
let hour: Int // het uur (0 tot 23)
let price: Double? // de prijs in euro's — nil als nog niet bekend
let date: Date // de datum van dit uur
}Stap 2: een hulpeigenschap toevoegen
Een computed property berekent zijn waarde op basis van andere eigenschappen:
struct EnergyPrice {
let hour: Int
let price: Double?
let date: Date
// Berekende eigenschap: is er een prijs beschikbaar?
var isAvailable: Bool {
price != nil
}
}Stap 3: een instantie maken en testen
let prijsAchtuur = EnergyPrice(hour: 8, price: 0.09, date: Date())
let prijsNegentien = EnergyPrice(hour: 19, price: nil, date: Date())
print(prijsAchtuur.isAvailable) // true
print(prijsNegentien.isAvailable) // falseStap 4: Codable toevoegen
Later willen we prijzen ophalen van het internet als JSON. Door Codable toe te voegen, kan Swift dat automatisch vertalen:
struct EnergyPrice: Codable {
let hour: Int
let price: Double?
let date: Date
}Meer over JSON en Codable lees je in H7.
In Swift zijn structs value types: als je een struct kopieert, krijg je een volledig onafhankelijke kopie. Wijzig je de kopie, dan verandert het origineel niet.
var a = Boek(titel: "Swift", auteur: "Bas", bladzijden: 300)
var b = a // b is een volledige kopie
b.bladzijden = 400 // a.bladzijden blijft 300Classes zijn reference types: kopiëren geeft geen nieuwe instantie maar een tweede verwijzing naar hetzelfde object. Wijzig je de “kopie”, dan wijzig je het origineel.
Swift moedigt het gebruik van structs aan. Ze zijn eenvoudiger te redeneren over, veiliger in concurrent code (meerdere threads), en goedkoper qua geheugen omdat ze op de stack leven in plaats van de heap.
De keuze voor struct vs. class heeft ook gevolgen voor automatic reference counting (ARC) — het mechanisme waarmee Swift geheugen vrijgeeft. Structs worden automatisch vrijgegeven zodra ze buiten scope gaan; classes hebben ARC nodig om bij te houden wanneer ze nog ergens naar verwezen worden.
3.5 Apple documentatie
Meer over structs en properties:
docs.swift.org — Structures and Classes
Meer over optionals:
docs.swift.org — The Basics: Optionals
Open de documentatie en zoek het gedeelte “Optional Binding”. Lees de eerste twee voorbeelden — die laten precies zien hoe het if let-patroon werkt.
3.6 Samenvatting
| Begrip | Betekenis |
|---|---|
struct |
Een mal waarmee je gegevens groepeert |
| Instantie | Een concreet object gemaakt van een struct |
let (in struct) |
Eigenschap die na aanmaken niet meer verandert |
var (in struct) |
Eigenschap die later nog aangepast kan worden |
Optional (?) |
Een waarde die er kan zijn, maar ook nil kan zijn |
nil |
Leeg — geen waarde |
if let |
Optional binding: controleer of een optional een waarde heeft |
| Computed property | Een eigenschap die zijn waarde berekent |
Codable |
Protocol dat een struct leesbaar maakt voor JSON |
3.7 Opdracht
Maak een eigen struct in een Xcode Playground: Stroom. Hij heeft drie eigenschappen:
uur: eenInt(welk uur van de dag)watt: eenDouble?(het verbruik in watt — optioneel, want soms onbekend)isSpitsuur: eenBool
Maak daarna twee instanties: één voor 8 uur ’s ochtends met een bekend verbruik, en één voor 3 uur ’s nachts zonder verbruik. Druk voor beide af of het spitsuur is en of het verbruik beschikbaar is.