I'm a Swift newbie and I'm trying to get my head around using Structs with optional properties. I've done quite a bit of searching and got something that works but it feels incredibly inefficient so wondered if there's a better/more manageable way of achieving my goal.
I'd like to use Structs to represent a business but I have no idea in advance which combination of properties any specific business is likely to have. This seems to mean that I have to create an init() for every possible combination of parameters.
Here's a simplified example (I have many more properties):
import Foundation struct Business { let name : String var web : String? var address: String? // just the business name init(busName: String) { self.name = busName } // business name + website init(busName: String, website: String) { self.name = busName self.web = website } // business name + address init(busName: String, address: String) { self.name = busName self.address = address } // business name + website + address init(busName: String, website: String, address: String) { self.name = busName self.web = website self.address = address } }
I can then initialise the class like this:
Business(busName: "Dave's Cafe", website: "http://www.davescafe.com") Business(busName: "Sarah's Brewhouse", address: "41 Acacia Ave, Smalltown")
Is there no way to create some kind of init() where the parameters are optional? If you could point me in the direction of terms or concepts to search for that would be great.
The reason you got this error is because by default the constructor or init method as it's called requires all stored properties to have an assigned value thus making them required even if they're optional, to get around this you simply need to create your own init method which accepts only the parameters you need it ...
In Swift, all structs come with a default initializer. This is called the memberwise initializer. A memberwise initializer assigns each property in the structure to self. This means you do not need to write an implementation for an initializer in your structure.
An initializer is a special type of function that is used to create an object of a class or struct. In Swift, we use the init() method to create an initializer.
Use default values:
init(busName: String, website: String? = nil, address: String? = nil) { self.name = busName self.web = website self.address = address }
Then you can call the init like this:
_ = Business(busName: "Foo") _ = Business(busName: "Foo", website: "www.foo.bar") _ = Business(busName: "Foo", address: "bar") _ = Business(busName: "Foo", website: "www.foo.bar", address: "bar")
One approach that you can borrow from other OOP languages is parameter builder pattern. Start with a static method that returns a builder, then add methods for individual parameters, and finally call build()
:
let bakery = Business .withName("Black Forest") .andWebSite("www.blackforest.com") .andAddress("1 Main St, Springfield, IA 98765") .build()
Here is a skeletal implementation that enables this kind of an API:
class Business { // Users never call this init, it's for the builder to use init(name: String, webSite: String?, address: String?) { ... } // Here is the method the users call: static func withName(name: String) { return BusinessBuilder(name) } // This class collects parameters before calling init class BusinessBuilder { var name : String var webSite : String? var address: String? func andAddress(address: String) -> BusinessBuilder { self.address = address return self } func andWebSite(webSite: String) -> BusinessBuilder { self.webSite = webSite return self } func build() -> Business { return Business(name, webSite, address) } init(name: String) { self.name = name } } }
This lets you pass as few or as many initializer parameters as you see fit, in any order that you find convenient in a given situation.
The main use of this approach is when you do not know which parameters you are going to get, for example, when they come from an XML or a database. You can call andXyz
methods in a loop, and then call build()
when you have no other attributes to set.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With