Skip to content

Commit

Permalink
Implement filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
Blackjacx committed Aug 6, 2019
1 parent fa589dc commit c867939
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 30 deletions.
4 changes: 4 additions & 0 deletions Columbus.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
B92A544022F3699C008FE261 /* CountryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B92A543F22F3699C008FE261 /* CountryListViewModel.swift */; };
B9397D4222DBC42E00E9D98E /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9397D4122DBC42E00E9D98E /* Extensions.swift */; };
B9397D4322DBC42E00E9D98E /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9397D4122DBC42E00E9D98E /* Extensions.swift */; };
B94B1AF921CD38B400FF1B72 /* Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F8788F21CD28EC00273F82 /* Country.swift */; };
Expand Down Expand Up @@ -65,6 +66,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
B92A543F22F3699C008FE261 /* CountryListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountryListViewModel.swift; sourceTree = "<group>"; };
B9397D4122DBC42E00E9D98E /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
B94B1B0621CD38B400FF1B72 /* Columbus.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Columbus.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B96D496221CD3DC20010E69B /* Resources.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Resources.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -220,6 +222,7 @@
B9F8789421CD28EC00273F82 /* Configuration.swift */,
B9F8788F21CD28EC00273F82 /* Country.swift */,
B9F8788E21CD28EC00273F82 /* CountryListView.swift */,
B92A543F22F3699C008FE261 /* CountryListViewModel.swift */,
B9B4EBCF22DA4889001346B0 /* CountryRow.swift */,
B9B4EBFF22DBB215001346B0 /* CountryStore.swift */,
B9397D4122DBC42E00E9D98E /* Extensions.swift */,
Expand Down Expand Up @@ -507,6 +510,7 @@
buildActionMask = 2147483647;
files = (
B9F8789D21CD28EC00273F82 /* Country.swift in Sources */,
B92A544022F3699C008FE261 /* CountryListViewModel.swift in Sources */,
B9F8789C21CD28EC00273F82 /* CountryListView.swift in Sources */,
B9F878A221CD28EC00273F82 /* Configuration.swift in Sources */,
B9B4EBD022DA4889001346B0 /* CountryRow.swift in Sources */,
Expand Down
6 changes: 4 additions & 2 deletions Example/Source/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
if let windowScene = scene as? UIWindowScene {
Columbus.config = CountryPickerConfig()
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: CountryListView())
let store = CountryStore()
let viewModel = CountryListViewModel(store: store)
window.rootViewController = UIHostingController(rootView: CountryListView(store: store).environmentObject(viewModel))
self.window = window
window.makeKeyAndVisible()
}
Expand Down Expand Up @@ -59,7 +61,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
}

public struct CountryPickerConfig: Configuration {
public var shadowRadius: CGFloat = 10.0
public var shadowRadius: CGFloat = 5.0
public var textAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 15)]
public var lineWidth: CGFloat = 1.0 / UIScreen.main.scale
public var rasterSize: CGFloat = 12.0
Expand Down
2 changes: 1 addition & 1 deletion Source/Classes/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public protocol Configuration {
}

public struct DefaultConfig: Configuration {
public var shadowRadius: CGFloat = 10.0
public var shadowRadius: CGFloat = 5.0
public var textAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 20)]
public var lineWidth: CGFloat = 1.0 / UIScreen.main.scale
public var rasterSize: CGFloat = 12.0
Expand Down
39 changes: 23 additions & 16 deletions Source/Classes/CountryListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,55 @@
import SwiftUI

public struct CountryListView: View {
@ObservedObject private var store = CountryStore()
@State var query: String = ""
@ObservedObject private var store: CountryStore
@EnvironmentObject var viewModel: CountryListViewModel
private let raster = Columbus.config.rasterSize

public init() {
public init(store: CountryStore) {
self.store = store
}

#warning("Implement a search bar!")
#warning("Implement an index bar!")
#warning("Find out how to elegate country to the outside world")
#warning("Initially show button to present/dismiss Columbus")
#warning("Find out how to delegate country to the outside world - Environment!")
#warning("Consider distributing the framework as Binary Package so it is not compiled all the time.")

#warning("Update README.md for SPM/Binary Package and usage instructions.")
#warning("Implement an index bar!")
public var body: some View {
NavigationView {

return NavigationView {
VStack(spacing: raster) {
HStack(spacing: raster) {
Image(systemName: "magnifyingglass")

TextField(Columbus.config.searchBarPlaceholder, text: $query) {
self.store.filter(query: self.query)
}
.textFieldStyle(RoundedBorderTextFieldStyle())
.foregroundColor(Color(.text))
TextField(Columbus.config.searchBarPlaceholder, text: $viewModel.query)
.textFieldStyle(RoundedBorderTextFieldStyle())
.foregroundColor(Color(.text))
}
.padding()

List(store.filteredCountries) { country in
CountryRow(country: country)
Text("SelectedCountry: \(self.viewModel.selectedCountry?.name ?? "none")")

List(self.viewModel.filteredCountries) { country in
ZStack {
CountryRow(country: country)
Button("", action: {
self.viewModel.selectedCountry = country
})
}
}
}
.navigationBarTitle(Text("Countries"))
}.onAppear {
self.store.load()
self.viewModel.filteredCountries = self.store.countries
}
}
}

#if DEBUG
struct CountryListView_Previews: PreviewProvider {
static var previews: some View {
CountryListView()
CountryListView(store: CountryStore())
}
}
#endif
71 changes: 71 additions & 0 deletions Source/Classes/CountryListViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// CountryListViewModel.swift
// Columbus-iOS
//
// Created by Stefan Herold on 01.08.19.
// Copyright © 2019 CodingCobra. All rights reserved.
//

import SwiftUI
import Combine

/// Advances in Networking 1: https://developer.apple.com/videos/play/wwdc2019/712/?time=705
/// https://www.reddit.com/r/SwiftUI/comments/c5wi5w/throttledebounce_binding/
/// https://github.com/Dimillian/MovieSwiftUI/blob/7ed177d18f83406c80ca7367a2e2fb3f73b9f156/MovieSwift/MovieSwift/binding/SearchTextBinding.swift
public final class CountryListViewModel: ObservableObject {

private var store: CountryStore

@Published var filteredCountries: [Country] = []
@Published var selectedCountry: Country? = nil
var query: String = "" {
willSet {
DispatchQueue.main.async {
self.searchSubject.send(newValue)
}
}
didSet {
DispatchQueue.main.async {
self.onUpdateText(text: self.query)
}
}
}


private let searchSubject = PassthroughSubject<String, Never>()

private var searchCancellable: Cancellable? {
didSet {
oldValue?.cancel()
}
}

deinit {
searchCancellable?.cancel()
}

public init(store: CountryStore) {
self.store = store

searchCancellable = searchSubject
.eraseToAnyPublisher().map { $0 }
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
// .throttle(for: 0.3, scheduler: RunLoop.main, latest: true)
.removeDuplicates()
// .filter { !$0.isEmpty }
.sink(receiveValue: { (searchText) in
self.filteredCountries = self.store.filtered(by: searchText)
self.onUpdateTextDebounced(text: searchText)
})
}


/// Overwrite by your subclass to get instant text update.
func onUpdateText(text: String) {

}

/// Overwrite by your subclass to get debounced text update.
func onUpdateTextDebounced(text: String) {
}
}
21 changes: 10 additions & 11 deletions Source/Classes/CountryStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,26 @@
import SwiftUI
import Combine

final class CountryStore: ObservableObject {
public final class CountryStore: ObservableObject {

@Published var countries: [Country] = []
@Published var filteredCountries: [Country] = []

init() {}

func filter(query: String) {
public init() {
load()
}

func filtered(by query: String) -> [Country] {
guard !query.isEmpty else {
return countries
}
let filteredByName = self.countries.filter {
$0.name.lowercased().contains(query.lowercased())
}
let filteredByDialingCode = self.countries.filter {
"+\($0.dialingCode)".contains(query)
}

if !filteredByName.isEmpty {
self.filteredCountries = filteredByName
} else {
self.filteredCountries = filteredByDialingCode
}
return filteredByName.isEmpty ? filteredByDialingCode : filteredByName
}

func load() {
Expand All @@ -39,7 +39,6 @@ final class CountryStore: ObservableObject {
return
}
self.countries = countries
self.filteredCountries = countries
}
}

Expand Down

0 comments on commit c867939

Please sign in to comment.