Running code when app is terminated. Xcode 12 - ios

I'm trying to run some code to refresh data and serve a local notification on iOS 14. I'm using Xcode 12 and all the help I find online is based on a structure having an appdelegate.swift swift file with several functions depending on the state of the app.
In Xcode 12 the appdelegate file isn't there anymore. How do I proceed, I don't know where to start. Does anybody know what to do?

Your issue is probably that you set up the project to use SwiftUI. You need to create an AppDelegate object that implements UIApplicationDelegate protocol:
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print("Your code here")
return true
}
// Implement other methods that you require.
}
On your SwiftUI file, add this line to tell iOS who's gonna be the listener for the AppDelegate's methods:
#main
struct SomeApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
You can then follow by checking Apple's documentation on how to use background app modes.

Related

Firebase app check not working with iOS 16

It looks like for my app the firebase app check is not working with iOS16. I have configured the app attest and its been working for more than a year, it's still working with older iOS versions but not iOS 16.
This is the code that I initialise app check
import SwiftUI
import Firebase
import FirebaseAppCheck
#main
struct appointmeparterAppApp: App {
#Environment(\.scenePhase) var scenePhase
#UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
init() {
let providerFactory = YourAppCheckProviderFactory()
AppCheck.setAppCheckProviderFactory(providerFactory)
FirebaseApp.configure()
}
}
class YourAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
return AppAttestProvider(app: app)
}
}
I am sure the problem is app check because if it's not enforced everything works as expected, and if I enforce it then i get permissions error. (this happens only on iOS 16)
import SwiftUI
import Firebase
import FirebaseCore
import FirebaseAppCheck
class AppDelegate: NSObject, UIApplicationDelegate {
let providerFactory = ACFTAppCheckProviderFactory()
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
AppCheck.setAppCheckProviderFactory(providerFactory)
FirebaseApp.configure()
return true
}
}
#main
struct ACFTApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class ACFTAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
return AppAttestProvider(app: app)
}
}
Set the App attest env. to production. Ensure you add App Attest capability to your app target. Remove the -FIRDebugEnabled in your app scheme/run/arguments. Possibly ensure you add the debug token to your registered apps/manage tokens on FB.

Why Firebase Analytics first_open event is not sent automatically unless I first logEvent a dummy event explicitly?

I am following the setup https://www.raywenderlich.com/18579842-firebase-analytics-getting-started
I am using flag -FIRAnalyticsDebugEnabled
I view the real-time result in Firebase Analytics Debug View
I also check the console output of XCode.
However, I notice that, if I write my code in the following way
Not receiving any Firebase analytics event
import Firebase
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
return true
}
But, if I write the code in the following way
Receiving Firebase analytics first_open event
import Firebase
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
let title = "xxx"
Analytics.logEvent(AnalyticsEventSelectContent, parameters: [
AnalyticsParameterItemID: "id-\(title)",
AnalyticsParameterItemName: title,
AnalyticsParameterContentType: "cont",
])
return true
}
I need to logEvent a dummy event explicitly, in order to receive first_open.
May I know why is it so? Is there a way, I can still receive first_open event automatically, without having to log a dummy event?
This is the code snippet I am using, to ensure first_open event is sent to Firebase console.
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
FirebaseApp.configure()
// Not sure why. We need to call this function for first_open to be sent automatically.
// https://stackoverflow.com/questions/73600417/why-firebase-analytics-first-open-event-is-not-sent-automatically-unless-i-first
Analytics.setAnalyticsCollectionEnabled(true)
return true
}
I have verified by looking at Firebase console. The first_open event is received.
Still, I am not sure why such extra code snippet is required. I thought it should be sent automatically?
In my app I have admob ads and analytics. My assumption is that because of admob sdk, analytics stops working. This is certainly a bug.
Solution:
You will have to request iOS ATT alert or UMP consent from Google.
I prefer UMP sdk because it solves ATT alert and GDPR.
UMP sdk: https://developers.google.com/admob/ump/ios/quick-start
How it works: https://support.google.com/admob/answer/10115027
#main
struct MyApp: App {
init() {
FirebaseApp.configure()
Analytics.setAnalyticsCollectionEnabled(true) //this is explicitly needed
showConsent()
}
var body: some Scene {
WindowGroup {
RouterView()
}
}
private func showConsent() {
//request UMP consent or IDFA consent
}

xmppStreamDidConnect never gets called in Swift

I am trying to connect to my chat server in a Swift app using XMPPFramework, but the didConnect delegate method never gets called.
I have created a basic app in Objective C and I can connect and authenticate in my chat sever without problems.
In the Swift project I tried to connect with the code:
class AppDelegate: UIResponder, UIApplicationDelegate {
var stream:XMPPStream = XMPPStream()
var reconnect:XMPPReconnect = XMPPReconnect()
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
stream.addDelegate(self, delegateQueue: DispatchQueue.main)
stream.myJID = XMPPJID(string: "user#chatserver.net")
reconnect.activate(stream)
do {
try stream.connect(withTimeout: XMPPStreamTimeoutNone)
}
catch let err{
print("error occured in connecting\(String(describing: err.localizedDescription))")
}
return true
}
I’ve debugged XMPPFramework and in the method - (void)handleStreamFeatures a call to the delegate is executed :
[multicastDelegate xmppStreamDidConnect:self];
I’ve watched the multicastDelegateObject and has a node with reference to my delegate, and to OS_dispatch_queue_main, but after execution my xmppStreamDidConnect method isn’t executed.
As it is described in this Github Issue, the problem was the method declaration.
My xmppStreamDidConnect need an underscore, the root problem was that if you don't import the swift extensions the compiler marks that declaration as incorrect, although it works.
So to fix my problem, I need to import the pod 'XMPPFramework/Swift' and change the method declaration to
func xmppStreamDidConnect(_ sender: XMPPStream) {

The app delegate must implement the window property if it wants to use a main storyboard file swift

I have just developed an app, but when running in the simulator the debugger console says:
The app delegate must implement the window property if it wants to use
a main storyboard file.
I have an app delegate file. What does the message mean, and how can I get my app working?
Make sure you have the following property declaration in your AppDelegate class:
var window: UIWindow?
If you run your project on earlier than iOS 13.0, in that case you will face the problem. Because of iOS 13 and later, app launch differently than earlier versions.
In iOS 13 and later, use UISceneDelegate objects to respond to life-cycle events in a scene-based app
In iOS 12 and earlier, use the UIApplicationDelegate object to respond to life-cycle events.
When you launch the app in iOS 12 and earlier then UIApplicationMain class expect a window property in your AppDelegate class as like SceneDelegate has. So your problem will be solved if you add the following line in your AppDelegate class.
var window: UIWindow?
For Objective-C
#property (strong, nonatomic) UIWindow *window;
You can find more here App's Life Cycle.
Just in case anyone comes across this again and is programming in Objective-C make sure you have this line of code in your AppDelegate.h file:
#property (strong, nonatomic) UIWindow *window;
I have received this error, when I created new project in XCode 11. I have not used SwiftUI. Here are the steps, I have considered to fix this.
Deleted Application Scene Manifest entry from Info.plist
Deleted SceneDelegate.swift file
Deleted all scene related methods in AppDelegate.swift class
added var window: UIWindow? property in AppDelegate.swift class
After these steps, I am able to run the app on version prior to iOS 13.
[EDIT]
Finally, your AppDelegate.swift file should look something like the following.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
}
Add the following window declaration in Appdelegate file
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window:UIWindow?
...
Implementation of this property is required if your app’s Info.plist file contains the UIMainStoryboardFile key.
The default value of this synthesized property is nil, which causes the app to create a generic UIWindow object and assign it to the property. If you want to provide a custom window for your app, you must implement the getter method of this property and use it to create and return your custom window.
I had the same issue, just add var window: UIWindow? as the debug error says.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
You can check your app delegate class:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
#available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
#available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
Error: The app delegate must implement the window property if it wants to use a main storyboard file
Swift 5 & Xcode 11
Make sure that SceneDelegate contains UIWindow property
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
//...
}
Setting in Info.plist
Application Scene Manifest > Enable Mutliple Windows > false.
This solved the problem for me.
Long ago answered, but to help understand the questions above about why simply adding the window property solves the problem, note that the app delegate conforms to the UIApplicationDelegate protocol which defines a property, #property (nullable, nonatomic, strong) UIWindow *window; that classes need to provide to specify the window to use when presenting a storyboard. Failure to provide that is causing the Xcode log warnings.
For Swift:
var window: UIWindow?
For Objective-C:
#property (strong, nonatomic) UIWindow *window;
In addition, you might have an Application Scene Manifest entry in your Info.plist file - if you do not use scenes, but only a window and view controllers (eg because you want to test something in the old UI setup), you should remove that entry to be able to see your views.

How to create an empty ios project in xcode 6?

i'm using xcode 6 and they no longer have the "empty project" option when creating a new project, only a single view one...Is there an option to get it or it's gone and I need to deal with it?
Thanks a bunch
You need to deal with it, but it's easy. Start with the Single View application. Delete the storyboard and delete the reference to it in the Info.plist (so that there is no longer a main storyboard). If you like, delete the view controller class as well. Now just do everything from scratch in the app delegate's application:didFinishLaunchingWithOption:, just as you did in Xcode 5.
I'm using Swift these days, so I'll show you what a pure code app delegate launch looks like in Swift; I'm sure you can translate into Objective-C:
import UIKit
#UIApplicationMain
class AppDelegate : UIResponder, UIApplicationDelegate {
var window : UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow(frame:UIScreen.mainScreen().bounds)
self.window!.rootViewController = ViewController() // or whatever you call it
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
return true
}
}

Resources