Swift Extensions are great, but wait, here’s how it can go wrong (in Burmese)
Prerequisite
- Basic knowledge of Swift syntax
What I’ll cover
- What is an ‘extension’?
- How can an extension go wrong
- The solution
- နိဂုံး
What is an ‘extension’?
Extension ဆိုတာ Swift ရဲ့ language တစ်ခုဖြစ်ပါတယ်။ Swift မှာရှိတဲ့ type တွေရဲ့ ပေးထားတဲ့ functionality ထက်ပိုပြီး လိုအပ်လာတဲ့အခါ extension တွေရေးထားလို့ရပါတယ်။ ဥပမာပြောရရင် ကိုယ့်ရဲ့ view controller မှာ iOS ရဲ့ native alert view တက်လာချင်တယ်ဆိုပါစို့။ ဒီလိုဆိုရင် UIAlertController
တစ်ခုဆောက်ပြီး present လုပ်ရမယ်။ အဲ့လို alert ပြချင်တာမျိုးက တစ်ခြား view controller တွေမှာလည်း alert pop up လုပ်ချင်တာ ရှိနိုင်တာပဲ။ အမြဲတမ်း alert controller ဆောက်တဲ့ code ကိုရေးနေရရင် DRY ကိုချိုးဖောက်ရာကျနေမယ်။ ဒီလိုအခြေအနေမျိုးမှာ UIViewController ထဲမှာ showAlert ဆိုပြီး extension function တစ်ခုရေးထားလို့ရတယ်။ ဘယ်လိုမျိုးလဲဆိုတာကို အောက်က code ကိုကြည့်လိုက်ပါ။
extension UIViewController {
func showAlert(title: String? = "", message: String?, actionTitle: String = "OK", actionCallback: (() -> Void)? = nil) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: actionTitle, style: .default, handler: { (_) in
actionCallback?()
}))
present(alertController, animated: true, completion: nil)
}
}
ရိုးရိုးရှင်းရှင်း information ပြရုံသက်သက် alert လေးတစ်ခုရေးထားပါတယ်။ အခုဆိုရင် extension ရေးထားတဲ့ module ထဲက UIViewController type တိုင်းမှာ showAlert ကို ခေါ်သုံးလို့ရပါပြီ။ UIViewController တင်သာမက သူ့ရဲ့ sub class ဖြစ်တဲ့ UINavigationController ကနေလည်း ခေါ်သုံးလို့ရပါတယ်။
How can an extension go wrong
ဒီလောက် အသုံးဝင်တဲ့ extension က ပြသနာရောပေးနိုင်သလားပေါ့။ အဖြေကတော့ ပေးနိုင်ပါတယ်။ ကျွန်တော်တို့ရဲ့ codebase က တအားမကြီးသေးဘူး သေးသေးလေးပဲဆိုရင်ပြသနာမရှိပေမယ့် team ပေါင်းမြောက်များစွာနဲ့ တစ်ကမ္ဘာလုံးနီးပါးသုံးကြတဲ့ larg scale project တစ်ခုဆိုရင်တော့ ပြသနာရှိလာနိုင်ပါတယ်။ Large scale project တွေမှာ in-house library တွေအပြင် အခြားအပြင်က 3rd party library တွေလည်း အများကြီး ထည့်သုံးရလေ့ရှိပါတယ်။ တစ်ကယ်လို့ ကိုယ့် project မှာသုံးထားတဲ့ library တစ်ခုခုမှာလည်း UIViewController
extension function တစ်ခုရှိပြီး သူ့ရဲ့ name နဲ့ method signature ကလည်း အကုန်အတူတူပဲဆိုရင် naming clash ဖြစ်သွားပါလိမ့်မယ်။ မြန်မာလိုပြောရရင်တော့ name တူ signature တူတဲ့ method နှစ်ခုဖြစ်သွားတဲ့အတွက် ဘယ် module ထဲက method ရဲ့ implementation ကို execute လုပ်ရမယ်ဆိုတာ မဆုံးဖြတ်နိုင်ပဲ build fail သွားတာဖြစ်ပါတယ်။
ဒီလိုအခြေအနေမျိုးက သာမန် small to medium size project တွေမှာဆို သိပ်စိတ်ပူစရာမရှိပေမယ့် world class software project တွေ ဒါမှမဟုတ် ကိုယ်က framework တစ်ခု ကိုယ့်ဘာသာ develop လုပ်နေတဲ့ framework engineer တစ်ယောက်ဆိုရင်တော့ ထည့်တွက်ထားသင့်ပါတယ်။ အထူးသဖြင့် ကိုယ် extension လုပ်လိုက်တဲ့ type က ကိုယ့်ကိုယ်ပိုင် type (ကိုယ့် module ထဲက type) မဟုတ်ပဲ developer တိုင်း သုံးနိုင်တဲ့ type (for example, UIKit types) တွေဆိုရင် အခုလို naming clash က ဖြစ်နိုင်ချေတစ်ခုအထိရှိနေပါတယ်။
The solution
တစ်ဖက်ကမလိုအပ်ဘဲ ကိုယ့်ဘက်က မေတ္တာတွေတစ်ဖက်သပ်ပေးရင် အနှောင့်အယှက်ဖြစ်သွားတတ်ပါတယ်။ အပေါ်က ကျွန်တော်တို့ရဲ့ code ကလည်း alert ပြဖို့လိုတဲ့ view controller ရော မလိုတဲ့ view controller ကိုပါ showAlert ကို တစ်ဖက်သပ်ပေးသလိုဖြစ်နေပါတယ်။ အဲ့လိုမဟုတ်ပဲ showAlert ကို လိုချင်တဲ့ view controller တွေကိုသာပေးပြီး မလိုတဲ့ view controller တွေကိုမပေးဘဲထားရင် ဒီပြသနာပြေလည်နိုင်ပါတယ်။
Protocol extension နဲ့ type constraint ကိုသုံးပြီး အပေါ်ကလိုပြသနာမျိုးကို ဖြေရှင်းကြည့်ပါမယ်။
// 1.
protocol AlertPresentableProtocol {
func showAlert(title: String? = "", message: String?, actionTitle: String = "OK", actionCallback: (() -> Void)? = nil)
}
// 2.
extension AlertPresentableProtocol where Self: UIViewController {
func showAlert(title: String? = "", message: String?, actionTitle: String = "OK", actionCallback: (() -> Void)? = nil) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: actionTitle, style: .default, handler: { (_) in
actionCallback?()
}))
present(alertController, animated: true, completion: nil)
}
}
- Alert တွေကို pop up လုပ်နိုင်တဲ့
AlertPresentableProtocol
ဆိုတဲ့ protocol တစ်ခုရေးလိုက်ပါတယ်။ - UIViewController ကို extension ရေးရမယ့်အစား ကျွန်တော်တို့ရေးထားတဲ့ protocol ကို extension ရေးလိုက်ပါတယ်။ ဒါပေမယ့်
AlertPresentableProtocol
ကို extend လုပ်တဲ့ type ကUIViewController
type ဖြစ်မှသာshowAlert
implementation ကို ready-made ရရှိမှာဖြစ်ပါတယ်။ အဲ့လိုမဟုတ်ရင်တော့ ကိုယ့်ဘာသာshowAlert
method ကို implement လုပ်ရပါလိမ့်မယ်။ ဒီလိုနည်းနဲ့ naming clash မဖြစ်စေပဲAlertPresentableProtocol
ကို extend လုပ်ထားတဲ့ view controller type တိုင်းမှာshowAlert
ကိုခေါ်လို့ရတော့မှာဖြစ်ပါတယ်။
ဒီ solution ရဲ့ trade-off ကတော့ view controller တိုင်းမှာ showAlert ကို ရတော့မှာမဟုတ်ပါဘူး။ ဒါပေမယ့် ကိုယ့် module မှာ naming clash မဖြစ်နိုင်ဘူးဆိုရင် explicitly opt-in လုပ်လို့ရပါတယ်။ အောက်က code ကိုကြည့်ပါ။
extension UIViewController: AlertPresentableProtocol {}
အခုလိုဆိုရင် showAlert ကို လိုတဲ့ view controller တွေကိုသာပေးပြီး by default ပါတာမဟုတ်တော့တဲ့အတွက် အကုန်လုံးမှာပါချင်လား တစ်ချို့တစ်ဝက်ကိုပဲပေးချင်တာလားဆိုတာ ကိုယ့်ဘာသာဆုံးဖြတ်လို့ရသွားပါပြီ။
နိဂုံး
Extension တွေက အခြား modern programming languages (C#, Kotlin etc) တွေမှာလည်းရှိပါတယ်။ Extension တွေပေါ်လာတဲ့အတွက် ကိုယ်ပိုင် type တွေသာမက ကိုယ်ယူသုံးနေတဲ့ library တွေထဲက type တွေရဲ့ functionality တွေကိုလည်း တိုးမြှင့်လို့ရလာပါတယ်။ ဒါပေမယ့် တစ်ခါတလေ naming clash လို ပြသနာမျိုးတွေ တက်နိုင်ပြီး ဘယ်လိုဖြေရှင်းလို့ရတယ်ဆိုတာကို ဒီ article မှာ ဆွေးနွေးပေးခဲ့တာဖြစ်ပါတယ်။