init class from another class swift - 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
}
}

Related

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

Settings as shared Instance

I tried to do this so get my settings saved whenever the App moves to the background or gets killed or whatever.
I want to access and set the property "useLimits" all over my App.
Why is it not working?
Is there a better more elegant way to achieve this?
import UIKit
class Settings: NSObject
{
static let sharedInstance = Settings()
private let kUseLimits = "kUseLimits"
var useLimits = false
override init()
{
super.init()
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(Settings.save),
name: UIApplicationWillResignActiveNotification,
object: nil)
let userdefaults = NSUserDefaults.standardUserDefaults()
self.useLimits = userdefaults.boolForKey(kUseLimits)
}
deinit
{
NSNotificationCenter.defaultCenter().removeObserver(self)
save()
}
func save()
{
let userdefaults = NSUserDefaults.standardUserDefaults()
userdefaults.setBool(self.useLimits, forKey: kUseLimits)
userdefaults.synchronize()
}
func reset()
{
self.useLimits = false
save()
}
}
I think something like this will be good:
class AppSettings {
private struct Keys {
static let useLimits = "AppSttings.useLimits"
}
static var useLimits: Bool {
set {
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: Keys.useLimits)
}
get {
return NSUserDefaults.standardUserDefaults().boolForKey(Keys.useLimits)
}
}
static func rest() {
useLimits = false
}
}
P.S. Starting from iOS 8 you don't need to call synchronize() in NSUserDefault
P.S.S. NSUserDefaults.standardUserDefaults().boolForKey(Keys.useLimits) will return false if there not such object, if you need specific default value please check on object or use NSUserDefaults.standardUserDefaults().registerDefaults()
P.S.S.S. It wont effect your performance much, so you can read from UD and write there just on on the run, but if you want too performance code, you can do something like this:
private static var _useLimits: Bool?
static var useLimits: Bool {
set {
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: Keys.useLimits)
_useLimits = newValue
}
get {
if _useLimits == nil {
_useLimits = NSUserDefaults.standardUserDefaults().boolForKey(Keys.useLimits)
}
return _useLimits!
}
}
or more elegant for current value:
private static var _useLimits: Bool = NSUserDefaults.standardUserDefaults().boolForKey(Keys.useLimits)
static var useLimits: Bool {
set {
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: Keys.useLimits)
_useLimits = newValue
}
get {
return _useLimits
}
}

Swift. Why singleton lost data?

Why my data is lost in singleton? Please take a look:
class YtDataManager {
static var shared_instance = YtDataManager()
let apiKey = /*...*/
let /*...*/
var channelData = [NSObject:AnyObject]()
var videosArray = [[NSObject:AnyObject]]()
var playlistId: String { return self.channelData["playlistId"] as! String}
var urlStringForRequestChannelDetails: String { return String("https://www.googleapis.com/youtube/v3/channels?part=contentDetails,snippet&forUsername=\(self./*ChannelName*/)&key=\(self.apiKey)") }
var urlStringForRequestChannelVideos: String { return String("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=\(self.playlistId)&key=\(self.apiKey)") }
func fn1() { /*...*/ }
func fn2() { /*...*/ }
// class definition continue...
Look, from one function(fn1) I'm writing data to channelData:
//...
self.channelData["title"] = snippetDict["title"]
self.channelData["playlistId"] = ((firstItemDict["contentDetails"] as! [NSObject:AnyObject])["relatedPlaylists"] as! [NSObject:AnyObject])["uploads"]
//...
and so on... From second function(fn2), I'm reading data:
//...
let targetURL = NSURL(fileURLWithPath: self.urlStringForRequestChannelVideos)
//...
Hence urlStringForRequestChannelVideos is computational property it uses playlistId (look code above).
Here I was surprised about emptiness of channedData from second function(I saw it in Debug mode, also I printed it's count to stdout outside of function). Why????
class YtFeedViewController: UIViewController {
#IBOutlet weak var menuButton:UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
YtDataManager.shared_instance.fn1()
print(YtDataManager.shared_instance.channelData.count) //0
YtDataManager.shared_instance.fn2() //errorness
}
self.navigationItem.title = "YouTube feed"
// Do any additional setup after loading the view.
}

Save data of a Label in swift

I have a TextField in my settingsController that i can modify selecting a name from a pickerView, the problem is this : when i change the text of my textField i also change the text of a label in another controller, it work but when i close and reopen my app the label is empty, i can't find a way to save the text that i give it with the pickerView.
My code in the settingsController
override func viewDidLoad() {
super.viewDidLoad()
var defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
if let firstNameIsNotNill = defaults.objectForKey("firstName") as? String {
self.currencyLabel.text = defaults.objectForKey("firstName") as String
}
currencyLabel.delegate = self
}
func currencyDoneClicked(sender: UIBarButtonItem) {
var myRow = picker.selectedRowInComponent(0)
currencyLabel.text = pickerData.objectAtIndex(myRow) as NSString
DataManager.sharedInstance.contenitore = currencyLabel.text
var defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(self.currencyLabel.text, forKey: "firstName")
defaults.synchronize()
}
DataManager :
import UIKit
class DataManager: NSObject {
class var sharedInstance:DataManager {
get {
struct Static {
static var instance : DataManager? = nil
static var token : dispatch_once_t = 0
}
dispatch_once(&Static.token) { Static.instance = DataManager() }
return Static.instance!
}
}
var contenitore : String!
}
And in the other controller :
override func viewDidLoad() {
super.viewDidLoad()
labelCurrency.text = DataManager.sharedInstance.contenitore
}
Just use NSUserDefaults.
NSUserDefaults.standardUserDefaults().setValue(textField.text, forKey: "savedTextField")
Then, when you start up again, populate the field in viewDidLoad or viewWillAppear.
if let text = NSUserDefaults.standardUserDefaults().stringForKey("savedTextField") {
textField.text = text
}