I am quite new to swift and I have a question regarding the definition of functions in a class.
I want to generate several items and give each of them a special function so I can run them by itemxy.useitem()
class itemĀ {
var name = "test"
func useitem(){
print("test")
}
}
let staff = item()
staff.name = "Staff"
staff.useitem() // prints: test
*override staff.useitem() = {print("this is a staff")}*
staff.useitem() // prints: this is a staff
how can I align a new function to my item staff?
These are not entirely swift related and are more general programming, you wont get your answers to such problems here. You should read up on basic programming principles before you tackle things further.
Having said that your problem is easily solved with Inheritance or Protocols.
Inheritance
class Item {
var name: String
init(name: String) {
self.name = name
}
func use() {
print("using a \(name)")
}
}
class Hat: Item {
override func use() {
print("I put on a \(name)")
}
}
class Pen: Item {
init() {
super.init(name: "Pen")
}
}
let pen = Pen()
pen.use()
let hat = Hat(name: "Beanie")
hat.use()
Protocol
protocol Item {
var name: String { get set }
func use()
}
extension Item {
func use() {
print("using a \(name)")
}
}
struct Pen: Item {
var name: String
init() {
self.name = "Pen"
}
}
struct Hat: Item {
var name: String
func use() {
print("I put on a \(name)")
}
}
let pen = Pen()
pen.use()
let hat = Hat(name: "Beanie")
hat.use()
Related
I have found a weird issue with SwiftUI's ForEach (and List) where if you use an Array of subclass types where the parent class implements BindableObject, the ForEach loop insists each item is of the base class type not the Subclass you are using, see my example code below. A little experimenting has found if the subclass implements BindableObject then the issue goes away, which in the example I have shown is OK, but often is not really suitable.
Anybody seen this know how you are suppose to deal with this or perhaps this is a bug and I should raise it with Apple?
class Bar: BindableObject {
let didChange = PassthroughSubject<Bar, Never>()
let name: String
init(name aName: String) {
name = aName
}
}
class Foo: Bar {
let value: Int
init(name aName: String, value aValue: Int) {
value = aValue
super.init(name:aName)
}
}
let arrayOfFoos: Array<Foo> = [ Foo(name:"Alpha",value:12), Foo(name:"Beta",value:13)]
struct ContentView : View {
var body: some View {
VStack {
ForEach(arrayOfFoos) { aFoo in
Text("\(aFoo.name) = \(aFoo.value)") // error aFoo is a Bar not a Foo
}
}
}
}
Tried this on Xcode Beta 2
I think this is not a bug but rather a "feature" of Swift type system and SwiftUI API.
If you look at the signature of ForEach (just Cmd + Click on ForEach)
public init(_ data: Data, content: #escaping (Data.Element.IdentifiedValue) -> Content)
you can notice that it accepts Data.Element.IdentifiedValue type
So, from your example
struct ContentView : View {
var body: some View {
VStack {
ForEach(arrayOfFoos) { aFoo in
Text("\(aFoo.name) = \(aFoo.value)") // error aFoo is a Bar not a Foo
}
}
}
}
aFoo local value has type Foo.IdentifiedValue
Lets ask Swift what it thinks about this type:
Foo.IdentifiedValue.self == Bar.IdentifiedValue.self // true
Foo.IdentifiedValue.self == Foo.self // false
Foo.IdentifiedValue.self == Bar.self // true
As you can see, Foo.IdentifiedValue is actually Bar.
To bypass this we can create a wrapper using a new feature of Swift 5.1 - 'Key Path Member Lookup'! :D
I updated your example. Added AnyBindable class and mapped elements of arrayOfFoos to it.
class Bar: BindableObject {
let didChange = PassthroughSubject<Void, Never>()
let name: String
init(name aName: String) {
name = aName
}
}
class Foo: Bar {
let value: Int
init(name aName: String, value aValue: Int) {
value = aValue
super.init(name:aName)
}
}
#dynamicMemberLookup
class AnyBindable<T: BindableObject>: BindableObject {
let didChange: T.PublisherType
let wrapped: T
init(wrapped: T) {
self.wrapped = wrapped
self.didChange = wrapped.didChange
}
subscript<U>(dynamicMember keyPath: KeyPath<T, U>) -> U {
return wrapped[keyPath: keyPath]
}
}
let arrayOfFoos = [ Foo(name:"Alpha",value:12), Foo(name:"Beta",value:13)]
.map(AnyBindable.init)
struct ContentView : View {
var body: some View {
VStack {
ForEach(arrayOfFoos) { aFoo in
Text("\(aFoo.name) = \(aFoo.value)") // it compiles now
}
}
}
}
From within a property wrapper in Swift, can you someone refer back to the instance of the class or struck that owns the property being wrapped? Using self doesn't obviously work, nor does super.
I tried to pass in self to the property wrapper's init() but that doesn't work either because self on Configuration is not yet defined when #propertywrapper is evaluated.
My use case is in a class for managing a large number of settings or configurations. If any property is changed, I just want to notify interested parties that something changed. They don't really need to know which value just, so use something like KVO or a Publisher for each property isn't really necessary.
A property wrapper looks ideal, but I can't figure out how to pass in some sort of reference to the owning instance that the wrapper can call back to.
References:
SE-0258
enum PropertyIdentifier {
case backgroundColor
case textColor
}
#propertyWrapper
struct Recorded<T> {
let identifier:PropertyIdentifier
var _value: T
init(_ identifier:PropertyIdentifier, defaultValue: T) {
self.identifier = identifier
self._value = defaultValue
}
var value: T {
get { _value }
set {
_value = newValue
// How to callback to Configuration.propertyWasSet()?
//
// [self/super/...].propertyWasSet(identifier)
}
}
}
struct Configuration {
#Recorded(.backgroundColor, defaultValue:NSColor.white)
var backgroundColor:NSColor
#Recorded(.textColor, defaultValue:NSColor.black)
var textColor:NSColor
func propertyWasSet(_ identifier:PropertyIdentifier) {
// Do something...
}
}
The answer is no, it's not possible with the current specification.
I wanted to do something similar. The best I could come up with was to use reflection in a function at the end of init(...). At least this way you can annotate your types and only add a single function call in init().
fileprivate protocol BindableObjectPropertySettable {
var didSet: () -> Void { get set }
}
#propertyDelegate
class BindableObjectProperty<T>: BindableObjectPropertySettable {
var value: T {
didSet {
self.didSet()
}
}
var didSet: () -> Void = { }
init(initialValue: T) {
self.value = initialValue
}
}
extension BindableObject {
// Call this at the end of init() after calling super
func bindProperties(_ didSet: #escaping () -> Void) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if var child = child.value as? BindableObjectPropertySettable {
child.didSet = didSet
}
}
}
}
You cannot do this out of the box currently.
However, the proposal you refer to discusses this as a future direction in the latest version:
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
For now, you would be able to use a projectedValue to assign self to.
You could then use that to trigger some action after setting the wrappedValue.
As an example:
import Foundation
#propertyWrapper
class Wrapper {
let name : String
var value = 0
weak var owner : Owner?
init(_ name: String) {
self.name = name
}
var wrappedValue : Int {
get { value }
set {
value = 0
owner?.wrapperDidSet(name: name)
}
}
var projectedValue : Wrapper {
self
}
}
class Owner {
#Wrapper("a") var a : Int
#Wrapper("b") var b : Int
init() {
$a.owner = self
$b.owner = self
}
func wrapperDidSet(name: String) {
print("WrapperDidSet(\(name))")
}
}
var owner = Owner()
owner.a = 4 // Prints: WrapperDidSet(a)
My experiments based on : https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
protocol Observer: AnyObject {
func observableValueDidChange<T>(newValue: T)
}
#propertyWrapper
public struct Observable<T: Equatable> {
public var stored: T
weak var observer: Observer?
init(wrappedValue: T, observer: Observer?) {
self.stored = wrappedValue
}
public var wrappedValue: T {
get { return stored }
set {
if newValue != stored {
observer?.observableValueDidChange(newValue: newValue)
}
stored = newValue
}
}
}
class testClass: Observer {
#Observable(observer: nil) var some: Int = 2
func observableValueDidChange<T>(newValue: T) {
print("lol")
}
init(){
_some.observer = self
}
}
let a = testClass()
a.some = 4
a.some = 6
The answer is yes! See this answer
Example code for calling ObservableObject publisher with a UserDefaults wrapper:
import Combine
import Foundation
class LocalSettings: ObservableObject {
static var shared = LocalSettings()
#Setting(key: "TabSelection")
var tabSelection: Int = 0
}
#propertyWrapper
struct Setting<T> {
private let key: String
private let defaultValue: T
init(wrappedValue value: T, key: String) {
self.key = key
self.defaultValue = value
}
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
public static subscript<EnclosingSelf: ObservableObject>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, T>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Setting<T>>
) -> T {
get {
return object[keyPath: storageKeyPath].wrappedValue
}
set {
(object.objectWillChange as? ObservableObjectPublisher)?.send()
UserDefaults.standard.set(newValue, forKey: object[keyPath: storageKeyPath].key)
}
}
}
I have two UICollectionView's and I am passing a unique type of data (Cats, Dogs) to the same UIViewController. Both types have unique properties like imageUrl, gender, age and etc. What would be the best way to populate this data to according Text Views?
fileprivate var isDog = false
var detailsData: Any? {
didSet {
if detailsData is Dog {
isDog = true
} else if detailsData is Cat {
isDog = false
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
isDog ? fillDogInfo(detailsData: detailsData as! Dog) : fillCatInfo(detailsData: detailsData as! Cat)
}
fileprivate func fillDogInfo(detailsData: Dog) {
contactButtonOutlet.isHidden = false
// Short Info
if let name = detailsData.name {
shortInfoMutableAttributedString.append("\(name)\n".customAttributedString(font: Font.header, textColor: UIColor.darkGray))
}
if let age = detailsData.age {
shortInfoMutableAttributedString.append("\(age)\n".customAttributedString(font: Font.header, textColor: UIColor.darkGray))
}
shortInfoTextView.attributedText = shortInfoMutableAttributedString
}
fileprivate func fillCatInfo(detailsData: Car) {
contactButtonOutlet.isHidden = true
if let name = detailsData.name {
shortInfoMutableAttributedString.append("\(name)\n".customAttributedString(font: Font.header, textColor: UIColor.darkGray))
}
if let gender = detailsData.gender {
shortInfoMutableAttributedString.append("\(gender)\n".customAttributedString(font: Font.header, textColor: UIColor.darkGray))
}
shortInfoTextView.attributedText = shortInfoMutableAttributedString
}
Is there any other way to populate this data with less code?
A classic case of Polymorphism, you should create a protocol and have the Dog and Cat classes implement it. Then you can just call the functions on this protocol instance.
Below is a simplified example. In this example, it does not make sense to make the distinction, since both Cat and Dog are the same class in terms of properties. This would become interesting if you want to make a distinction, like for example add a bark() function to only the Dog class
protocol Pet{
var name: String { get set}
func infoString() -> String
}
class Dog: Pet{
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func infoString() -> String {
return "Dog \(name) has age \(age)"
}
}
class Cat: Pet{
var name: String
var gender: String
init(name: String, gender: String) {
self.name = name
self.gender = gender
}
func infoString() -> String {
return "Cat \(name) has gender \(gender)"
}
}
fileprivate func fillPetInfo(pet: Pet){
textView.text = pet.infoString()
}
let dog = Dog(name: "Ruffer", age: 8)
fillPetInfo(pet: dog)
I haven't found any good ways to design a protocol oriented item architecture for games.
Heres the first version with Structs:
protocol Usable {
func useItem()
}
protocol Item {
var name: String { get }
var amount: Int { get }
var image: String { get }
}
struct Sword: Item, Usable {
var name = ""
var amount = 0
var image = ""
func useItem() {
}
}
struct Shield: Item, Usable {
var name = ""
var amount = 0
var image = ""
func useItem() {
}
}
The problem with this is I have to copy paste the variables which are A LOT of code across items.
Heres the second version with Classes:
protocol Usable {
func useItem()
}
class BaseItem {
var name = ""
var amount = 0
var image = ""
}
class SwordClass: BaseItem, Usable {
func useItem() {
}
}
This looks pretty good, but the problem is these are reference types and I would prefer them to be value types.
What is the right way to solve this problem?
You should create a generic struct which conforms to your protocols and which requires initialisation with default values and a 'use' closure:
protocol Usable {
func useItem()
}
protocol Item {
var name: String { get }
var amount: Int { get }
var image: String { get }
}
struct UsableItem: Item, Usable {
var name = ""
var amount = 0
var image = ""
let use: (Void -> Void)
init(name: String, image: String, use: (Void -> Void)) {
self.name = name
self.image = image
self.use = use
}
func useItem() {
self.use()
}
}
Then your JSON processing would create instances with the appropriate logic:
var sword = UsableItem(name: "sword", image: "sword") {
print("cut")
}
I'm using Playground in Xcode, and my objects aren't being initialized with their names. I feel like it's because I'm using the convenience init incorrectly in my sublcasses, and I was wondering what is the proper way to use them in subclasses. I've read the other similar questions, but I think my question is different in the way that it has overriding inits and convenience inits.
class Animal
{
var name:String
init(name:String)
{
self.name = name
}
convenience init() { self.init(name: "") }
func speak() { }
}
class Fox: Animal
{
override init(name: String)
{
super.init(name: name)
}
convenience init() { self.init(name: "Fox") }
override func speak()
{
println("Ring")
}
}
class Cat: Animal
{
override init(name: String)
{
super.init(name: name)
}
convenience init() { self.init(name:"Cat") }
override func speak() {
println("Meow")
}
}
class Dog: Animal {
override init(name: String) {
super.init(name: name)
}
convenience init()
{
self.init(name:"Dog")
}
override func speak() {
println("Woof")
}
}
let animals = [ Dog(), Cat(), Fox()]
for animal in animals
{
animal.speak()
}
There are no errors in your code.
I think the problems is to understand how the Xcode's playground works.
probably you had pressed "show results" icon and you're watching at something like this:
but this is telling us that the "Module name" dot "Class name" of the three animals; the prefix __lldb_expr_25 ( in my picture ) is not an error but a dynamic module name that is ok in playground.
Indeed you should look at the "assistant editor":
to see the output of speck() method:
This can be even more pronounced with a slight modification to the code:
so the output is:
Let me answer according to what I understand so far -
override inits is like overriding the same to same super class init method. Here you can't add an extra behaviour as a init method parameter such as:
class Animal
{
var name:String
init(name:String)
{
self.name = name
}
func speak() { }
}
class Cat: Animal
{
override init(name: String)
{
super.init(name: name)
}
override func speak() {
println("Meow")
}
}
convenience inits is like custom init method of subclass means if you want to implement a init method into your sub-class but with some extra behaviours along with the super class init method. such as:
class Animal
{
var name:String
init(name:String)
{
self.name = name
}
func speak() { }
}
class Cat: Animal
{
var type: String = "Maine Coons"
convenience init(type:string, name: String)
{
self.type = type
self.init(name: name)
}
override func speak() {
println("Meow")
}
}
I hope it will help you.
Thanks