Skip to content

Commit

Permalink
chore: Update (#85)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
michaelhenry and pre-commit-ci[bot] authored Jun 28, 2024
1 parent 2da392f commit 3585563
Show file tree
Hide file tree
Showing 12 changed files with 523 additions and 542 deletions.
3 changes: 2 additions & 1 deletion Demo/DemoTests/Helpers/Snapshotting+WindowImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ extension Snapshotting where Value: UIViewController, Format == UIImage {
Snapshotting<UIImage, UIImage>.image(precision: precision).asyncPullback { vc in
Async<UIImage> { callback in
UIView.setAnimationsEnabled(false)
let window = UIApplication.shared.windows[0]
guard let window = UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }).first?.windows.first else { return }
window.rootViewController = vc
action()
DispatchQueue.main.async {
let image = UIGraphicsImageRenderer(bounds: window.bounds).image { _ in
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
}
callback(image)
window.rootViewController = UIViewController()
UIView.setAnimationsEnabled(true)
}
}
Expand Down
12 changes: 5 additions & 7 deletions Sources/XConfigs/DataSources/TableViewDataSource.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#if canImport(UIKit)
import UIKit
import UIKit

final class TableViewDataSource<Section: Hashable, Item: Hashable>: UITableViewDiffableDataSource<Section, Item> {
override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
(snapshot().sectionIdentifiers[section] as? CustomStringConvertible)?.description
}
final class TableViewDataSource<Section: Hashable, Item: Hashable>: UITableViewDiffableDataSource<Section, Item> {
override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
(snapshot().sectionIdentifiers[section] as? CustomStringConvertible)?.description
}
#endif
}
118 changes: 58 additions & 60 deletions Sources/XConfigs/Extensions/UIView++.swift
Original file line number Diff line number Diff line change
@@ -1,80 +1,78 @@
#if canImport(UIKit)
import Combine
import UIKit
import Combine
import UIKit

// Provide a default `reuseIdentifier` equal to the class name.
private extension UITableViewCell {
static var reuseIdentifier: String {
String(describing: self)
}
// Provide a default `reuseIdentifier` equal to the class name.
private extension UITableViewCell {
static var reuseIdentifier: String {
String(describing: self)
}
}

extension UITableView {
func registerCell<Cell: UITableViewCell>(_ type: Cell.Type) {
register(type, forCellReuseIdentifier: type.reuseIdentifier)
}
extension UITableView {
func registerCell<Cell: UITableViewCell>(_ type: Cell.Type) {
register(type, forCellReuseIdentifier: type.reuseIdentifier)
}

// MARK: Dequeue Table View Cell
// MARK: Dequeue Table View Cell

func dequeueCell<Cell: UITableViewCell>(_ type: Cell.Type, for _: IndexPath) -> Cell {
guard let cell = dequeueReusableCell(withIdentifier: type.reuseIdentifier) as? Cell else {
fatalError("Unregistered cell: \(type.reuseIdentifier)")
}
return cell
func dequeueCell<Cell: UITableViewCell>(_ type: Cell.Type, for _: IndexPath) -> Cell {
guard let cell = dequeueReusableCell(withIdentifier: type.reuseIdentifier) as? Cell else {
fatalError("Unregistered cell: \(type.reuseIdentifier)")
}
return cell
}
}

extension UIView {
func bind(to view: UIView, margins: UIEdgeInsets = .zero) {
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: view.topAnchor, constant: margins.top),
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: margins.left),
view.trailingAnchor.constraint(equalTo: trailingAnchor, constant: margins.right),
view.bottomAnchor.constraint(equalTo: bottomAnchor, constant: margins.bottom),
])
}
extension UIView {
func bind(to view: UIView, margins: UIEdgeInsets = .zero) {
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: view.topAnchor, constant: margins.top),
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: margins.left),
view.trailingAnchor.constraint(equalTo: trailingAnchor, constant: margins.right),
view.bottomAnchor.constraint(equalTo: bottomAnchor, constant: margins.bottom),
])
}

func bindToSuperview(margins: UIEdgeInsets = .zero) {
guard let superview = superview else { return }
bind(to: superview, margins: margins)
}
func bindToSuperview(margins: UIEdgeInsets = .zero) {
guard let superview = superview else { return }
bind(to: superview, margins: margins)
}
}

protocol ConfigurableView: UIView {
associatedtype ViewModel
protocol ConfigurableView: UIView {
associatedtype ViewModel

func configure(with viewModel: ViewModel)
}
func configure(with viewModel: ViewModel)
}

final class UIViewTableWrapperCell<MainView: ConfigurableView>: UITableViewCell {
let mainView: MainView
final class UIViewTableWrapperCell<MainView: ConfigurableView>: UITableViewCell {
let mainView: MainView

var subscriptions = Set<AnyCancellable>()
var subscriptions = Set<AnyCancellable>()

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
mainView = MainView()
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
mainView = MainView()
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}

@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func configure(with viewModel: MainView.ViewModel) {
mainView.configure(with: viewModel)
}
func configure(with viewModel: MainView.ViewModel) {
mainView.configure(with: viewModel)
}

private func setupUI() {
contentView.addSubview(mainView)
mainView.bindToSuperview()
}
private func setupUI() {
contentView.addSubview(mainView)
mainView.bindToSuperview()
}

override func prepareForReuse() {
super.prepareForReuse()
subscriptions = .init()
}
override func prepareForReuse() {
super.prepareForReuse()
subscriptions = .init()
}
#endif
}
28 changes: 13 additions & 15 deletions Sources/XConfigs/Extensions/UIViewController++.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
#if canImport(UIKit)
import UIKit
import UIKit

extension UIViewController {
func preferAsHalfSheet(preferredCornerRadius: CGFloat = 10) -> UIViewController {
if #available(iOS 15.0, *) {
if let sheet = sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
sheet.preferredCornerRadius = preferredCornerRadius
}
extension UIViewController {
func preferAsHalfSheet(preferredCornerRadius: CGFloat = 10) -> UIViewController {
if #available(iOS 15.0, *) {
if let sheet = sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
sheet.preferredCornerRadius = preferredCornerRadius
}
return self
}
return self
}

func wrapInsideNavVC() -> UINavigationController {
UINavigationController(rootViewController: self)
}
func wrapInsideNavVC() -> UINavigationController {
UINavigationController(rootViewController: self)
}
#endif
}
19 changes: 8 additions & 11 deletions Sources/XConfigs/Models/SectionItemsModel.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import UIKit.NSDiffableDataSourceSectionSnapshot

struct SectionItemsModel<Section: Hashable, Item: Hashable>: Hashable {
var section: Section
Expand All @@ -15,16 +16,12 @@ extension SectionItemsModel: Equatable {

// MARK: - SectionItemsModel + NSDiffableDataSourceSnapshot

#if canImport(UIKit)
import UIKit

extension Sequence {
@available(iOS 13.0, *)
func snapshot<Section: Hashable, Item: Hashable>() -> NSDiffableDataSourceSnapshot<Section, Item> where Element == SectionItemsModel<Section, Item> {
reduce(into: NSDiffableDataSourceSnapshot<Section, Item>()) { snapshot, sectionModel in
snapshot.appendSections([sectionModel.section])
snapshot.appendItems(sectionModel.items, toSection: sectionModel.section)
}
extension Sequence {
@available(iOS 13.0, *)
func snapshot<Section: Hashable, Item: Hashable>() -> NSDiffableDataSourceSnapshot<Section, Item> where Element == SectionItemsModel<Section, Item> {
reduce(into: NSDiffableDataSourceSnapshot<Section, Item>()) { snapshot, sectionModel in
snapshot.appendSections([sectionModel.section])
snapshot.appendItems(sectionModel.items, toSection: sectionModel.section)
}
}
#endif
}
142 changes: 70 additions & 72 deletions Sources/XConfigs/ViewControllers/InputValueViewController.swift
Original file line number Diff line number Diff line change
@@ -1,88 +1,86 @@
#if canImport(UIKit)
import Combine
import CombineCocoa
import Highlightr
import UIKit
import Combine
import CombineCocoa
import Highlightr
import UIKit

final class InputValueViewController: UIViewController, UITextViewDelegate {
typealias ViewModel = InputValueViewModel
final class InputValueViewController: UIViewController, UITextViewDelegate {
typealias ViewModel = InputValueViewModel

private lazy var textContainer = NSTextContainer().apply {
let textStorage = CodeAttributedString()
textStorage.language = "Javascript"
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer($0)
textStorage.highlightr.setTheme(to: "vs")
}
private lazy var textContainer = NSTextContainer().apply {
let textStorage = CodeAttributedString()
textStorage.language = "Javascript"
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer($0)
textStorage.highlightr.setTheme(to: "vs")
}

private lazy var textView = UITextView(frame: .zero, textContainer: textContainer).apply {
$0.font = .preferredFont(forTextStyle: .body)
$0.isEditable = true
}
private lazy var textView = UITextView(frame: .zero, textContainer: textContainer).apply {
$0.font = .preferredFont(forTextStyle: .body)
$0.isEditable = true
}

private let viewModel: ViewModel
private var subscriptions = Set<AnyCancellable>()
private let viewModel: ViewModel
private var subscriptions = Set<AnyCancellable>()

private var textSubject = PassthroughSubject<String, Never>()
private var textSubject = PassthroughSubject<String, Never>()

var valuePublisher: AnyPublisher<String, Never> {
textSubject.eraseToAnyPublisher()
}
var valuePublisher: AnyPublisher<String, Never> {
textSubject.eraseToAnyPublisher()
}

init(viewModel: ViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
init(viewModel: ViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}

@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()
setupUI()
handleViewModelOutput()
}
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
handleViewModelOutput()
}

private func setupUI() {
navigationItem.rightBarButtonItem = .init(systemItem: .done)
navigationItem.leftBarButtonItem = .init(systemItem: .cancel)
view.backgroundColor = .systemBackground
view.addSubview(textView)
textView.bindToSuperview(margins: .init(top: 20, left: 20, bottom: 20, right: 5))
}
private func setupUI() {
navigationItem.rightBarButtonItem = .init(systemItem: .done)
navigationItem.leftBarButtonItem = .init(systemItem: .cancel)
view.backgroundColor = .systemBackground
view.addSubview(textView)
textView.bindToSuperview(margins: .init(top: 20, left: 20, bottom: 20, right: 5))
}

private func handleViewModelOutput() {
guard let leftNavItem = navigationItem.leftBarButtonItem,
let rightNavITem = navigationItem.rightBarButtonItem
else { return }
let output = viewModel.transform(input: .init(
textPublisher: textView.textPublisher.compactMap { $0 }.eraseToAnyPublisher(),
dismissPublisher: leftNavItem.tapPublisher,
donePublisher: rightNavITem.tapPublisher
))
output.title.sink { [weak self] title in
self?.title = title
}
.store(in: &subscriptions)
private func handleViewModelOutput() {
guard let leftNavItem = navigationItem.leftBarButtonItem,
let rightNavITem = navigationItem.rightBarButtonItem
else { return }
let output = viewModel.transform(input: .init(
textPublisher: textView.textPublisher.compactMap { $0 }.eraseToAnyPublisher(),
dismissPublisher: leftNavItem.tapPublisher,
donePublisher: rightNavITem.tapPublisher
))
output.title.sink { [weak self] title in
self?.title = title
}
.store(in: &subscriptions)

output.value.sink { [weak self] value in
self?.textView.text = value
}
.store(in: &subscriptions)
output.value.sink { [weak self] value in
self?.textView.text = value
}
.store(in: &subscriptions)

output.action.sink { [weak self] action in
switch action {
case .cancel:
self?.dismiss(animated: true)
case let .done(text):
self?.textSubject.send(text)
self?.dismiss(animated: true)
}
output.action.sink { [weak self] action in
switch action {
case .cancel:
self?.dismiss(animated: true)
case let .done(text):
self?.textSubject.send(text)
self?.dismiss(animated: true)
}
.store(in: &subscriptions)
}
.store(in: &subscriptions)
}
#endif
}
Loading

0 comments on commit 3585563

Please sign in to comment.