How to create my game architecture in a protocol oriented way? - swift

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")
}

Related

#ObservedObject not updating after successful network call

I've hit a brick wall in my widget extension. I'm using AlamoFire and ObjectMapper to match the networking we have in the main app. I can tell that my AlamoFire network call is getting triggered and that I'm getting results back, and in the correct, expected format. However, saving the response of that network call to a #Published var doesn't seem to be working. My view and models/structs are below:
struct WidgetEntryView: View {
var entry: ResourceCategoryEntry
#ObservedObject var viewModel = WidgetResourcesView(widgetSize: .medium)
var body: some View {
if UserDefaults.forAppGroup.object(forKey: "sessionToken") as? String == nil {
PleaseLogIn()
} else if viewModel.mediumResources.count < 1 {
ErrorScreen()
} else {
MediumResourcesView(resources: viewModel.mediumResources)
}
}
}
class WidgetResourcesView: ObservableObject {
#Published var resourceGroups: [WidgetResouceGroup] = [WidgetResouceGroup]()
var widgetSize: WidgetSize = .small
var selectedCategory: String?
init(widgetSize: WidgetSize) {
self.widgetSize = widgetSize
self.selectedCategory = UserDefaults.forAppGroup.string(forKey: ResourceCategoryEntry.userDefaultKey)
getResources()
}
func getResources() {
WidgetNetworkService.getResources(widgetSize: self.widgetSize.rawValue, selectedCategory: self.selectedCategory) { resourceGroups in
DispatchQueue.main.async {
self.resourceGroups = resourceGroups
}
} failure: { _ in
print("Error Received")
}
}
var mediumResources: [WidgetResource] {
var resources = [WidgetResource]()
if let featuredResourceGroup = resourceGroups.featuredResourceGroup {
for resource in featuredResourceGroup.resources { resources.append(resource) }
}
if let nonFeaturedResourceGroup = resourceGroups.nonFeaturedResourceGroup {
for resource in nonFeaturedResourceGroup.resources { resources.append(resource) }
}
return resources
}
}
class WidgetResouceGroup: NSObject, Mappable, Identifiable {
var id = UUID()
var widgetCategory: WidgetCategory = .featured
var resources = [WidgetResource]()
required init?(map: Map) {}
func mapping(map: Map) {
id <- map["section"]
widgetCategory <- map["section"]
resources <- map["resources"]
}
}
typealias WidgetResourceGroupCollection = [WidgetResouceGroup]
extension WidgetResourceGroupCollection {
var featuredResourceGroup: WidgetResouceGroup? {
return first(where: {$0.widgetCategory == .featured})
}
var nonFeaturedResourceGroup: WidgetResouceGroup? {
return first(where: {$0.widgetCategory != .featured})
}
}
class WidgetResource: NSObject, Mappable, Identifiable {
enum ResourceType: String {
case text = "text"
case audio = "audio"
case video = "video"
}
var id = 0
var title = ""
var imageInfo: WidgetImageInfo?
var resourceType: ResourceType = .text
required init?(map: Map) {}
func mapping(map: Map) {
id <- map["object_id"]
title <- map["title"]
imageInfo <- map["image_info"]
resourceType <- map["content_type"]
}
}
You can use the objectWillChange - Property in your observable object to specifiy when the observable object should be refreshed.
Apple Dev Doku
Example by Paul Hudson
WidgetEntryView instantiates WidgetResourcesView using the ObservedObject wrapper. This causes a new instance of WidgetResourcesView to be instantiated again on every refresh. Try switching that to StateObject, and the original object will be kept in memory between view updates. I believe this is the only change needed, but I’m away so can’t test it!

Can a Swift Property Wrapper reference the owner of the property its wrapping?

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)
}
}
}

How I can access to one Struct property that implements two protocols?

Im just learning Swift 4 and I have some troubles trying to access a property of an struct that have to implement 2 protocols
here is my struct
struct FigureA {
static func load() -> Model {
return Model.make(
name: "FigureName",
status: "Painted",
image: UIImage(named: "FigureA"),
description: "Good figure")
}
}
here the protocol 1
protocol ListModel: class {
var name: String { get }
var status: String { get }
var image: UIImage? { get }
}
here the protocol 2
protocol DetailModel: ListModel {
var categoryName: String { get }
var modelDescription: String? { get }
}
And I want to get the access to the description of the Struct but I don't know how at all.
Can someone give me a bit of light.
Here is good start for you:
protocol BaseProtocol {
var id: Int { get set }
}
protocol PersonProtocol: BaseProtocol {
var firstName: String { get set }
var lastName: String { get set }
var name: String { get }
}
struct Person: PersonProtocol {
var id: Int
var firstName: String
var lastName: String
var name: String { return firstName + " " + lastName }
}
//≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠≠
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Create Struct Instance & Print properties.
let person = Person(id: 1001, firstName: "Manish", lastName: "Rathi")
print(person.id)
print(person.firstName)
print(person.lastName)
print(person.name)
}
}
#HappyCoding 😊

Type casting in a generic swift function

Supposing I have a UICollectionViewCell and a UITableViewCell with identical properties. Rather than have two functions which populate those cells, could I have a generic that takes something , determine what that was and then cast it to the correct thing to perform actions on it before returning?
my thinking is:
func setUpCell<T>(event: Event, cell:T) -> T {
// figure out what T is and cast it
cell.event.bar = event.bar
return cell
}
is this a good way of avoiding large amounts of code duplication?
Given your model type
struct Event {
let title: String
let desc: String
}
define this protocol
protocol EventCell: class {
var id: String? { get set }
var desc: String? { get set }
}
Now conform your UITabelViewCell and UICollectionViewCell to it
class TableCell: UITableViewController, EventCell {
var id: String?
var desc: String?
}
class CollectionCell: UICollectionViewCell, EventCell {
var id: String?
var desc: String?
}
And finally define this extension
extension EventCell {
func populate(event:Event) {
self.id = event.id
self.desc = event.desc
}
}
That's it. Now both your cells (UITabelViewCell and UICollectionViewCell) have the populate method!
Does this match what you were thinking?
import UIKit
struct Event {
var bar:Int = 0
}
// Protocol to group common additions
protocol ViewCellAdditions {
init()
var separatorInset:Int { get set }
var event:Event { get set}
}
// Generic function to work on any class that adopts ViewCellAdditions
func setUpCell<T: ViewCellAdditions>(event: Event, cell:T, foo:Int) -> T {
var newCell = T()
newCell.separatorInset = foo
newCell.event.bar = event.bar
return newCell
}
// Class that adopts ViewCellAdditions
class NewCellClass: ViewCellAdditions {
required init() {}
var separatorInset:Int = 10
var event:Event = Event()
}
// How to use it
let aCell = NewCellClass()
let aEvent = Event()
let newCell = setUpCell(aEvent, cell: aCell, foo: 5)

override func in class swift

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()