Sometimes, we would like to add methods to an existing class. We already know how to do this; we just need to go to its Swift source file and add a new method within the class body. However, sometimes, we cannot access the source code for the class, or it isn't convenient to make changes to it. A typical example of this situation is a class, struct, or any other type that is part of the standard language elements. For example, we might want to add a method that we can call in any Int
value to initialize either a 2D or 3D point with all its elements set to the Int
value.
The following lines declare a simple Point2D
class that represents a mutable 2D point with the x
and y
elements. The class conforms to the CustomStringConvertible
protocol; therefore, it declares a description computed property that returns a string representation for the 2D point. The code file for the sample is included in the swift_3_oop_chapter_08_01
folder.
open class Point2D: CustomStringConvertible { open var x: Int open var y: Int open var valuesAsDescription: String { return "x: (x), y: (y)" } open var description: String { get { return "((valuesAsDescription))" } } init(x: Int, y: Int) { self.x = x self.y = y } }
The Point2D
class declares two stored properties: x
and y
. The valueAsDescription
computed property returns a string with the values for x
and y
without parentheses. The description
computed property encloses the value returned by valueAsDescription
in parentheses.
The following lines declare a Point3D
class that inherits from the previously created Point2D
class and adds a z
element to the inherited x
and y
elements. The code file for the sample is included in the swift_3_oop_chapter_08_01
folder.
open class Point3D: Point2D { open var z: Int open override var valuesAsDescription: String { return "(super.valuesAsDescription), z:(z)" } init(x: Int, y: Int, z: Int) { self.z = z super.init(x: x, y: y) } }
The Point3D
class declares the z
stored property and overrides the valueAsDescription
computed property to concatenate the value of the z
stored property to the string value of this property in the superclass. This way, the description computed property declared in the Point2D
superclass will generate the values for x
, y
, and z
enclosed within parentheses.
Now that we have a Point2D
class and a Point3D
class, we want to extend the Int
type to provide methods that generate instances of these classes with all their elements initialized with the Int
value. Specifically, we want to be able to write the following line to generate a Point2D
instance with the x
and y
values initialized to 3
:
var point2D1 = 3.toPoint2D()
In addition, we want to be able to write the following line to generate a Point3D
instance with the x
, y
, and z
values initialized to 5
:
var point3D1 = 5.toPoint3D()
The following lines use the extension
keyword to add two methods to the Int
standard type: toPoint2D
and toPoint3D
. The code file for the sample is included in the swift_3_oop_chapter_08_01
folder.
public extension Int { public func toPoint2D() -> Point2D { return Point2D(x: self, y: self) } public func toPoint3D() -> Point3D { return Point3D(x: self, y: self, z: self) } }
The toPoint2D
method returns a new instance of Point2D
with the x
and y
arguments of the initializer set to self. In this case, self
represents the actual value for Int
. The toPoint3D
method returns a new instance of Point3D
with the x
, y
, and z
arguments of the initializer set to self
.
The following lines use the previously added methods to create instances of both Point2D
and Point3D
. The code file for the sample is included in the swift_3_oop_chapter_08_01
folder.
print(3.toPoint2D()) print(5.toPoint2D()) print(3.toPoint3D()) print(5.toPoint3D())
The following lines show the output generated by the preceding code:
(x: 3, y: 3) (x: 5, y: 5) (x: 3, y: 3, z:3) (x: 5, y: 5, z:5)
The following screenshot shows the results of executing the previous lines in the Playground:
Now, let's imagine that both the Point2D
and Point3D
classes are included in an external framework or library and that we aren't able to access the source code. Our code needs to convert instances of Point3D
to a (Int, Int, Int)
tuple. It is a nice feature to generate a tuple with named elements. Given that we cannot access the source code, we can use the extension
keyword to add a toTuple
method to the Point3D
class. This way, we can easily convert a Point3D
instance to a tuple. The following lines do the job. The code file for the sample is included in the swift_3_oop_chapter_08_02
folder.
public extension Point3D { public func toTuple() -> (x: Int, y: Int, z: Int) { return (x: x, y: y, z: z) } }
The following lines create an instance of the Point3D
class and then call the recently added toTuple
method to generate a tuple composed of three Int
values: (Int, Int, Int)
. Then, the code prints the string representation of the generated tuple. The next line uses a let statement to retrieve the three elements from the tuple generated by another call to the toTuple
method. Then, the code prints the values for the three retrieved elements. The last two lines use the element names (x
, y
, and z
) and numbers (0
, 1
, and 2
) to access the generated tuple values. The code file for the sample is included in the swift_3_oop_chapter_08_02
folder.
var point3D1 = Point3D(x: 10, y: 20, z: 15) var point3D1Tuple = point3D1.toTuple() print(point3D1Tuple) let (point3D1x, point3D1y, point3D1z) = point3D1.toTuple() print(point3D1x, point3D1y, point3D1z) print(point3D1Tuple.x, point3D1Tuple.y, point3D1Tuple.z) print(point3D1Tuple.0, point3D1Tuple.1, point3D1Tuple.2)
The following lines show the output generated by the preceding code.
(10, 20, 15) 10 20 15 10 20 15 10 20 15
The following screenshot shows the result of executing the previous lines in the Playground:
3.145.84.112