Use of undeclared type 'AppDelegate' Swift - ios

I've just spent half a day trying to solve next problem.
I am testing the CoreData using Swift language.
Follow this tutorial everything works fine.
But after titorial I've tried to modify the structure and my code. The 'src' and groups inside it is folders, not just groups created by xCode.
NSSExpense.swift
import Foundation
import CoreData
class NSSExpense: NSManagedObject {
#NSManaged var name: String
#NSManaged var descr: String
#NSManaged var value: NSNumber
#NSManaged var isMonthly: NSNumber
#NSManaged var payDayInMonth: NSNumber
class func createInManagedObjectContext(moc: NSManagedObjectContext, name: String, value: Double, payDayInMonth: Int16, isMonthly: Bool, descr: String!) -> NSSExpense {
let newExpense = NSEntityDescription.insertNewObjectForEntityForName("NSSExpense", inManagedObjectContext: moc) as NSSExpense
newExpense.name = name
newExpense.value = NSNumber(double: value)
newExpense.payDayInMonth = NSNumber(short: payDayInMonth)
newExpense.isMonthly = NSNumber(bool: isMonthly)
if let expenseDesctiption = descr {
newExpense.descr = expenseDesctiption
} else {
newExpense.descr = ""
}
return newExpense
}
}
NSSDataManager.swift
import UIKit
import CoreData
class NSSDataManager: NSObject {
class var sharedDataManager: NSSDataManager {
struct Static {
static var instance: NSSDataManager?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = NSSDataManager()
}
return Static.instance!
}
lazy var managedObjectContext : NSManagedObjectContext? = {
// Error at the next line "Use of undeclared type 'NSSAppDelegate'"
let appDelegate = UIApplication.sharedApplication().delegate as NSSAppDelegate
if let managedObjectContext = appDelegate.managedObjectContext {
return managedObjectContext
} else {
return nil
}
}()
var expensesInMemory : [NSSExpense] {
get {
let fetchRequest = NSFetchRequest(entityName: "NSSExpense")
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [NSSExpense] {
return fetchResults
} else {
return [NSSExpense]()
}
}
}
func addExpenseWithName(name: String, value: Double, payDayInMonth: Int16, isMonthly: Bool, descr: String!) -> NSSExpense {
return NSSExpense.createInManagedObjectContext(managedObjectContext!, name: name, value: value, payDayInMonth: payDayInMonth, isMonthly: isMonthly, descr: descr?)
}
}
I've tried to solve this problem different ways:
1) Create new project (Swift main language) and make the same structure again (failed)
2) Create new project (Objective-C main language). So I have the AppDelegate.h and AppDelegate.m. Add it to Swift files using Bridging-Header. The same problem. (failed)
Really interesting next thing. If I put next code to the ViewController.swift which creates automatically with new project everything works fine. But when I put this code to any other class. I've code this error.
lazy var managedObjectContext : NSManagedObjectContext? = {
// Error at the next line "Use of undeclared type 'NSSAppDelegate'"
let appDelegate = UIApplication.sharedApplication().delegate as NSSAppDelegate
if let managedObjectContext = appDelegate.managedObjectContext {
return managedObjectContext
} else {
return nil
}
}()
[UPDATE 1]
I've tried to create another class right at the same folder as NSSAppDelegate.swift and now everything works fine. However it's still an issue, how can I use the classes which stored in other folders?
[UPDATE 2]
Just tried to do the same thing in other project.
If the file structure is like this so the AppDelegate.swift and NGDataManager.swift are in the same folder everything works great.
BUT, if I put the NGDataManager.swift inside the 'src' folder like this (not just group, folder) the error occurs. May be I should create the other question for this.
[UPDATE 3]
I don't know how, but you can forget all I've said in UDATE 2. Because Now thats all not work. I even create a new project with CoreData named "Test" and just create a new class named "TestClass". The magic is in the next thing: if I put this code inside the TestClass.swift
import UIKit
class TestClass: NSObject {
func someFunc() {
let appDel = UIApplication.sharedApplication().delegate as AppDelegate
}
}
the error occurs. But if I put this line in viewDidLoad in ViewController.swift which was generated automatically by xCode
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let appDel = UIApplication.sharedApplication().delegate as AppDelegate
}
}
their is no error and everything works great. I don't know what to say...
You may see the AppDelegate code here but I didn't modify anything generated automatically by xCode. I've created a Single View Application with this settings.

Chances are when you created your project you also created a '{ProjectName}Tests' target. The problem is AppDelegate is not assigned membership in the '{ProjectName}Tests' target.
Select AppDelegate.swift then in the right-hand inspector click on the File Inspector (the paper icon) then make sure in the "Target Membership" both your project and the test target checkmarks are set to ON.
Clean, rebuild.

If trying to access an Objective-C AppDelegate in Swift code, make sure you have #import "AppDelegate.h" in your bridging header file.
This had me stuck for about an hour :-/

I had a similar problem that was resolved when I did a "Clean" option in the Product menu

Ran into this issue in Xcode 8 and this was the first result when I searched on google so adding my solution.
If your project has UI and Unit Tests, make sure your AppDelegate is added to both target memberships. Once I added it to my Tests I was able to access any variables I had in my AppDelegate without having to use UIApplication.shared regardless of how my files were grouped.
To check, go to your AppDelegate, click on the file inspector, and look at Target Membership.

Make sure the Target Memberships are set the same in each file. Target memberships can be found in the properties inspector when you click on a specific file.

It suppose to be like this.
let appDelegate: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)

Instead of:
let appDelegate = UIApplication.sharedApplication().delegate as NSSAppDelegate
try:
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate

Related

Today Widget unable to load with a specific line of code

I've added a Today extension to my app and it all works fine until a specific line of code is compiled. NB: compiled, not executed!
My TodayViewController is:
class StoredDoses {
func getDoses(doses: inout [Dose]) {
if let userD = UserDefaults(suiteName: "com.btv.mySuite") {
if let dosesData = userD.object(forKey: "doses_key") {
do {
// -----------------------------------------------
// Comment the line below out and the widget works
doses = try PropertyListDecoder().decode([Dose].self, from: dosesData as! Data)
// -----------------------------------------------
} catch {
print ("ERROR")
}
}
}
}
}
class TodayViewController: UIViewController, NCWidgetProviding {
#IBOutlet weak var aText: UILabel!
#IBOutlet weak var bText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view from its nib.
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
//Just for development stage - not real, final code
let form = DateFormatter()
form.timeStyle = .short
aText.text = form.string(from: Date())
completionHandler(NCUpdateResult.newData)
}
}
So, the above code isn't well written, but it's what I've used to finally narrow down the cause of the unloading widget. The array of Doses is a custom, codable class, but if I try to get an array of String then it's the same. The StoredDoses code is included in the main app and doesn't cause any problems.
Just to re-iterate: I'm not trying to execute any method in the StoredDoses class. I don't even have an instance of it in the widget. When the doses = ... line is merely commented out then the widget loads and the aText label in the widget appears with the current time in it.
Ok, so thanks to #Chris' apparently unconnected advise I got it sorted!
It appears to have been an Interface Builder issue: somehow it had retained the original name of the UILabel that was auto-created when I added the Today extension in Xcode. At some point, after connecting an IBOutlet to the label with "Hello World" in it, I'd renamed it to something slightly more relevant but hadn't unconnected it before over-typing the new name in the TodayViewController.
The console didn't throw up any problems and at times seemed to work, but when the line with
try PropertyListDecoder().decode([Dose].self, from: dosesData as! Data)
was present then it stopped working without any console messages.
I only found that out after I explored #Chris comment about the as! Data. I re-wrote to first get the Data:
if let userD = UserDefaults(suiteName: "com.btv.mySuite") {
if let dosesData = userD.object(forKey: "doses_key") {
if let unwrappedData = dosesData as? Data {
do {
doses = try PropertyListDecoder().decode([SplitDose].self, from: unwrappedData)
} catch {
doses.removeAll()
}
}
}
}
Once this was compiled (remember, it's still not being executed - this is just sitting there waiting to be used) the console threw up a message and the app crashed out showing the old UILabel name as not key-compliant. Reconnecting the UILabel in IB fixed everything and I could compile the original code....
This probably deserves a Radar entry but right now I don't want to waste another day re-creating (if at all possible) this problem!

Swift: avoid to force unwrap constant variable

I'm trying to avoid a force unwrap of global variables. How can I do that in this particular example:
let AppDelegate = UIApplication.shared.delegate as! AppDelegate
If you don't want to force-unwrap (which is fine here in this case), then use if let.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
// Do something with appDelegate
}
But the force-unwrap is fine for this. You want the app to crash during development if you make a typo on the type of the app delegate. It will never fail at runtime unless you make a change to the code and ship it to Apple without at least running your app once.
Made this an extension so no copy paste anymore. This is as type safe as it gets since it only makes sense for the AppDelegate to implement UIApplicationDelegate and the one is always reachable via UIApplication.shared.delegate.
#if os(macOS)
import Cocoa
typealias ApplicationDelegate = NSApplicationDelegate
typealias Application = NSApplication
#else
import UIKit
typealias ApplicationDelegate = UIApplicationDelegate
typealias Application = UIApplication
#endif
public extension ApplicationDelegate {
static var shared: Self {
return Application.shared.delegate as! Self
}
}
If you want to use the variable locally then:
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
//logic here
}
or
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
I don't think there is a way to use AppDelegate globally without using force casting.
Edited
To create a global variable, just declare it anywhere outside a class. For example:
var globalVariable = 1
class Person {
//logic here
}

How to update UITabBarController from a Helper class?

I have a help class like this:
class NotificationHelper {
var managedObjectContext: NSManagedObjectContext!
init(context: NSManagedObjectContext) {
//super.init()
managedObjectContext = context
}
//functions related to Core Data
//update Badge
func updateBadge() {
var count = 1
let currentCount = self.tabBarController?.tabBar.items?[3].badgeValue
if currentCount != nil {
count = Int(currentCount!)! + 1
}
self.tabBarController?.tabBar.items?[3].badgeValue = String(count)
}
}
I'm just not sure how to get a reference to tabBarController so I can update it. I tried making the class inherit from UIViewController, but I think I was going down the wrong path there.
Also, am I correct in passing managedObjectContext like this? So that this class will be able to access Core Data.
Solved.
Instead of trying to inherit from somewhere, I decided to pass the UITabBarController as a parameter when needed:
func updateTabBarBadge(tabBarController: UITabBarController) {
It just means I have to call updateTabBarBadge every time I want to update it, instead of having other functions update it for me.

Accessing sub assembly of an assembly will generate EXC_BAD_ACCESS

Use case:
I have an ApplicationAssembly. This assembly has a property called CoreAssembly of type TyphoonAssembly. I'm using PLIST integration and have registered both Assemblies. I'm injecting the ApplicationAssembly into the Appdelegate.
Afterwads in a ViewController, I want to get an object which will be generated by CoreAssembly
var appdelegate = UIApplication.sharedApplication().delegate as! AppDelegate;
tagHandler = appdelegate.assembly.coreAssembly.tagHandler() as! GoogleTagsHandler;
If I do this, I get an EXC_BAD_ACCESS
This seems to be a bug, we've logged it. In the meantime, as a workaround, please inject both ApplicationAssembly and CoreAssembly into your app delegate as follows:
public dynamic func appDelegate() -> AnyObject {
return TyphoonDefinition.withClass(AppDelegate.self) {
(definition) in
definition.injectProperty("assembly", with: self)
definition.injectProperty("coreAssembly", with: self)
}
}

Swift Passing data from appDelegate to another class

I need to pass a variable from the AppDelegate to another class that I have created to hold global variables of the project and I'm not able to find a way to make it work.
This is the code in the AppDelegate:
func application(application: UIApplication!, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData!) {
println("Device's token is: \(deviceToken)")
//Global Variables Class Instance
let globals:Globals = Globals()
globals.setDeviceToken("test1") //method1 not working
globals.deviceToken = "test2" //method2 not working
}
This is my Globals Class:
public class Globals {
var deviceToken = String()
init() {
//nothing
}
func setDeviceToken(s:String){
deviceToken = s
}
func getDeviceToken() -> String {
return deviceToken
}
}
If i try to print the value, from other files of the project, I'm not able to get anything, just an empty string.
class ViewController: UIViewController {
//Global Variables Class Instance
let globals:Globals = Globals()
override func viewDidLoad() {
println(globals.getDeviceToken()) //return empty string
println(globals.deviceToken) //return empty string
There are several patterns you can use to achieve what you want
You could access the AppDelegate through the UIApplication:
let delegate = UIApplication.sharedApplication().delegate as AppDelegate
let deviceToken = delegate.deviceToken
Look into singletons. A quick google search for 'Swift singleton' will get you a long way. The first result:
class SingletonB {
class var sharedInstance : SingletonB {
struct Static {
static let instance : SingletonB = SingletonB()
}
return Static.instance
}
}
Then use sharedInstance to instantiate the singleton anywhere and access the same variables.
The first one is quick and dirty, so for more serious projects I would recommend the singleton pattern.
There are probably a million ways to do this, but this should get you started
(More at this link, which explores a few ways to implement singletons: https://github.com/hpique/SwiftSingleton )
I simply solved my problem using NSUserDefaults
in the AppDelegate:
NSUserDefaults.standardUserDefaults().setObject(deviceToken, forKey: "deviceToken")
NSUserDefaults.standardUserDefaults().synchronize()
From other classes:
NSUserDefaults.standardUserDefaults().objectForKey("deviceToken")
Honestly I don't know if this is a good way to do it but it's working

Resources