Created
October 4, 2023 06:03
-
-
Save mbotsu/a4c51b1a9b8495e10a025500d00c2e7a to your computer and use it in GitHub Desktop.
Using queries combined with arrays in RealmSwift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import RealmSwift | |
struct ContentView: View { | |
@ObservedObject var search = TodoSearchData() | |
var body: some View { | |
VStack { | |
List { | |
HStack { | |
ZStack { | |
RoundedRectangle(cornerRadius: 10) | |
.fill(Color(red: 239 / 255, | |
green: 239 / 255, | |
blue: 241 / 255)) | |
.frame(height: 36) | |
HStack(spacing: 6) { | |
Spacer() | |
.frame(width: 0) | |
Image(systemName: "magnifyingglass") | |
.foregroundColor(.gray) | |
TextField("テキスト", text: $search.text).onSubmit { | |
search.run() | |
} | |
.submitLabel(.search) | |
} | |
} | |
.padding(.horizontal) | |
} | |
HStack { | |
Button("日付", action: { | |
search.isDate.toggle() | |
search.run() | |
}) | |
.buttonStyle(.bordered) | |
.controlSize(.small) | |
} | |
.frame(maxWidth: .infinity, alignment: .leading) | |
if search.isDate { | |
VStack(alignment: .leading){ | |
Text("日付").font(.caption) | |
HStack { | |
DatePicker("", selection: $search.dateFrom, displayedComponents: .date) | |
.onChange(of: search.dateFrom, perform: { _ in | |
search.run() | |
}) | |
.datePickerStyle(.compact) | |
Text("〜") | |
DatePicker("", selection: $search.dateTo, displayedComponents: .date) | |
.onChange(of: search.dateTo, perform: { _ in | |
search.run() | |
}) | |
.datePickerStyle(.compact) | |
} | |
} | |
.frame(maxWidth: .infinity, alignment: .leading) | |
.labelsHidden() | |
.environment(\.locale, .init(identifier: "ja_JP")) | |
.font(.body) | |
.ignoresSafeArea() | |
} | |
if let todos = search.results { | |
ForEach(todos.indices, id: \.self) { idx in | |
Text(todos[idx].title + " " + todos[idx].date.string) | |
} | |
} | |
} | |
} | |
.onAppear { | |
UITextField.appearance().clearButtonMode = .whileEditing | |
search.run() | |
} | |
} | |
} | |
class TodoSearchData: ObservableObject { | |
@Published var isDate = false | |
@Published var dateFrom = Date() | |
@Published var dateTo = Date() | |
@Published var text = "" | |
@ObservedResults(Todo.self) var todos | |
@Published var results: Results<Todo>? | |
private func query(_ query: Query<Todo>) -> Query<Bool>? { | |
var queries = [Query<Bool>]() | |
if isDate { | |
let dateFrom = dateFrom.from | |
let dateTo = dateTo.to | |
if dateFrom < dateTo { | |
queries.append(query.date.contains(dateFrom...dateTo)) | |
} | |
} | |
if text != "" { | |
queries.append(query.title.contains(text)) | |
} | |
return queries.andCompound() | |
} | |
func run(){ | |
results = todos.where{ todo in | |
query(todo) ?? (todo.title != "") | |
} | |
.sorted(byKeyPath: "_id", ascending: true) | |
} | |
} | |
func DateToString(date: Date) -> String { | |
let f = DateFormatter() | |
f.dateFormat = "yyyy/MM/dd" | |
f.locale = Locale(identifier: "ja_JP") | |
return f.string(from: date) | |
} | |
extension Date { | |
var string: String { | |
return DateToString(date: self) | |
} | |
var from: Date { | |
return Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: self)! | |
} | |
var to: Date { | |
return Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: self)! | |
} | |
} | |
// References | |
// Compound Array of Query<Bool> #7731 | |
// https://github.com/realm/realm-swift/issues/7731 | |
extension Sequence where Element == Query<Bool> { | |
func andCompound() -> Query<Bool>? { | |
var iterator = makeIterator() | |
var accumulator = iterator.next() | |
while let element = iterator.next() { | |
if let unwrappedAccumulator = accumulator { | |
accumulator = unwrappedAccumulator && element | |
} else { | |
accumulator = element | |
} | |
} | |
return accumulator | |
} | |
func orCompound() -> Query<Bool>? { | |
var iterator = makeIterator() | |
var accumulator = iterator.next() | |
while let element = iterator.next() { | |
if let unwrappedAccumulator = accumulator { | |
accumulator = unwrappedAccumulator || element | |
} else { | |
accumulator = element | |
} | |
} | |
return accumulator | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import RealmSwift | |
let schemaVersion:UInt64 = 1 | |
@main | |
struct TodoExampleApp: SwiftUI.App { | |
var realm: Realm | |
init(){ | |
let config = Realm.Configuration(schemaVersion: schemaVersion) | |
Realm.Configuration.defaultConfiguration = config | |
realm = try! Realm() | |
// let todo1 = Todo(value: ["title": "Hello1", "date": makeDate(2023, 10, 10)] as [String : Any]) | |
// let todo2 = Todo(value: ["title": "Hello2", "date": makeDate(2023, 11, 20)] as [String : Any]) | |
// let todo3 = Todo(value: ["title": "Hello3", "date": makeDate(2023, 12, 30)] as [String : Any]) | |
try! realm.write { | |
let _ = realm.objects(Todo.self) | |
// realm.add([todo1, todo2, todo3]) | |
} | |
} | |
var body: some Scene { | |
WindowGroup { | |
ContentView() | |
.environment(\.realm, realm) | |
} | |
} | |
} | |
func makeDate(_ year: Int,_ month: Int,_ day: Int) -> Date { | |
let date = Calendar.current.date(from: DateComponents(year : year, month: month, day: day)) ?? Date() | |
return Calendar.current.startOfDay(for: date) | |
} | |
final class Todo: Object, ObjectKeyIdentifiable { | |
@Persisted(primaryKey: true) var _id: ObjectId | |
@Persisted var title = "" | |
@Persisted var date = Date() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment