Inherit Variables With Swift Classes - swift

I am working on a project in swift that involves multiple Swift files, each with a class in them. My goal is to have some classes that inherit properties from the others. For some reason, I cannot access any class' variables from any other class. For example, here is one file:
class Enemy {
var ready = false
var someVal = 0
func someFunctions() {
}
}
In another file, I've tried to create a class that inherits from "Enemy"
class badGuy: Enemy {
ready = true // This doesn't work as I would expect it to
func badGuyFunction() {
}
}
If I attempt to access the variables someVal or ready from either class, I am given an error;
class randomClass {
func test() {
print(Enemy.ready) //This doesn't work
print (badGuy.ready) // This doesn't work
}
}
What am I doing wrong here? I've tried to use init() functions in each of the classes, but that doesn't work. Just to clarify, I'd like to have a base class, then have a subclass whose "type" is the base class, then in the subclass define values for each of the variables the base class supports. badGuy should automatically be able to set it's own someVal. Thanks in advance.

You're very close. With just a few minor edits it works as you intend.
Here is the new Enemy class, which is almost identical to your original.
class Enemy {
var ready = false
var someVal = 0
func someFunction() {}
}
The BadGuy subclass can set its properties in its initializer.
class BadGuy: Enemy {
override init() {
super.init()
ready = true
}
func badGuyFunction() {}
}
And then you should be able to use them like this:
let badGuy = BadGuy()
print(badGuy.ready) // prints `true`

Issue #1 occurs because you have to override ready in the init method
class Enemy {
var ready = false
}
class BadGuy: Enemy {
override init() {
super.init()
ready = true
}
}
Stored properties cannot be overridden directly.
Issue #2 occurs because you are calling the instance method on the type. You need to create instances of the classes.
class RandomClass {
func test() {
let enemy = Enemy()
let badGuy = BadGuy()
print(enemy.ready)
print(badGuy.ready)
}
}
let randomClass = RandomClass()
randomClass.test() // prints two lines `false` and `true`

Related

Access variable declared inside of method in parent class from subclass (swift)

I'm fairly certain there is something obvious I am missing and/or doing incorrectly here.
I have a base class with a handleTapped() function that handles a button being tapped on.
In a subclass, I want to override that function but need to access the variables that I declare in the handleTapped() function in the parent class.
Ex:
class ParentClass {
#objc func handleTapped(sender: UITapGestureRecognizer) {
var a = [Int]()
var b = 10
if a[1] == b {
// do something
}
}
}
class SubClass: ParentClass {
#objc override func handleTapped(sender: UITapGestureRecognizer) {
super.handleTapped(sender: sender)
if a[1] == b {
// do something else
}
}
}
Variables declared in a function cannot be accessed from outside of that scope, including by methods that override their behaviour (unless you use them as a return of that function, which we can't do here).
In this case, you'll need to store the necessary information in the parent class itself, and make sure the access rights are set correctly (internal, or public depending on how your project is set up) so that the Subclass can access and modify that data.
Alternatively, there may be an alternative way to implement this. This might be a candidate where duplication of the code is a more preferable option to exposing data to all subclasses.
Take the variables out of the method and into the class like so:
class ParentClass {
var a = [Int]()
var b = 10
func handleTapped(sender: UITapGestureRecognizer) {
if a[1] == b {
// do something
}
}
}
class SubClass: ParentClass {
override func handleTapped(sender: UITapGestureRecognizer) {
super.handleTapped(sender: sender)
if a[1] == b {
// do something else
}
}
}

Swift variable observers not called before super.init called

Okay so I was reading up on how willSet/didSet are used in swift and I came across a note on apples swift docs that just doesn't make any sense to me and I hope someone can explain. Here's the note:
The willSet and didSet observers of superclass properties are called
when a property is set in a subclass initializer, after the superclass
initializer has been called. They are not called while a class is
setting its own properties, before the superclass initializer has been
called.
From: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
What confuses me is that they point out that the observers on superclass A properties in a subclass B aren't called before the super.init call by B to A.
class A {
var p: Bool
init() {
p = false
}
}
class B: A {
override var p: Bool {
didSet {
print("didSet p")
}
}
override init() {
p = true // Compiler error
super.init()
}
}
However the property is never even accessible in that time from either A nor B, so who's gonna call the observers anyway? Attempting to read/write the property will even result in a compiler error so it's never even possible to do it by mistake in Swift. Am I missing something or is this just a misleading note that points out the wrong thing?
They are talking about following scenario:
class A {
var p: Bool {
didSet {
print(">>> didSet p to \(p)")
}
}
init() {
p = false // here didSet won't be called
}
}
class B: A {
override init() {
// here you could set B's properties, but not those inherited, only after super.init()
super.init()
p = true // here didSet will be called
}
}
B()
It will print following:
>>> didSet p to true
While to you it might seems natural, the documentation has to explicitly document this behavior.

Extension property not working as instance property

I have created an protocol extension of UIImageView and added a bool property isFlipped to the extension. My problem is that if I set isFlipped true of false for one object is sets the same values for the all the UIImageView objects. Can anyone guide how to handle it separately for each UIImageView objects.
protocol FlipImage {
var isFlipped: Bool { get set }
}
var flippedValue = false
extension UIImageView:FlipImage{
var isFlipped: Bool {
get {
return flippedValue
}
set {
flippedValue = newValue
}
}
}
If you want to add stored properties using extensions, here's a little trick I used that allows you to do it for base classes that you can override (namely view controllers, which is the one I used it for):
This protocol allows a base class to be extended with stored properties in extensions and in protocols:
protocol ExtensibleObject:class
{
var extendedProperties:[String:Any] { get set }
}
extension ExtensibleObject
{
func get<T>(_ defaultValue:T, _ file:String = #file, _ line:Int = #line) -> T
{
return (extendedProperties["\(file):\(line)"] as? T) ?? defaultValue
}
func set<T>(_ newValue:T, _ file:String = #file, _ line:Int = #line)
{
return extendedProperties["\(file):\(line)"] = newValue
}
}
To use the protocol, you need to create a subclass of the base class to add storage for all extended properties (for the class and all its sub classes).
class ExtensibleViewController:UIViewController, ExtensibleObject
{
var extendedProperties:[String:Any] = [:]
}
Note that you can do it directly in the base class if it is yours.
You would then use the "extensible" base class for your own subclass instead of the base class:
class MyVC:ExtensibleViewController
{}
From then on, any of the subclass can receive new "stored" properties in extensions:
extension MyVC
{
var newStoredProperty:Int
{ get { return get(0) } set { set(newValue) } } // set and get must be on same line
}
Stored properties can also be added through protocol adoption for classes implementing the ExtensibleObject protocol:
protocol ListManager:ExtensibleObject
{
var listContent:[String] { get set }
}
extension ListManager
{
var listContent:[String]
{ get { return get([]) } set { set(newValue) } }
}
extension MyVC:ListManager {}
Bear in mind that these extended properties behave as lazy variables and that they have some additional overhead when used. For UI components and view controllers, this is usually not a problem.

How can an existing protocol implemented by a delegate be extended with a var?

I've tried many combinations and the problem still remains. I can't figure out why Swift won't compile the following code. I've used multiple variations (using 'where' to constraint protocol, moving setter & getter inside the protocol, etc...) Still no luck. Can you see where the problem is?
// GameScene.swift
import SpriteKit
extension SKSceneDelegate { // adding 'where Self: Game'
// causes err to moves somewhere else
var playerDirection: PlayerDirection { get set } // doesn't like this!
}
class GameScene: SKScene {
override func keyDown(theEvent: NSEvent) {
switch (theEvent.keyCode) {
case 123:
delegate!.playerDirection = .Left;
case 124:
delegate!.playerDirection = .Right;
default:
break
}
}
}
// SomeGame.swift
import Foundation
import SpriteKit
class Game: NSObject, SKSceneDelegate {
var _playerDirection: PlayerDirection = .None
// moving that code to the protocol, compiler can't find _playerDirection
var playerDirection: PlayerDirection {
set {
_playerDirection = newValue
}
get {
return _playerDirection
}
}
lazy var scene: GameScene = {
let scene = GameScene(size: CGSizeMake(CGFloat(100), CGFloat(100)))
scene.delegate = self
return scene
}()
func update(currentTime: NSTimeInterval, forScene scene: SKScene) {
}
}
// PlayerControlComponent.swift
import Foundation
enum PlayerDirection {
case None, Left, Right, Down, Up
}
I think you're approaching your problem from the wrong angle. It looks like what you want is to be able to access playerDirection from the keyDown() function in GameScene. Instead of attempting to make playerDirection a property of the SKSceneDelegate protocol, you should probably be checking to see if the delegate property in GameScene is a Game and, if it is, casting delegate to Game so that the playerDirection property becomes available to you.
You can do that very easily with an if let and the as? operator like this:
override func keyDown(theEvent: NSEvent) {
if let game = delegate as? Game {
switch (theEvent.keyCode) {
case 123:
game.playerDirection = .Left;
case 124:
game.playerDirection = .Right;
default:
break
}
}
}
This is doubly nice because now you're also checking to make sure that delegate actually exists before using it. Forcibly unwrapping it, like you were doing before, could cause a runtime exception if delegate isn't set when before that function is called.

Swift.... Class method vs. Instance method

Thanks in advance for help!!
I'm trying to call a func from within my Class and I keep getting an error saying that:
Missing parameter for argument #1.............Read a few posts saying it's an instance vs class problem? I don't get it..I'm calling the method from within the Class??? There has to be an instance of the class if the method is being called????? right? Here is my code...Thanks
import Foundation
import Parse
class TestViewController {
let photos = getWallImages() //-----This is the line requesting an argument
func getWallImages() -> [WallPost] {
let query = WallPost.query()!
query.findObjectsInBackgroundWithBlock { objects, error in
if error == nil {
if let objects = objects as? [WallPost] {
return objects
println("We have \(objects.count)")
}
} else if let error = error {
println(error)
}
}
}
}
So the "crime" you are committing is the fact that the method is applied in an instance of the class and not as a class method. The function is expecting a self parameter (a reference to the instance). That explains the error message.
Now to fix that you have two quick options:
1. Make it a class function and call it that way too:
class TestViewController {
let photos = TestViewController.getWallImages()
class func getWallImages() -> [WallPost] {
// mumbo jumbo
}
}
This approach is problematic in case you would want to do some instance specific operations, because class func is static method and doesn't provide you with some of the object benefits.
2. Instantiate the object you are calling the method on:
class TestViewController {
let photos = TestViewController().getWallImages()
func getWallImages() -> [WallPost] {
// mumbo jumbo
}
}
This approach isn't correct with your given structure - it doesn't make sense to instantiate another view controller, but if you take the method and put it in a separate class, maybe it would then make sense.
Then of course you have multiple other ways of changing your code to make it work. Maybe you could initialize it with lazy parameter, maybe you could initialize it in the init method. Whatever suits you best. My answer is simply explaining where you've gone wrong.
There are a few ways you can set your property appropriately. You can make getWallImages() a type method:
class TestViewController {
let photos = TestViewController.getWallImages()
class func getWallImages() -> [WallPost] {
....
}
}
Or, you can keep your method an instance method and set your property upon initialization:
class TestViewController {
let photos: [WallPost]!
init() {
super.init()
photos = getWallImages()
}
func getWallImages() -> [WallPost] {
....
}
}
If you're asking a question you should reduce your code to a minimum, discarding unnecessary details.
You probably want something like this:
class MyClass {
let x = MyClass.getStuff()
static func getStuff() -> Int {
return 0
}
}
However your method getWallImages() can't do something like this, because it's returning the result asynchronous, which means you get the result much later after the function has returned.
You could do something like this though (this is how I'd be doing it):
class MyClass {
var x : Int? {
didSet {
if let x = x {
// Do something with x, here it's assigned
} else {
// x was set to nil, something failed
}
}
}
init() {
getStuffAsynchronous()
}
func getStuffAsynchronous() {
// Do your query stuff here, assign x to your objects to invoke the didSet
x = 0
// If it fails somehow, assign set x to nil
// if fail {
// x = nil
// }
}
}