Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Initialize Struct with optional stored properties

Tags:

ios

swift

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.

like image 549
James Avatar asked May 31 '16 19:05

James


People also ask

Can struct be optional Swift?

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 ...

How do you initialize a struct in Swift?

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.

What are Initializers in Swift?

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.


2 Answers

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") 
like image 111
dasdom Avatar answered Sep 22 '22 05:09

dasdom


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.

like image 22
Sergey Kalinichenko Avatar answered Sep 20 '22 05:09

Sergey Kalinichenko