Swift 3 '[UIApplicationLaunchOptionsKey : Any]?' is not convertible to '[String : NSString]' - ios

I have a TVOS app that has been converted form Swift 2 to Swift 3 and I am getting the following error. I am unsure how to silence it.
'[UIApplicationLaunchOptionsKey : Any]?' is not convertible to '[String : NSString]'
It is showing up in this piece of code
appControllerContext.launchOptions["BASEURL"] = AppDelegate.TVBaseURL
if let launchOptions = launchOptions as? [String: AnyObject] {
for (kind, value) in launchOptions {
appControllerContext.launchOptions[kind] = value
}
}
ADDED:
/*
Copyright (C) 2015 Hani Hamrouni. All Rights Reserved.
*/
import UIKit
import TVMLKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, TVApplicationControllerDelegate {
// MARK: Properties
var window: UIWindow?
var appController: TVApplicationController?
//change the link to your host url
static let TVBaseURL = "http://google.com"
static let TVBootURL = "\(AppDelegate.TVBaseURL)js/application.js"
// MARK: UIApplication Overrides
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
/*
Create the TVApplicationControllerContext for this application
and set the properties that will be passed to the `App.onLaunch` function
in JavaScript.
*/
let appControllerContext = TVApplicationControllerContext()
/*
The JavaScript URL is used to create the JavaScript context for your
TVMLKit application. Although it is possible to separate your JavaScript
into separate files, to help reduce the launch time of your application
we recommend creating minified and compressed version of this resource.
This will allow for the resource to be retrieved and UI presented to
the user quickly.
*/
if let javaScriptURL = URL(string: AppDelegate.TVBootURL) {
appControllerContext.javaScriptApplicationURL = javaScriptURL
}
appControllerContext.launchOptions["BASEURL"] = AppDelegate.TVBaseURL
if let launchOptions = launchOptions {
for (kind, value) in launchOptions {
appControllerContext.launchOptions[kind.rawValue] = value as AnyObject
}
}
appController = TVApplicationController(context: appControllerContext, window: window, delegate: self)
return true
}
// MARK: TVApplicationControllerDelegate
func appController(_ appController: TVApplicationController, didFinishLaunching options: [String: Any]?) {
print("\(#function) invoked with options: \(options)")
}
func appController(_ appController: TVApplicationController, didFail error: Error) {
print("\(#function) invoked with error: \(error)")
let title = "Error Launching Application"
//error message
let message = error.localizedDescription
let alertController = UIAlertController(title: title, message: message, preferredStyle:.alert )
self.appController?.navigationController.present(alertController, animated: true, completion: { () -> Void in
// ...
})
}
func appController(_ appController: TVApplicationController, didStop options: [String: Any]?) {
print("\(#function) invoked with options: \(options)")
}
}

You'd better work with [UIApplicationLaunchOptionsKey : Any] as it is.
How is this?
if let launchOptions = launchOptions {
for (kind, value) in launchOptions {
appControllerContext.launchOptions[kind.rawValue] = value
}
}
UPDATED
Seems the type of the property launchOptions of TVApplicationControllerContext is [String: Any], so you have no need to cast with as AnyObject.

try this code please and tell me what happen.
appControllerContext.launchOptions["BASEURL"] = AppDelegate.TVBaseURL
if let launchOptions = launchOptions as? [String: Any] {
for (kind, value) in launchOptions {
appControllerContext.launchOptions[kind] = value
}
}

Related

Print DataBase in swift [duplicate]

This question already has answers here:
Where to configure Firebase in my iOS app in the new SwiftUI App life cycle without AppDelegate and SceneDelegate?
(2 answers)
Closed 1 year ago.
I made the front end of an application but now I have to make the back end and therefore print a DB the problem is that I have an error "Failed to get FirebaseApp instance. Please call FirebaseApp.configure() before using Firestore" :
App delegate :
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
}
DataBase :
struct New: Identifiable {
var id: String = UUID().uuidString
var news: String
}
class NewsViewModel: ObservableObject {
#Published var news = [New]()
private var db = Firestore.firestore()
func fetchDate() {
db.collection("News").addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No documents")
return
}
self.news = documents.map { (QueryDocumentSnapshot) -> New in
let data = QueryDocumentSnapshot.data()
let news = data["News"] as? String ?? "ya r"
return New(news: news)
}
}
}
}
Print DB :
NavigationView {
List(viewModel.news) { news in
VStack(alignment: .leading) {
Text(news.news)
}
.navigationTitle("News")
}
}
.onAppear() {
self.viewModel.fetchDate()
}
Thanks for help
Can you add it inside willFinishLaunchingWithOptions
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}

Why Firebase Remote Config doesn't update values?

I'm using Firebase Remote Config, I have some troubles to update values.
My values are updated only if I close and relaunch the app.
But never if my app enters in foreground.
The developer is activated, with no cache delay.
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
let _ = RCFirebaseValues.sharedInstance
}
}
My Firebase Remote Config class:
enum ValueKey: String {
case force_update
}
class RCFirebaseValues {
static let sharedInstance = RCFirebaseValues()
var loadingDoneCallback: (() -> ())?
var fetchComplete: Bool = false
private init() {
loadDefaultValues()
fetchCloudValues()
}
func loadDefaultValues() {
RemoteConfig.remoteConfig().setDefaults(fromPlist: "RemoteConfigDefaults")
}
func fetchCloudValues() {
#if DEBUG
let expirationDuration: TimeInterval = 0
RemoteConfig.remoteConfig().configSettings = RemoteConfigSettings(developerModeEnabled: true)
#else
let expirationDuration: TimeInterval = 3600
#endif
RemoteConfig.remoteConfig().fetch(withExpirationDuration: expirationDuration) {
[weak self] (status, error) in
guard error == nil else {
DLog(message:"Uh-oh. Got an error fetching remote values \(String(describing: error))")
return
}
RemoteConfig.remoteConfig().activateFetched()
self?.fetchComplete = true
self?.loadingDoneCallback?()
}
}
func bool(forKey key: ValueKey) -> Bool {
return RemoteConfig.remoteConfig()[key.rawValue].boolValue
}
func string(forKey key: ValueKey) -> String {
return RemoteConfig.remoteConfig()[key.rawValue].stringValue ?? ""
}
func double(forKey key: ValueKey) -> Double {
if let numberValue = RemoteConfig.remoteConfig()[key.rawValue].numberValue {
return numberValue.doubleValue
} else {
return 0.0
}
}
}
What's wrong?
EDIT after Mosbah's response:
class AppDelegate: UIResponder, UIApplicationDelegate {
var remoteConfig:RCFirebaseValues!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
self. remoteConfig = RCFirebaseValues.sharedInstance
}
}
Your RCFirebaseValues scope is wrong, it will be nil as soon as you are out of application: didFinishLaunchingWithOptions: so you should keep a strong reference to your object (create a var on AppDelegate).

How to get Notificationcenter data from didFinishLaunchingWithOptions on swift 4

I'm working on an app that receives data from a remote notification, I'm trying to pass that data from didFinishLaunchingWithOptions to my ViewController using Notificationcenter when opening the app through tapping on the notification using launchOptions. The problem is that the observer on my viewDidAppear is not getting any data.
This is my code on the didFinishLaunchingWithOptions method:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let remoteNotification = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {
let nameSchool = remoteNotification["name_school" as! String]
NotificationCenter.default.post(name: Notification.Name.nameSchool, object: nameSchool)
}
}
And the observer in the viewDidAppear method:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NotificationCenter.default.addObserver(forName: Notification.Name.nameSchool, object: nil, queue: OperationQueue.main) { (nameSchool) in
let schoolName = nameSchool.object as! String
self.messagePopup(message: "Data received")
}
}
Since your application(,didFinishLaunchingWithOptions:) will be called before the viewDidAppear (as per your comment), then you will have to temporarily store the result you get from the function until your code can retrieve it later.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var remoteNotificationAtLaunch: [AnyHashable: Any]?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.remoteNotificationAtLaunch = launchOptions?[.remoteNotification] as? [AnyHashable : Any]
}
...
}
Obviously keep the part you already have in your AppDelegate that generates the post to NotificationCenter when a remote notification is received. Then in your view controller, update your viewDidAppear...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
observeNotifications()
checkForNotificationAtLaunch()
}
private func observeNotifications() {
NotificationCenter.default.addObserver(forName: Notification.Name.nameSchool, object: nil, queue: OperationQueue.main) { (nameSchool) in
let schoolName = nameSchool.object as! String
self.processNotification(schoolName: schoolName)
}
}
private func checkForNotificationAtLaunch() {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
if let notificationAtLaunch = appDelegate.remoteNotificationAtLaunch,
let schoolName = notificationAtLaunch["name_school"] as? String {
processNotification(schoolName: schoolName)
}
}
}
private func processNotification(schoolName: String) {
self.messagePopup(message: "data received")
// do something with schoolName....
}

How to handle launch options in Swift 3 when a notification is tapped? Getting syntax problems

I am trying to handle the launch option and open a specific view controller upon tapping a remote notification that I receive in swift 3. I have seen similar question, for instance here, but nothing for the new swift 3 implementation. I saw a similar question (and ) In AppDelegate.swift I have the following in didFinishLaunchingWithOptions:
var localNotif = (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] as! String)
if localNotif {
var itemName = (localNotif.userInfo!["aps"] as! String)
print("Custom: \(itemName)")
}
else {
print("//////////////////////////")
}
but Xcode is giving me this error:
Type '[NSObject: AnyObject]?' has no subscript members
I also tried this:
if let launchOptions = launchOptions {
var notificationPayload: NSDictionary = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] as NSDictionary!
}
and I get this error:
error: ambiguous reference to member 'subscript'
I got similar errors wherever I had previously used similar code to get a value from a dictionary by the key and I had to replace the codes and basically safely unwrap the dictionary first. But that doesn't seem to work here. Any help would be appreciated. Thanks.
Apple made plenty of changes in Swift 3 and this one of them.
Edit: This works for Swift 4 as well.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//Launched from push notification
let remoteNotif = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? [String: Any]
if remoteNotif != nil {
let aps = remoteNotif!["aps"] as? [String:AnyObject]
NSLog("\n Custom: \(String(describing: aps))")
}
else {
NSLog("//////////////////////////Normal launch")
}
}
Swift 5:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//Launched from push notification
guard let options = launchOptions,
let remoteNotif = options[UIApplication.LaunchOptionsKey.remoteNotification] as? [String: Any]
else {
return
}
let aps = remoteNotif["aps"] as? [String: Any]
NSLog("\n Custom: \(String(describing: aps))")
handleRemoteNotification(remoteNotif)
}
And for more on LaunchOptionsKey read Apple's documentation.
So it turned out the whole method signature has changed and when I implemented the new signature things worked just fine. Below is the code.
new didFinishLaunchingWithOptions method:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
//and then
if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
// Do what you want to happen when a remote notification is tapped.
}
}
Hope this helps.
Swift 4
// Check if launched from the remote notification and application is close
if let remoteNotification = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {
// Do what you want to happen when a remote notification is tapped.
let aps = remoteNotification["aps" as String] as? [String:AnyObject]
let apsString = String(describing: aps)
debugPrint("\n last incoming aps: \(apsString)")
}
swift 3:
if let notification = launchOptions?[.localNotification] as? NSDictionary{
#if DEBUG
print("iOS9 didFinishLaunchingWithOptions notification\n \(notification)")
#endif
if let remoteNotif = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? [String: Any] {
if let notification = remoteNotif["aps"] as? [AnyHashable : Any] {
//handle PN
}
}

Is this how I should be opening push notifications?

When the notification is clicked on I want it to open a specific page, but for some reason it keeps crashing with the error fatal error: unexpectedly found nil while unwrapping an Optional value. Why is this error happening? Am I opening the page correctly?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let launchOptions = launchOptions as? [String : AnyObject] {
if let notificationDictionary = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] as? [NSObject : AnyObject] {
self.application(application, didReceiveRemoteNotification: notificationDictionary)
let text = launchOptions["aps"]!["alert"]
print(text)
let viewController = storyBoard.instantiateViewControllerWithIdentifier("Player") as UIViewController
self.window?.rootViewController = viewController
}
}
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
PFPush.handlePush(userInfo)
let text = userInfo["aps"]!["alert"]
print(text)
let viewController = storyBoard.instantiateViewControllerWithIdentifier("Player") as UIViewController
self.window?.rootViewController = viewController
}
Hope this will help :
Find visible view controller when receive Notification.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let currentViewControlelr :UIViewController = topViewController(UIApplication.sharedApplication().keyWindow?.rootViewController)!;
if(currentViewController == PlayerViewController()){
//Implement other function according to your needs
}else{
//If any other view controller at top of stack, then create object of Player View controller and push to that
let playerViewController = storyBoard.instantiateViewControllerWithIdentifier("Player") as UIViewController
currentViewControlelr.navigationController?.pushViewController(playerViewController, animated: true)
NSLog("UserInfo : %#",userInfo);
}
Helper Method to get Top ViewController which is visible at the moment
func topViewController(base: UIViewController? ) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}

Resources