Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Support for different preference input types #32

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
751 changes: 396 additions & 355 deletions Example/Pods/Pods.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

38 changes: 25 additions & 13 deletions Example/Sentinel/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ enum AppUrl {
static var baseURL = "http://google.com"
}

enum AppSwitches {
enum AppPreferences {
static var analyticsEnabled = true
static var crashlyticsEnabled = true
static var loggingEnabled = false
static var textInput = "text"
}

private extension AppDelegate {
Expand Down Expand Up @@ -59,29 +60,40 @@ private extension AppDelegate {
)
}

var optionSwitchItems: [OptionSwitchItem] {
var optionSwitchItems: [any PreferenceItem] {
[
// This class is used here to check if the code is backward compatible.
OptionSwitchItem(
name: "Analytics",
setter: { AppSwitches.analyticsEnabled = $0 },
getter: { AppSwitches.analyticsEnabled },
setter: { AppPreferences.analyticsEnabled = $0 },
getter: { AppPreferences.analyticsEnabled },
userDefaults: .standard,
userDefaultsKey: "com.infinum.sentinel.optionSwitch.analytics"
userDefaultsKey: "com.infinum.sentinel.switch.analytics"
),
OptionSwitchItem(
PreferenceSwitchItem(
name: "Crashlytics",
setter: { AppSwitches.crashlyticsEnabled = $0 },
getter: { AppSwitches.crashlyticsEnabled },
setter: { AppPreferences.crashlyticsEnabled = $0 },
getter: { AppPreferences.crashlyticsEnabled },
userDefaults: .standard,
userDefaultsKey: "com.infinum.sentinel.optionSwitch.crashlytics"
userDefaultsKey: "com.infinum.sentinel.switch.crashlytics"
),
OptionSwitchItem(
PreferenceSwitchItem(
name: "Logging",
setter: { AppSwitches.loggingEnabled = $0 },
getter: { AppSwitches.loggingEnabled },
setter: { AppPreferences.loggingEnabled = $0 },
getter: { AppPreferences.loggingEnabled },
userDefaults: .standard,
userDefaultsKey: "com.infinum.sentinel.optionSwitch.logging"
userDefaultsKey: "com.infinum.sentinel.switch.logging"
),
PreferenceTextItem(
name: "Text input",
setter: { AppPreferences.textInput = $0 },
getter: { AppPreferences.textInput },
validators: [
AnyPreferenceValidator(validator: PreferenceCountValidator(min: 0, max: 10))
],
userDefaults: .standard,
userDefaultsKey: "com.infinum.sentinel.text.inputText"
)
]

}
Expand Down
4 changes: 2 additions & 2 deletions Sentinel/Classes/Core/Internal/SentinelInternal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extension Sentinel {
/// - viewController: The view controller from where will the Sentinel be presented.
func present(
tools: [Tool],
preferences: [OptionSwitchItem],
preferences: [any PreferenceItem],
on viewController: UIViewController
) {
let tabItems = createTabItems(
Expand Down Expand Up @@ -47,7 +47,7 @@ extension Sentinel {
private extension Sentinel {
func createTabItems(
with tools: [Tool],
preferences: [OptionSwitchItem],
preferences: [any PreferenceItem],
viewController: UIViewController
) -> [SentinelTabItem] {
return [
Expand Down
2 changes: 1 addition & 1 deletion Sentinel/Classes/Core/Internal/SentinelTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ enum SentinelTab {
case device
case application
case tools(items: [Tool])
case preferences(items: [OptionSwitchItem])
case preferences(items: [any PreferenceItem])
case performance
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// OptionSwitchItem.swift
// Sentinel
//
// Created by Nikola Majcen on 02/10/2020.
//

import UIKit

/// Deprecated. Previously named `OptionSwitchItem` and only supported switch/boolean.
@available(*, deprecated, renamed: "PreferenceSwitchItem")
public typealias OptionSwitchItem = PreferenceSwitchItem

/// Provides option to change enabled state of the feature.
@objcMembers
public class PreferenceSwitchItem: NSObject {

// MARK: - Public properties

public let name: String
public let setter: (Bool) -> ()
public let getter: () -> (Bool)
public let validators: [AnyPreferenceValidator<Bool>]
public let userDefaults: UserDefaults
public let userDefaultsKey: String?

// MARK: - Lifecycle

public init(
name: String,
setter: @escaping (Bool) -> (),
getter: @escaping () -> (Bool),
validators: [AnyPreferenceValidator<Bool>] = [],
userDefaults: UserDefaults = .standard,
userDefaultsKey: String?
) {
self.name = name
self.setter = setter
self.getter = getter
self.validators = validators
self.userDefaults = userDefaults
self.userDefaultsKey = userDefaultsKey
super.init()
loadStoredValue()
}

// MARK: - Internal methods

/// Changes current enabled state of the feature.
///
/// This is mostly used inside option switch module
/// but it is also exposed for external change.
@objc(changeToValue:)
func change(to value: Bool) {
store(newValue: value)
}
}

extension PreferenceSwitchItem: PreferenceItem {

public func cell(from tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(ofType: PreferenceSwitchTableViewCell.self, for: indexPath)
cell.configure(with: self)
return cell
}

public func register(at tableView: UITableView) {
tableView.registerNib(cellOfType: PreferenceSwitchTableViewCell.self)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import UIKit

class OptionSwitchTableViewCell: UITableViewCell {
class PreferenceSwitchTableViewCell: UITableViewCell {

// MARK: - IBOutlets

Expand All @@ -20,7 +20,7 @@ class OptionSwitchTableViewCell: UITableViewCell {

// MARK: - Public methods

func configure(with item: OptionSwitchItem) {
func configure(with item: PreferenceSwitchItem) {
titleLabel.text = item.name
optionSwitch.isOn = item.getter()
optionSwitchActionHandler = { item.change(to: $0) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="OptionSwitchTableViewCell" id="iGa-no-8AY" customClass="OptionSwitchTableViewCell" customModule="Sentinel">
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="PreferenceSwitchTableViewCell" id="iGa-no-8AY" customClass="PreferenceSwitchTableViewCell" customModule="Sentinel">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="iGa-no-8AY" id="al1-dt-ptS">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import UIKit

@objcMembers
public class PreferenceTextItem: NSObject {

// MARK: - Public properties

public let name: String
public let setter: (String) -> ()
public let getter: () -> String
public let validators: [AnyPreferenceValidator<String>]
public let userDefaults: UserDefaults
public let userDefaultsKey: String?

// MARK: - Lifecycle

public init(
name: String,
setter: @escaping (String) -> Void,
getter: @escaping () -> String,
validators: [AnyPreferenceValidator<String>] = [],
userDefaults: UserDefaults = .standard,
userDefaultsKey: String?
) {
self.name = name
self.setter = setter
self.getter = getter
self.validators = validators
self.userDefaults = userDefaults
self.userDefaultsKey = userDefaultsKey
super.init()
loadStoredValue()
}

// MARK: - Internal methods

/// Changes current enabled state of the feature.
///
/// This is mostly used inside option switch module
/// but it is also exposed for external change.
@objc(changeToValue:)
func change(to value: String) {
store(newValue: value)
}
}

// MARK: - PreferenceItem

extension PreferenceTextItem: PreferenceItem {

public func cell(from tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(ofType: PreferenceTextTableViewCell.self, for: indexPath)
cell.configure(with: self)
return cell
}

public func register(at tableView: UITableView) {
tableView.registerNib(cellOfType: PreferenceTextTableViewCell.self)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import UIKit

class PreferenceTextTableViewCell: UITableViewCell {

// MARK: - IBOutlets

@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var inputField: UITextField!

// MARK: - Private properties

private var inputValidator: ((String) -> Bool)?
private var inputValueActionHandler: ((String?) -> Void)?

// MARK: - Lifecycle

override func awakeFromNib() {
super.awakeFromNib()
inputField.delegate = self
}

// MARK: - Internal methods

func configure(with item: PreferenceTextItem) {
titleLabel.text = item.name
inputField.text = item.getter()
inputValidator = { value in
item.validators.reduce(true) { partialResult, validator in
partialResult && validator.validate(value: value)
}
}
inputValueActionHandler = { item.change(to: $0 ?? "") }
}
}

extension PreferenceTextTableViewCell: UITextFieldDelegate {

func textField(
_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String
) -> Bool {
let proposedString = (textField.text ?? "") + string
return inputValidator?(proposedString) ?? true
}

func textFieldDidEndEditing(_ textField: UITextField) {
inputValueActionHandler?(textField.text)
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
inputValueActionHandler?(textField.text)
_ = textField.resignFirstResponder()
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="82" id="KGk-i7-Jjw" customClass="PreferenceTextTableViewCell" customModule="Sentinel" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="713" height="82"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="713" height="82"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="ocF-JB-5jJ">
<rect key="frame" x="20" y="6" width="673" height="70"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BfR-Hb-K9f">
<rect key="frame" x="0.0" y="25" width="328.66666666666669" height="20.333333333333329"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="pIm-cj-bfU">
<rect key="frame" x="344.66666666666674" y="18" width="328.33333333333326" height="34"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" returnKeyType="done" enablesReturnKeyAutomatically="YES"/>
</textField>
</subviews>
</stackView>
</subviews>
<constraints>
<constraint firstItem="ocF-JB-5jJ" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leadingMargin" id="UhO-W3-emU"/>
<constraint firstItem="ocF-JB-5jJ" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="6" id="nFX-OG-wcD"/>
<constraint firstAttribute="trailingMargin" secondItem="ocF-JB-5jJ" secondAttribute="trailing" id="sXW-p4-4Xe"/>
<constraint firstAttribute="bottom" secondItem="ocF-JB-5jJ" secondAttribute="bottom" constant="6" id="svB-xb-rV7"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="inputField" destination="pIm-cj-bfU" id="7Ri-Cc-1Kc"/>
<outlet property="titleLabel" destination="BfR-Hb-K9f" id="q5W-au-E97"/>
</connections>
<point key="canvasLocation" x="429.7709923664122" y="2.1126760563380285"/>
</tableViewCell>
</objects>
</document>
Loading