How to automatically create an initializer for a Swift class? - ios

UPDATE:
Use structs and not classes. Struct is better in many ways has got an initializer of its own.
This is my model class. Is it possible to create the init method automatically? Everytime I have to initialize all the variables one by one and it costs a lot of time.
class Profile {
var id: String
var name: String
var image: String
init(id: String, name: String, image: String) {
self.id = id
self.name = name
self.image = image
}
}
I want self.id = id and other variables to initialize automatically.

Update As of Xcode 11.4
You can refactor (right-click mouse menu) to generate the memberwise initializer for class and struct.
Note that struct automatic initializers are internal. You may want to generate memberwise initializer when defining a module to make it public.
Right-click > Refactor > 'Generate Memberwise Initializer'
For older Xcode
There are handy plugins:
https://github.com/rjoudrey/swift-init-generator https://github.com/Bouke/SwiftInitializerGenerator

Given the following class (or for structs if temporarily change the keyword struct to class and after refactor set back to struct):
class MyClass {
let myIntProperty: Int
let myStringProperty: String
let myOptionalStringProperty: String?
let myForcedUnwrappedOptionalStringProperty: String!
}
Go to Xcode and:
Double click the class name
Right click
Refactor
Generate Member-wise Initializer
Above steps look like this:
Just a tiny second later, Xcode generates this initializer:
internal init(myIntProperty: Int, myStringProperty: String, myOptionalStringProperty: String?, myForcedUnwrappedOptionalStringProperty: String?) {
self.myIntProperty = myIntProperty
self.myStringProperty = myStringProperty
self.myOptionalStringProperty = myOptionalStringProperty
self.myForcedUnwrappedOptionalStringProperty = myForcedUnwrappedOptionalStringProperty
}

No, there is no such feature for classes. But, if you design this as a struct, you get an memberwise initializer for free — assuming you don't define others initializers yourself.
For instance:
struct Point {
var x: Float
var y: Float
}
...
var p = Point(x: 1, y: 2)
From The Swift Programming Language book:
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.
The memberwise initializer is a shorthand way to initialize the member properties of new structure instances. Initial values for the properties of the new instance can be passed to the memberwise initializer by name.

You can use struct if you want automatically created init without actual need to define it.
If you want to define it as fast as possible for classes, you can use automatic initializer completion luckily introduced in Xcode 14 🥳 (source - 60399329)

Related

private(set) with let properties - 'private(set)' modifier cannot be applied to read-only properties

I'm already aware of how private(set) works. But the below code is give compile-time error,
class Person {
private(set) let name: String //Error.
private(set) let age: Int //Error.
init(name: String, age: Int){
self.name = name
self.age = age
}
}
Error:
'private(set)' modifier cannot be applied to read-only properties
Since name and age are not read-only properties, it shouldn't give such an error.
If I use let instead of var, it is working fine. Just trying to know why?
private(set) let is a contradiction in terms. Any setter requires a variable.
Please be aware that assigning a default value to a property is not setting the property in terms of initialization. For this reason the didSet property observer is not called after assigning a default value to a property.
In Swift there is only one case to use private(set): If the class contains code which modifies the variable
class Foo {
let name : String
private(set) var expiryDate : Date
init(name: String, expiryDate: Date){
self.name = name
self.expiryDate = expiryDate
}
func extendExpiryDate(to newDate : Date) {
expiryDate = newDate
}
}
If the property is only initialized during init it's a constant – as correctly mentioned in the other answers – so declare it as let constant. Unlike other programming languages Swift provides explicit constants with the benefit of more security, less memory usage and better performance.
Let are constants ... you cant change their value one you assign them. Thats why they are considered readOnly ... so error is valid ... either convert them to var if you want to set them after assign them any value or remove private(set) and make them just private
happy coding =)
They are read-only, because you’ve declared them with let, not var.
You don't need a private setter when it is a let constant.
Just initialize it in the constructor or on the same line as the declaration.
If you want to change it outside of the constructor, you need to make it a var
From the Swift.org under Getters and Setters
You can give a setter a lower access level than its corresponding
getter, to restrict the read-write scope of that variable, property,
or subscript. You assign a lower access level by writing
fileprivate(set), private(set), or internal(set) before the var or
subscript introducer.
Related post: Swift: What does this error: 'private(set)' modifier cannot be applied to read-only properties mean?

Swift init(), defining custom init is it overriding?

I have some confusion over this:
class Person {
var name: String
var age: Int
init(){
name = “Tim”
age = 30
}
}
A basic custom init. What exactly is the code below doing?:
init(){
//Code here
}
It's not an override because we don't use override func init(). Someone said it's actually calling a default init method that comes with the class. If that's the case then what do the curly brackets do?
Because you haven't subclassed from NSObject, there is no init() method to override. Hence why you don't need it in this case.
In this case, nothing else is called when you call your init() method, there are no other default initializers. The curly brackets are there simply because you're not accepting any arguments to your init method. You could for example accept some arguments:
init(withName name: String, andAge age: Int) {
self.name = name
self.age = age
}
And call it like so:
Person(withName: "Chris", andAge: 23)
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.
You can customize the initialization process with input parameters and optional property types, or by assigning constant properties during initialization.
You can provide initialization parameters as part of an initializer’s definition, to define the types and names of values that customize the initialization process. Initialization parameters have the same capabilities and syntax as function and method parameters.

Determining from Objective-c if a Swift property is declared as dynamic

I have been trying for some time to inspect a Swift class, and determine if any of the properties are declared as dynamic. My example class is as below:
class SwiftTestClass : DBObject {
dynamic var SwiftTestString : String!
dynamic var SwiftTestNumber : NSNumber!
dynamic var lowercaseField : String!
var nonDynamicVariable : String!
func testThyself() {
SwiftTestClass.query().fetchLightweight().removeAll()
let newObject = SwiftTestClass();
newObject.SwiftTestString = "hello, world"
newObject.SwiftTestNumber = 123
newObject.lowercaseField = "lowercase"
newObject.nonDynamicVariable = "should not be persisted"
newObject.commit()
let result = SwiftTestClass.query().fetch().firstObject;
print(result)
}
}
I am basically trying to pick out the fact that the property nonDynamicVariable is not declared as dynamic as the rest of them are.
DBObject is a subclass of NSObject.
I have tried:
Looking at the type encoding of the property, they are identical (type for type)
Seeing if they have a difference in the method implementations, they do not. (e.g. class_getMethod), the dynamic properties still have getter/setter methods.
Grabbing the Ivars to see if there is any difference there
Looking at all of the property attributes, also identical.
What I do know:
If I try to class_replaceMethod for the <propertyName>/set<propertyName>, it works for a dynamic property (as you would expect, because it adds objc compatibility) but fails to work (but does replace?, well, the memory address of the method changes!) or be actioned on the non dynamic property.
Does anyone know how to differentiate the two property declarations in swift from objc?
Thanks

initializing class properties before use in Swift/iOS

I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash

Swift dynamic variable can't be of type Printable

I have a Swift project that contains two UITableViewControllers. The second UITableViewController is linked to a MVC model called Model. According to the UITableViewCell I select in the first UITableViewController, I want to initialize some properties of Model with Ints or Strings. Therefore, I've decided to define those properties with Printable protocol type. In the same time, I want to perform Key Value Observing on one of these properties.
Right now, Model looks like this:
class Model: NSObject {
let title: String
let array: [Printable]
dynamic var selectedValue: Printable //error message
init(title: String, array: [Printable], selectedValue: Printable) {
self.title = title
self.array = array
self.selectedValue = selectedValue
}
}
The problem here is that the following error message appears on the selectedValue declaration line:
Property cannot be marked dynamic because its type cannot be
represented in Objective-C
If I go to the Xcode Issue Navigator, I can also read the following line:
Protocol 'Printable' is not '#objc'
Is there any workaround?
There is no way to do what you want. Non-#objc protocols cannot be represented in Objective-C. One reason is that Non-#objc protocols can represent non-class types (and indeed, you said that you wanted to use it for Int and String, both non-class types), and protocols in Objective-C are only for objects.
KVO is a feature designed for Objective-C, so you must think about what you expect it to see from the perspective of Objective-C. If you were doing this in Objective-C, you would not want to have a property that could either be an object like id or a non-object like int -- you can't even declare that. Instead, as you said in your comment, you probably want it to be just objects. And you want to be able to use Foundation's bridging to turn Int into NSNumber * and String into NSString *. These are regular Cocoa classes that inherit from NSObject, which implements Printable.
So it seems to me you should just use NSObject or NSObjectProtocol.
Unfortunately ObjC does not treat protocols as types, they are just a convenient way of grouping members. Under the covers they are of type Any, so regretfully you will have to make the property Any and cast to Printable.
The best I can thing of is:
dynamic var selectedValue: Any
var printableValue : Printable {
get {
return (Printable)selectedValue
}
set {
selectedValue = newValue
}
}

Resources