Consider following code:
private class MyClass {
private required init(_ i: Int) {
}
}
Now, I'm getting compiler error:
'required' initializer must be as accessible as its enclosing type. But the type is declared as private.
I thought the issue might be that the class is declared on a file level, so it's
private type
restricts the use of an entity to the enclosing declaration.
as documentation says. So it effectively makes it fileprivate, since the enclosing declaration is a file. But I'm getting the same error even for nested private classes.
When I declare initializer as fileprivate it works:
private class MyClass {
private class MyNested {
fileprivate required init(_ i: Int) {
}
}
}
Question: why private is not correct here? Is it possible to declare private required initializer?
I ask because I want to understand the error in the first place.
Why I need it is rather different question, but initially I wrote such code:
private class MyClass {
private class MyNested {
private init(_ i: Int) {
}
func foo() {
var f = type(of: self).init(1)
}
}
}
and then had to add required declaration specifier as I was getting the error: constructing an object of class type 'MyClass.MyNested' with a metatype value must use a 'required' initializer
Answer:
Thanks to #SørenMortensen:
The issue with the private initialiser is that the MyNested class is accessible within MyClass, but the initialiser is only accessible within MyNested, and therefore is not as accessible as its enclosing type. And as #Hamish points out, the concept of a required initialiser is mutually exclusive with the concept of a private initialiser.
So it looks like it is not possible to have private required initializer, indeed. And in my case I can make MyNested class to be final and then make the initializer private not making it required.
Why would you want one?
The purpose of required means that one is forced to use this initialization when declaring this class. Like #Hamish said, setting it as private would just result in classes unable to see it and cannot declare it. If you want additional setup but don't want it to be accessible, why not just call a private func within a required init?
Related
To create a singleton class, I wrote something like this:
class SingletonEx{
var name = ""
private init(){}
static let sharedInstance = SingletonEx()
func instanceMethod(){
}
static func classMethod(){
}
}
Some tutorials say final is necessary while others just ignore final keyword. After I tried subclassing SingletonEx, I got the following results.
It seems I can't write an initializer for subclass, which means I can't use an override instance method in a subclass.
As far as I know, singleton definition is all about single instantiation and accessing instance methods through the only instance. So I don't think it is necessary to use final in the singleton definition. But both my teachers and some online tutorials say it is necessary.
I got confused, since you can't create a subclass instance anyway, even you override the instance methods, you can't use it or access it, what's the point to say final is necessary for a singleton class?
If I am wrong, please point out.
Super Class
First of all you need to know the properties and methods that are marked with private are just known to the Super class and Sub classes won't access them!
A class can inherit methods, properties, and other characteristics from another class. When one class inherits from another, the inheriting class is known as a subclass, and the class it inherits from is known as its superclass. Inheritance is a fundamental behavior that differentiates classes from other types in Swift.
Classes in Swift can call and access methods, properties, and subscripts belonging to their superclass and can provide their own overriding versions of those methods, properties, and subscripts to refine or modify their behavior. Swift helps to ensure your overrides are correct by checking that the override definition has a matching superclass definition.
In your case in SingletonEx class you market init with private which means that you can create object just in the body of the class! that means no one, no where, can't create an object of SingletonEx!
If you want to a method end up in Super Class and you don't want to Sub classes overide that method you need to mark that method with final which means it's not private but its available only from Super class!
Sub Class
When class Y Inheritance from SingletonEx which means that cant create an object outside of the class ! because Super class initializer is unavailable during init() method from class Y ! While you need to call the super.init() if you want to initialize an object from Y class !
if you remove private from private init() {} from SingletonEx class you be able to create object from SingletonEx class and also from Y class !
your code should looks like this :
Swift 4 :
class SingletonEx{
var name = ""
init(){}
static let sharedInstance = SingletonEx()
func instanceMethod(){
}
static func classMethod(){
}
}
class Y : SingletonEx {
private var yName = "Y name is : "
init(name:String) {
super.init()
self.name = self.yName + name
}
}
Usage :
override func viewDidLoad() {
super.viewDidLoad()
let yObject = Y.init(name: "badGirl :D ")
print(yObject)
// --> Output : Y name is : badGirl :D
}
If I declare
public class A: NSObject {
public class X { }
public init?(x: X? = nil) { }
}
all is fine. When using it like let a = A(), the initializer is called as expected.
Now, I'd like to have the nested class X private, and the parameterized init as well (has to be, of course). But a simple init?() should stay publicly available as it was before. So I write
public class B: NSObject {
private class X { }
private init?(x: X?) { }
public convenience override init?() { self.init(x: nil) }
}
But this gives an error with the init?() initializer: failable initializer 'init()' cannot override a non-failable initializer with the overridden initializer being the public init() in NSObject.
How comes I can effectively declare an initializer A.init?() without the conflict but not B.init?()?
Bonus question: Why am I not allowed to override a non-failable initializer with a failable one? The opposite is legal: I can override a failable initializer with a non-failable, which requires using a forced super.init()! and thus introduces the risk of a runtime error. To me, letting the subclass have the failable initializer feels more sensible since an extension of functionality introduces more chance of failure. But maybe I am missing something here – explanation greatly appreciated.
This is how I solved the problem for me:
I can declare
public convenience init?(_: Void) { self.init(x: nil) }
and use it like
let b = B(())
or even
let b = B()
— which is logical since its signature is (kind of) different, so no overriding here. Only using a Void parameter and omitting it in the call feels a bit strange… But the end justifies the means, I suppose. :-)
After a bit of fiddling I think I understand. Let's consider a protocol requiring this initializer and a class implementing it:
protocol I {
init()
}
class A : I {
init() {}
}
This gives the error: "Initializer requirement 'init()' can only be satisfied by a required initializer in non-final class 'A'". This makes sense, as you could always declare a subclass of A that doesn't inherit that initializer:
class B : A {
// init() is not inherited
init(n: Int) {}
}
So we need to make our initializer in A required:
class A : I {
required init() {}
}
Now if we look at the NSObject interface we can see that the initializer is not required:
public class NSObject : NSObjectProtocol {
[...]
public init()
[...]
}
We can confirm this by subclassing it, adding a different initializer and trying to use the normal one:
class MyObject : NSObject {
init(n: Int) {}
}
MyObject() // Error: Missing argument for parameter 'n:' in call
Now here comes the weird thing: We can extend NSObject to conform to the I protocol, even though it doesn't require this initializer:
extension NSObject : I {} // No error (!)
I honestly think this is either a bug or a requirement for ObjC interop to work (EDIT: It's a bug and already fixed in the latest version). This error shouldn't be possible:
extension I {
static func get() -> Self { return Self() }
}
MyObject.get()
// Runtime error: use of unimplemented initializer 'init()' for class '__lldb_expr_248.MyObject'
Now to answer your actual question:
In your second code sample, the compiler is right in that you cannot override a non-failable with a failable initializer.
In the first one, you aren't actually overriding the initializer (no override keyword either), but instead declaring a new one by which the other one can't be inherited.
Now that I wrote this much I'm not even sure what the first part of my answer has to do with your question, but it's nice to find a bug anyways.
I suggest you to do this instead:
public convenience override init() { self.init(x: nil)! }
Also have a look at the Initialization section of the Swift reference.
In the Swift documentation section about Protocols, it is written that :
Always prefix type property requirements with the static keyword when you define them in a protocol. This rule pertains even though type property requirements can be prefixed with the class or static keyword when implemented by a class
I don't understand if you should always do that or just in specific cases (because later in the docs, static isn't always there).
Why is it recommended to use static? What does it mean concretely?
Thank you.
EDIT : The same question applies to methods requirements.
later in the docs, static isn't always there
This section talks about type requirement, i.e. a requirement of the conforming type to have a static property of the specific name and type. In other words, when you write this
protocol MyProtocol {
static var myProperty: Int { get set }
}
the conforming class must do this
class MyClass : MyProtocol {
static var myProperty: Int
}// ^^^^^^
It also has an option to do this:
class MyClass : MyProtocol {
class var myProperty: Int
}// ^^^^^
but there is no such option when defining a protocol.
When static is not used, as happens later in the docs, the requirement becomes an instance requirement, not a type requirement:
protocol MyProtocol2 {
var myProperty2: Int { get set }
}
Now the class must provide an instance variable:
class MyClass2 : MyProtocol2 {
var myProperty2: Int
}
Note that there is a similar limitation on declaring instance requirements in protocols: you always use var, even though an implementation may use let to satisfy the requirement:
protocol MyProtocol3 {
var myProperty3: Int { get }
}// ^^^
class MyClass3 : MyProtocol3 {
let myProperty3 = 42
}// ^^^
When creating a public class, is it necessary to make the designated initializer public?
What's the difference between making it public vs not?
e.g.
public class A {
init() {}
}
or
public class A {
public init() {}
}
No
You do not need to make it public. In fact, you can make it private. The only thing special about a designated initializer (and your classes are allowed more than one) is that they are responsible for ensuring the object is fully initialized.
A convenience initializer (an initializer is either designated or convenience) (marked by the convenience keyword) does not have this responsibility. A convenience initializer delegates to a designated initializer first, and then after that returns, it is given the opportunity to overwrite some of the initial values. Importantly, the convenience initializer must always "delegate across". It must always call another initializer within the class.
By contrast, a designated initializer (any initializer not marked with the convenience keyword) must take responsibility for making sure every property in the class gets a valid initial value, then it must "delegate up" (call a super class initializer, if there is a super class), then it can overwrite values if it wants.
But nothing prevents you from create a class with nothing but private designated initializers (nothing at all).
Even when we are inheriting from a class with a required initializer, our subclass can simply implement that initializer as a convenience initializer.
class ParentClass {
let foo: Int
required init(foo: Int) {
self.foo = foo
}
}
class ChildClass: ParentClass {
let bar: Int
private init(foo: Int, bar: Int) {
self.bar = bar
super.init(foo: foo)
}
required convenience init(foo: Int) {
self.init(foo: foo, bar: foo)
}
}
The above is perfectly valid Swift.
ChildClass has a private designated initializer. It has inherited from a parent class with a required initializer, but it has implemented that initializer as a convenience initializer. (Moreover, in contrast to your conjecture in a comment here and a question else where, the required initializer doesn't actually have to be public necessarily... context matters, and perhaps I will expand on that in an answer to your other question.)
Moreover, in direct contrast to subjective_c's answer, you don't have to make parts of your Swift code public in order to use that code from Objective-C, and the passage from Apple he has quoted in his answer actually indicates that. You only need to mark public the things you want to be available outside of the framework in which your code has been implemented. You can put Objective-C & Swift code within the same application and the Objective-C code can see anything marked as public or internal.
You don't need to make it public unless you are making it available as a framework for usage in Objective-C as well.
See: https://developer.apple.com/swift/blog/?id=5
When mixing Objective-C and Swift, because the generated header for a
framework is part of the framework’s public Objective-C interface,
only declarations marked public appear in the generated header for a
Swift framework.
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)