How do I modify function to be used in MVP architecture? - swift

I have the function below. It works properly.
When a user types any character it validates the user input and hides some imageView based on the input.
#IBAction func onEmailValueChanged(_ sender: UITextField) {
let hasMinimumLength = TextValidationHelper.validateHasMinimumLength(password: sender.text!)
passLengthCheckmarkImageView.isHidden = hasMinimumLength ? false : true
let hasCapitalLetter = TextValidationHelper.validateHasCapitalLetter(password: sender.text!)
passHasUppercaseCheckmarkImageView.isHidden = hasCapitalLetter ? false : true
let hasNumber = TextValidationHelper.validateHasNumber(password: sender.text!)
passHasNumberCheckmarkImageView.isHidden = hasNumber ? false : true
let hasSpecialCharacter = TextValidationHelper.validateHasSpecialCharacter(password: sender.text!)
passHasSymbolCheckmarkImageView.isHidden = hasSpecialCharacter ? false : true
resetButton.isHidden = hasMinimumLength && hasCapitalLetter && hasNumber && hasSpecialCharacter ? false : true
}
But now I want to apply an MVP model on this function to remove the function from the ViewController file.
How can I do that?
Do I need to publish more code to make it possible to create an answer for this question?

It is not a good practice to use any architectural pattern only for method. So assuming you are having a complete app with many classes or files.
An important thing is that it is not fixed/compulsory to use any specific pattern. It actually depends on the code, sometimes you end up writing much code just to handle a method. So try to think the optimal approach to make the code more testable and scalable.
But for your reference, you can check the following code:
On ViewController:
lazy var presenter:Presenter = Presenter(view:self)
#IBAction func onEmailValueChanged(_ sender: UITextField) {
presenter.validateHasMinimumLength(password: sender.text!)
presenter.validateHasCapitalLetter(password: sender.text!)
presenter.validateHasNumber(password: sender.text!)
presenter.validateHasSpecialCharacter(password: sender.text!)
}
//Adopting ViewController:PrensenterViewProtocol on ViewController
extension ViewController:PrensenterViewProtocol {
func updateLengthCheckmarkImageView(isHidden:Bool) {
passLengthCheckmarkImageView.isHidden = isHidden
}
func updateUpperCaseCheckmarkImageView(isHidden:Bool) {
passHasUppercaseCheckmarkImageView.isHidden = isHidden
}
func updateNumberCheckmarkImageView(isHidden:Bool) {
passHasNumberCheckmarkImageView.isHidden = isHidden
}
func updateSymbolCheckmarkImageView(isHidden:Bool) {
passHasSymbolCheckmarkImageView.isHidden = isHidden
}
func updateResetButton(isHidden:Bool) {
resetButton.isHidden = isHidden
}
}
PresenterView protocol as:
protocol PrensenterViewProtocol:NSObjectProtocol {
func updateLengthCheckmarkImageView(isHidden:Bool)
func updateUpperCaseCheckmarkImageView(isHidden:Bool)
func updateNumberCheckmarkImageView(isHidden:Bool)
func updateSymbolCheckmarkImageView(isHidden:Bool)
func updateResetButton(isHidden:Bool)
}
Presenter as:
class Presenter {
weak var view:PrensenterViewProtocol!
private var hasMinimumLength:Bool = false
private var hasCapitalLetter:Bool = false
private var hasNumber:Bool = false
private var hasSpecialCharacter:Bool = false
init(view:PrensenterViewProtocol) {
self.view = view
}
func validateHasMinimumLength(password:String?) {
hasMinimumLength = TextValidationHelper.validateHasMinimumLength(password: password)
self.view.updateLengthCheckmarkImageView(isHidden: hasMinimumLength)
checkAllValidations()
}
func validateHasCapitalLetter(password:String?) {
hasCapitalLetter = TextValidationHelper.validateHasCapitalLetter(password: password)
self.view.updateUpperCaseCheckmarkImageView(isHidden:hasCapitalLetter )
checkAllValidations()
}
func validateHasNumber(password:String?) {
hasNumber = TextValidationHelper.validateHasNumber(password: password)
self.view.updateNumberCheckmarkImageView(isHidden: hasNumber)
checkAllValidations()
}
func validateHasSpecialCharacter(password:String?) {
hasSpecialCharacter = TextValidationHelper.validateHasSpecialCharacter(password: password)
self.view.updateSymbolCheckmarkImageView(isHidden: hasSpecialCharacter)
checkAllValidations()
}
func checkAllValidations() {
let areAllValid:Bool = hasMinimumLength && hasCapitalLetter && hasNumber && hasSpecialCharacter ? false : true
self.view.updateResetButton(isHidden: areAllValid)
}
}

Related

init class from another class swift

I have this ScreenSecurityWorker
final class ScreenSecurityWorker {
weak var delegate: ScreenSecurityWorkerDelegate?
private var isPassPhraseOn: Bool { return SettingsManager.shared.load(.passPhrase) }
private var isPassPhraseForced: Bool { return environment.forcePassPhraseEnabled }
var screenSecurityEnabled = false
inside my Mainrouter I am changing screenSecurityEnabled bool value to true at activateScreenSecurity
func activateScreenSecurity() {
guard !screenSecurityWorker.passwordSecurityInProgress else { return }
screenSecurityVC = ScreenSecurityBuiler.make()
app.dismissKeyboardOnTopVC()
app.window.rootViewController = screenSecurityVC
screenSecurityWorker.passPhareSuccess = false
screenSecurityWorker.screenSecurityEnabled = true
}
I am trying to reach to screenSecurityEnabled from ChatVC
but if I init it as
let screenSecurityWorker = ScreenSecurityWorker()
it has no values inside at all.
if I try like this it gives error
let screenSecurityWorker : ScreenSecurityWorker?
init(screenSecurityWorker: ScreenSecurityWorker){
self.screenSecurityWorker = screenSecurityWorker
}
I need this screenSecurityWorker.screenSecurityEnabled to change subviews in this function inside ChatVC
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if screenSecurityWorker!.screenSecurityEnabled {
let place = chatTextAreaContainerView.bounds.height + CGFloat(lastContentOffset ?? collectionView.contentOffset.y - chatTextAreaContainerView.bounds.height)
self.collectionView.contentOffset.y = place
screenSecurityWorker!.screenSecurityEnabled = false
}
}

How to determine a struct's return value based on UI Objects

I currently have a struct in which my ViewController references to return a random value from a group of arrays (aList, bList, cList, & dList). I ideally would like to be have aList always enabled while being able to toggle bList, cList, & dList on/off.
I have all this in a struct (shown below) but I'm very new to Swift and am learning that structs are "value types" while the ViewController is a "reference type" and apparently UI objects can only be referenced from reference types?
import GameKit
import Foundation
struct PromptProvider {
var aListIndex = 0
var bListIndex = 0
var cListIndex = 0
var dListIndex = 0
var includeB = false
var includeC = false
var includeD = false
var aList = [
“A1”,
“A2”,
“A3”,
“A4”]
var bList = [
“B1”,
“B2”,
“B3”,
“B4”]
var cList = [
“C1”,
“C2”,
“C3”,
“C4”]
var dList = [
“D1”,
“D2”,
“D3”,
“D4”]
init() {
self.restart()
}
mutating func restart() {
if includeB == true {self.aList.append(contentsOf: bList)}
if includeC == true {self.aList.append(contentsOf: cList)}
if includeD == true {self.aList.append(contentsOf: dList)}
self.aList.shuffle()
self.aListIndex = 0
}
mutating func randomPrompt() -> String {
if aListIndex == aList.count {
return "Out of String Values"
}
else {
defer {aListIndex += 1}
return aList[aListIndex]
}
}
}
I think I'm treating the struct as a reference type here but am unable to connect a UI object to the structs code.
Any help/guidance on how to pivot my strategy with this would be greatly appreciated. Thank you
As long as you have a single instance of PromptProvider you can make changes to that instance at will. If you need multiple references to a PromptProvider then you will need to make PromptProvider a class.
I mentioned that you had a bug in your restart method. You were appending data to your aList array without clearing out the previous appended data. What you need is a master list to contain your aList as well as the contents of your other lists so you can reset it as needed.
If I were you, I'd implement your PromptProvider struct like this:
struct PromptProvider {
let aList = [
"A1",
"A2",
"A3",
"A4"]
let bList = [
"B1",
"B2",
"B3",
"B4"]
let cList = [
"C1",
"C2",
"C3",
"C4"]
let dList = [
"D1",
"D2",
"D3",
"D4"]
var includeB = false {
didSet { restart() }
}
var includeC = false {
didSet { restart() }
}
var includeD = false {
didSet { restart() }
}
var masterList: [String] = []
init() {
self.restart()
}
mutating func restart() {
masterList.removeAll()
masterList.append(contentsOf: aList)
if includeB == true {masterList.append(contentsOf: bList)}
if includeC == true {masterList.append(contentsOf: cList)}
if includeD == true {masterList.append(contentsOf: dList)}
masterList.shuffle()
}
mutating func randomPrompt() -> String? {
return masterList.popLast()
}
}
And then in your view controller you can declare and use your PromptProvider like so:
class ViewController: UIViewController {
var promptProvider = PromptProvider()
#IBAction func onPrintRandomPrompt(_ sender: UIButton) {
if let prompt = promptProvider.randomPrompt() {
print(prompt)
} else {
print("Out of String Values")
promptProvider.restart()
}
}
}
If you want to change the contents of the provider all you have to do is toggle its flags and that will reset the contents of the master list.
extension ViewController {
#IBAction func onIncludeB(_ sender: UIButton) {
promptProvider.includeB.toggle()
sender.setTitleColor(promptProvider.includeB ? .green : .red, for: .normal)
}
#IBAction func onIncludeC(_ sender: UIButton) {
promptProvider.includeC.toggle()
sender.setTitleColor(promptProvider.includeC ? .green : .red, for: .normal)
}
#IBAction func onIncludeD(_ sender: UIButton) {
promptProvider.includeD.toggle()
sender.setTitleColor(promptProvider.includeD ? .green : .red, for: .normal)
}
}

Button Click Twice

As we all know, to avoid clicking twice, we can set the code bellow on the tap method and add a HUD such as SVProgress.show().
isUserInteractionEnabled = false
After the network request, set it to true and SVProgress.dismiss().
I wonder if there is a method to extract the function for those button which needs to send a request. I have thought to use method swizzling. Add this to the button extension, the codes is bellow. It seems not good. Do you guys have some good ways to extract the function? Using inheritance, protocol or something else?
extension UIButton {
private struct AssociatedKeys {
static var cp_submitComplete = "cp_submitComplete"
static var cp_defaultMessage:String = NSLocalizedString("Loading", comment: "prompt")
static var cp_customMessage = "cp_customMessage"
}
var submitNotComplete: Bool {
get {
let objc_Get = objc_getAssociatedObject(self, &AssociatedKeys.cp_submitComplete)
if objc_Get != nil {
if let objc_Get = objc_Get as? Bool, objc_Get == true {
return true
}
return false
} else {
return false
}
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.cp_submitComplete, newValue as Bool, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
if !newValue {
isUserInteractionEnabled = true
SVProgressHUD.dismiss()
}
}
}
var customMessage: String {
get {
let cp_customMessage = objc_getAssociatedObject(self, &AssociatedKeys.cp_customMessage)
if let message = cp_customMessage {
return message as! String
} else {
return AssociatedKeys.cp_defaultMessage
}
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.cp_customMessage, newValue as String, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
override open class func initialize() {
if self == UIButton.self {
DispatchQueue.once(NSUUID().uuidString, block: {
let systemSel = #selector(UIButton.sendAction(_:to:for:))
let swizzSel = #selector(UIButton.cpSendAction(_:to:for:))
let systemMethod = class_getInstanceMethod(self, systemSel)
let swizzMethod = class_getInstanceMethod(self, swizzSel)
let isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod))
if isAdd {
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
} else {
method_exchangeImplementations(systemMethod, swizzMethod);
}
})
}
}
private dynamic func cpSendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {
cpSendAction(action, to: target, for: event)
if submitNotComplete {
//begin submit
isUserInteractionEnabled = false
SVProgressHUD.show(withStatus: customMessage)
}
}
}
I think it's a bad idea to handle this kind of logic in UIButton. I would rather make the view controller responsible for enabling/disabling the button.
func handleTap(_ sender: UIButton) {
sender.isEnabled = false
SVProgressHUD.show(withStatus: customMessage)
doSomeTaskAsync(withCompletion: {
sender.isEnabled = true
SVProgressHUD.dismiss()
})
}

How to pass a reference to a Boolean rather than its value?

I want to use booleans as state storage.
In order to do that, I need to be able to change their state from difference places in the project.
To do that, I need somewhere to store them, and a way to pass a reference to them.
I've tried storing them as static variables in a GameManager, but passing references to these only seems to pass the value of true of false, not a reference.
How do I achieve this goal of having a passable boolean reference I can change the state of it from any part of the project?
UPDATE:
This can't be the best way to do this, but this achieves the goal of having a bunch of state booleans that I can use around the game world:
class GameManager {
static let sharedInstance = GameManager()
var previewAudioIsON: Bool = false
var previewVisuaIsOn: Bool = false
var timerDisplayIsOn: Bool = false
var quickStartIsOn: Bool = false
func touchedPreviewAudioButton() -> Bool {
if previewAudioIsON { previewAudioIsON = false}
else { previewAudioIsON = true }
return previewAudioIsON
}
func touchedPreviewVisualButton() -> Bool {
if previewVisuaIsOn { previewVisuaIsOn = false }
else { previewVisuaIsOn = true }
return previewVisuaIsOn
}
func touchedTimeDisplayButton() -> Bool {
if timerDisplayIsOn { timerDisplayIsOn = false }
else { timerDisplayIsOn = true }
return timerDisplayIsOn
}
func touchedQuickStartButton() -> Bool {
if quickStartIsOn { quickStartIsOn = false }
else { quickStartIsOn = true }
return quickStartIsOn
}
}
I gave you partially wrong information the other day ( I was having a brain fart), and need to apologize for that. I had overlooked something in my testing...
Here is what you need if you don't want to make the RefBool instances as I suggested (requires more legwork, not recommended):
/// Mutates a boolean:
func toggle(_ boolean: inout Bool) -> Bool {
boolean ? (boolean = false) : (boolean = true)
return boolean
}
/// Static state manager for Booleans
struct IsOn {
private static var
_previewAudio = false,
_previewVisual = false,
_timerDisplal = false,
_quickStart = false
enum State { case toggle, get }
static func previewAudio(_ toggleVal: State = .get) -> Bool {
if toggleVal == .toggle { toggle(&_previewAudio) }; return _previewAudio
}
// ... others
}
Testing:
let referenceToPA = IsOn.previewAudio
print ( IsOn.previewAudio() ) // False (default pram works)
print ( referenceToPA(.get) ) // False (can't use default pram)
referenceToPA(.toggle)
print ( IsOn.previewAudio() ) // True
print ( referenceToPA(.get) ) // True
IsOn.previewAudio(.toggle)
print ( IsOn.previewAudio() ) // False
print ( referenceToPA(.get) ) // False
But honestly, it would be easier to just do the RefBool from my other answer, then you wouldn't need the enum or the functions:
/// Holds a boolean in .val:
final class RefBool { var val: Bool; init(_ boolean: Bool) { val = boolean } }
/// Static state manager for Booleans
struct IsOn {
static var
previewAudio = RefBool(false),
previewVisual = RefBool(false),
timerDisplal = RefBool(false),
quickStart = RefBool(false)
}
Convenience Funcs (not necessary):
/// Mutates a boolean:
func toggle(_ boolean: inout Bool) -> Bool {
boolean ? (boolean = false) : (boolean = true)
return boolean
}
/// Mutates .val:
func toggle(_ refBool: RefBool) -> Bool {
refBool.val ? (refBool.val = false) : (refBool.val = true)
return refBool.val
}
Testing2:
let refToPA = IsOn.previewAudio
refToPA.val = true
print(refToPA.val) // true
print(IsOn.previewAudio.val) // true
toggle(&refToPA.val)
print(refToPA.val) // false
print(IsOn.previewAudio.val) // false
toggle(refToPA) // Using our fancy second toggle
print(refToPA.val) // true
print(IsOn.previewAudio.val) // true
try something like that:
Full example
import UIKit
enum ButtonType {
case PreviewAudio;
case PreviewVisua;
case TimerDisplay;
case QuickStart;
}
class SwitchProperty {
var type: ButtonType
var value: Bool
init (type: ButtonType) {
self.type = type
self.value = false
}
var description: String {
var result = "type = \(type)\n"
result += "value = \(value)"
return result
}
func switchValue() {
value.invertValue()
}
}
class GameManager {
static var previewAudioIsON = SwitchProperty(type: .PreviewAudio)
static var previewVisuaIsOn = SwitchProperty(type: .PreviewVisua)
static var timerDisplayIsOn = SwitchProperty(type: .TimerDisplay)
static var quickStartIsOn = SwitchProperty(type: .QuickStart)
}
class Button: UIButton {
var switchValue: SwitchProperty
init (type: ButtonType, frame: CGRect) {
switch type {
case .PreviewAudio:
switchValue = GameManager.previewAudioIsON
case .PreviewVisua:
switchValue = GameManager.previewVisuaIsOn
case .TimerDisplay:
switchValue = GameManager.timerDisplayIsOn
case .QuickStart:
switchValue = GameManager.quickStartIsOn
}
super.init(frame: frame)
addTarget(self, action: #selector(Button.buttonTouched), for: .touchUpInside)
}
func buttonTouched() {
switchValue.switchValue()
print(switchValue.description)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension Bool {
mutating func invertValue() {
self = !self
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
addButton(type: .PreviewVisua, frame: CGRect(x: 40, y: 40, width: 200, height: 40));
addButton(type: .PreviewAudio, frame: CGRect(x: 40, y: 100, width: 200, height: 40));
}
func addButton(type: ButtonType, frame: CGRect) {
let button = Button(type: type, frame: frame);
button.setTitleColor(UIColor.blue, for: .normal)
button.setTitle("\(type)", for: .normal)
view.addSubview(button)
}
}
Properties accsess from another code
// change value
GameManager.previewAudioIsON.value = false
// check type
if (GameManager.previewAudioIsON.type == .PreviewAudio) {
print("previewAudioIsON")
}
Result

Hidden Statusbar in a function

I would like to hide the Statusbar when i hide Navigationbar and Toolbar.
How i can make that that the Statusbar is hidden in if NavigationBar.hidden == false && Toolbar.hidden == false{} ??
I have no idea how i can make that, i know the func to return the Statusbarhidden but thats in the whole ViewController and i would to hide it in the func.
Thanks for your Help.
func ImageTapGesture () {
if NavigationBar.hidden == false && Toolbar.hidden == false{
NavigationBar.hidden = true
Toolbar.hidden = true
} else if NavigationBar.hidden == true && Toolbar.hidden == true {
NavigationBar.hidden = false
Toolbar.hidden = false
}
}
Under the Swift language, you can refer to the following code hidden, don't need animation effects can be commented out.
var isHidden:Bool = false
#IBAction func clicked(_ sender: AnyObject) {
isHidden = !isHidden
UIView.animate(withDuration: 0.5, animations: { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
})
}
override var preferredStatusBarUpdateAnimation : UIStatusBarAnimation {
return UIStatusBarAnimation.slide
}
override var prefersStatusBarHidden : Bool {
return isHidden
}
Can also refer to the following link to the Demo, is a time I write the project requirements.
Github:https://github.com/ReverseScale/HiddenStatusBar
Wish I could help you.
A Swift 2.x compatible workaround to make what do you need to do :
func hideStatusBar(yOffset:CGFloat) { // -20.0 for example
let statusBarWindow = UIApplication.sharedApplication().valueForKey("statusBarWindow") as! UIWindow
statusBarWindow.frame = CGRectMake(0, yOffset, statusBarWindow.frame.size.width, statusBarWindow.frame.size.height)
}
func showStatusBar() {
let statusBarWindow = UIApplication.sharedApplication().valueForKey("statusBarWindow") as! UIWindow
statusBarWindow.frame = CGRectMake(0, 0, statusBarWindow.frame.size.width, statusBarWindow.frame.size.height)
}
To use it for example you can launch:
hideStatusBar(-20.0)