Why do we need to specify init method? - swift

The following block of code runs fine even without specifying the init method. If this is the case, what purpose does the init method serve?
struct Person {
var name: String
var age: Int
init(name: String, age: Int){
self.name = name
self.age = age
}
}
let somePerson = Person(name: "Sam", age: 21)
somePerson.name
somePerson.age
Thank you your feedback.

As pointed out in this doc
Swift initializers
Swift provides a default initializer for any structure or class that
provides default values for all of its properties and does not provide
at least one initializer itself. The default initializer simply
creates a new instance with all of its properties set to their default
values.
Structure types automatically receive a memberwise initializer if they do not
define any of their own custom initializers. Unlike a default
initializer, the structure receives a memberwise initializer even if
it has stored properties that do not have default values.
So if you write one init method, then you MUST write the default initializer yourself, if you want it to exist.
If you only need the default initializer, then you can omit it.

It's the behavior of Struct in swift.
See (Memberwise Initializers for Structure Types) in https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
"Structure types automatically receive a memberwise initializer if
they do not define any of their own custom initializers. Unlike a
default initializer, the structure receives a memberwise initializer
even if it has stored properties that do not have default values."
If you do not specify any memberwise init method, it will create for you. The init that you declare allow you to do more than just an simple init. For example:
struct Person {
var name: String
var age: Int
init(name: String, age: Int){
self.name = name.uppercaseString()
self.age = age + 22
//and more works ...
}
}

You don't need it (anymore). Memberwise initialization is a new feature of Swift 2.2. The author either wrote this code for an earlier version of Swift or didn't know that it's no longer necessary.

Related

How to omit argument name in a struct

Swift beginner here... When passing a value into a struct instance during instantiation, is there a way to omit the argument name? I can't find anything in Swift documentation.
struct Dog {
var _ name: String
}
var buddy = Dog("Buddy")
Obviously this does not work. In functions you can use an underscore before the parameter name to omit it during calling, is this possible in some way with structs?
Properties always have names. What can differ are the keyword labels of the initializer. If you don't specify your own initializer, the compiler will synthesize a default memberwise initializer for you, which will have argument labels that match the member names.
If you wish to change that, you would forego the compiler-provided initializer, and specify your own:
struct Dog {
var name: String
init(_ name: String) {
self.name = name
}
}

In Swift do you not have to use a getter and setter when creating an object type, such as class, that conforms to a protocol requiring so?

protocol UserType {
var name: String { get }
var age: Int { get set }
struct Person: UserType {
var name: String //<- why is this okay?
var age: Int //<- why is this okay? dont we need a getter setter
}
let somePerson = Person(name: "Billy", age: 22)
Why in the above examples, we dont use get constructs or set constructs for age and a get construct for name? Is it because in the protocol when the property is said to "{ get set }" that essentially means it has to be able to be read and has to be able to be changed, which can be done through a declaration in the syntax of a stored property?
Thanks!
protocol UserType {
var name: String { get }
var age: Int { get set }
}
The important thing to understand is that { get } and { get set } are merely notations. They have nothing to do with "getter and setter". They are merely ways of describing to the compiler what the protocol's requirements are. (You could argue that the notation is confusing or misleading, but it's what we've got, so we have to live with it.)
So, basically, all this says is:
An adopter must declare a name String instance property.
An adopter must also declare an age Int instance property and that property must be writable.
That's all it means. Well, you have satisfied those requirements in your adopter (Person). Your code is legal. The end.
One thing to note first: Every property has an implicit getter and setter unless otherwise stated. e.g. a simple var age: Int implicitly has a get and set, and let age: Int implicitly has a get. When you declare a manual getter and setter, you're overriding something, not creating something that didn't otherwise exist.
As for protocols, in explicit terms:
var name: String { get } means the adhering Type must have a property with a signature of name:String that can be read, AKA. get
var age: Int { get set } means the adhering Type must have a property with a signature of age:String that can be read or assigned, AKA get and set
Note that these rules are inclusive not exclusive. The requirements don't care how they are satisfied, and don't disallow anything else.
This means with your given example, a property of let name: String would work because it only requires access, but let age: Int wouldn't work, because it cannot be changed. It also doesn't care about internal details, so using computed properties, private setters, etc. is fine as long as they have the necessary access.

swift - can I call a struct default memberwise init from my custom init method?

If I create a swift struct with no init, then I can call the compiler-generated default memberwise initialiser, like so:
struct OrderFill {
let price:Int
let qty: Int
let timeStamp: NSDate
}
let o = OrderFill(price: 2, qty: 1, timeStamp: someDate)
What I'd like to do is create a convenience init method to deserialise from a dictionary, which then chains to the default memberwise init. Something like
struct OrderFill {
let price:Int
let qty: Int
let timeStamp: NSDate
init(dict:[String:AnyObject]) throws {
self.init(
price: dict["price"] as! Int
qty: dict["qty"] as! Int
timeStamp: try parseDate(dict["ts"] as! String)
}
}
let o = OrderFill(someDict)
When I try write this code though, the compiler (Xcode 7.2) gives me the error "Extra argument 'qty' in call" as though it doesn't see the default memberwise and is trying to recursively call init(dictionary)
I can write my own memberwise init, or I can simply assign the properties directly from my init(dictionary), but it'd be nice if I could chain the call. Is there any way to do this in swift?
Add your own initializer as an extension to your struct. Extensions cannot remove existing functionalities, so it will preserve struct's default initializer.
struct OrderFill {
let price: Int
let qty: Int
let timeStamp: NSDate
}
extension OrderFill {
init(dict: [String: AnyObject]) throws {
self.init(
price: dict["price"] as! Int,
qty: dict["qty"] as! Int,
timeStamp: try parseDate(dict["ts"] as! String)
)
}
}
let o = OrderFill(someDict)
From the apple docs about Initialization:
Structure types automatically receive a memberwise initializer if they do not define any of their own custom initializers
and
Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise initializer, if it is a structure) for that type. This constraint prevents a situation in which additional essential setup provided in a more complex initializer is circumvented by someone accidentally using one of the automatic initializers instead.
Therefore the answer is NO. You cannot provide a custom initializer and use the memberwise initializer at the same time.
It sounds like this swift evolution proposal talks about extending the capabilities of the current memberwise initializer. But of course that is not out yet, you can however still take a look to learn a bit about what the limitations of the current situation are.

Init with default vs convenience initialiser

Is there any difference between init with default value vs calling convenience initialiser ? I try to figure out when should I use convenience instead of init with default.
class Food
{
var name :String
init(name : String = "Unknow")
{
self.name = name
}
}
And this :
class Food
{
var name :String
init(name : String)
{
self.name = name
}
convenience init()
{
self.init(name : "Unkow")
}
}
Convenience initializer is easier for users of the class than a designated initializer with a default value because Xcode lists the two initializers by autocompletion.
Autocompletion for a designated initializer with a default value:
Autocompletion for a designated initializer and convenience initializer:
The screenshots are taken with Xcode 6.2. Unless Xcode supports autocompletion for default values, the convenience initializer is easier for the users, especially if you design a framework for people.
A disadvantage of a convenience initializer is, as Kelvin mentioned in his comment, you cannot use the convenience initializer from the subclass initializer.

How can I make the memberwise initialiser public, by default, for structs in Swift?

I have a Swift framework that defines a struct:
public struct CollectionTO {
var index: Order
var title: String
var description: String
}
However, I can't seem to use the implicit memberwise initialiser from another project that imports the library. The error is:
'CollectionTO' cannot be initialised because it has no accessible initialisers
i.e. the default synthesized memberwise initialiser is not public.
var collection1 = CollectionTO(index: 1, title: "New Releases", description: "All the new releases")
I'm having to add my own init method like so:
public struct CollectionTO {
var index: Order
var title: String
var description: String
public init(index: Order, title: String, description: String) {
self.index = index;
self.title = title;
self.description = description;
}
}
... but is there a way to do this without explicitly defining a public init?
Quoting the manual:
"Default Memberwise Initializers for Structure Types
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Otherwise, the initializer has an access level of internal.
As with the default initializer above, if you want a public structure type to be initializable with a memberwise initializer when used in another module, you must provide a public memberwise initializer yourself as part of the type’s definition."
Excerpt from "The Swift Programming Language", section "Access Control".
While it is not possible to have the default memberwise initializer at least you can make one quickly with the following steps:
UPDATE: Xcode 11 and later
As mentioned by Brock Batsell on the comments, for Xcode 11 and later all you need to is this:
Right click the class or struct name and choose refactor ->
Generate Memberwise Initializer
Xcode 10 and earlier answer
Make the object a class temporarily instead of a struct
Save
Right click the class name and choose refactor ->
Generate Memberwise Initializer
Change it back to a struct
We now have a ruby gem 💎 to parse a complete swift data model file, line-by-line, and add public access modifiers, public member-wise default initializers, and other things into a separate auto-generated output swift file.
This gem is called swift_republic
Please check out the following documentation for running this gem:
https://github.com/mehul90/swift_republic
Sometimes it's really annoying having an initializer when you don't need one. If you're constantly updating the variables to the object, it becomes bothersome very quickly to update the variables in 3 places (variable declaration, initializer parameter, and initializer implementation).
A workaround I've used for this issue is to have a static variable on the struct to act as (or essentially wrap) the "initializer". For instance:
struct MyStruct {
static var empty = Self()
static func empty(name: String) -> Self {
.init(privateName: name)
}
private var identifier: String = ""
}
Then you can call it similar to how you would an initializer (with autocomplete and everything!):
func someFunction(_ value: MyStruct) { ... }
//someFunction(.init()) -> ERROR, invalid due to `private` variable
someFunction(.empty)
someFunction(.empty(name: "Dan IRL"))
let myObject = MyStruct.empty
let myObject2 = MyStruct.empty(name: "Monty Python")
You have to define public init by yourself, luckily starting from Xcode 14 🥳 there is an automatic initializer completion (source - 60399329)