Skip to content

Instantly share code, notes, and snippets.

@mbotsu
Created October 4, 2023 06:03
Show Gist options
  • Save mbotsu/a4c51b1a9b8495e10a025500d00c2e7a to your computer and use it in GitHub Desktop.
Save mbotsu/a4c51b1a9b8495e10a025500d00c2e7a to your computer and use it in GitHub Desktop.
Using queries combined with arrays in RealmSwift
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
}
}
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