swiftでiPhone標準の時計アプリを作ろう 完成コード アラーム

2019年9月12日木曜日

swift アラーム 時計

t f B! P L
Xcode 10.3
ios 12.4
swit5

完成品

UI

今回はViewが多いですがなるべく標準に似せて設置しました。

コード

AppDelegate

import UIKit
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
        
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        // 通知許可の取得
        UNUserNotificationCenter.current().requestAuthorization(
        options: [.alert, .sound, .badge]){
            (granted, _) in
            if granted{
                UNUserNotificationCenter.current().delegate = self
            }
        }
        return true
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
        let center = UNUserNotificationCenter.current()
        center.getDeliveredNotifications { (notifications: [UNNotification]) in
            for notification in notifications {
                _ = AlarmVC.shared.getAlarm(from: notification.request.identifier)
                NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil)
            }
        }
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate{
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        // アプリ起動中でもアラートと音で通知
        completionHandler([.alert, .sound])
        let uuid = notification.request.identifier
        _ = AlarmVC.shared.getAlarm(from: uuid)
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil)
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let identifier = response.actionIdentifier
        
        if identifier == "snooze"{
            let snoozeAction = UNNotificationAction(
                identifier: "snooze",
                title: "Snooze 5 Minutes",
                options: []
            )
            let noAction = UNNotificationAction(
                identifier: "stop",
                title: "stop",
                options: []
            )
            let alarmCategory = UNNotificationCategory(
                identifier: "alarmCategory",
                actions: [snoozeAction, noAction],
                intentIdentifiers: [],
                options: [])
            UNUserNotificationCenter.current().setNotificationCategories([alarmCategory])
            let content = UNMutableNotificationContent()
            content.title = "Snooze"
            content.sound = UNNotificationSound.default
            content.categoryIdentifier = "alarmCategory"
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5 , repeats: false)
            let request = UNNotificationRequest(identifier: "Snooze", content: content, trigger: trigger)
            UNUserNotificationCenter.current().add(request) { (error) in
                if let error = error {
                    print(error.localizedDescription)
                }
            }
        }
        let uuid = response.notification.request.identifier
        _ = AlarmVC.shared.getAlarm(from: uuid)
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: nil)
        completionHandler()
    }
}

AlarmVC

import UIKit
import UserNotifications
class AlarmVC: UIViewController,UITableViewDelegate,UITableViewDataSource {
    
    static let shared = AlarmVC()
    var appDelegate = UIApplication.shared

    @IBOutlet weak var tableView: UITableView!
    var userDefaults = UserDefaults.standard
    var index:Int!

    var timeArray:[AlarmTimeArray] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.allowsSelectionDuringEditing = true
        tableView.allowsSelection = false
        tableView.register(UINib(nibName: "AlarmTimeCell", bundle: nil), forCellReuseIdentifier: "AlarmTimeCell")
        self.navigationItem.setLeftBarButton(self.editButtonItem, animated: true)
        timeLoad()
        tableView.reloadData()
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }
    @objc func methodOfReceivedNotification(notification: Notification) {
        timeLoad()
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
    }

    
    func timeLoad(){
        if let timeArrayData = UserDefaults.standard.object(forKey: "timeArray") as? Data {
            if let getTimeArray = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(timeArrayData) as? [AlarmTimeArray] {
                timeArray = getTimeArray
            }
        }
    }
    
    override func setEditing(_ editing: Bool, animated: Bool) {
        super.setEditing(editing, animated: animated)
        tableView.setEditing(editing, animated: animated)

    }
    
    func setCellLabel(index:Int) -> String{
        if timeArray[index].repeatLabel == "Never" {
            return timeArray[index].label

        }else{
            return timeArray[index].label+","+timeArray[index].repeatLabel
        }
    }
    
    func getAlarm(from uuid: String){
        timeLoad()
        guard let alarm = timeArray.first(where: { $0.uuidString == uuid }) else {return }
        if alarm.week.isEmpty {
            alarm.onOff = false
        }
        saveDate()
        let center = UNUserNotificationCenter.current()
        center.removeAllDeliveredNotifications()
    }
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return timeArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmTimeCell") as! AlarmTimeCell
        
        cell.timeLabel.text = getTime(date: timeArray[indexPath.row].date)
        cell.label.text = setCellLabel(index: indexPath.row)
        cell.sw.isOn = timeArray[indexPath.row].onOff
        cell.editingAccessoryType = .disclosureIndicator

        return cell
    }
    
  
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 画面遷移の準備
        if tableView.isEditing {
            index = indexPath.row
            performSegue(withIdentifier: "showAlarmAdd", sender: nil)
        }
    }
    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [timeArray[indexPath.row].uuidString])
            timeArray.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
            saveDate()
        }
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        
        return 72
    }
    @IBAction func addButton(_ sender: Any) {
        self.performSegue(withIdentifier: "showAlarmAdd", sender: nil)
    }
    
    func getTime(date:Date) -> String {
        let f = DateFormatter()
        f.timeStyle = .short
        f.locale = Locale(identifier: "ja_JP")
        return f.string(from: date)
    }
    
    func saveDate(){
        let timeArrayData = try! NSKeyedArchiver.archivedData(withRootObject: timeArray, requiringSecureCoding: false)
        userDefaults.set(timeArrayData, forKey: "timeArray")
        userDefaults.synchronize()
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showAlarmAdd"{
            guard let nvc = segue.destination as? UINavigationController else {return}
            guard let vc = nvc.topViewController as? AlarmAddVC else {return}
            vc.delegate = self
            vc.isEdit = tableView.isEditing
            if tableView.isEditing {
                vc.alarmTime = timeArray[index]
            }
    }
}
}


extension AlarmVC:AlarmAddDelegate{
    func AlarmAddVC(alarmAdd: AlarmAddVC, alarmTime: AlarmTimeArray) {
        if tableView.isEditing {
            timeArray[index] = alarmTime
        }else{
           timeArray.append(alarmTime)
        }
        timeArray.sort(){$0.date < $1.date}
        saveDate()
        self.setEditing(false, animated: false)
        tableView.reloadData()
    }
    
    func AlarmAddVC(alarmDelete: AlarmAddVC, alarmTime: AlarmTimeArray) {
        self.setEditing(false, animated: false)
        timeArray.remove(at: index)
        saveDate()
        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [timeArray[index].uuidString])

    }
    
    func AlarmAddVC(alarmCancel:AlarmAddVC){
        self.setEditing(false, animated: false)
    }
}

AlarmTimeCell

import UIKit

protocol AlarmTimeCellDelegate {
    func alarmTimeCell(switchTappe:UITableViewCell,isOn:Bool)
}
class AlarmTimeCell: UITableViewCell {

    @IBOutlet weak var timeLabel: UILabel!
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var sw: UISwitch!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        accessoryView = sw
    }
}

AlarmAddVC

import UIKit
import UserNotifications
protocol AlarmAddDelegate {
    func AlarmAddVC(alarmAdd:AlarmAddVC,alarmTime:AlarmTimeArray)
    func AlarmAddVC(alarmDelete:AlarmAddVC,alarmTime:AlarmTimeArray)
    func AlarmAddVC(alarmCancel:AlarmAddVC)
}

class AlarmAddVC: UIViewController ,UITableViewDelegate,UITableViewDataSource{
    
    @IBOutlet weak var datePicker: UIDatePicker!
    @IBOutlet weak var tableView: UITableView!
    var delegate:AlarmAddDelegate!
    var alarmTime:AlarmTimeArray = AlarmTimeArray()
    var isEdit: Bool = false
    var titleText = ["Repeat","Label","Sound"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        datePicker.date = alarmTime.date
        registerCell(cellName: "AlarmSnoozeCell")
        registerCell(cellName: "AlarmAddCell")
        registerCell(cellName: "AlarmDeleteCell")
        tableView.tableFooterView = UIView()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if let indexPathForSelectedRow = tableView.indexPathForSelectedRow {
            tableView.deselectRow(at: indexPathForSelectedRow, animated: true)
        }
    }
    //cell登録
    func registerCell(cellName:String){
        tableView.register(UINib(nibName: cellName, bundle: nil), forCellReuseIdentifier: cellName)
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        switch section {
        case 0:
            return 4
        case 1:
            return 1
        default:
            return 0
        }
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return isEdit ? 2:1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.section {
        case 0:
            switch indexPath.row{
            case 0:
                let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmAddCell") as! AlarmAddCell
                cell.titleLabel.text = titleText[indexPath.row]
                cell.subTitleLabel.text = alarmTime.repeatLabel
                return cell
            case 1:
                let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmAddCell") as! AlarmAddCell
                cell.titleLabel.text = titleText[indexPath.row]
                cell.subTitleLabel.text = alarmTime.label
                return cell
            case 2:
                let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmAddCell") as! AlarmAddCell
                cell.titleLabel.text = titleText[indexPath.row]
                cell.subTitleLabel.text = "Default"
                cell.selectionStyle = .none

                return cell
                
            case 3:
                    let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmSnoozeCell") as! AlarmSnoozeCell
                    cell.delegate = self
                    cell.snoozeSwitch.isOn = alarmTime.snooze
                    return cell
            default:
                break
            }
        case 1:
            let cell = tableView.dequeueReusableCell(withIdentifier: "AlarmDeleteCell") as! AlarmDeleteCell
            cell.delegate = self
            return cell
        default:
            break
        }
        return UITableViewCell()
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        switch indexPath.section {
        case 0:
            switch indexPath.row {
            case 0:
                self.performSegue(withIdentifier: "showRepeat", sender: nil)
            case 1:
                performSegue(withIdentifier: "showLabel",sender: nil)
                break
            case 2:break
            default:break
            }
        default:
            break
        }
    }
    
    public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        if section == 1 {
            return 50
        }else{
            return 0
        }
    }
    
    //アラーム設定時間を保存
    @IBAction func saveButton(_ sender: Any) {        
        alarmSet()
        delegate.AlarmAddVC(alarmAdd: self, alarmTime: alarmTime)
        dismiss(animated: true, completion: nil)
    }
    //スヌーズ設定
    func setCategories(){
        let snoozeAction = UNNotificationAction(
            identifier: "snooze",
            title: "Snooze 5 Minutes",
            options: []
        )
        let noAction = UNNotificationAction(
            identifier: "stop",
            title: "stop",
            options: []
        )
        var alarmCategory:UNNotificationCategory!
        if alarmTime.snooze {
           alarmCategory = UNNotificationCategory(
            identifier: "alarmCategory",
            actions: [snoozeAction, noAction],
            intentIdentifiers: [],
            options: [])
        }else{
            alarmCategory = UNNotificationCategory(
                identifier: "alarmCategory",
                actions: [],
                intentIdentifiers: [],
                options: [])
        }
        UNUserNotificationCenter.current().setNotificationCategories([alarmCategory])
    }
    
    //通知設定
    func setNotificationC(day:String, repeats:Bool){
        let content = UNMutableNotificationContent()
        content.title = alarmTime.label
        content.sound = UNNotificationSound.default
        content.categoryIdentifier = "alarmCategory"
        var dateComponents = DateComponents()
        
        if !day.isEmpty {
        dateComponents.weekday = weekDay(day: day)
        }
        dateComponents.hour = Calendar.current.component(.hour, from: datePicker.date)
        dateComponents.minute = Calendar.current.component(.minute, from: datePicker.date)
        let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: repeats)
        let request = UNNotificationRequest(identifier: alarmTime.uuidString+day, content: content, trigger: trigger)

        UNUserNotificationCenter.current().add(request) { (error) in
            if let error = error {
                print(error.localizedDescription)
            }
        }
        alarmTime.date = datePicker.date
    }
    //アラート設定
    func alarmSet(){
        removeAlarm(identifiers: alarmTime.uuidString)
        let shortWeekday = DateFormatter().shortWeekdaySymbols!
        for i in shortWeekday {
            removeAlarm(identifiers: alarmTime.uuidString+i)
        }
        if alarmTime.week.isEmpty {
            setCategories()
            
            setNotificationC(day:"", repeats: false)
        }else{
        for i in alarmTime.week {
                setCategories()
            setNotificationC(day: i, repeats: true)
            }
        }
    }
    //アラート設定削除
    func removeAlarm(identifiers:String){
        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [identifiers])
    }
    //曜日
    func weekDay(day:String) -> Int{
        var week = DateFormatter().weekdaySymbols!
        switch day {
        case week[0]:
            return 1
        case week[1]:
            return 2
        case week[2]:
            return 3
        case week[3]:
            return 4
        case week[4]:
            return 5
        case week[5]:
            return 6
        case week[6]:
            return 7
        default:
            return Int()
        }
    }
    
    //キャンセル
    @IBAction func cancelButton(_ sender: Any) {
        delegate.AlarmAddVC(alarmCancel: self)
        dismiss(animated: true, completion: nil)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        switch segue.identifier {
        case "showRepeat":
            guard let nextVC:AlarmRepeatVC = segue.destination as? AlarmRepeatVC else {return}
            nextVC.delegate = self
        
            nextVC.selectDay = alarmTime.week
        case "showLabel":
            guard let nextVC:AlarmAddLabelVC = segue.destination as? AlarmAddLabelVC else {return}
            nextVC.delegate = self
            nextVC.text = alarmTime.label
        default:
            return
        }
        
    }
}

extension AlarmAddVC:AlarmRepeatVCDelegate {
    func AlarmRepeatVC(addRepeat: AlarmRepeatVC, week: [String]) {
        alarmTime.week = []
        alarmTime.repeatLabel = ""
        alarmTime.week += week
        if alarmTime.week.count == 1 {
            alarmTime.repeatLabel = "Every"+alarmTime.week[0]
        }else if alarmTime.week.isEmpty {
            alarmTime.repeatLabel = "Never"
        }else if alarmTime.week.count == 7{
            alarmTime.repeatLabel = "Every day"
        }else{
            let shortWeekday = DateFormatter().shortWeekdaySymbols!
            for i in alarmTime.week {
                if alarmTime.repeatLabel != "" {
                    alarmTime.repeatLabel += ","
                }
            alarmTime.repeatLabel += shortWeekday[weekDay(day: i)]
            }
        }
        tableView.reloadData()
    }
    
}

extension AlarmAddVC:AlarmAddLabelDelegate {
    func alarmAddLabel(labelText: AlarmAddLabelVC, text: String) {
        alarmTime.label = text
        tableView.reloadData()
    }
}

extension AlarmAddVC:AlarmSnoozeCellDelegte{
    func alarmSnoozeCell(swichOn: AlarmSnoozeCell, On: Bool) {
        alarmTime.snooze = On
    }
}

extension AlarmAddVC:AlarmDeleteCellDelegate{
    func alarmDeleteCell(delete: UITableViewCell) {
        delegate.AlarmAddVC(alarmDelete: self,alarmTime:alarmTime)
        dismiss(animated: true, completion: nil)
    }
}

AlarmAddCell

import UIKit

class AlarmAddCell: UITableViewCell {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var subTitleLabel: UILabel!
}

AlarmSnoozeCell

import UIKit

protocol AlarmSnoozeCellDelegte {
    func alarmSnoozeCell(swichOn:AlarmSnoozeCell,On:Bool)
}

class AlarmSnoozeCell: UITableViewCell {

    @IBOutlet weak var snoozeSwitch: UISwitch!
    var delegate:AlarmSnoozeCellDelegte!
    @IBAction func switchChanged(_ sender: UISwitch) {
        delegate.alarmSnoozeCell(swichOn: self, On: sender.isOn)
    }
}

AlarmRepeatVC

import UIKit
protocol AlarmRepeatVCDelegate {
    func AlarmRepeatVC(addRepeat:AlarmRepeatVC,week:[String])
}
class AlarmRepeatVC: UIViewController ,UITableViewDelegate,UITableViewDataSource{

    @IBOutlet weak var tableView: UITableView!
    var delegate:AlarmRepeatVCDelegate!
    var week:[String] = []
    var selectDay:[String] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        // 複数選択可にする
        tableView.allowsMultipleSelection = true
         week = DateFormatter().weekdaySymbols!
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        delegate.AlarmRepeatVC(addRepeat: self, week:sortWeek(selectDays: selectDay))
    }
    
    func sortWeek(selectDays: [String]) ->  [String]{
        var week = DateFormatter().weekdaySymbols!
        var dayDictionary: [String: Int] = [:]
        for i in 0...6 {
            dayDictionary[week[i]] = i
        }
        var daysOfWeek: [String] = selectDays
         daysOfWeek.sort { (dayDictionary[$0] ?? 7) < (dayDictionary[$1] ?? 7)}
        return daysOfWeek
    }
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return week.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // セルを取得する
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "weekCell", for: indexPath)
        cell.textLabel!.text = "Every"+week[indexPath.row]
        cell.selectionStyle = .none
        for i in selectDay {
             if week[indexPath.row] == i {
                cell.accessoryType = .checkmark
                break
            }else{
                cell.accessoryType = .none
            }
        }
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at:indexPath)
        // チェックマークを入れる
        cell?.accessoryType = .checkmark
        selectDay.append(week[indexPath.row])
    }
    
    // セルの選択が外れた時に呼び出される
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        let cell = tableView.cellForRow(at:indexPath)
        // チェックマークを外す
        cell?.accessoryType = .none
        selectDay = selectDay.filter { $0 != week[indexPath.row] }
    }
}

AlarmAddLabelVC

import UIKit

protocol AlarmAddLabelDelegate {
    func alarmAddLabel(labelText:AlarmAddLabelVC,text:String)
}

class AlarmAddLabelVC: UIViewController,UITextFieldDelegate {
    
    var text:String!

    @IBOutlet weak var textField: UITextField!
    var delegate:AlarmAddLabelDelegate!
    override func viewDidLoad() {
        super.viewDidLoad()
        // テキストを全消去するボタンを表示
        textField.clearButtonMode = .always
        // 改行ボタンの種類を設定
        textField.returnKeyType = .done
        // UITextFieldを追加
        textField.delegate = self
        //キーボードを表示する
        textField.becomeFirstResponder()
        
        textField.text = text
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        //textFieldの中身が空でない時
        if textField.text != "", let text = textField.text{
            delegate.alarmAddLabel(labelText: self, text:text)
        }
    }
    
    // 完了ボタンを押した時の処理
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        //textFieldの中身が空でない時
        if textField.text != "", let text = textField.text{
            delegate.alarmAddLabel(labelText: self, text:text)
            self.navigationController?.popViewController(animated: true)
        }
        return true
    }
}

AlarmTimeArray

import UIKit

class AlarmTimeArray: NSObject,NSCoding {
    
    var date:Date
    var uuidString:String
    var label:String
    var sound:Bool
    var snooze:Bool
    var onOff:Bool
    var repeatLabel:String
    var week:[String]
    
    override init() {
        self.date = Date()
        self.uuidString = UUID().uuidString
        self.label = "Alarm"
        self.sound = true
        self.snooze = true
        self.onOff = true
        self.week = []
        self.repeatLabel = "Never"
    }
    
    func encode(with aCoder: NSCoder) {
        aCoder.encode(self.date, forKey: "date")
        aCoder.encode(self.uuidString, forKey: "uuidString")
        aCoder.encode(self.label, forKey: "label")
        aCoder.encode(self.sound, forKey: "sound")
        aCoder.encode(self.snooze, forKey: "snooze")
        aCoder.encode(self.onOff, forKey: "onOff")
        aCoder.encode(self.week, forKey: "week")
        aCoder.encode(self.repeatLabel, forKey: "repeatLabel")

    }
    
    required init?(coder aDecoder: NSCoder) {
        date = aDecoder.decodeObject(forKey: "date") as! Date
        uuidString = aDecoder.decodeObject(forKey: "uuidString") as! String
        label = aDecoder.decodeObject(forKey: "label") as! String
        sound = aDecoder.decodeBool(forKey: "sound")
        snooze = aDecoder.decodeBool(forKey: "snooze")
        onOff = aDecoder.decodeBool(forKey: "onOff")
        week = aDecoder.decodeObject(forKey: "week") as! [String]
        repeatLabel = aDecoder.decodeObject(forKey: "repeatLabel") as! String
    }
}
githubはこちら

参考

UITableViewの編集モードを使ってCellの削除を実装するまで
UITableViewのセルを選択不可にする方法
【Swift4】配列の中身(数値・文字・日付)を比較してソートする方法【Xcode9】
UITableViewでセルを複数選択する
【Swift】UserDefaultsに自作クラスのデータを保存する方法(iOS12対応)[Swift][Obj-C]UUIDStringの使い分け

QooQ