目次
概要
データベースのデータを表示するのはいいが、数が多くなってくるとどこにどのデータがあるかわかりづらいし、何よりもユーザーにも不親切。
その問題を解消するためにソートを行う。
本コードは他のページのRelmのページのコードに追記している。
ソースコード
import SwiftUI
@main
struct TestApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
DatabaseListView()
}
}
}
View関連
simport SwiftUI
struct DatabaseListView: View {
@ObservedObject var viewModel: DatabaseListViewModel = DatabaseListViewModel()
@State var inputText: String = ""
var body: some View {
VStack {
Spacer()
HStack {
Spacer()
TextField("", text: self.$inputText)
.textFieldStyle(.roundedBorder)
Spacer()
Button {
self.viewModel.registerData(inputText: self.inputText)
self.inputText = ""
} label: {
Text("登録")
}
Spacer()
}
Spacer()
List {
ForEach(self.viewModel.data, id: \.self) { data in
VStack(alignment: .leading) {
Text("ID: \(data.id)")
Text("入力文字: \(data.title)")
Text("登録日: \(data.registerDate.transeJapaneseDateString())")
Text("更新日: \(data.updateDate.transeJapaneseDateString())")
}
}
}
}
.alert(isPresented: self.$viewModel.isShownDialog) {
Alert(title: Text("Error"),
message: Text("登録に失敗しました"),
dismissButton: .default(Text("閉じる")))
}
}
}
処理部分
import Foundation
import RealmSwift
class DatabaseListViewModel: ObservableObject {
@Published var isShownDialog: Bool = false
@Published var data: [DatabaseTableData]
init() {
self.data = DatabaseManager.shared.getInstance()
}
func registerData(inputText: String) {
let data = DatabaseTableData(id: self.data.count + 1,
title: inputText,
registerDate: Date(),
updateDate: Date())
DatabaseManager.shared.registerFavoriteSetting(data: data)
self.data = DatabaseManager.shared.getInstance()
}
}
日付表示部分のExtension
import Foundation
extension Date {
func transeJapaneseDateString() -> String {
let format = Date.FormatStyle().locale(Locale(identifier: "ja_JP"))
.year()
.month(.twoDigits)
.day(.twoDigits)
.weekday(.abbreviated)
.hour(.twoDigits(amPM: .wide))
.minute(.twoDigits)
.second(.twoDigits)
return format.format(self)
}
}
DB処理部分
データベースの項目クラス(テーブルのカラム設定のようなもの)
import Foundation
import RealmSwift
class DatabaseTableData: Object {
@objc dynamic var id: Int
@objc dynamic var title: String
@objc dynamic var registerDate: Date
@objc dynamic var updateDate: Date
override init() {
self.id = 0
self.title = ""
self.registerDate = Date()
self.updateDate = Date()
}
init(id: Int, title: String, registerDate: Date, updateDate: Date) {
self.id = id
self.title = title
self.registerDate = registerDate
self.updateDate = updateDate
}
}
DBの処理部分
import Foundation
import RealmSwift
enum DataBaseOrder: String, CaseIterable, Identifiable {
case idAscendingOrder = "ID昇順"
case idDescendingOrder = "ID降順"
case titleAscendingOrder = "タイトル昇順"
case titleDescendingOrder = "タイトル降順"
case registerDateAscendingOrder = "登録が古い順"
case registerDateDescendingOrder = "登録が新しい順"
case updateDateAscendingOrder = "更新が古い順"
case updateDateDescendingOrder = "更新が新しい順"
var id: String { rawValue }
}
class DatabaseManager {
static let shared = DatabaseManager()
var realm: Realm? = nil
@Published var selectedOrder: DataBaseOrder = .idAscendingOrder
private init() {
}
func getInstance() -> [DatabaseTableData] {
do {
self.realm = try? Realm()
}
guard var objects = self.realm?.objects(DatabaseTableData.self) else {
return []
}
objects = sortedObjects(objects: objects)
return Array(objects)
}
func registerData(data: DatabaseTableData) {
do {
self.realm = try? Realm()
try? self.realm?.write {
self.realm?.add(data)
}
}
}
func updateData(data: DatabaseTableData, title: String) {
do {
self.realm = try? Realm()
try? self.realm?.write {
data.title = title
data.updateDate = Date()
}
}
}
func deleteData(data: DatabaseTableData) {
do {
self.realm = try? Realm()
try? self.realm?.write {
self.realm?.delete(data)
}
}
}
private func sortedObjects(objects: Results<DatabaseTableData>) -> Results<DatabaseTableData> {
switch DatabaseManager.shared.selectedOrder {
case .idAscendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "id", ascending: true),
]
return objects.sorted(by: sortDescriptors)
case .idDescendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "id", ascending: false),
]
return objects.sorted(by: sortDescriptors)
case .titleAscendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "title", ascending: true),
]
return objects.sorted(by: sortDescriptors)
case .titleDescendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "title", ascending: false),
]
return objects.sorted(by: sortDescriptors)
case .registerDateAscendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "registerDate", ascending: true),
]
return objects.sorted(by: sortDescriptors)
case .registerDateDescendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "registerDate", ascending: false),
]
return objects.sorted(by: sortDescriptors)
case .updateDateAscendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "updateDate", ascending: true),
]
return objects.sorted(by: sortDescriptors)
case .updateDateDescendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "updateDate", ascending: false),
]
return objects.sorted(by: sortDescriptors)
}
}
}
デモ動画
詳細
ソートする際に必要なのはソートする種類を格納する列挙体。
ソースコードの例では以下。
enum DataBaseOrder: String, CaseIterable, Identifiable {
case idAscendingOrder = "ID昇順"
case idDescendingOrder = "ID降順"
case titleAscendingOrder = "タイトル昇順"
case titleDescendingOrder = "タイトル降順"
case registerDateAscendingOrder = "登録が古い順"
case registerDateDescendingOrder = "登録が新しい順"
case updateDateAscendingOrder = "更新が古い順"
case updateDateDescendingOrder = "更新が新しい順"
var id: String { rawValue }
}
文字列はViewに記載したソート用のボタンに表示する文言として定義している。
そのため、単に処理だけを行う場合は文字列は不要。今回は画面右上にソートボタンを設置したかったため、文字列まで定義している。
ちなみに、ソートボタンの実装部分は以下。
.navigationTitle("データベーステスト")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
listOrderMenu()
}
}
private func listOrderMenu() -> some View {
return Menu {
ForEach(DataBaseOrder.allCases, id:\.self) { dataOrder in
Button {
DatabaseManager.shared.selectedOrder = dataOrder
viewModel.data = DatabaseManager.shared.getInstance()
} label: {
HStack {
if dataOrder == DatabaseManager.shared.selectedOrder {
Image(systemName: "checkmark")
}
Text(dataOrder.rawValue)
}
}
}
} label: {
Image(systemName: "arrow.up.and.down.text.horizontal")
}
}
そして、実際のDBのソート処理は以下
func getInstance() -> [DatabaseTableData] {
do {
self.realm = try? Realm()
}
guard var objects = self.realm?.objects(DatabaseTableData.self) else {
return []
}
objects = sortedObjects(objects: objects)
return Array(objects)
}
private func sortedObjects(objects: Results<DatabaseTableData>) -> Results<DatabaseTableData> {
switch DatabaseManager.shared.selectedOrder {
case .idAscendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "id", ascending: true),
]
return objects.sorted(by: sortDescriptors)
case .idDescendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "id", ascending: false),
]
return objects.sorted(by: sortDescriptors)
case .titleAscendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "title", ascending: true),
]
return objects.sorted(by: sortDescriptors)
case .titleDescendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "title", ascending: false),
]
return objects.sorted(by: sortDescriptors)
case .registerDateAscendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "registerDate", ascending: true),
]
return objects.sorted(by: sortDescriptors)
case .registerDateDescendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "registerDate", ascending: false),
]
return objects.sorted(by: sortDescriptors)
case .updateDateAscendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "updateDate", ascending: true),
]
return objects.sorted(by: sortDescriptors)
case .updateDateDescendingOrder:
let sortDescriptors = [
SortDescriptor(keyPath: "updateDate", ascending: false),
]
return objects.sorted(by: sortDescriptors)
}
}
要は、何でソートするかはSortDescriptorのkeyPathにデータベースのカラムのクラスの変数名を文字列として設定し、
昇順にするか降順にするかはascendingをtrueにしたら昇順、falseにしたら降順になる。
仕上げに、データを取得したobjectsのsortedメソッドを使用してデータのソートの処理を行う。