I am just shifted from Android base to ios, looking for builder pattern get and set method in swift unable to find anything like it. Found following only
var ptype : String? {
get{
return self.ptype
}set (ptype) {
self.ptype = ptype
}
}
After using so many libraries written in Swift, I have rarely seen people use the builder pattern in Swift.
I think the Builder Pattern's main advantages can already be achieved with Swift's other language features. You can totally create a constructor where all the parameters are optional, and you almost just recreated the builder pattern in Swift:
class Foo {
let a: Int
let b: String
let c: Bool
init(a: Int = 0, b: String = "", c: Bool = false) {
self.a = a
self.b = b
self.c = c
}
}
You can create a Foo like this:
// You can omit any of the arguments, just like the builder pattern
Foo(
a: 123
b: "Hello World"
c: true
)
I would argue that's an even cleaner version of something like this in Java:
new FooBuilder()
.setA(123)
.setB("Hello World")
.setC(true)
.build()
But if you insist, here is some really verbose Swift that implements the Builder pattern:
class Foo {
private(set) var a: Int = 0
private(set) var b: String = ""
private(set) var c: Bool = false
init(a: Int = 0, b: String = "", c: Bool = false) {
self.a = a
self.b = b
self.c = c
}
class Builder {
private var innerFoo = Foo()
#discardableResult
func withA(_ a: Int) -> Builder {
innerFoo.a = a
return self
}
#discardableResult
func withB(_ b: String) -> Builder {
innerFoo.b = b
return self
}
#discardableResult
func withC(_ c: Bool) -> Builder {
innerFoo.c = c
return self
}
func build() -> Foo {
return innerFoo
}
}
}
The following tip looks like what you wanted https://github.com/vincent-pradeilles/swift-tips#implementing-the-builder-pattern-with-keypaths. I'll copy code here for quick looking.
protocol With {}
extension With where Self: AnyObject {
#discardableResult
func with<T>(_ property: ReferenceWritableKeyPath<Self, T>, setTo value: T) -> Self {
self[keyPath: property] = value
return self
}
}
extension UIView: With {}
let view = UIView()
let label = UILabel()
.with(\.textColor, setTo: .red)
.with(\.text, setTo: "Foo")
.with(\.textAlignment, setTo: .right)
.with(\.layer.cornerRadius, setTo: 5)
view.addSubview(label)
Related
I have created multiple classes, all of which need to implement the NSCopying protocol, but there are a lot of properties in my classes, is there an easier way?
Below is my current way:
class TestA: NSObject, NSCopying {
var a: CGFloat = 0
var b: CGFloat = 0
required override init() {
}
func copy(with zone: NSZone? = nil) -> Any {
let item = type(of: self).init()
item.a = a
item.b = b
return item
}
}
class TestB: TestA {
var c: CGFloat = 0
var d: CGFloat = 0
override func copy(with zone: NSZone? = nil) -> Any {
let item = super.copy(with: zone) as! TestB
item.c = c
item.b = b
return item
}
}
My thought is, can we take all the properties of the class, automatically create a new object, assign values to the new object?
Use the initializer.
class TestA: NSObject, NSCopying {
var a: CGFloat = 0
var b: CGFloat = 0
required override init() {}
convenience init(a: CGFloat, b: CGFloat) {
self.init()
self.a = a
self.b = b
}
func copy(with zone: NSZone? = nil) -> Any {
let item = TestA(a: a, b: b)
return item
}
}
Doing it this way doesn't really save code since you still need an initializer that takes values for all properties, but you do get a simplified copy method and another initializer that might be useful in other situations too.
You can look at KeyValueCoding package with KeyValueCoding protocol which implements enumeration of all properties of an object and setting values by key paths for pure swift classes and structs.
Based on it you can implement Copying protocol:
protocol Copying: KeyValueCoding {
init()
}
extension Copying {
func makeCopy() -> Self {
var item = Self()
var _self = self
metadata.properties.forEach {
item[$0.name] = _self[$0.name]
}
return item
}
}
How it works:
class TestA: Copying {
var a: CGFloat = 1
var b: Int = 2
required init() {}
}
class TestB: TestA {
let c: String = "Hello Copy!"
let d: Date = Date(timeIntervalSince1970: 123456789)
}
let objectA = TestA()
objectA.a = 100
objectA.b = 200
let copiedA = objectA.makeCopy()
print(copiedA.a) // "100.0"
print(copiedA.b) // "200"
let objectB = TestB()
objectB.a = 100
objectB.b = 200
let copiedB = objectB.makeCopy()
print(copiedB.a) // "100.0"
print(copiedB.b) // "200"
print(copiedB.c) // "Hello Copy!"
print(copiedB.d.timeIntervalSince1970) // "123456789.0"
So as you can see this approach works with inherited properties as well.
Moreover it works with structs:
struct MyStruct: Copying {
let a = 1.0
let b = 2
let c = "c"
}
let myStruct = MyStruct()
let copied = myStruct.makeCopy()
print(copied) // MyStruct(a: 1.0, b: 2, c: "c")
I think I've found a solution, but I'm not sure if this will have any ill effects. Can someone tell me what's wrong with this。Thanks!
#objcMembers class TestA: NSObject, NSCopying {
var a: CGFloat = 0
var b: CGFloat = 0
var c: CGFloat = 0
required override init() {
}
func copy(with zone: NSZone? = nil) -> Any {
let item = type(of: self).init()
for property in getAllPropertys() {
let value = self.value(forKey: property)
item.setValue(value, forKey: property)
}
return item
}
func getAllPropertys()->[String]{
var result = [String]()
var count:UInt32 = 0
let proList = class_copyPropertyList(object_getClass(self),&count)
for i in 0..<numericCast(count) {
let property = property_getName((proList?[i])!);
let proper = String.init(cString: property)
result.append(proper)
}
return result
}
}
To understand the origin of the question, let's start with some code:
protocol MyProtocol {
var val1: Int { get set }
}
struct StructA: MyProtocol {
var val1: Int
var structAVal: Int
}
struct StructB: MyProtocol {
var val1: Int
var structBVal: Int
var thirdProperty: Int
}
And then I have a struct with a heterogeneous array of type MyProtocol:
struct Values {
var arr: [MyProtocol] = [StructA(val1: 0, structAVal: 0), StructB(val1: 0, structBVal: 0)]
}
if I was to change one of the values with a method in Values such as:
struct Values {
var arr: [MyProtocol] = [StructA(val1: 0, structAVal: 0), StructB(val1: 0, structBVal: 0)]
mutating func set<T: MyProtocol>(at index: Int, _ newValue: T) {
arr[index] = newValue
}
}
That would be smooth.
The problem which I am facing is, say I wanted to change var thirdProperty: Int in the structB item in var arr: [MyProtocol], I would not be able to do so which my mutating func set<T: MyProtocol>(at index: Int, _ newValue: T), since It only knows of MyProtocol types.
So my 2 cents to resolve this matter was using a closure something like this:
mutating func set<T: MyProtocol>(at index: Int, closure: (T?) -> (T)) {
arr[index] = closure(arr[index] as? T)
}
The problem with this is that every time I invoke this method, I would first need to downcast the parameter (from MyProtocol to StructB). which seems more of a workaround which could invite unwanted behaviours along the road.
So I started thinking maybe there is a way to constraint the generic parameter to a sibling parameter something like this (pseudo code):
mutating func set<T: MyProtocol>(type: MyProtocol.Type, at index: Int, closure: (T?) -> (T)) where T == type {
arr[index] = closure(arr[index] as? T)
}
Which as you guessed, does not compile.
Any thought on how to approach this matter in a better manner. T.I.A
Use T.Type instead of MyProtocol.Type in the set(type:at:closure:) method.
struct Values {
var arr: [MyProtocol] = [StructA(val1: 0, structAVal: 0), StructB(val1: 0, structBVal: 0, thirdProperty: 0)]
mutating func set<T: MyProtocol>(type: T.Type, at index: Int, closure: ((T?) -> (T?))) {
if let value = closure(arr[index] as? T) {
arr[index] = value
}
}
}
Example:
var v = Values()
v.set(type: StructB.self, at: 1) {
var value = $0
value?.thirdProperty = 20
return value
}
Do let me know if this is the right understanding of your requirement.
PGDev's solution gets to the heart of the question, but IMO the following is a bit easier to use:
enum Error: Swift.Error { case unexpectedType }
mutating func set<T: MyProtocol>(type: T.Type = T.self, at index: Int,
applying: ((inout T) throws -> Void)) throws {
guard var value = arr[index] as? T else { throw Error.unexpectedType }
try applying(&value)
arr[index] = value
}
...
var v = Values()
try v.set(type: StructB.self, at: 1) {
$0.thirdProperty = 20
}
The = T.self syntax allows this to be simplified a little when the type is known:
func updateThirdProperty(v: inout StructB) {
v.thirdProperty = 20
}
try v.set(at: 1, applying: updateThirdProperty)
Another approach that is more flexible, but slightly harder on the caller, would be a closure that returns MyProtocol, so the updating function can modify the type. I'd only add this if it were actually useful in your program:
mutating func set<T: MyProtocol>(type: T.Type = T.self, at index: Int,
applying: ((T) throws -> MyProtocol)) throws {
guard let value = arr[index] as? T else { throw Error.unexpectedType }
arr[index] = try applying(value)
}
...
try v.set(type: StructB.self, at: 1) {
var value = $0
value.thirdProperty = 20
return value // This could return a StructA, or any other MyProtocol
}
(Which is very close to PGDev's example, but doesn't require Optionals.)
protocol Decodable {
init?(data: [String: AnyObject])
}
struct A: Decodable {
var data: [String: AnyObject]!
init?(data: [String: AnyObject]) {
self.data = data
}
}
This works when i want to create an object
let d = ["name":"Rahul"]
let a = A(data: d)
I am trying to achieve the following but it is giving errors at the compile time.
let dArray = [["name":"Rahul"],["name":"Rahul"],["name":"Rahul"]]
let aArray = [A](data: dArray)
The following code is giving me error 'nil is the only return value permitted in an initializer'.
public extension CollectionType where Generator.Element: Decodable {
init?(data: [[String: AnyObject]]) {
var elements: [Generator.Element] = []
for d in data {
let element = Generator.Element(data: d)
if let element = element {
element.append(element)
}
}
return elements
}
}
=================================
Answer : -
public extension Array where Element: Decodable {
init?(data: [String: AnyObject]) {
var elements: [Element] = []
for d in data {
let element = Element(data: d)
if let element = element {
element.append(element)
}
}
self = elements
}
}
This will allow you to initialise using the following code
let dArray = [["name":"Rahul"],["name":"Rahul"],["name":"Rahul"]]
let aArray = [A](data: dArray)
Your error is because CollectionType is a protocol, which cannot be initialised.
Have you tried to create a helper class method to return you the collection, e.g.
func collectionWithData(data: [[String: AnyObject]]) -> [Generator.Element] {
let's define a protocol
protocol P {
init?(b: Bool)
}
as mentioned by Oliver, protocol cannot be initialized, but we still are able to define init in protocol extension
extension P {
init?(b: Bool) {
print("init defined in extension")
if b == false { return nil }
self.init(b: b)
}
}
see, that you cannot return anything from init but nil (if initialization failed)
let's define a class which conforms to our protocol
class C: P {
var b: Bool
required init(b: Bool) {
self.b = b
}
}
and see what happens in next snippet
let c1: C? = C(b: false)
let c2 = C(b: false)
dump(c1)
dump(c2)
/*
init defined in extension
- nil
▿ C #0
- b: false
*/
the same expression C(b: false) give us two different results :-). So, be careful if you define init and init? via protocol extension. If you try to define init and init? with the same parameters without protocol extension, the compiler will complain.
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?
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
}
}