Swift Code in App Delegate to Load Storyboard Causes Crash - ios

I have an universal app I am writing and I initially wrote it using two storyboard files. I had code in the App Delegate didFinishLaunchingWithOptions routine to decide which storyboard to load and away it would go. I have since realised that that was a silly thing to do because I had duplicate code, so I have deleted one storyboard and made the other universal. I have fixed up the VCs to point at the right class and everything. However, my app now refuses to launch. When I run it in the sim, it gives me the error
The app delegate must implement the window property if it wants to use a main storyboard file.
.
Here is my code in App Delegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
var deviceIdiom = UIDevice.currentDevice().userInterfaceIdiom
if deviceIdiom == UIUserInterfaceIdiom.Phone {
strDevice = "iPhone"
} else if deviceIdiom == UIUserInterfaceIdiom.Pad {
strDevice = "iPad"
} else {
strDevice = "Unknown"
}
return true
}
What am I doing wrong?
(Questions I've already looked at but didn't help):
Unique Issue displaying first storyboard scene in Xcode
The app delegate must implement the window property if it wants to use a main storyboard file
Managing two storyboards in App Delegate
How to manually set which storyboard view to show in app delegate
Swift - Load storyboard programatically

Your app delegate needs to start like such:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
//...
}

Related

How to kick user back on homepage without using Navigations in IOS app

Background- I have an Ios Native app for which i am automating test scripts in XCUITest. The current test case flow in a class is as setup device,install app open app-> login ->perform action
for the second test case method it logs out first from the first test case action using navigations and starts
Is there any way that after end of each test case method run it directly jumps on the homepage without using navigations? Navigations takes extra time for large suite to run.
Swift 4.2
I think don't understand your question but if you want jump to homepage without navigation, you could use this line of code:
UIApplication.shared.keyWindow?.rootViewController = HomeViewController()
and if you don't want to jump up with little animation, use this:
DispatchQueue.main.async {
guard let window = UIApplication.shared.keyWindow else { return }
UIView.transition(with: window, duration: 0.5, options: .allowUserInteraction, animations: {
UIApplication.shared.keyWindow?.rootViewController = HomeViewController()
}, completion: { completed in
})
}
If you want to achieve faster navigation for your UI tests, you can use the benefit of launching app with launch arguments.
Edit Scheme > Run > add "UITEST" to Arguments Passed On Launch
For example in your codes define
class AppDelegate: UIResponder, UIApplicationDelegate {
var uiTestModeKey = "UITEST"
static var uiTestMode = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
if (CommandLine.arguments.contains(uiTestModeKey)) {
AppDelegate.uiTestMode = true
}
return true
}
}
and in the part you want to navigate back quickly you could define something like:
func onActionComplete() {
if (AppDelegate.uiTestMode) {
navigationController?.popToRootViewController(animated: false)
//Or set the window to a new instance like
//UIApplication.shared.keyWindow?.rootViewController = HomeViewController()
}
}

AVPlayer View Controller Autorotation off?

I'm working on an iOS app in portrait mode only, and it's working fine.
Issue is that I'm presenting AVPlayerViewController above navigationController (both created programmatically). This player supports all the rotations (I don't know why!).
I want to fix this to portrait mode only just like other sections of app.
How can we do that?
Create new class that inherits from AVPlayerViewController and implement the supported interface method like this. After this instead of initializing object of AVPlayerViewController just create object of the class that you have created.
class MyPortraitVideoController: AVPlayerViewController {
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
Edited to add:
Note that the Documentation for AVPlayerViewController states:
Important Do not subclass AVPlayerViewController. Overriding this
class’s methods is unsupported and results in undefined behavior.
As this is the accepted answer, I assume the OP has tried it and it worked, but I thought I should add a note about it here to make sure future readers are aware of any potential problems. Undefined behavior means this could stop working in a future release of iOS.
In my case, I had to restricted landscape orientation for some views.So I set supportedInterfaceOrientationsFor as portrait in AppDelegate. When I present AVPlayerViewController I had to set supportedInterfaceOrientationsFor as all in AppDelegate.
Check below codes as well.
AppDelegate.swift
var shouldSupportLandscape = Bool()
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if self.shouldSupportLandscape == true {
return .all
}else {
return .portrait
}
}
When present AVPlayerViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.shouldSupportLandscape = true
When dismiss AVPlayerViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.shouldSupportLandscape = false

Firebase observer during all the app life

I'm trying to create a firebase observer that remains alive during all the app life. What I want is to change a property of my tabBarController when some data change in firebase. Here's my code:
self.ref.child("mySubRef").observe(.value , with: {snapshot in
self.tabBarController?.tabBar.items?[3].badgeValue = "!"
})
So, I've tried creating it in the viewDidLoad of my first viewController and also in the viewDidAppear. I don't remove it since I want it to be there always. In the viewDidAppear it works only if I'm in that viewController at the moment of the change. If I want that change to happen no matter where I am (always inside the tabBar) where do I have to put that code?
Thanks for the help!
I have found the answer. The problem was that when I changed between viewControllers the reference to the observer was deallocated. So, to fix it, I have created a class like this:
class NotificationListener: NSObject {
let ref:FIRDatabaseReference = FIRDatabase.database().reference()
var user:User?
func setUpListener(tabBarController:UITabBarController){
self.user = User()
self.ref.child("users/" + self.user!.uid + "/notifications").observe(.value , with: {snapshot in
tabBarController.tabBar.items?[3].badgeValue = "!"
})
}
}
Now I have a property of that class in every viewController and every one has a reference to the same object. When I change between VC it will not deallocate the object because it will still be referenced.
I think, you can use Appdelegate didFinishLaunchingWithOptions method but I'm not sure.
Like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
FIRDatabase.database().reference().child("mySubRef").observe(.value, with: { (snapshot) in
//I'm not sure for this part.
UITabBarController.init().tabBar.items?[3].badgeValue = "!"
})
return true
}

Loading Initial View from Framework

Is it possible to create an application that loads its initial view from a storyboard in a framework?
Let's say I have a framework, titled MyCoolFramework. This framework has a storyboard called Home.storyboard that is the initial view of an application.
Is there a way to load it into the application at launch?
My AppDelegate looks like this:
import UIKit
import MyCoolFramework
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let bundle = NSBundle(identifier: "com.myprojects.MyCoolFramework")
let homeStoryboard = UIStoryboard(name: "Home", bundle: bundle)
let initialView = homeStoryboard.instantiateInitialViewController()
self.window?.rootViewController = initialView
self.window?.makeKeyAndVisible()
return true
}
}
It seems that at this point in the application the application does not have access to the framework or its contents. Anyone know of the proper way of doing this? This is the companion test-app for a framework I'm developing.
I have deleted the main storyboard directive in the info.plist
I think its likely to be the path to your framework isn't correct.
You can try this (sorry Objective-C I'm sure you can convert to Swift)
[NSBundle bundleForClass: a name of your class in the framework]
Or if that doesn't work, add a method to your framework which returns the path of its own bundle and the code above as the implementation of that method.
NSBundle:mainBundle and NSBundle:bundleForClass will return different paths when called from within a framework (though if called from a library they will be the same). So experiment until you get the correct path for your storyboard.

Exception on API Key of Google Maps API for iOS

I am developing an iOS app using Google Maps API for IOS. And I installed the CocoaPod for my project and configure them according to tutorial on Google Developer. However, when I run my project, it says
*** Terminating app due to uncaught exception 'GMSServicesException', reason: 'Google Maps SDK for iOS must > be initialized via [GMSServices provideAPIKey:...] prior to use'
But I already call "GMSServices.provideAPIKey on the AppDelegate.swift. Following is the code:
....
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
GMSServices.provideAPIKey("***********************")
return true
}
....
(**************) is my API Key.
And because Google Maps API use Objective C, so I created a Bridging Header to import the library.
I tried to set breakpoint on [application:didFinishLaunchingWithOption]. But it will raise exception before run that function, which I think is very weird.
So confused about it. Thanks in advance.
Problem finally solved, the reason is that I initialize a fields using Google Maps library in the one model class and it will be created before the app run. So this error happens. When I moved this variable into the method, problem solved. Following is the code that causes error:
class PlaceManager {
let placeClient = GMSPlacesClient()
...
func getSuggestions(queryString:String) -> [String]{
...
}
}
After
class PlaceManager {
func getSuggestions(queryString:String) -> [String]{
let placeClient = GMSPlacesClient()
...
}
}
Instead of didFinishLaunchingWithOptions, move the call to willFinishLaunchingWithOptions. This method is called after state restoration has occurred but before your app’s window and other UI have been presented. (Which in your case might be a UI that consumes Google Map API)
func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
GMSServices.provideAPIKey("***********************")
return true
}
I just had the same problem. I created a GMSMapView object and it was initialized before maybe the api key could be read. So I moved it inside the viewDidLoad method and problem solved.
Before :
class ViewController: ..... {
let mapView = GMSMapView()
After :
class ViewController: ..... {
var mapView : GMSMapView?
override viewDidLoad(){
mapView = GMSMapView()

Resources