Does Swift have access to main - swift

If you run the following code in the Swift REPL, it will print out main.SomeClass. Here is a Swiftstub to try it out: http://swiftstub.com/887338044
class SomeClass {
func doesSomething() {
print(self) // prints "main.SomeClass
}
}
let someClass = SomeClass()
someClass.doesSomething()
Is it possible to get access to main the object/variable/constant to inspect it? What is main?

You are always in a module (namespace). In an iOS app, it's the app, and it has the name of the project by default (you can change that in the build settings). In the REPL, we have to make something up, so the module is called main. It isn't an "object", "variable", or "constant" that you can "access"; it's just the namespace. Your class's real name simply is main.SomeClass.

Related

private Helper class with public static func access modifier

I think I probably have some blind spot. The following code in test target actually works that I thought it should not: (MyHelper is private already, but the caller still can use myHelperFunc())
// both MyClass and MyHelper are in the same file
class MyClass: XCTestCase {
func testDoWork() {
MyHelper.myHelperFunc()
}
}
private class MyHelper {
static func myHelperFunc() -> String {
return "something"
}
}
If I move the code to main target (delete the XCTestCase), compiler immediately flag MyHelper is not accessible that seems the right behavior? Is there something specific for test target that I missed?
private at file scope is equivalent to fileprivate.
#testable import makes internal code accessible to a test target.
Access Levels in The Swift Programming Language explains how private works:
Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file.
"The enclosing declaration" of MyHelper is "the file." This is perhaps a bit more clearly stated in the Declaration Modifiers section of the Swift Language Reference:
private
Apply this modifier to a declaration to indicate the declaration can be accessed only by code within the declaration’s immediate enclosing scope.
Again, the "enclosing scope" of MyHelper is the file. If MyHelper were enclosed in some other class, then it would be limited to that scope rather than the whole file:
// Things change if MyHelper has a different enclosing scope than MyClass.
class MyClass {
func testDoWork() {
MyHelper.myHelperFunc() // Cannot find 'MyHelper' in scope
}
}
class C {
// MyClass can't access C.MyHelper now.
private class MyHelper {
static func myHelperFunc() -> String {
return "something"
}
}
}
In your example, myHelperFunc() has no annotation, so it initially receives the default level of internal. This is noted in Access Levels again:
Default Access Levels
All entities in your code (with a few specific exceptions, as described later in this chapter) have a default access level of internal if you don’t specify an explicit access level yourself.
However, as noted in "Guiding Principle of Access Levels:"
No entity can be defined in terms of another entity that has a lower (more restrictive) access level.
It is not allowed for MyHelper.myHelperFunc() to have a broader access level than its encoding type (MyHelper), so it's limited to file scope (which is effectively the same as fileprivate).

Hiding property setters by class in Swift

I would like to hide some property setters and initializers on my Swift model objects. These are reference data that the server provides, and under no circumstances should they be created or modified by the application. This is simple enough in Swift.
However, there is application in my project (a separate target) that needs to break this rule. It is a tool I use to populate the data in bulk, so of course needs to be able to initialize new model objects and set their properties.
What are my options for accomplishing this? I would rather not use a completely new project since it will mean a lot of code duplication. Is there some language-level way to keep this mutability hidden from one application but available to another?
If you declare a property with the let keyword. It can then only be set in the init of the type.
You can also declare a private setter to make the property readonly from the caller of the type but read/write inside the type
struct Foo {
private(set) var bar: Bool = true
func toggle() {
bar.toggle()
}
}
var foo = Foo()
let barState = foo.bar // This works
foo.toggle() // This works too
foo.bar.toggle() // This will make a compile time error

kotlin coroutine - what is default scope?

How does coroutines default scope work if i do not specify anything. lets take a look at this example:
class MyAppCompatActivity:AppCompatActivity{
fun getContact() {
GlobalScope.launch {
val contact = contacts.getContact() // suspended function
withContext(Dispatchers.Default) {
phoneContact.value = contact }
}
}
}
which simply updates the UI when a contact is retrieved. this is added to the global scope of so the coroutine life span can be that of the entire application.
but lets do the same thing again without a globalScope:
class MyAppCompatActivity:AppCompatActivity{
fun getContact() {
launch {
val contact = contacts.getContact() // suspended function
withContext(Dispatchers.Default) {
phoneContact.value = contact }
}
}
}
what is the lifespan of getContact now that i have removed the globalScope ? is it tied to the MyAppCompatActivity scope ?
Your code will fail to compile because launch must be called on a CoroutineScope object. This object specifies the lifespan of the coroutine. Since your activity does not implement a scope it will fail to compile or call a completely unrelated launch function.
I don't think this is a good idea anymore, as it looks like they're just functions for testing (doesn't launch coroutines). Maybe this was an API available previously, but now you should be using lifecycleScope to scope a coroutine to a fragment or activity, and viewModelScope to scope them to a View Model's life cycle. These are properties available in those classes by already.
Here's what I see when I try to use launch:

Singletons in Swift and Interface Builder

Background
I have a singleton class in my app, declared according following the one line singleton (with a private init()) in this blog post. Specifically, it looks like this:
#objc class Singleton {
static let Singleton sharedInstance = Singleton()
#objc dynamic var aProperty = false
private init() {
}
}
I would like to bind the state of aProperty to whether a menu item is hidden.
How I tried to solve the problem
Here are the steps I followed to do this:
Go to the Object Library in Interface Builder and add a generic "Object" to my Application scene. In the Identity inspector, configure "Class" to Singleton.
Create a referencing outlet in my App Delegate by Ctrl-dragging from the singleton object in Interface Builder to my App Delegate code. It ends up looking like this:
#IBOutlet weak var singleton: Singleton!
Go to the Bindings inspector for the menu item, choose "Hidden" under "Availability", check "Bind to", select "Singleton" in the combo box in front of it, and type aProperty under "Model Key Path".
The issue
Unfortunately, this doesn't work: changing the property has no effect on the menu item in question.
Investigating the cause
The issue appears to be that, despite declaring init() as private, Interface Builder is managing to create another instance of my singleton. To prove this, I added NSLog("singleton init") to the private init() method as well as the following code to applicationDidFinishLaunching() in my app delegate:
NSLog("sharedInstance = \(Singleton.sharedInstance) singleton = \(singleton)")
When I run the app, this is output in the logs:
singleton init
singleton init
sharedInstance = <MyModule.Singleton: 0x600000c616b0> singleton = Optional(<MyModule.Singleton: 0x600000c07330>)
Therefore, there are indeed two different instances. I also added this code somewhere else in my app delegate:
NSLog("aProperty: [\(singleton!.aProperty),\(String(describing:singleton!.value(forKey: "aProperty"))),\(Singleton.sharedInstance.singleton),\(String(describing:Singleton.sharedInstance.value(forKey: "aProperty")))] hidden: \(myMenuItem.isHidden)")
At one point, this produces the following output:
aProperty: [false,Optional(0),true,Optional(1)] hidden: false
Obviously, being a singleton, all values should match, yet singleton produces one output and Singleton.sharedInstance produces a different one. As can be seen, the calls to value(forKey:) match their respective objects, so KVC shouldn't be an issue.
The question
How do I declare a singleton class in Swift and wire it up with Interface Builder to avoid it being instantiated twice?
If that's not possible, how else would I go about solving the problem of binding a global property to a control in Interface Builder?
Is an MCVE necessary?
I hope the description was detailed enough, but if anyone feels an MCVE is necessary, leave a comment and I'll create one and upload to GitHub.
I just want to start my answer by stating that singletons should not be used for sharing global state. While they might seem easier to use in the beginning, they tend to generate lots of headaches later on, since they can be changed virtually from any place, making your program unpredictable some times.
That being said, it's not impossible to achieve what you need, but with a little bit of ceremony:
#objc class Singleton: NSObject {
// using this class behind the scenes, this is the actual singleton
class SingletonStorage: NSObject {
#objc dynamic var aProperty = false
}
private static var storage = SingletonStorage()
// making sure all instances use the same storage, regardless how
// they were created
#objc dynamic var storage = Singleton.storage
// we need to tell to KVO which changes in related properties affect
// the ones we're interested into
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
switch key {
case "aProperty":
return ["storage.aProperty"]
default: return super.keyPathsForValuesAffectingValue(forKey: key)
}
}
// and simply convert it to a computed property
#objc dynamic var aProperty: Bool {
get { return Singleton.storage.aProperty }
set { Singleton.storage.aProperty = newValue }
}
}
Unfortunately you can’t return a different instance from init in Swift.
Here are some possible workarounds:
Make an outlet for an instance of your class in Interface Builder and then only reference that instance throughout your code. (Not a singleton per se, but you could add some runtime checks to make sure it’s only instantiated from a nib file and not from code).
Create a helper class for use in Interface Builder and expose your singleton as its property. I.e. any instance of that helper class will always return a single instance of your singleton.
Make an Objective-C subclass of your Swift singleton class and make its init's always return a shared Swift singleton instance.
There is a way around the problem in my particular case.
Recall from the question that I only wanted to hide and unhide a menu according to the state of aProperty in this singleton. While I was attempting to avoid write as much code as possible, by doing everything in Interface Builder, it seems in this case it's much less hassle to just write the binding programmatically:
menuItem.bind(NSBindingName.hidden, to: Singleton.sharedInstance, withKeyPath: "aProperty", options: nil)

Can you refactor function parameter's name in Xcode?

I just upgraded to Xcode 10 and and noticed that I cannot refactor the parameter's name in a function.
I haven't been using Xcode 9 for long time. So I am not sure if this was possible in Xcode 9 and Xcode 10 lost it.
Thanks
How to rename a Swift init parameter
I would expect to rename an init parameter where it is declared. Xcode doesn't allow this.
With some experiments, I stumbled upon an unexpected place where init parameters can be renamed: Wherever they are used, you can control click on an init parameter and rename it using the context menu.
class Rename {
// Do NOT control click on parameter here:
init(parameterToBeRenamed: String) {
}
}
class Use {
func test() {
// to rename parameterToBeRenamed, control click on parameter here:
let _ = Rename(parameterToBeRenamed: "Hello World")
}
}