Swift Extension

Extensions ใน Swift มีไว้สำหรับเพิ่ม functionality ใหม่ๆให้กับ class, structure, enumeration หรือ protocol ที่มีอยู่แล้ว ซึ่งรวมไปถึง type ที่เราไม่สามารถเข้าถึง source code ของมันได้ (Retroactive Modelling)

สิ่งที่ Extension สามารถทำได้

  • สร้าง Computed Instance/Type Properties
  • สร้าง Instance/Type Methods
  • เพิ่ม Initialiser
  • สร้าง Subscripts
  • สร้าง Nested Types
  • ทำให้ Type ที่มีอยู่มัน conform กับ protocol ใดๆ

มาดูรายละเอียดกันเลยดีกว่า

Computed Properties

extension Int {
var pack: Int { return self * 12 }
var piece: Int { return self }
}
let food = 5.piece            // 5
let soap = 1.pack // 12
let allItems = food + soap // 17

Property ข้างต้นเป็น read-only computed properties จึงไม่ต้องใช้ keyword get

  • ไม่สามารถใช้ Extension ในการเพิ่ม stored properties ได้
  • ไม่สามารถเพิ่ม property observer ให้กับ property ที่มีอยู่แล้วได้

Initializer

เราสามารถใช้ Extension ในการสร้าง Initializer ของ Type ที่มีอยู่แล้วได้ ดังตัวอย่างต่อไปนี้

struct Circle {
var center = CGPoint()
var radius = 0.0
}
extension Circle {
init(rect: CGRect) {
radius = Double(min(rect.size.width, rect.size.height)) / 2.0
center = CGPoint(x: radius, y: radius)
}
}
let c1 = Circle()         // center -> (0, 0)   radius -> 0
let c2 = Circle(rect: CGRect(x: 0, y: 0, width: 100, height: 50))
// center -> (25, 25) radius -> 25

Method

Extension สามารถถูกใช้ในการเพิ่ม Instance Method และ Type method ไปใน type ที่มีอยู่แล้ว

extension String {
func pr() {
print("==> \(NSDate()) \(self)")
}
}
"hello".pr()           // 2016-12-22 16:17:04 +0000 hello

Instance Method ที่ modify (mutate) ตัวเอง ต้องถูก mask ไว้ด้วย keyword mutating

ตัวอย่างที่ 1

extension Int {
mutating func decreaseByOne() {
self = self - 1
}
}
var num = 1
num.decreaseByOne() // 0

ตัวอย่างที่ 2

extension Circle {
mutating func decreaseRadiusByOne() {
if radius > 1 {
radius = radius — 1
} else {
radius = 0
}
}
}
var c = Circle(center: CGPoint(x: 1, y: 1) , radius: 20)
c.decreaseRadiusByOne() // center -> (1, 1) radius -> 19

Subscripts

Extension สามารถใช้ในการสร้าง subscript ได้เหมือนกัน

extension UIView {
subscript(index: Int) -> UIView {
let size = self.bounds.size.width * CGFloat(index)
let view = UIView()
view.bounds.size.width = size
view.bounds.size.height = size
return view
}
}
let myView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
myView.backgroundColor = UIColor.red
let v1 = myView[1]
v1.backgroundColor = UIColor.yellow
let v2 = myView[2]
v2.backgroundColor = UIColor.red
let v3 = myView[3]
v3.backgroundColor = UIColor.blue
view.addSubview(v3)
view.addSubview(v2)
view.addSubview(v1)

จากโค้ดด้านบน เราใช้ตัวเลข subscript ในการสร้าง UIView ใหม่ที่มีขนาดเป็น n เท่า เทียบกับ UIView ตั้งต้น

เช่น myView[3] จะรีเทิร์น UIView ที่มีขนาดเป็น 3 เท่าของ myView

Nested Types

สามารถเพิ่ม nested types เข้าไปใน classes, structures หรือ enumerations ที่มีอยู่แล้วได้ด้วยการใช้ Extension ดังนี้

class Score {
var gradeString: String = ""
var rawScore: Int = 0
}
extension Score {
var grade: String {
switch rawScore {
case 90...100: return "A"
case 80...89: return "B"
case 70...79: return "C"
case 60...69: return "D"
default: return "F"
}
}
}
let s = Score()
s.rawScore = 90
print("grade: \(s.grade)")   // print grade: A

Reference

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.