Typhoon Dependency Injection and Swift 3: Appdelegate isn't AnyObject - ios

This code works with Typhoon Dependency Injection library (Obj-C) in Swift 2.3 but doesn't in Swift 3:
AppDelegate:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var rootViewController: RootViewController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = self.rootViewController
self.window?.makeKeyAndVisible()
return true
}
...
}
Application Assembly:
public class ApplicationAssembly: TyphoonAssembly {
public dynamic func config() -> AnyObject {
return TyphoonDefinition.withConfigName("Configuration.plist")
}
public dynamic func appDelegate() -> AnyObject {
return TyphoonDefinition.withClass(AppDelegate.self) {
(definition) in
definition!.injectProperty(#selector(ApplicationAssembly.rootViewController), with: self.rootViewController())
}
}
...
}
However the following error is displayed in ApplicationAssembly for any Swift 3 file expected to return 'AnyObject':
"No 'withClass' candidates produce the expected contextual result type 'AnyObject'
Might anyone have an insight into the incompatibility of the Obj-c Typhoon code base with Swift 3?
Screen capture of error line

You may want to switch the return type from AnyObject to Any.
The withClass function returns an id type in Objective-C, see the source code:
+ (id)withClass:(Class)clazz block:(TyphoonBlockDefinitionInitializerBlock)block;
The id type used to be mapped to AnyObject in Swift 2, but in Swift 3 it's mapped to Any for increased flexibility. You can read more about this change here.

Related

issue while accessing global func in app delegate

I am new to swift and i created one swift file with name mySession and in that file i am storing login data like below
Store Login Data
func setLoginData (data:Data) {
let preferences = UserDefaults.standard
let Key_Login = "Login"
preferences.set(data, forKey: Key_Login)
preferences.synchronize()
}
and second func for use that stored data
Get Data
func getLoginData ()->Data {
let preferences = UserDefaults.standard
let Key_Login = "Login"
if preferences.object(forKey: Key_Login) == nil {
return data
} else {
return preferences.data(forKey: Key_Login)!
}
}
So now my question is that i want to use getLoginData func in my app delegate to check user is logged in or not so not able to under stand how to use that func in app delegate
You should encapsulate these methods inside a class and use an instance of that class inside AppDelegate
class MySession {
func setLogin(_ data: Data){...}
func getLoginData() -> Data {...}
}
At the call site, instantiate MySession and use your methods to do what's needed.
class AppDelegate {
//...
let sessionHandler = MySession()
sessionHandler.getLoginData()
}
Sidenote, make sure you're using proper Swift 4 naming conventions.
First Import the file name in AppDelegate
Import mySession
Then just call the method in your desired function.
setLoginData(data:<your Data>)
For example.If you want to use it in didFinishLaunchingWithOptions method, follow this
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DefaultNetworkManager.appsConfigSetup()
window = UIWindow(frame: UIScreen.main.bounds)
setupApplication()
setLoginData(data: <Your Data>)
return true
}

Transform UIApplicationDelegate methods into RxSwift Observables

In RxSwift / RxCocoa you can create a reactive wrapper for a delegate (e.g. UIScrollViewDelegate or CLLocationManagerDelegate) to enable Rx observable sequences for certain delegate methods.
I am trying to implement this for the UIApplicationDelegate method applicationDidBecomeActive:
What I tried so far is pretty straightforward and similar to the DelegateProxy subclasses that are included in RxCocoa.
I created my DelegateProxy subclass:
class RxUIApplicationDelegateProxy: DelegateProxy, UIApplicationDelegate, DelegateProxyType {
static func currentDelegateFor(object: AnyObject) -> AnyObject? {
let application: UIApplication = object as! UIApplication
return application.delegate
}
static func setCurrentDelegate(delegate: AnyObject?, toObject object: AnyObject) {
let application: UIApplication = object as! UIApplication
application.delegate = delegate as? UIApplicationDelegate
}
}
And an Rx extension for UIApplication:
extension UIApplication {
public var rx_delegate: DelegateProxy {
return proxyForObject(RxUIApplicationDelegateProxy.self, self)
}
public var rx_applicationDidBecomeActive: Observable<Void> {
return rx_delegate.observe("applicationDidBecomeActive:")
.map { _ in
return
}
}
}
In my AppDelegate I subscribe to the observable:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// the usual setup
// and then:
application.rx_applicationDidBecomeActive
.subscribeNext { _ in
print("Active!")
}
.addDisposableTo(disposeBag)
return true
}
When I start my app "Active!" gets printed and then I get the following crash in RxCocoa's _RXDelegateProxy_ class:
Does anybody have an idea what the problem might be? Or has anybody successfully implemented something like rx_applicationDidBecomeActive?
It looks like a really tricky issue with RxSwift and memory management.
The default implementation of DelegateProxyType sets an instance of a delegate proxy (in this case, RxUIApplicationDelegateProxy) to the delegate of UIApplication.
It also stores the original AppDelegate as a property called forwardToDelegate so all the delegate methods can still be passed to it.
The problem is that, when the new app delegate is set:
application.delegate = delegate as? UIApplicationDelegate
the original one is deallocated! You can check it by overriding deinit in AppDelegate. The reasons are explained in this answer. And because the property forwardToDelegate is of type assign, your app crashes as the property points to a deallocated object.
I have found a workaround for that. I'm not really sure if it is a recommended way, so be warned. You can override a method from DelegateProxyType in RxUIApplicationDelegateProxy:
override func setForwardToDelegate(delegate: AnyObject?, retainDelegate: Bool) {
super.setForwardToDelegate(delegate, retainDelegate: true)
}
In normal circumstances, you don't want to retain the delegate as it leads to a retain cycle. But in this special case, this is not a problem: your UIApplication object will exist the entire time while your application is alive anyway.

Storyboards + generics = inline initializers not called?

I'm looking for an explanation of why the following prints false when run on iOS. The view controller is instantiated from a dead-simple Storyboard which just contains a single table view.
I'm using the preloading trick suggested by https://stackoverflow.com/a/33658528/1919412 to allow Interface Builder to find the MyTableViewController class. I'm sure that has something to do with the problem, but I'd like to better understand what's going on.
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
// If we don't create this dummy instance before our real view controller gets loaded from storyboard, we get this error:
// "Unknown class _TtC21ViewControllerInitBug21MyTableViewController in Interface Builder file."
// See: https://stackoverflow.com/a/33658528/1919412
let foo = MyTableViewController()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
return true
}
}
class Foo<T>:UITableViewController {
init() {
super.init(style:.Plain)
}
}
class MyTableViewController:Foo<String> {
private var foo = true
override func viewDidLoad() {
super.viewDidLoad()
print(self.foo)
}
}

Swift Framework Class to Swift Application

Getting problem with adding my Swift Framework to Test Swift Application.
What i have:
Framework class code, Target Called 'Project':
public let Instance = Library()
public class Library {
public class var sharedInstance: Library {
return Instance
}
/* SOME CODE */
}
Than i build it and add Project.framework to Test Swift Application
Usage:
import Project
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
/* NOT WORKING
ERROR: Use of Unresolved Identifier 'Instance' */
Instance.Setup("d573732b9f8b478d",apiKey: "3XmdIY=", enablePush: true)
/* NOT WORKING TOO
ERROR: Use of Unresolved Identifier 'Library' */
let lib = Library()
return true
}
Functions public, DeviredData deleted, what i'm doing wrong?
Answer was very easy:
Adding constructor function
public init(){
}
Problem solved.
Yes, making public constructor will help you, as per my knowledge default constructor has internal scope. It works for me.
public override init() {
}

How to pass swift enum with #objc tag

I need to define a protocol which can be called in a class that use some Objective-c type
But doing that doesn't work:
enum NewsCellActionType: Int {
case Vote = 0
case Comments
case Time
}
#objc protocol NewsCellDelegate {
func newsCellDidSelectButton(cell: NewsCell, actionType: NewsCellActionType)
}
You get he error
Swift enums cannot be represented in Objective-C
If I don't put the #objc tag on my protocol it'll crash the app as soon as it's called in a class which adopt the protocol AND inherit from an Objective-C type class (like a UIViewController).
So my question is, how should I declare and pass my enum with the #objc tag?
Apple just announced today that Swift 1.2 (included with xcode 6.3) will support exposing enums to objective-c
https://developer.apple.com/swift/blog/
Swift enums are very different from Obj-C (or C) enums and they can't be passed directly to Obj-C.
As a workaround, you can declare your method with an Int parameter.
func newsCellDidSelectButton(cell: NewsCell, actionType: Int)
and pass it as NewsCellActionType.Vote.toRaw(). You won't be able to access the enum names from Obj-C though and it makes the code much more difficult.
A better solution might be to implement the enum in Obj-C (for example, in your briding header) because then it will be automatically accessible in Swift and it will be possible to pass it as a parameter.
EDIT
It is not required to add #objc simply to use it for an Obj-C class. If your code is pure Swift, you can use enums without problems, see the following example as a proof:
enum NewsCellActionType : Int {
case Vote = 0
case Comments
case Time
}
protocol NewsCellDelegate {
func newsCellDidSelectButton(cell: UITableViewCell?, actionType: NewsCellActionType )
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, NewsCellDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
test()
return true;
}
func newsCellDidSelectButton(cell: UITableViewCell?, actionType: NewsCellActionType) {
println(actionType.toRaw());
}
func test() {
self.newsCellDidSelectButton(nil, actionType: NewsCellActionType.Vote)
}
}

Resources