self constraint protocol extention for UIViewController - swift

I'm working on a legacy Swift 2.2 project, and I want to implement some well-known protocol-oriented practices to my code.
protocol SuccessPresenting {
func presentSucess(title: String, message: String)
}
extension SuccessPresenting where Self: UIViewController {
func presentSucess(title: String?, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let dismissAction = UIAlertAction(title: "ОК", style: .Default, handler: nil)
alertController.addAction(dismissAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
}
class NewViewController: UIViewController, SuccessPresenting {
func foo() {
presentSucess(nil, message: "Done!")
}
}
Though, it is works on Swift 3.1, here I get an error: The NewViewController doesn't conform to protocol SuccessPresenting
But why should I write protocol implementation in my VC, as I have already done that using protocol extension?
I'll appreciate any help.
Please remind, this is Swift 2.2

Is this a direct paste? Because your extension contains an optional instead of a regular string, whereas your protocol has a normal String. This might cause the compiler to regard it is a different method, rendering the optionallness of your protocol invalid in this particular case.

Related

Swift - Unable to simultaneously satisfy constraints

i'm trying to make a action in other storyboard but i'm getting this error
2019-12-24 07:44:32.861248-0800 test[1588:142069] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x600003631e50 UIView:0x7fc09d715410.width == - 16 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600003631e50 UIView:0x7fc09d715410.width == - 16 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in
<UIKitCore/UIView.h> may also be helpful.
i'm new in ios development, i know that other request are posted in stack overflow but i'm don't understand how to revolve my problem.
i create un Alert.storyboard with nothing
AlertViewController.swift with :
import UIKit
class AlertViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
a()
}
func a() {
let alert = UIAlertController(title: "Something", message: nil, preferredStyle: .actionSheet)
let opt1 = UIAlertAction( title: "Action 1", style: .default) { (action) in
print("dskhgohg")
self.dismiss(animated: true)
}
alert.addAction(opt1)
present( alert, animated: true, completion: nil )
}
}
AlertService.swift
import UIKit
class AlertService {
func alert() -> AlertViewController {
let storyboard = UIStoryboard(name: "Alert", bundle: .main)
let alertVC = storyboard.instantiateViewController(identifier: "AlertVC") as! AlertViewController
return alertVC
}
}
and i added a button in Main.storyboard with action in viewController
let alertService = AlertService()
#IBAction func btn() {
let alertVC = alertService.alert()
present(alertVC, animated: true)
print("dsgjodig")
}
maybe i shoud not action sheet in viewDidAppear
All action sheets do that. It’s an Apple bug. It has no effect on your app’s functionality. Ignore it and move on.

Method overriding in swift

Basically I am currently adding functionX to everywhere i present an UIAlertController as seen below:
let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)
let okAction = UIAlertAction(title: "ok", style: .default)
alert.addAction(okAction)
functionX(actionSheet: alert, controller: self)
self.present(alert, animated: true, completion: nil)
// or it can be
// tableviewController.present(alert, animated: true, completion: nil)
Instead of calling functionX every time, I want to override a present method and call functionX there. I attempted the following:
extension UIViewController {
override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if (viewControllerToPresent is UIAlertController) {
functionX(actionSheet: viewControllerToPresent, controller: /* what should this be? */ )
}
super.present() //error here
}
}
Is this an appropriate approach? Can you help me fill the missing parameters?
i.e.:
What should be the controller? What would self or tableviewController from the first code stub be in the overriding present function?
How should I call the present method in the overriding present function?
According to the Swift guide,
Extensions can add new functionality to a type, but they cannot override existing functionality.
So you shouldn't really be overriding an existing method in a UIViewController in an extension.
What you could do is, to add your own present, called functionXAndPresent:
extension UIViewController {
func functionXAndPresent(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if (viewControllerToPresent is UIAlertController) {
// to answer your second question, you should use "self" here
functionX(actionSheet: viewControllerToPresent, controller: self)
}
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
You can't do this by overriding because as you have found out, you can't really refer to the "non-overridden" method at the end. super.present doesn't work because you are in an extension, not a subclass.
You can simply create a common method showAlert(with:and:) in a UIViewController extension and call functionX when the alert is presented, i.e.
extension UIViewController {
func showAlert(with title: String?, message: String?) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default)
alert.addAction(okAction)
self.present(alert, animated: true, completion: {
self.functionX(actionSheet: alert, controller: self)
})
}
func functionX(actionSheet: UIAlertController, controller: UIViewController) {
//your code here...
}
}
Usage:
Call the showAlert(with:and:) method from whatever controller you want to, be it a UIViewController or a UITableViewController or any other, i.e
self.showAlert(with: "Alery..!!!", message: "This is a sample alert.")
Overrides of NSObject's derivatives in Swift's static extensions is only for Objective-C compatibility. You cannot override in extensions of pure Swift declarations. Think of it in such manner that if the class itself adds an override, and then a static extension adds an override. Which implementation should the linker link? And which implementation does super call refer to? In Objective-C this behavior is undefined, in Swift the extension override is ignored all together.
What you could do instead is move the overriding function from the extension directly to the class.

Pass "function with parameter" as parameter

This doesn't compile
func showAlert(_ title: String, message: String,
onOk: (()->())? = nil,
onAnotherAction:((anotherActionTitle : String)-> Void)? = nil) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default) { (action) in
onOk?()
}
let anotherAction = UIAlertAction(title: anotherActionTitle, style: .default) { (action) in
onAnotherAction?()
}
alertController.addAction(ok)
alertController.addAction(anotherAction)
...
}
This compiles
func showAlert(_ title: String, message: String,
onOk: (()->())? = nil,
onAnotherAction:((String)-> Void)? = nil)
However, I have to declare another parameter for the title anotherActionTitle of onAnotherAction().
Is there a way make the first approach work? Thanks!
However, I have to declare another parameter for the title anotherActionTitle of onAnotherAction()
No, you don't have to do that. Just make it a normal parameter of the function as a whole:
func showAlert(_ title: String, message: String,
onOk: (()->())? = nil,
anotherActionTitle: String? = nil,
onAnotherAction: (()->())? = nil) {
The rest of your function will then compile and work correctly.
Since the implementation of SE-0111 as part of Swift 3, it is no longer possible to have named parameters for closure types.
There is a conceptual roadmap that the Swift core team has laid out for restoring named closure parameters at some point in the future, but no timeline for implementation that I am aware of:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160711/024331.html

Call function in UIViewController from an extension

I have a UIViewController which loads up some json data from the server. If the server is down or the user has data turned off I throw up an alert telling the user such. This is done using a UIAlertController. This works great. So I put this into an extension since it is used by all UIViewControllers which need data. Now the UIAlertController has an action set as well
Alert code
extension UIViewController {
func connectionLost(){
var message = "Your device has lost connection to the server. Check that you have a valid internet connection and then retry."
let alertController = UIAlertController( title: "Connection Lost",
message: message,
preferredStyle: .alert)
let retryAction = UIAlertAction(title:"Retry", style: .default, handler: {
action in
//call function in the viewcontroller that raised this alert to reload the data
})
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
}
}
When the user taps the retry button I want to call a function in the uiviewcontroller that raised the alert.
I tried creating a delegate in the extension but struggled with getting it wired up like you do in a class. What sort of approaches are there to call a function from an extension in the viewcontroller that raised the alert?
You should create a BaseViewController and use Inheritance. It could be useful for other implementations too.
class BaseViewController: UIViewController {
func onRetryClick() {
// override to customize or write here the common behaviour
}
}
class FirstViewController: BaseViewController {
override func onRetryClick() {
// do something specific for FirstViewController
}
}
class SecondViewController: BaseViewController {
override func onRetryClick() {
// do something specific for SecondViewController
}
}
class ThirdViewController: BaseViewController {
// if you don't override this method, super class (BaseViewController) implementation will be executed
}
extension BaseViewController {
func connectionLost(){
var message = "Your device has lost connection to the server. Check that you have a valid internet connection and then retry."
let alertController = UIAlertController( title: "Connection Lost",
message: message,
preferredStyle: .alert)
let retryAction = UIAlertAction(title:"Retry", style: .default, handler: { action in
self.onRetryClick()
})
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
}
}
Hope this makes sense.
class MyVC: UIViewController {
func retry() {
}
func checkConnection() {
connectionLost { (retry) -> (Void) in
if retry {
self.retry()
}
}
}
}
extension UIViewController {
func connectionLost(completion: #escaping (_ retry: Bool) -> (Void)) {
let message = "Your device has lost connection to the server. Check that you have a valid internet connection and then retry."
let alertController = UIAlertController( title: "Connection Lost",
message: message,
preferredStyle: .alert)
let retryAction = UIAlertAction(title:"Retry", style: .default, handler: {
action in
completion(true)//may be 'false', you decide
})
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
}
}

Swift expecting declaration but already declared

I am trying to create an alert view that performs a certain action when the button is clicked. I have tried creating a new class for the alert view, but when I try to add an action to the alert view controller, Xcode tells me that it is expecting a declaration, although the variable is declared just two steps above the line where the error occurs. Here's the code
class alerts: UIAlertController {
var alertThenGenerateNewThingController: UIAlertController = UIAlertController()
var generateNewThingOkButton = UIAlertAction (title: "OK", style: UIAlertActionStyle.Default) {
UIAlertAction in
println ("generate new thing action")
}
alertThenGenerateNewThingController.addAction (generateNewThingOkButton) // Here is where Xcode says it expected a declaration
func alertThenGenerateNewThing (alertTitle: String, alertMessage: String) {
alertThenGenerateNewThingController = UIAlertController (title: alertTitle, message: alertMessage, preferredStyle: .Alert)
self.presentViewController (alertThenGenerateNewThingController, animated: true, completion: nil)
}
}
You can't subclass UIAlertController, for one thing. Second of all, you can only interact with object properties inside of methods, functions, or the global scope. Run this code inside of the view controller where you plan on presenting your alert:
class viewController: UIViewController {
var alertThenGenerateNewThingController: UIAlertController = UIAlertController()
var generateNewThingOkButton = UIAlertAction (title: "OK", style: UIAlertActionStyle.Default) {
UIAlertAction in
println ("generate new thing action")
}
override func viewDidLoad() {
alertThenGenerateNewThingController.addAction(generateNewThingOkButton)
}
func alertThenGenerateNewThing (alertTitle: String, alertMessage: String) {
alertThenGenerateNewThingController = UIAlertController (title: alertTitle, message: alertMessage, preferredStyle: .Alert)
self.presentViewController (alertThenGenerateNewThingController, animated: true, completion: nil)
}
}