Custom Protocol Swift - ios

I have a Protocol
import UIKit
import Alamofire
import SwiftyJSON
protocol RequestProtocol: class {
func RequestConnection(json: JSON, status: Int, Message: String)
}
class API: UIViewController {
var delegate: RequestProtocol?
var json: JSON = []
override func viewDidLoad() {
super.viewDidLoad()
}
func RequestConnection() {
Alamofire.request(variablesClass.url).responseJSON { (response) -> Void in
switch response.result {
case .success:
let result = response.result.value
if response.response?.statusCode == 200 {
self.json = JSON(result!)
DispatchQueue.main.async(execute: {
self.delegate?.RequestConnection(json: self.json, status: (response.response?.statusCode)!, Message: "\(self.json["Message"])")
})
} else {
self.json = JSON(result!)
DispatchQueue.main.async(execute: {
self.delegate?.RequestConnection(json: self.json, status: (response.response?.statusCode)!, Message: "\(self.json["Message"])")
})
}
break;
case .failure(let error):
print(error)
break;
}
}
}
}
I have it called in my Main View
class ViewController: UIViewController, RequestProtocol {
func RequestConnection(json: JSON, status: Int, Message: String) {
func showAlertView()
}
override func viewDidLoad() {
super.viewDidLoad()
var senderDelegateRequest = API()
var receiverDelegateViewController = ViewController()
senderDelegateRequest.delegate = receiverDelegateRequestRegistro
senderDelegateRequest.RequestConnection()
}
func showAlertView(){
let alertView = UIAlertController(title: "You need to log in first", message: "To access the special features of the app you need to log in first.", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "Login", style: .default, handler: { (alertAction) -> Void in
}))
alertView.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertView, animated: true, completion: nil)
}
Why the Protocol does'nt execute the function to display the alert.
There is some way that as soon as the protocol is executed it can execute some function

In your ViewController class you are conforming to the protocol RequestProtocol. This means you can be the delegate of API. You created an instance called receiverDelegateViewController but this instance isn't loaded yet. You should change:
senderDelegateRequest.delegate = receiverDelegateRequestRegistro
to
senderDelegateRequest.delegate = self
And the delegate in API should call RequestConnection, not your ViewController.

Related

Swift 5 - Email Class Helper / Manager

Edit:
Big thanks to Paulw11 for helping me solve this issue. I've added the full code here for easy reuse:
Class:
import UIKit
import MessageUI
struct Feedback {
let recipients: [String]
let subject: String
let body: String
let footer: String
}
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate {
private var feedback: Feedback
private var completion: ((Result<MFMailComposeResult,Error>)->Void)?
override init() {
fatalError("Use FeedbackManager(feedback:)")
}
init?(feedback: Feedback) {
guard MFMailComposeViewController.canSendMail() else {
return nil
}
self.feedback = feedback
}
func send(on viewController: UIViewController, completion:(#escaping(Result<MFMailComposeResult,Error>)->Void)) {
let mailVC = MFMailComposeViewController()
self.completion = completion
mailVC.mailComposeDelegate = self
mailVC.setToRecipients(feedback.recipients)
mailVC.setSubject(feedback.subject)
mailVC.setMessageBody("<p>\(feedback.body)<br><br><br><br><br>\(feedback.footer)</p>", isHTML: true)
viewController.present(mailVC, animated:true)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if let error = error {
completion?(.failure(error))
controller.dismiss(animated: true)
} else {
completion?(.success(result))
controller.dismiss(animated: true)
}
}
}
In View Controller:
Add Variable:
var feedbackManager: FeedbackManager?
Use:
let feedback = Feedback(recipients: "String", subject: "String", body: "Body", footer: "String")
if let feedManager = FeedbackManager(feedback: feedback) {
self.feedbackManager = feedManager
self.feedbackManager?.send(on: self) { [weak self] result in
switch result {
case .failure(let error):
print("error: ", error.localizedDescription)
// Do something with the error
case .success(let mailResult):
print("Success")
// Do something with the result
}
self?.feedbackManager = nil
}
} else { // Cant Send Email: // Added UI Alert:
let failedMenu = UIAlertController(title: "String", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "String", style: .default)
failedMenu.addAction(okAlert)
present(failedMenu, animated: true)
}
I'm trying to make a class that handles initializing a MFMailComposeViewController to send an email inside of the app.
I'm having issues making it work. Well, rather making it not crash if it doesn't work.
class:
import UIKit
import MessageUI
struct Feedback {
let recipients = "String"
let subject: String
let body: String
}
class FeedbackManager: MFMailComposeViewController, MFMailComposeViewControllerDelegate {
func sendEmail(feedback: Feedback) {
if MFMailComposeViewController.canSendMail() {
self.mailComposeDelegate = self
self.setToRecipients([feedback.recipients])
self.setSubject("Feedback: \(feedback.subject)")
self.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
} else {
print("else:")
mailFailed()
}
}
func mailFailed() {
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
self.present(failedMenu, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
And then calling it from a different view controller:
let feedbackManager = FeedbackManager()
feedbackManager.sendEmail(feedback: Feedback(subject: "String", body: "String"))
self.present(feedbackManager, animated: true, completion: nil)
tableView.deselectRow(at: indexPath, animated: true)
The above works just fine if MFMailComposeViewController.canSendMail() == true. The problem I'm facing is that if canSendMail() is not true, then the class obviously cant initialize and crashes. Which makes sense.
Error:
Unable to initialize due to + [MFMailComposeViewController canSendMail] returns NO.
I'm not sure where to go from here on how to get this working. I've tried changing FeedbackManager from MFMailComposeViewController to a UIViewController. And that seems to work but because it's adding a view on the stack, it's causing a weird graphical display.
The other thing I could do is import MessageUI, and conform to MFMailComposeViewController for every controller I want to be able to send an email from. So that I can check against canSendMail() before trying to initialize FeedbackManager(). But that also doesn't seem like the best answer.
How else can I get this working?
EDIT:
I've gotten the code to work with this however, there is an ugly transition with the addition of the view onto the stack before it presents the MFMailComposeViewController.
class FeedbackManager: UIViewController, MFMailComposeViewControllerDelegate {
func sendEmail(feedback: Feedback, presentingViewController: UIViewController) -> UIViewController {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([feedback.recipients])
mail.setSubject("Feedback: \(feedback.subject)")
mail.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
present(mail, animated: true)
return self
} else {
print("else:")
return mailFailed(presentingViewController: presentingViewController)
}
}
func mailFailed(presentingViewController: UIViewController) -> UIViewController {
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
return failedMenu
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
self.dismiss(animated: false)
}
}
Subclassing MFMailComposeViewController is the wrong approach. This class is intended to be used "as-is". You can build a wrapper class if you like:
struct Feedback {
let recipients = "String"
let subject: String
let body: String
}
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate {
private var feedback: Feedback
private var completion: ((Result<MFMailComposeResult,Error>)->Void)?
override init() {
fatalError("Use FeedbackManager(feedback:)")
}
init?(feedback: Feedback) {
guard MFMailComposeViewController.canSendMail() else {
return nil
}
self.feedback = feedback
}
func send(on viewController: UIViewController, completion:(#escaping(Result<MFMailComposeResult,Error>)->Void)) {
let mailVC = MFMailComposeViewController()
self.completion = completion
mailVC.mailComposeDelegate = self
mailVC.setToRecipients([feedback.recipients])
mailVC.setSubject("Feedback: \(feedback.subject)")
mailVC.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
viewController.present(mailVC, animated:true)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if let error = error {
completion?(.failure(error))
} else {
completion?(.success(result))
}
}
}
And then to use it from a view controller:
let feedback = Feedback(subject: "String", body: "Body")
if let feedbackMgr = FeedbackManager(feedback: feedback) {
self.feedbackManager = feedbackMgr
feedback.send(on: self) { [weak self], result in
switch result {
case .failure(let error):
// Do something with the error
case .success(let mailResult):
// Do something with the result
}
self.feedbackManager = nil
}
} else {
// Can't send email
}
You will need to hold a strong reference to the FeedbackManager in a property otherwise it will be released as soon as the containing function exits. My code above refers to a property
var feedbackManager: FeedbackManager?
While this will work, a better UX is if you check canSendMail directly and disable/hide the UI component that allows them to send feedback
Solved this by first adding a class that checks if .canSendMail is true. If it is, it then taps into the postal sending class to present the MFMailComposeViewController.
This is the only workaround I've come up with that allows MFMailComposeViewController to be it's own MFMailComposeViewControllerDelegate. While also preventing a crash if .canSendMail = false.
import UIKit
import MessageUI
struct Feedback {
let recipients = ["Strings"]
let subject: String
let body: String
}
class FeedbackManager {
func tryMail() -> Bool {
if MFMailComposeViewController.canSendMail() {
return true
} else {
return false
}
}
func mailFailed() -> UIViewController {
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
return failedMenu
}
}
class PostalManager: MFMailComposeViewController, MFMailComposeViewControllerDelegate {
func sendEmail(feedback: Feedback) -> MFMailComposeViewController {
if MFMailComposeViewController.canSendMail() {
self.mailComposeDelegate = self
self.setToRecipients(feedback.recipients)
self.setSubject("Feedback: \(feedback.subject)")
self.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
}
return self
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
Called with:
let feedbackManager = FeedbackManager()
let feedback = Feedback(subject: "String", body: "Body")
switch feedbackManager.tryMail() {
case true:
let postalManager = PostalManager()
present(postalManager.sendEmail(feedback: feedback), animated: true)
case false:
present(feedbackManager.mailFailed(), animated: true)
}
You can change the code as follows.
struct Feedback {
let recipients = "String"
let subject: String
let body: String
}
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate {
func sendEmail(presentingViewController: UIViewController)) {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([feedback.recipients])
mail.setSubject("Feedback: \(feedback.subject)")
mail.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
presentingViewController.present(mail, animated: true)
} else {
print("else:")
mailFailed(presentingViewController: presentingViewController)
}
}
func mailFailed(presentingViewController: UIViewController) {
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
presentingViewController.present(failedMenu, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
Now, mailComposer can be opened as follows from another UIViewController class.
let feedbackManager = FeedbackManager()
feedbackManager.sendEmail(presentingViewController: self)
Hope it helps

Calling a function inside enum's function in the same class

I came across a scenario where i had to call a function inside enum's function in swift 3.
The scenario is as follows:
class SomeViewController: UIViewController {
enum Address {
case primary
case secondary
func getAddress() {
let closure = { (text: String) in
showAlert(for: "")
}
}
}
func showAlert(for text: String) {
let alertController = UIAlertController(title: text, message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title:NSLocalizedString("OK", comment:"OK button title"), style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
}
As you can see from the above code I get an error on line 10 (showAlert(for: ""))
The error is:
instance member showAlert cannot be used on type SomeViewController; did you mean to use a value of this type instead?
How can I call a function from enum's function then?
Alternative approach:
You can use a static method of SomeViewController to present the alert.
Example:
static func showAlert(for text: String)
{
let alertController = UIAlertController(title: text, message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title:NSLocalizedString("OK", comment:"OK button title"), style: .cancel, handler: nil))
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
}
Using it:
enum Address
{
case primary
case secondary
func getAddress()
{
let closure = { (text: String) in
SomeViewController.showAlert(for: "")
}
closure("hello")
}
}
override func viewDidLoad()
{
super.viewDidLoad()
let addr = Address.primary
addr.getAddress()
}
The enum does not know the instance and thus can not access its members. One approach to deal with this situation would be to inform the client of the enum that something went wrong.
class SomeViewController: UIViewController {
enum Address {
case primary
case secondary
func getAddress() -> String? {
//allGood == whatever your logic is to consider a valid address
if allGood {
return "theAddress"
} else {
return nil;
}
}
}
func funcThatUsesAddress() {
let address = Address.primary
guard let addressString = address.getAddress() else {
showAlert(for: "")
return
}
// use your valid addressString here
print(addressString)
}
func showAlert(for text: String) {
let alertController = UIAlertController(title: text, message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title:NSLocalizedString("OK", comment:"OK button title"), style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
}

Model class gets the object but doesn't fulfill the TableView

I know this is a common problem but I can't find the solution whether I searched for hours so I decided to open a new question. I'm getting "[UIRefreshControl copyWithZone:]: unrecognized selector sent to instance 0x102029400" problem. When I first open the view. Loading appears but It doesn't fulfill the table. However when I check model class, It gets the values from database.
In viewcontroller declaration;
model.delegate = self
model.refresh_history(sensor_name: send_item)
// Set up a refresh control.
mTableView.refreshControl = UIRefreshControl()
mTableView.refreshControl?.addTarget(model, action: #selector(model.refresh_history), for: .valueChanged)
Delegate;
extension ChooseHistoryViewController: ModelDelegate {
func modelUpdated() {
mTableView.refreshControl?.endRefreshing()
mTableView.reloadData()
}
func errorUpdating(_ error: NSError) {
let message: String
if error.code == 1 {
message = "Error"
} else {
message = error.localizedDescription
}
let alertController = UIAlertController(title: nil,
message: message,
preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
present(alertController, animated: true, completion: nil)
}
}
and my model class;
protocol ModelDelegate {
func errorUpdating(_ error: NSError)
func modelUpdated()
}
class Model{
var user: User
static let sharedInstance = Model()
var sensor_ecg: Sensor_ECG?
var delegate: ModelDelegate?
var history: [String] = []
init()
{
user = User()
}
#objc func refresh_history(sensor_name: String){
let parameters: Parameters = ["q" : "{\"member_id\" :\"\(user.id!)\"}", "apiKey": "2ABdhQTy1GAWiwfvsKfJyeZVfrHeloQI"]
Alamofire.request("https://api.mlab.com/api/1/databases/mysignal/collections/\(sensor_name)", method: .get, parameters: parameters,encoding: URLEncoding.default, headers: nil).responseJSON{ response in
let json = JSON(data: response.data!)
print(json)
if(response.response == nil) {
return
}
let history = json[0]["date"].string!
print(history)
}
DispatchQueue.main.async {
self.delegate?.modelUpdated()
}
}
You need to change
If you declared your method as "refresh_history:" (i.e. with a parameter), you need to add a colon to the "#selector" bit.
In other words, one line changes with one character:
mTableView.refreshControl?.addTarget(model, action: #selector(model.refresh_history(sensor_name:)), for: .valueChanged)

In AWS iOS SDK, how do I handle FORCE_CHANGE_PASSWORD User Status

I have followed the sample here
https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample
To integrate interactive cognito login to my iOS app. This is all working well, but when a new user is created in the pool, they initially have a FORCE_CHANGE_PASSWORD status.
For android you can follow the procedure below
http://docs.aws.amazon.com/cognito/latest/developerguide/using-amazon-cognito-user-identity-pools-android-sdk-authenticate-admin-created-user.html
But for iOS I can't find out how to do this. Using the sample, if I attempt to login with a user in FORCE_CHANGE_PASSWORD status, I see the following output in the console logs (with some values removed for brevity):
{"ChallengeName":"NEW_PASSWORD_REQUIRED","ChallengeParameters":{"requiredAttributes":"[]","userAttributes":"{\"email_verified\":\"true\",\"custom:autoconfirm\":\"Y\","Session":"xyz"}
The following is the code from the SignInViewController from the sample detailed above.
import Foundation
import AWSCognitoIdentityProvider
class SignInViewController: UIViewController {
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
var passwordAuthenticationCompletion: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>?
var usernameText: String?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.password.text = nil
self.username.text = usernameText
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
#IBAction func signInPressed(_ sender: AnyObject) {
if (self.username.text != nil && self.password.text != nil) {
let authDetails = AWSCognitoIdentityPasswordAuthenticationDetails(username: self.username.text!, password: self.password.text! )
self.passwordAuthenticationCompletion?.set(result: authDetails)
} else {
let alertController = UIAlertController(title: "Missing information",
message: "Please enter a valid user name and password",
preferredStyle: .alert)
let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
alertController.addAction(retryAction)
}
}
}
extension SignInViewController: AWSCognitoIdentityPasswordAuthentication {
public func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>) {
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
DispatchQueue.main.async {
if (self.usernameText == nil) {
self.usernameText = authenticationInput.lastKnownUsername
}
}
}
public func didCompleteStepWithError(_ error: Error?) {
DispatchQueue.main.async {
if let error = error as? NSError {
let alertController = UIAlertController(title: error.userInfo["__type"] as? String,
message: error.userInfo["message"] as? String,
preferredStyle: .alert)
let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
} else {
self.username.text = nil
self.dismiss(animated: true, completion: nil)
}
}
}
}
When didCompleteStepWithError executes, error is null where I would expect it to indicate something to tell us that the user is required to change password.
My question is really how to catch the "ChallengeName":"NEW_PASSWORD_REQUIRED" json that is output to the console?
Sorted this now. Posting it here in case it helps anyone else.
First, you need to create a view controller that will allow the user to specify the new password. The ViewController should have a AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails> as a property:
var newPasswordCompletion: AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails>?
Following the pattern in the sample referred to in the question, I then implemented AWSCognitoIdentityNewPasswordRequired as an extension to the view controller as follows:
extension NewPasswordRequiredViewController: AWSCognitoIdentityNewPasswordRequired {
func getNewPasswordDetails(_ newPasswordRequiredInput: AWSCognitoIdentityNewPasswordRequiredInput, newPasswordRequiredCompletionSource:
AWSTaskCompletionSource<AWSCognitoIdentityNewPasswordRequiredDetails>) {
self.newPasswordCompletion = newPasswordRequiredCompletionSource
}
func didCompleteNewPasswordStepWithError(_ error: Error?) {
if let error = error as? NSError {
// Handle error
} else {
// Handle success, in my case simply dismiss the view controller
self.dismiss(animated: true, completion: nil)
}
}
}
Again following the design of the sample detailed in the question, add a function to the AWSCognitoIdentityInteractiveAuthenticationDelegate extension to AppDelegate:
extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate {
func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
//Existing implementation
}
func startNewPasswordRequired() -> AWSCognitoIdentityNewPasswordRequired {
// Your code to handle how the NewPasswordRequiredViewController is created / presented, the view controller should be returned
return self.newPasswordRequiredViewController!
}
}
The perfect example to implement the ResetPassword when u create the user in Cognito Console.
But, Integrating this ResetPassword mechanism to your existing app is your responsibility.
https://github.com/davidtucker/CognitoSampleApplication/tree/article1/CognitoApplication

Swift overriding protocol extension keeping the extension behaviour

I have this simple class and an hypothetical protocol called 'ShowAlert', it's extension to do the default implementation and a default ViewController and it's ShowAlert protocol implementation.
protocol ShowAlert {
var titleForAlert: String! { get }
func messageForAlert() -> String!
func show()
}
extension ShowAlert where Self: UIViewController {
func show(){
let alert = UIAlertController(title: self.titleForAlert, message: self.messageForAlert(), preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func showItNow(sender: AnyObject) {
self.show()
}
}
extension ViewController: ShowAlert {
var titleForAlert: String! {
get{
return "Foo"
}
}
func messageForAlert() -> String! {
return "Bar"
}
func show() {
// here I want to call the default implementation of the protocol to show the alert, then do something else
print("Good day sir!")
}
}
It's like on a subclass where I could call a 'super.show()' and then continue implementing whatever I want to do after that.
There's any way to do it? Or my logic go against what protocols are design for and that don't suppose to happen?
There is a simple solution: Just add a defaultShow method to the extension.
extension ShowAlert where Self: UIViewController {
func defaultShow(){
let alert = UIAlertController(title: self.titleForAlert, message: self.messageForAlert(), preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
func show() {
defaultShow()
}
}
So in your code you can just call defaultShow:
extension ViewController: ShowAlert {
// ...
func show() {
self.defaultShow()
print("Good day sir!")
}
}
There is also another solution where you can call .show() instead of .defaultShow(). However it uses casting and breaks encapsulation. If you want to see it let me know.

Resources