Hiding inner class in swift - swift

I'm trying to hide a inner class so the caller is not aware of it. If the caller want, they can get an instance of the inner class by using a public method. But the caller should not be able to directly get the inner class.
Example to show what I want:
class A {
static func getSettings() -> Settings {
return Settings()
}
class Settings {
func turnOnSomeThing() {
}
}
}
class test {
func testFunc() {
A.getSettings().turnOnSomeThing() // correct way of calling
A.Settings().turnOnSomeThing() // should not be possible
}
}
Is this possbile in Swift?

Why do both calls work in the test caller?
Both currently work because:
The first case, A.getSettings()... is a classic getter that returns an object. I'm just puzzled with the fact that a new Settings is constructed each time, but this is perfectly legit.
The second case, A.Settings().... invokes the constructor of Settings to create an anonymous object, and invokes some methods on it. It's ok, because the inner class is public, but it's weird.
Can you make the inner class private?
You could make an inner class private to avoid it being accessed from the outside world:
This would work perfectly well for an private helper class totally invisible to the outside world.
For Settings this is not possible, because getSettings() returns objects of this class to the outside world, and this makes only sense if the outside world knows the class to deal with it.
Exemple:
class A {
static func getSettings() -> Settings { // ERROR if Settings would be private
let helper = Helper() // purely internal use: Helper can be private :-)
helper.demo()
return Settings()
}
class Settings { // making private is not possible (see above)
func turnOnSomeThing() {
print ("On")
}
}
private class Helper { // Cannot be used outside
func demo() {
print ("Demo")
}
}
}
But how to do with Settings?
If you want to return Settings objects to the wild outside world you need to keep that class public. However, if you want to avoid that the outside world misues the inner class and avoid objects to be created from the outside wolrd, you can use access control on the constructor:
class A {
static func getSettings() -> Settings {
...
}
class Settings {
fileprivate init() { /// <=== ACCESS CONTROL internal or fileprivate
}
func turnOnSomeThing() {
...
}
}
}
This prevents the calls of the form A.Settings()..., but only according to swift access control: with internal you can still call the constructor from another file of the same module; with fileprivate the constructor can only be called from within the source file in which you've defined your external class.
This technique of making the constructor inaccessible while keeping the class usable is frequently used for classes which instances shall only be created via a factory.

Related

SwiftUI calling helper functions from another swift class?

I have created my app using swiftui. Then I found that sometimes I may need to do some tasks quite frequently. Instead of placing it in the same swiftui files, I think that it should be placed inside another class. However, I wonder how can we call the function of another class inside swiftui? is that I must have new an object to call it?
As I understand your question:
class MyHelper {
static func helpMe(needHelp: Bool) -> String {
if(needHelp) {
return "Help in on the way!"
} else {
return "You are ok!"
}
}
}
Usage:
let result = MyHelper.helpMe(needHelp: true)
You can use static or class functions inside the helper class, and access to them by '.' syntax. Difference between static and class functions, that you can override class function in the subclass (which is not really needed with helper classes), static not.

Shared instance accross inheritage

Say a framework provides customizable services through an open class A, that exposes a shared instance to use as follows:
open class A {
public static let shared = A()
open func aService() {}
}
The regular usage of the service is as follows:
A.shared.aService()
Important note: the shared instance itself is also used from within the class A code or related code in the Framework.
Say you want to customize the service through inheritance, and you still want to keep the use of the shared instanced as follows:
override class B: A {
public override func aService() {
super.aService()
}
}
When you refer to the shared instance, unfortunately, it refers to the class A instance where you would like it refers to the inherited class instance.
B.shared.aService() // Failed!: This actually calls A.shared.aService()
One way to fix it is to make the construct as follows:
class A {
public static var shared = A()
}
Then you are sure before any use of the service within you app, to change the instance as follows:
A.shared = B()
B.shared.aService() // OK! This actually calls B.aService()
Though the whole thing works, I would like to make it automatic and not rely on the initial line that changes the shared instance.
How would you do that?
[CODE FOR PLAYGROUND] that illustrates the goal to achieve and to help you better understand the question
open class A {
public static var shared = A()
open func aService() {
print("\(type(of: self)): service (from A)")
}
}
class B: A {
public override func aService() {
super.aService()
print("\(type(of: self)): service (from B)")
}
}
A.shared = B() // Question : How to remove the need to this line, yet achieving the same functionality (and output)
A.shared.aService()
B.shared.aService()
// The output (which is correct) :
//B: service (from A)
//B: service (from B)
//B: service (from A)
//B: service (from B)
There's a solution, but...
I do, personally, agree with the other comments here. This really doesn't sound like a job for the singleton pattern.
In your current solution, you seem perfectly happy to overwrite the singleton instance mid-execution, which means it's not a singleton. There's only ever supposed to be one instance of a singleton, and if you can continually create and assign a new instance to the shared variable without breaking anything, then it's just not necessary to have this global state across your application.
However, for the record, you can achieve what you want with a struct that composes the static instances of each class, and which can detect the calling context and return the appropriate one each time shared is accessed:
protocol ExampleProtocol {
static var shared: ExampleProtocol { get }
func service()
}
struct ExampleProvider {
private static var a = A()
private static var b = B()
static func instance(type: ExampleProtocol.Type) -> ExampleProtocol {
return type == A.self ? ExampleProvider.a : ExampleProvider.b
}
}
class A: ExampleProtocol {
static var shared: ExampleProtocol {
return ExampleProvider.instance(type: Self.self)
}
func service() {
print("Hello")
}
}
class B: A {
override func service() {
print("Goodbye")
}
}
A.shared.service() // Hello
B.shared.service() // Goodbye
So, yes, you can achieve what you want. But there's a pretty strong case for saying that you shouldn't...

Private qualifier to an extension produces fileprivate behavior

Person.swift
class Person {
var name: String
init(name: String){
self.name = name
}
func greet() {
print("Hello, I am \(name)")
}
}
Workplace.swift
class WorkPlace {
var employees: [Person]
init(employees: [Person]) {
self.employees = employees
}
func greet() {
for employee in employees {
employee.customGreeting()
}
}
}
private extension Person {
func customGreeting() {
print("Hi")
}
}
Here I have a simple class called Person defined in Person.swift and a Workplace class defined in Workplace.swift.
The following is an excerpt taken from the Swift language guide (Access Control)
Alternatively, you can mark an extension with an explicit access-level modifier (for example, private) to set a new default access level for all members defined within the extension.
According to this I would expect the method customGreeting in the private extension of Place not be visible inside Person.swift as it would be a private method and it is defined in a different file than the one the class is declared in, which is exactly what happens. I would also expect that method to not be visible inside Workplace.swift but it does. I am able to compile this code without any errors.
If I mark the method as private explicitly then it is not visible inside Workplace.swift. Shouldn't specifying the extension as private be enough to make the method private like it says in the language guide?
private extension Person {
private func customGreeting() {
print("Hi")
}
}
I know that this is a contrived example and I am just trying to get a clear picture of how access control works in swift.
The issue isn't that methods declared in a private extension available in other classes, but rather, more narrowly, that private qualifier to an extension produces fileprivate behavior.
This is explicitly acknowledged in SE-0025, which says:
As before, an extension with an explicit access modifier overrides the default internal access by specifying a default scope. Therefore, within an extension marked private, the default access level is fileprivate (since extensions are always declared at file scope). This matches the behavior of types declared private at file scope.
This seems inconsistent with their broader statement:
... you can mark an extension with an explicit access-level modifier (for example, private) to set a new default access level for all members defined within the extension. This new default can still be overridden within the extension for individual type members.
While there is an inconsistency here, it would appear to be a conscious decision.

Instance member cannot be used on type Class?

I'm trying to access the name variable from Indicator class, which inherits from Person. However, I believe I'm not doing my initialization right.
I get the following: 'error: instance member 'name' cannot be used on type 'Indicator'`.
class Person {
var name: String
init(myName: String){
self.name = myName
}
deinit {
Indicator.letKnowPersonDeinitialized()
}
}
class Indicator: Person {
convenience init() {
self.init()
}
static func letKnowPersonDeinitialized() {
print("\(name)")
}
}
You cannot access non-static stuff directly in a static method.
The method letKnowPersonDeinitialized is static because it is modified with the static modifier:
static func letKnowPersonDeinitialized() {
^
|
here!
}
The name property of Person is not static because it is not modified by static.
Since non-static members belong to each individual instance of that class and static members belong to the class itself, static members have no direct access to non-static members. They can only access non-static members when an instance is present.
To solve your problem, add a parameter to the letKnowPersonDeinitialized method:
static func letKnowPersonDeinitialized(person: Person) {
print(person.name)
}
And in the deinitializer:
deinit {
Indicator.letKnowPersonDeinitialized(self)
}
VERY IMPORTANT STUFF:
I don't think your code is designed well. This is not how you use inheritance.
Inheritance means "is a kind of". So if Indicator inherits from Person, it means that an indicator is a kind of person.
According to common sense, an indicator is not a person. Therefore, it is not suitable to use inheritance here. It makes little sense.

Swift error when a method has not been called previously

Hi I would like to ask if it is any way to get an error on xCode at compiling, when you are using a class method that needs to call first to another method?
I explain:
class MyClass {
func initializeClass(){
}
func loadConfig() {
}
}
var myClass = MyClass()
myClass.loadConfig() --> Throw Error while coding, the same as you get when you don't implement a required protocol function
Correct way :
myClass.initializeClass().loadConfig()
One way to approach this situation is by using the Proxy Design Pattern. Rather than adding both methods to MyClass, make loadConfig() an instance method of MyClassInitProxy:
public class MyClass {
public class MyClassInitProxy {
let owner:MyClass
public func loadConfig() {
// Do the config work using owner to access MyClass
}
private init(owner:MyClass) {
self.owner = owner
}
}
public func initializeClass() -> MyClassInitProxy {
// Do preparation, then
return MyClassInitProxy(owner:self)
}
}
Now the only way one could call loadConfig is by obtaining MyClassInitProxy through initializeClass() call:
var myClass = MyClass()
myClass.initializeClass().loadConfig()
The short answer is - No, you can't have a compile-time error using this approach.
However if you want to make it impossible for anyone to instantiate your class without having configuration prepared, then just require the configuration as an argument in the initializer. E.g.
class MyClass {
init(config: YourConfigType) {
// do something with the config parameter
}
}
... read config from somewhere and create config variable ...
let x = MyClass(config: config)
And then you have a "compile time error" whenever someone wants to use your class without having config setup initially.