Swift 2 Correct way to get level of any object game, protocol? struct? extension? - ios

This is my first game, and I'm new on swift and sprite kit.
I must have a level for each class that needs get level. Like car lev1 car lev 2 etc. I have read about protocol extension etc, witch is the best way to approach level management?
I have tried to use LevelTraker as extension of this protocol:
protocol LevelTracker {
typealias TypeUnit: TypeGame
var nameClass: String! {get set}
var currentLevel : Int {get set}
mutating func levelIncreases()
}
but with extension, i must write 3 var each class that needs level.
i try the same extension LevelTraker with struct LevelTraker:
func getClassName (theClass:AnyObject) -> String {
let name = _stdlib_getDemangledTypeName(theClass); return name}
protocol TypeGame {}
enum transportType : TypeGame {
case ground, sea, air
}
struct LevelTracker {
var sender: AnyObject
var TypeUnit: TypeGame
private func getSaveFileWhitName() -> String {
let saveWithName = getClassName(sender) + "." + String(TypeUnit)
return saveWithName
}
var currentLevel : Int {
get {
let stringName = getSaveFileWhitName()
let returnValue : Int = dataBase.read(stringName) as? Int ?? 1 //Check for first run of app, if = nil, set = 1
return returnValue
}
set (newValue) {
let stringName = getSaveFileWhitName()
let level : Int = self.currentLevel
let val = newValue
if (newValue > level) {dataBase.write(val, key: stringName)}
}
}
mutating func levelIncreases() {self.currentLevel++}
///SERVE SOLO PER SVILUPPO
mutating func RESETLEVEL() {dataBase.write(1, key: getSaveFileWhitName())}
}
To use: (thanks #Krzak)
class car {
init () {
let level = LevelTracker(sender: self, TypeUnit: transportType.ground).currentLevel
}
}
But I don't want modify all init object that use level, and the super super class in common, some class don't have propriety level.

The reason why you have compiler error is in your last line. You're missing the .ground
I'm not sure how you're thinking though that this will work, shouldn't it be var?
var level = LevelTracker(sender: self, TypeUnit: transportType.ground).currentLevel

What I am reading it sounds like you are doing this:
class Level : AnyObject
{
private func getSaveFileWhitName() -> String {
let saveWithName = getClassName(sender) + "." + String(TypeUnit)
return saveWithName
}
var currentLevel : Int {
get {
let stringName = getSaveFileWhitName()
let returnValue : Int = dataBase.read(stringName) as? Int ?? 1 //Check for first run of app, if = nil, set = 1
return returnValue
}
set (newValue) {
let stringName = getSaveFileWhitName()
let level : Int = self.currentLevel
let val = newValue
if (newValue > level) {dataBase.write(val, key: stringName)}
}
}
mutating func levelIncreases() {self.currentLevel++}
///SERVE SOLO PER SVILUPPO
mutating func RESETLEVEL() {dataBase.write(1, key: getSaveFileWhitName())}
}
class car : Level
{
init () {
let level = self.currentLevel
}
}

I found a solution, I'm happy to have some comment.
protocol TypeGame {}
enum transportType : TypeGame {
case car, bus, trak
}
protocol LevelTracker {
var nameClass: String! {get}
var currentLevel : Int {get set}
mutating func levelIncreases()
}
extension LevelTracker {
var currentLevel : Int {
get {/*set to DB*/ return 1}
set (newValue) {/*set to DB*/}
}
mutating func levelIncreases() {self.currentLevel++}}
A protocol only for transport object:
protocol Transport : LevelTracker {}
Ok, now my (simplified) class are:
class AllNode {//SKSpriteNode
init(){}
}
class TransportGame:AllNode, Transport {
var nameClass : String! = "Transport"
override init() {
super.init()
self.nameClass = nameClass + "." + getClassName(self)}
}
class Car : TransportGame {}
class miniCar : Car {}
class Bus: TransportGame {}
class Tree: AllNode {}
var carOne = Car()
let levelCar = carOne.currentLevel
var busOne = Bus()
let levelBue = busOne.currentLevel
var treeOne = Tree()
tree.currentLevel //ERROR YUPPI!!!! :)
Now the tree class can't access to level!
What do you think about this solution?

Related

Swift - toggle model to readonly momentarily

I have a phone number model which looks like this:
import UIKit
import Foundation
struct PhoneValidation : OptionSet {
let rawValue: Int
static let phoneInValid = PhoneValidation(rawValue: 1 << 0)
static let phoneValid = PhoneValidation(rawValue: 1 << 1)
static let smsValidationAttempted = PhoneValidation(rawValue: 1 << 2)
static let smsValidationFailed = PhoneValidation(rawValue: 1 << 3)
static let smsValidationSuccessful = PhoneValidation(rawValue: 1 << 4) // OTP is successfully validated in backend. The field should be non-editable in this duration
static let smsValidationOTPTriggered = PhoneValidation(rawValue: 1 << 5) // OTP validation triggered. The field should be non-editable in this duration
}
class PhonesViewModel: NSCopying {
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
public var phone: String?
public var code: String?
public var countryCode: String?
public var isValid : PhoneValidation?
func copy(with zone: NSZone? = nil) -> Any {
let copy = PhonesViewModel()
copy.phoneType = phoneType
copy.phone = phone
copy.code = code
copy.countryCode = countryCode
copy.isValid = isValid
return copy
}
}
As you can see above the phone model can transition between different states. The SMS validation is available for few countries and for few it is not applicable. So, I plan on setting smsValidationOTPTriggered state when SMS validation is applicable for a country and while the validation is in progress.
What I need here is, while the states smsValidationOTPTriggered or smsValidationSuccessful are set I would not want any module of the application to modify the values(phoneType, phone, code, countryCode) of the model. In other words, I would like the model to switch to a read-only mode while these 2 states are set in model and would like the module to be informed with an error or exception when a modification is attempted.
Is there a best practice already available for what I am trying to achieve here? I have searched before raising this question but did not find any. How can I achieve this?
Thanks,
Raj Pawan Gumdal
How about something like this, I think its better to use property wrappers for your case! The below is not an exact solution but can modify/change to accommodate your need
import UIKit
enum PhoneNumberType {
case mobile
}
enum PhoneValidation {
case phoneInValid
case phoneValid
case smsValidationAttempted
case smsValidationFailed
case smsValidationSuccessful
case smsValidationOTPTriggered
}
struct PhonesViewModel {
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
public var phone: String?
public var code: String?
public var countryCode: String?
public var phoneValidation : PhoneValidation?
func validate(value: [PhoneValidation]) -> Bool {
//add proper check here
return false
}
}
#propertyWrapper
struct Wrapper {
private(set) var value: PhonesViewModel? = nil
var validators: [PhoneValidation] = []
var wrappedValue: PhonesViewModel? {
get { value }
set {
if let model = newValue, model.validate(value: validators) {
value = newValue
print("Value assigned")
} else {
print("Value not assigned")
}
}
}
}
struct SomeOtherClass {
#Wrapper(validators: [PhoneValidation.phoneInValid])
var model: PhonesViewModel?
}
var a = SomeOtherClass()
a.model = PhonesViewModel()
a.model = PhonesViewModel()
You can use a technique with the name "popsicle immutability". An object is initially mutable, but can be "frozen". Modifications for frozen objects are forbidden. In your case PhonesViewModel become frozen when isValid property have value smsValidationOTPTriggered or smsValidationSuccessful.
Let's add Freezable protocol for requirements to objects that can become immutable and conforming for PhonesViewModel:
protocol Freezable: class {
var isFrozen: Bool { get }
}
extension PhonesViewModel: Freezable {
var isFrozen: Bool {
isValid == .smsValidationOTPTriggered || isValid == .smsValidationSuccessful
}
}
Now we must add validation for isFrozen value when a property is assigned. It can be added in property observers like:
...
public var phone: String? {
didSet {
validate()
}
}
...
private func validate() {
assert(!isFrozen)
}
Or using property wrapper:
#propertyWrapper
struct Guarded<Value> {
private var value: Value
init(wrappedValue: Value) {
value = wrappedValue
}
#available(*, unavailable)
var wrappedValue: Value {
get { fatalError("only works on instance properties of classes that conforms to Freezable protocol") }
set { fatalError("only works on instance properties of classes that conforms to Freezable protocol") }
}
static subscript<EnclosingSelf: Freezable>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
) -> Value {
get {
object[keyPath: storageKeyPath].value
}
set {
precondition(!object.isFrozen, "Object \(object) is frozen! Modifications are forbidden")
object[keyPath: storageKeyPath].value = newValue
}
}
}
So your class will look like:
class PhonesViewModel: NSCopying {
#Guarded
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
#Guarded
public var phone: String?
#Guarded
public var code: String?
#Guarded
public var countryCode: String?
#Guarded
public var isValid : PhoneValidation?
func copy(with zone: NSZone? = nil) -> Any {
let copy = PhonesViewModel()
copy.phoneType = phoneType
copy.phone = phone
copy.code = code
copy.countryCode = countryCode
copy.isValid = isValid
return copy
}
}

Observation is not getting fired in Swift 4.2

Any ideas why Swift is not smart enough to infer the parameters passed to the observeWrapper function.
Code:
let implementation = QuestionJSONStrategy(name: questionGroup.course.rawValue)
_ = observeWrapper(implementation)
showQuestion()
}
func observeWrapper<T: NSObject & QuestionStrategy>(_ object: T) -> NSKeyValueObservation {
return object.observe(\.questionIndex, options: .new) { _, change in
guard let newValue = change.newValue else { return }
print(newValue)
}
}
QuestionStrategy Protocol:
#objc protocol QuestionStrategy :AnyObject {
var questions :[Question] { get set}
var questionIndex :Int { get set }
init(name :String)
func nextQuestion() -> Question
}
QuestionJSONStrategy Class:
#objc public class QuestionJSONStrategy :NSObject, QuestionStrategy {
var questions: [Question] = [Question]()
#objc dynamic var questionIndex: Int = 0

how to clone object model swift

I want using Mirror to add to the parent class without having to add a clone of the child's class
Do you think this is possible?
base class :
class BaseModel {
func clone() -> BaseModel {
let mirror = Mirror(reflection : self)
for (lable , value) in mirror.children {
}
return ...
}
}
subclass :
class UserModel:BaseModel {
var name:String!
var family:String!
}
usage :
let cloneModel = self.userModel.clone()
You have to implement NSCopying protocol and override copy(with:) function:
class BaseModel: NSCopying {
var xx: String?
var yy: Int?
private init(xx: String, yy: Int) {
self.xx = xx
self.yy = yy
}
func copy(with zone: NSZone? = nil) -> Any {
let copy = BaseModel(xx: xx, yy: yy)
return copy
}
}
Usage:
let clone = model.copy() as! BaseModel
Or you can refer to this answer:
https://stackoverflow.com/a/32113575/3882414

How to use generics as params?(Swift 2.0)

Code in playground is here
class ProductModel {
var productID : Int = 0
init(id:Int) {
productID = id
}
}
protocol GenericListProtocol {
typealias T = ProductModel
var list : [T] { get set }
var filteredlist : [T] { get set }
func setData(list : [T])
}
extension GenericListProtocol {
func setData(list: [T]) {
list.forEach { item in
guard let productItem = item as? ProductModel else {
return
}
print(productItem.productID)
}
}
}
class testProtocol {
class func myfunc<N:GenericListProtocol>(re:N){
var list : [ProductModel] = [ProductModel(id: 1),ProductModel(id: 2),ProductModel(id: 3),ProductModel(id: 4)]
re.setData(list)
}
}
But in the line re.setData(list)
get compile error:
Cannot convert value of type '[ProductModel]' to expected argument
type '[_]'.
My Question is How to use setData method in GenericListProtocol?
Anyone could help will be appreciated.
Moving the ProductModel type into the extension and removing the constraint from the generic protocol seems to work.
class ProductModel {
var productID : Int = 0
init(id:Int) {
productID = id
}
}
protocol GenericListProtocol {
typealias T
var list : [T] { get set }
var filteredlist : [T] { get set }
func setData(list : [T])
}
extension GenericListProtocol {
func setData(list: [ProductModel]) {
list.forEach { item in
print(item.productID)
}
}
}
class testProtocol {
class func myfunc<N:GenericListProtocol>(re:N) {
let list : [ProductModel] = [ProductModel(id: 1),ProductModel(id: 2),ProductModel(id: 3),ProductModel(id: 4)]
re.setData(list)
}
}
I found this question interesting and thought how best we could solve it in generic way.
protocol Product {
var productID : Int {get set}
}
class ProductModel: Product {
var productID : Int = 0
init(id:Int) {
productID = id
}
}
protocol GenericListProtocol {
typealias T : Product
var list : [T] { get set }
var filteredlist : [T] { get set }
}
extension GenericListProtocol {
func setData(list: [T]) {
list.forEach { item in
print(item.productID)
}
}
}
class GenericListProtocolClass : GenericListProtocol
{
typealias T = ProductModel
var intVal = 0
var list = [T]()
var filteredlist = [T]()
}
class testProtocol {
class func myfunc(re: GenericListProtocolClass){
let list : [ProductModel] = [ProductModel(id: 1),ProductModel(id: 2),ProductModel(id: 3),ProductModel(id: 4)]
re.setData(list)
}
}
let temp = GenericListProtocolClass()
testProtocol.myfunc(temp)
Appreciate your thought and suggestion if it can be improved further.

Swift Protocol Conformance

Here is an example of a set of value relationships that I am toying around with.
protocol Configurable {
func configure(data: Any?) -> Void
}
class RelatedObject {
var x: String = ""
var y: String = ""
}
class Example {
var a: String = ""
var b: String = ""
var c: String = ""
}
extension Example: Configurable {
func configure(data: Any?) //I want the parameter to be of type RelatedObject?, not Any?
{
if let d = data as? RelatedObject { //I don't want to have to do this every time i implement Configurable on an object.
//do stuff
a = d.x
b = d.y
c = d.x + d.y
}
}
}
Is there a way for my classes that implement the Configurable protocol to be able to restrict the specific type of object they accept within the function signature?
I feel like Swift would/could/should have a way avoid a situation where I have to check class types for what gets passed into my object that I want configured.
You are looking for typealias in your protocol definition.
protocol Configurable {
typealias InputData
func configure(data: InputData) -> Void
}
In anything that implements your protocol you set the typealias to the type you would like.
class RelatedObject {
var x: String = ""
var y: String = ""
}
class Example {
var a: String = ""
var b: String = ""
var c: String = ""
}
extension Example: Configurable {
typealias InputData = RelatedObject
func configure(data: InputData) {
a = data.x
b = data.y
c = data.x + data.y
}
}

Resources