After refactoring function inside closure is unused - swift

I was extracting closure to method but I always get this error:
Function is unused
This is whole working func:
fileprivate func attemptToChangePassword() {
passwordChanger.change(securityToken: securityToken, oldPassword: oldPassword.text ?? "", newPassword: newPassword.text ?? "", onSuccess:{[weak self] in
self?.hideSpinner()
let alertController = UIAlertController(
title: nil,
message: "Your password has been successfully changed.",
preferredStyle: .alert)
let okButton = UIAlertAction(
title: "OK",
style: .default) { [weak self] _ in
self?.dismiss(animated: true)
}
alertController.addAction(okButton)
alertController.preferredAction = okButton
self?.present(alertController, animated: true)
}, onFailure: {[weak self] message in
self?.hideSpinner()
self?.showAlert(message: message) { [weak self] _ in
self?.oldPassword.text = ""
self?.newPassword.text = ""
self?.confirmPassword.text = ""
self?.oldPassword.becomeFirstResponder()
self?.view.backgroundColor = .white
self?.blurView.removeFromSuperview()
self?.cancel.isEnabled = true
}
})
}
This is how I extracted last closure to method:
fileprivate func startOver() -> (UIAlertAction) -> Void {
return { [weak self] _ in
self?.oldPassword.text = ""
self?.newPassword.text = ""
self?.confirmPassword.text = ""
self?.oldPassword.becomeFirstResponder()
self?.view.backgroundColor = .white
self?.blurView.removeFromSuperview()
self?.cancel.isEnabled = true
}
}
If I try this, the error "Function is unused" shows up:
onFailure: { [weak self] message in
self?.hideSpinner()
self?.showAlert(message: message) { [weak self] _ in
self?.startOver()//FUNCTION IS UNUSED
}
})
Edit:
Here is alert method that is used:
fileprivate func showAlert( message: String, okAction: #escaping (UIAlertAction) -> Void) {
let ac = UIAlertController(title: nil, message: message, preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler:okAction)
ac.addAction(ok)
ac.preferredAction = ok
self.present(ac, animated: true)
}
If I add back alert action button it works:
let okButton = UIAlertAction(
title: "OK",
style: .default,
handler:startOver())

You're currently calling that function inside of the action you pass to your showAlert function but then throwing away the action it returns. Instead, you want to pass the action that it returns directly to your showAlert method rather than wrapping it inside another action with the trailing closure syntax:
self?.showAlert(message: message, okAction: self!.startOver())

Related

Can't pass data between View Controller and Model

There is UIAlertController with text field in my View Controller. When user enter name of the city, this data must be transmitted to Model, when I get coordinates of this city. But I can't to pass name of the city from View Controller to Model
My UIAlertController:
class MainScrenenViewController: UIViewController {
var delegate: ILocationGroup?
#objc func locationButtonTap() {
let alert = UIAlertController(title: "Add city", message: nil, preferredStyle: .alert)
let addButton = UIAlertAction(title: "Add", style: .default) { action in
self.delegate?.addLocation(alert.textFields?.first?.text ?? "No City")
}
alert.addAction(addButton)
let cancelButton = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alert.addAction(cancelButton)
alert.addTextField { textField in
textField.placeholder = "Your City"
}
present(alert, animated: true, completion: nil)
}
My Model:
protocol ILocationGroup {
func addLocation(_ name: String)
}
class LocationGroup: ILocationGroup {
var mainScreenViewController: MainScrenenViewController?
func addLocation(_ name: String) {
mainScreenViewController?.delegate = self
let url = "https://geocode-maps.yandex.ru/1.x/?apikey=fd93783b-fe25-4428-8c3b-38b155941c8c&format=json&geocode=\(name)"
guard let url = URL(string: url) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
do {
let result = try JSONDecoder().decode(LocationData.self, from: data)
print(result.response.geoObjectCollection.metaDataProperty.geocoderResponseMetaData.boundedBy.envelope.lowerCorner)
}
catch {
print("failed to convert \(error)")
}
}
task.resume()
}
}
I think it is supposed to be var delegate: LocationGroup()
Also, I wouldn't be calling it delegate because registered delegate is a keyword in swift
https://manasaprema04.medium.com/different-ways-to-pass-data-between-viewcontrollers-views-8b7095e9b1bf

How can I create SignIn and SignOut with 1 button in RxSwift?

Here my code to change text in button:
func transform(input: Input) -> Output {
let tappedSigninCheck = input.signinTrigger
.scan(false) { lastState, _ in
return !lastState }
let singupButtonTitle = tappedSigninCheck.map {
return $0 == true ? "Sign Out" : "Sign In"
}
return Output(signinButtonTitle: singupButtonTitle)
}
Now when I clicked SignIn, button text will change to "Sign Out" and I want when click Sign Out, sign out will display 1 alert and when click Yes in alert then button text change to Sign In.
My problem: whenever I click in button then button text change text :(((
func bindViewModel() {
let signoutTrigger = signinButton.rx.tap.flatMap {
return Observable<Void>.create { (observer) -> Disposable in
let alert = UIAlertController(title: "Are you sure to sign out?",
message: nil,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No",
style: .cancel,
handler: nil))
alert.addAction(UIAlertAction(title: "Yes",
style: .destructive,
handler: { _ in
observer.onNext(Void())
}))
self.present(alert, animated: true)
return Disposables.create()
}
}
let input = ProfileViewModel.Input(signinTrigger: signinButton.rx.tap.asDriver(),
signoutTrigger: signoutTrigger.asDriver(onErrorJustReturn: Void()))
let output = profileViewModel.transform(input: input)
output
.signinButtonTitle
.drive(signinButton.rx.title)
.disposed(by: disposeBag)
}
For this you will need a feedback loop, and that calls for a Subject. I suggest you do it something like below. Also note that you forgot to emit a completed event on your alert.:
class ViewModel {
struct Input {
let logButton: Observable<Void>
let okayButton: Observable<Void>
}
struct Output {
let buttonTitle: Observable<String>
let displayAlert: Observable<Void>
}
private enum Action {
case tapped
case okay
}
private enum State {
case offline
case online
case check
}
func transform(_ input: Input) -> Output {
let state = Observable.merge(
input.logButton.map(to: ViewModel.Action.tapped),
input.okayButton.map(to: ViewModel.Action.okay)
)
.scan(ViewModel.State.offline) { state, action in
switch (state, action) {
case (.offline, .tapped):
return .online
case (.online, .tapped):
return .check
case (.check, .okay):
return .offline
case (.check, .tapped):
return .check
default:
assert(false)
return state
}
}
.share()
let buttonTitle = state
.map { $0 == .offline ? "Log In" : "Log Out" }
let displayAlert = state
.filter { $0 == .check }
.map(to: ())
return Output(
buttonTitle: buttonTitle,
displayAlert: displayAlert
)
}
}
class ViewController: UIViewController {
var button: UIButton!
var viewModoel: ViewModel!
let disposeBag = DisposeBag()
func bind() {
let logout = PublishSubject<Void>()
let input = ViewModel.Input(
logButton: button.rx.tap.asObservable(),
okayButton: logout
)
let output = viewModoel.transform(input)
output.buttonTitle
.bind(to: button.rx.title(for: .normal))
.disposed(by: disposeBag)
output.displayAlert
.flatMap { [unowned self] _ in
self.areYouSure()
}
.bind(to: logout)
.disposed(by: disposeBag)
}
func areYouSure() -> Observable<Void> {
Observable.create { [unowned self] observer in
let alert = UIAlertController(
title: "Are you sure to sign out?",
message: nil,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(
title: "Yes", style: .destructive, handler: { _ in
observer.onNext(())
observer.onCompleted()
}
))
alert.addAction(UIAlertAction(
title: "No", style: .default, handler: { _ in
observer.onCompleted()
}
))
self.present(alert, animated: true)
return Disposables.create()
}
}
}

makeAlert Function Returns Always False

The function below always returns false. I tried to put return inside of the completion it did not accept either.
Can you please help me?
// MARK: - make Alert for user Input
func makeAlert(message: String, defaultButtonText: String, cancelButtonText: String) - > Bool {
var answer = Bool()
let alert = UIAlertController(title: "Warning", message: message, preferredStyle: .alert)
let actionYes = UIAlertAction(title: defaultButtonText, style: .default) {
(action) in
answer = true
}
let actionNo = UIAlertAction(title: cancelButtonText, style: .default) {
(action) in
answer = false
}
alert.addAction(actionNo)
alert.addAction(actionYes)
self.present(alert, animated: true, completion: {
print(answer)
})
return answer
}
You have to use completion like this.
func makeAlert(message: String,defaultButtonText: String, cancelButtonText: String, completion: #escaping ((Bool) -> Void)) {
let alert = UIAlertController(title: "Warning", message: message, preferredStyle: .alert)
let actionYes = UIAlertAction(title: defaultButtonText, style: .default) { (action) in
completion(true)
}
let actionNo = UIAlertAction(title: cancelButtonText, style: .default) { (action) in
completion(false)
}
alert.addAction(actionNo)
alert.addAction(actionYes)
self.present(alert, animated: true, completion: {
})
}
Usage :
makeAlert(message: "Test", defaultButtonText: "Test", cancelButtonText: "Test") { (action) in
if action {
// Do code for true part
} else {
// Do code for false part
}
}
EDIT
As per the commnet. How to use in FSCalendar
func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
makeAlert(message: "Test", defaultButtonText: "Yeah", cancelButtonText: "No") { (action) in
if action {
calendar.select(date)
}
}
return false
}

How can I add alert button till my data count? How can I save my data choose from a action index?

I'm present a alert when I click the button. I choose from a list (if how much data is available.) How can I save my data choose from a list index?
You can see UI in here
My AccountServices
class AccountServices {
static let databaseReference = Database.database().reference(withPath: "Accounts").child((Auth.auth().currentUser?.uid)!)
static var account = Account()
static func saveChanges() {
databaseReference.setValue(try! FirebaseEncoder().encode(AccountServices.account))
}
static func getAccount() {
databaseReference.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { (snapshot, _) in
account = try! FirebaseDecoder().decode(Account.self, from: snapshot.value!)
})
}
}
Variable
var product: ProductViewModel?
addButton Tapped
#IBAction func addToCartButtonTapped(_ sender: UIButton) {
let alert = UIAlertController(title: "Bu ürünü hangi sepetinize eklemek istersiniz ?", message: "", preferredStyle: .actionSheet)
var indexer = 0
for cart in AccountServices.account.cart! {
if cart.product == nil{
AccountServices.account.cart![indexer].product = [Product]()
}
let action = UIAlertAction(title: cart.name , style: .default, handler: { (sender) in
if let index = alert.actions.firstIndex(where: { $0 === sender }) {
AccountServices.account.cart?[index].product?.append(self.product) `//Error: Cannot convert value of type 'ProductViewModel?' to expected argument type 'Product'`
AccountServices.saveChanges()//TODO...
}
let addAlert = UIAlertController(title: "Sepetinize Eklendi.", message: "Ürününüz sepetinize eklendi.", preferredStyle: .alert)
let okButton = UIAlertAction(title: "Tamam", style: .default, handler: nil)
addAlert.addAction(okButton)
self.present(addAlert, animated: true, completion: nil)
})
alert.addAction(action)
indexer += 1
}
let cancelaction = UIAlertAction(title: "Vazgeç", style: .cancel, handler: nil)
alert.addAction(cancelaction)
present(alert, animated: true, completion: nil)
}
}

Error: Cannot convert value of type '(_) throws -> ()' to expected argument type '((UIAlertAction) -> Void)?'

let alertController = UIAlertController(title: "Email?", message: "Please input your email:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store your data
UserDefaults.standard.set(try.text, forKey: "userEmail")
UserDefaults.standard.synchronize()
} else {
// user did not fill field
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
textField.placeholder = "Email"
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
Typo:
UserDefaults.standard.set(field.text, forKey: "userEmail")
rather than
UserDefaults.standard.set(try.text, forKey: "userEmail")
The error message points it out:
... value of type '(_) throws -> ()'
use this ....
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (action) -> Void in
if let field = alertController.textFields?[0] {
// store your data
UserDefaults.standard.set(try.text, forKey: "userEmail")
UserDefaults.standard.synchronize()
} else {
// user did not fill field
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) -> Void in
alertController.addTextField { (textField) in
textField.placeholder = "Email"
})