在Swift中如何获取对AppDelegate的引用?

479

我如何在Swift中获得对AppDelegate的引用?

最终,我想使用该引用来访问托管对象上下文。


9
请注意,有些人认为将应用程序委托用作管理对象上下文或其他“全局”对象的容器是一种反模式。考虑让应用程序委托将MOC传递给控制器,而不是让控制器去查找它。 - Kristopher Johnson
1
@KristopherJohnson 嘿Kris,我该如何促进AppDelegate将依赖项注入视图控制器?通过编程方式实例化视图控制器并使用反射来决定要实例化/注入什么,递归地进行?有没有任何框架可以帮助处理这个问题?如果我必须为每个视图控制器手动执行此操作,我的AppDelegate将会变得非常庞大!哦,最好不要为所有内容创建工厂 :) - Jimbo
1
一个粗略的搜索发现了 Swinject,它也具有自动连线 :) - Jimbo
11
对于那些建议不要将任何“非必要”内容放在应用程序委托中的程序员来说,这有点荒谬,因为苹果文档明确指出,UIApplicationDelegate 单例的一个“关键作用”是“存储您的应用程序的中央数据对象或任何没有拥有视图控制器的内容。” https://developer.apple.com/documentation/uikit/uiapplicationdelegate - trndjc
18个回答

839

另一种解决方案是正确的,它将为您提供对应用程序委托的引用,但这不允许您访问由UIApplication的子类添加的任何方法或变量,比如您的托管对象上下文。要解决这个问题,只需将其向下转换为"AppDelegate"或您的UIApplication子类名称。在Swift 3、4和5中,操作如下:

let appDelegate = UIApplication.shared.delegate as! AppDelegate
let aVariable = appDelegate.someVariable

11
如果仍有人遇到问题,针对 OS X 必须导入 Cocoa 才能让 NSApplication.sharedApplication() 正常工作。我猜想 iOS 也需要相应的操作。 - smaurice
2
这是两个答案中更有用的一个。 - Joe
3
通过使用那种方法,你可以得到属性的自动完成提示,但它实际上不会起作用。那种方式每次使用时都会创建一个新的AppDelegate类的实例。你最好使用通过sharedApplication单例可访问的委托。至于你的第二个问题,是的,你可能想要使用一个常量。即使你可能会更改AppDelegate的属性,但你很可能不会将指针重新分配给其他任何东西,在这种情况下,就不需要变量了。然而,这完全取决于你的需求。做适合你自己的事情。 - Mick MacCallum
2
非常好的建议!我曾错误地认为AppDelegate()是单例的,但在思考了您所说的内容后,我意识到如果它是单例的话,就不会有我们可以这样使用的sharedApplication模式。当然,如果不必要就不想生成多余的实例,因此您的常量声明将非常适合;谢谢。 - zacjordaan
5
我不想将这个作为单独的答案添加,但是如果你想在同时使用Swift和Obj-c的项目中获取AppDelegate并使用自己的方法和属性,你应该将 #import "AppDelegate.h" 添加到 "ProjectName-BridgingHeader.h" 文件中。 - Marcin Mierzejewski
显示剩余11条评论

54

Swift 4.2

在 Swift 中,可以轻松访问您的 VC。

extension UIViewController {
    var appDelegate: AppDelegate {
    return UIApplication.shared.delegate as! AppDelegate
   }
}

40

便利构造器

将以下代码添加到AppDelegate类的末尾

Swift 5.7

 func appDelegate() -> AppDelegate {
    guard let delegate = UIApplication.shared.delegate as? AppDelegate else {
        fatalError("could not get app delegate ")
    }
    return delegate
 }

在代码中的任何地方使用AppDelegate引用?
例如:调用名为setRoot的AppDelegate方法 appDelegate().setRoot()
场景委托示例:
func sceneDelegate() -> SceneDelegate {
    guard let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate else {
      fatalError("could not get scene delegate ")
    }
   return sceneDelegate
}

2
很好解释一下setRoot()的作用以及何时调用它会更好。例如,从ViewController中调用它是否可以?在窗口尚未设置的单元测试中是否可以调用它?更多上下文信息将会更有帮助。 - Houman
@Houman setRoot 是来自 AppDelegate 的一个方法,可用于在多个 Root 或 ParentViewController 之间切换,并且可以是任何方法。讨论是关于如何获取 AppDelegate 的引用以进一步访问,但希望 setRoot 也必须清楚明确。 - AiOsN

24

这可以用于OS X操作系统

let appDelegate = NSApplication.sharedApplication().delegate as AppDelegate
var managedObjectContext = appDelegate.managedObjectContext?

3
简单地说,let appDelegate = NSApp.delegate as AppDelegate的意思是将NSApp的代理转换为AppDelegate类型,并将其赋给appDelegate变量。 - Vladimir Prudnikov
1
Swift 3 中使用 let appDelegate = NSApplication.shared().delegate as AppDelegate - Dirk
4
在Swift 4中:NSApp.delegate as? AppDelegate 的意思是将 NSApp.delegate 转换为类型 AppDelegate,并使用可选项来处理可能的类型转换错误。 - Nick
2
NSApplication.shared.delegate as? AppDelegate 更新至 Swift 5* - Roshan Sah

21

它基本上和Objective-C中的一样

let del = UIApplication.sharedApplication().delegate

17

这里是 Swift 5 版本:

let delegate = UIApplication.shared.delegate as? AppDelegate

要访问受管理的对象上下文:

    if let delegate = UIApplication.shared.delegate as? AppDelegate {

        let moc = delegate.managedObjectContext
        
        // your code here
        
    }

或者,使用 guard:

    guard let delegate = UIApplication.shared.delegate as? AppDelegate  else {
        return
    }

    let moc = delegate.managedObjectContext
        
    // your code here
    

15

除此之外,就我个人而言,我错过了导入UIKit:

import UIKit

这个解决方案修复了我遇到的错误:无法在作用域中找到“UIApplication” - Marlhex

12

SWIFT < 3

在AppDelegate类中创建一个方法,例如:

func sharedInstance() -> AppDelegate{
        return UIApplication.sharedApplication().delegate as! AppDelegate
    }

并在其他地方调用它,例如

let appDelegate : AppDelegate = AppDelegate().sharedInstance()

SWIFT >= 3.0

func sharedInstance() -> AppDelegate{
    return UIApplication.shared.delegate as! AppDelegate
}

2
这也可以运行,我喜欢sharedInstance命名的想法。谢谢! - John Griffiths
2
每次调用 AppDelegate().sharedInstance(),您的示例仍会实例化一个新的 AppDelegate 对象。 - pxpgraphics
1
我喜欢这个解决方案比被接受的那个更好(如果你不得不一直使用它,那么它就会变得“笨重”)。在找到这个答案之前,我正在考虑尝试为NSApplication编写一个extension,但这个方法要好得多。现在,你可能想要使用一个类计算只读属性shared,也许你想发布一个Swift 3的更新? - Patru
1
我正在重构我的代码,以使用static var shared:TournamentDelegate?{get {return NSApplication.shared()。delegate as?TournamentDelegate}},这更符合开发Swift 3风格,使用计算只读属性来处理此类事情。一旦重构完成,我可能能够删除?,你觉得呢?(抱歉,在评论中的代码格式总是很混乱。) - Patru
@pxpgraphics UIApplication是一个单例类,因此不需要担心多个实例化的问题。 - Abhijith

8

请尝试以下方法:

Swift 4

// Call the method
(UIApplication.shared.delegate as? AppDelegate)?.whateverWillOccur()

在你的AppDelegate中:

// MARK: - Whatever
func whateverWillOccur() {

    // Your code here.

}

7

这里有一个针对 UIApplicationDelegate 的扩展,可以避免直接硬编码 AppDelegate 类名:

extension UIApplicationDelegate {

    static var shared: Self {    
        return UIApplication.shared.delegate! as! Self
    }
}

// use like this:
let appDelegate = MyAppDelegate.shared // will be of type MyAppDelegate

在 Mac 上使用 NSApplicationDelegate 时,会出现以下提示:'Self' is only available in a protocol or as the result of a method in a class; did you mean 'AppDelegate'?。这是过时的吗? - pkamb
@pkamb 我刚在一个新项目中尝试了一下,没有出现这个错误。我将 UIApplicationDelegate 替换为 NSApplicationDelegateUIApplication 替换为 NSApplication - nyg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接