How to embed a navigation controller inside a custom framework - ios

I have created a custom framework with storyboard. However eventhough the root viewcontroller been embedded froma navigation controller, every time i check "self.navigationController" it prints "nil". What am I missing here.
However my objective is to pop back to the root viewController once I click a button from my 4th VC. currently the implementation as follows
My Custom Storyboard looks like bellow.
How I navigate as bellow.
if let urlString = Bundle.main.path(forResource: "FAUMESDK", ofType: "framework", inDirectory: "Frameworks") {
let bundle = (Bundle(url: NSURL(fileURLWithPath: urlString) as URL))
let sb = UIStoryboard(name: "FAUMEStoryboard", bundle: bundle)
let vc = sb.instantiateViewController(withIdentifier: "MessagePriviewVC")
vc.modalPresentationStyle = .fullScreen
self.show(vc, sender: nil)
}
I tried with the bellow code but didnt work
let vc = self.storyboard?.instantiateViewController(
withIdentifier: "MyVCIdentifier") as! MessagePreviewUIViewController
self.navigationController?.pushViewController(vc, animated: true)
The way I'm trying to pop back to the rootVC as below (currently its not working, and this is where I need a solution).
navigationController?.popToRootViewController(animated: true)
What am I missing here ???

Although you have embedded your view controller in a navigation controller in your storyboard, you are requesting a specific view controller by its identifier; so you get just that view controller, not that view controller embedded in a UINavigationController.
If you use instantiateInitialViewController then you will get the navigation controller that is the entry point to your storyboard, and it's root view controller will be your MessagePreviewViewController (Assuming that is the name of the view controller immediately after the navigation controller in your storyboard)
if let urlString = Bundle.main.path(forResource: "FAUMESDK", ofType: "framework", inDirectory: "Frameworks") {
let bundle = (Bundle(url: NSURL(fileURLWithPath: urlString) as URL))
let sb = UIStoryboard(name: "FAUMEStoryboard", bundle: bundle)
let vc = sb.instantiateInitialViewController()
vc.modalPresentationStyle = .fullScreen
self.show(vc, sender: nil)
}

Related

Why does pushViewController doesn't work after navigating through Dynamic Link?

I'm building an app that can receive Firebase's Dynamic Link and redirects it into a certain UIViewController after clicking the link. So based on this question that I asked, I have the code in AppDelegate.swift that navigates the app to the UIViewController like this:
func goToDynamicLinkVC() { // This function is in AppDelegate.swift
let destinationStoryboard: UIStoryboard = UIStoryboard(name: "DynamicLink", bundle: nil)
let destinationVC = destinationStoryboard.instantiateViewController(withIdentifier: "DynamicLinkView") as? DynamicLinkVC
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = destinationVC
self.window?.makeKeyAndVisible()
}
And in that UIViewController I also have a function that moves to another UIViewController if a user pressed a button like this:
func routeToDynamicForm() { // This function is in DynamicLinkVC class
let destinationVC = UIStoryboard(name: "DynamicLink", bundle: nil).instantiateViewController(withIdentifier: "DynamicFormView") as! DynamicFormVC
self.navigationController?.pushViewController(destinationVC, animated: true)
}
But weirdly enough now that I pressed the button it doesn't move me to the next View Controller (in this case, DynamicFormVC). I've tried to debug the button whether it's working or not by doing this:
print("button tapped")
It shows the message on the debug area, but it still doesn't redirects to the next view controller. So what can I do to resolve this? If you need more information feel free to ask and I will provide it to you. Thank you.
I changed the goToDynamicLinkVC function to something like this:
func goToDynamicLinkVC() { // This function is in AppDelegate.swift
let destinationStoryboard: UIStoryboard = UIStoryboard(name: "DynamicLink", bundle: nil)
let destinationVC = destinationStoryboard.instantiateViewController(withIdentifier: "DynamicLinkView")
let navController = UINavigationController()
navController.setViewControllers([destinationVC], animated: true)
self.window?.rootViewController = navController
}

Swift go back to root view controller

I am new to swift but I have implemented FCM but I am having an issue. When I click the notification it loads the NotificationsViewController with the following code.
let sb = UIStoryboard(name: "Main", bundle: nil)
let otherVC = sb.instantiateViewController(withIdentifier: "NotificationsViewController") as! NotificationsViewController
self.window?.rootViewController = otherVC;
Once the NotificationsViewController is loaded I use the following code in on the back button to reassign the root view back to the normal ViewController.
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let redViewController = mainStoryBoard.instantiateViewController(withIdentifier: "splashScreen") as! ViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = redViewController
Once I am on the splashScreen it should show a quick image and then move on to the main dashboard using this code.
let vw = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DashboardViewController") as! DashboardViewController
self.navigationController?.pushViewController(vw, animated: true)
The problem I am having is the splashScreen code is not working when the app loads it from NotificationsViewController, it just hangs on that VC, but if I load the splashScreen without reassigning the root view it works.
So is there some other way I should be doing this? I just want to take the user to the NotificationsViewController when they click the notification and then back to the main part of the app when they click a back button.
If you just need to show the NotificationViewController, you just present it in fullscreen. You can set it by vc.modalPresentationStyle = .fullScreen. Then to go back to the main app, you just need to dismiss it. :)

"Attempt to present while already presenting" still appearing after checks?

let appDelegate = UIKit.UIApplication.shared.delegate!
if let tabBarController = appDelegate.window??.rootViewController as? UITabBarController {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let signInVC = storyboard.instantiateViewController(withIdentifier: "SignInVC") as! SignInVC
guard !signInVC.isBeingPresented else {
log.warning("Attempt to present sign in sheet when it is already showing")
return
}
signInVC.modalPresentationStyle = UIModalPresentationStyle.formSheet
tabBarController.present(signInVC, animated: true, completion: nil)
}
This code can be called multiple times despite signInVC being presented. I already added this check:
guard !signInVC.isBeingPresented else {
log.warning("Attempt to present sign in sheet when it is already showing")
return
}
but it doesn't seem to prevent this error:
Warning: Attempt to present <App.SignInVC: 0x101f2f280> on <UITabBarController: 0x101e05880> which is already presenting <App.SignInVC: 0x101f4e4c0>
Your guard isn't a valid check. The isBeingPresented is being called on a brand new view controller instance that hasn't yet been presented. So isBeingPresented will always be false. Besides that, that property can only be used from within a view controller's view[Will|Did]Appear method.
What you want to check is to see if the tabBarController has already presented another view controller or not.
And lastly, only create and setup the sign-in view controller if it should be presented.
let appDelegate = UIKit.UIApplication.shared.delegate!
if let tabBarController = appDelegate.window?.rootViewController as? UITabBarController {
if tabBarController.presentedViewController == nil {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let signInVC = storyboard.instantiateViewController(withIdentifier: "SignInVC") as! SignInVC
signInVC.modalPresentationStyle = UIModalPresentationStyle.formSheet
tabBarController.present(signInVC, animated: true, completion: nil)
}
}

How to reopen application after login

I'm an android developer. in android, when user login in application, I will re-open the MainActivity class ( controller ) to refresh some views.
in iOS applications, how to do this scenario ?
You can reopen you default/LandingViewController.
Suppose you have a View Controller with name LandingViewController
When you successfully logged in all you need is to re instantiate the LandingViewController
In AppDelegate class make a function with name
func userDidLoggedIn(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)//Replace Main With your own storyboard name containing LandingViewController
let landingViewController = storyboard.instantiateViewController(withIdentifier: "LandingViewControllerIdentifier")//Pass your LandingViewController Identier that you have set in your storyboard file.
guard let subviews = self.window?.subviews else {
return
}
for view in subviews {
view.removeFromSuperview()
}
self.window?.rootViewController = landingViewController
}
Now Simply Call this Function where ever in the entire project like this In your case write below lines in the completion block of login request API.
let delegate = UIApplication.shared.delegate as! AppDelegate
delegate. userDidLoggedIn()
Once user login, you can change your rootviewcontroller like this:
var nav_VC: UIViewController?
func onSuccessfulLogin()
{
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
nav_VC = nil
if nav_VC == nil {
nav_VC = storyboard.instantiateViewController(withIdentifier: "home_nav")
}
self.window?.rootViewController = nav_VC
self.window?.makeKeyAndVisible()
}

How to show my cocoa touch framework storyboard screen?

I created a simple Cocoa touch framework with a storyboard. In my framework i have a MainViewController.swift viewcontroller.
I created a new single view project, imported my framework and tried to load my framework viewcontroller, but i got black screen. And I dont know why.
I tried load framework with this code:
let frameworkScreen : UIViewController = MainViewController()
self.presentViewController(frameworkScreen, animated: true, completion: nil)
You need to load the view controller by instantiating it from the storyboard in the framework.
Here's how. First some initial conditions:
Let's say your framework is called Coolness.
Let's say your framework's storyboard is called CoolnessStoryboard.storyboard.
Let's say your framework has a public class called CoolnessViewController.
Let's say that CoolnessStoryboard has a scene whose view controller is CoolnessViewController and that this is its initial view controller.
Then in your main code you would import Coolness and, to present your CoolnessViewController from the storyboard, you would say:
let s = UIStoryboard (
name: "CoolnessStoryboard", bundle: NSBundle(forClass: CoolnessViewController.self)
)
let vc = s.instantiateInitialViewController() as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
Note the strategy here. Work backwards from the goal. We need the instance of CoolnessViewController that is in the storyboard. To get that, we need a reference to that storyboard. To do that, we need to identify that storyboard. How? We can identify it by name and by the bundle that it is in. But how can we identify that bundle? We can identify the bundle by means of a class in that bundle. We have such a class, because we have imported the framework (import Coolness) and the class there is public (so we can speak of it).
I had the same problem. This is how I solved after a little research:
1 - I have a framework called "Messenger.framework"
2 - Inside it, I have a storyboard file called "Messenger.Storyboard"
3- My initial view controller is a UINavigationController with a rootViewController and my ChatController.
So this is how a present my framework's chat UIViewController:
- (void)presentChatController
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Messenger"
bundle:[NSBundle bundleForClass:ChatController.class]];
ChatController *controller = [storyboard instantiateViewControllerWithIdentifier:#"Chat"];
[self.navigationController pushViewController:controller animated:YES];
}
I hope this helps you.
If your framework name is MyFramework, and storyboard name is Main, with a viewcontoller with storyboard id vc
if let urlString = NSBundle.mainBundle().pathForResource("MyFramework", ofType: "framework", inDirectory: "Frameworks") {
let bundle = (NSBundle(URL: NSURL(fileURLWithPath: urlString)))
let sb = UIStoryboard(name: "Main", bundle: bundle)
let vc = sb.instantiateViewControllerWithIdentifier("vc")
self.showViewController(vc, sender: nil)
}
I have inculded the framework using cocoapods.
In Swift 5.5, Xcode 13 and iOS 15
if let urlString = Bundle.main.path(forResource: "MyFramework", ofType: "framework", inDirectory: "Frameworks") {
let bundle = (Bundle(url: NSURL(fileURLWithPath: urlString) as URL))
let sb = UIStoryboard(name: "Main", bundle: bundle)
let vc = sb.instantiateViewController(withIdentifier: "vc")
self.show(vc, sender: nil)
}

Resources