Parse SDK and Swift 1.2: Can´t subclass PFUser - ios

Before updating to Swift 1.2 subclassing PFUser worked just fine, now I can´t make it work.
My custom PFUser-class:
public class CustomUser: PFUser, PFSubclassing {
#NSManaged var fullName : String!
#NSManaged var phone : String!
public override class func initialize(){
self.registerSubclass()
}
}
When I use this class in my code the method calls still goes to the PFUser class:
reason: '-[PFUser fullName]: unrecognized selector sent to instance
0x17018fbe0'
This behavior started with Swift 1.2. I´ve updated the Parse SDK to the lastest version as well.

I've just been through this. The change in behaviour is a huge pain. You need to register your subclasses before you set your Parse app ID (typically in your application delegate).
So, in your app delegate...
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
CustomUser.registerSubclass()
Parse.setApplicationId("XXX", clientKey: "YYY")
......
return true
}

Another solution seems to be a singleton, provided in the Parse manual as follows, which works without any problems. This code works for all subclasses not only PFUser. If done this way, there is no need to register the subclass in didFinishLaunchingWithOptions.
class User: PFUser, PFSubclassing {
// MARK: PFUser Subclassing
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
// non essential code removed
}

Related

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.

Override function error in swift

I got a struct :
struct ErrorResultType: ErrorType {
var description: String
var code: Int
}
and a protocol:
protocol XProtocol {
func dealError(error: ErrorResultType)
}
Now I want to make an extention of UIViewController:
extension UIViewController: XProtocol {
func dealError(error: ErrorResultType) {
// do something
}
}
So I can subclass from this and override the function like:
class ABCViewController: UIViewController {
--->override func dealError(error: ErrorResultType) {
super.dealError(error)
// do something custom
}
}
But it goes wrong with: Declarations from extensions cannot be overridden yet
It doesn't make any sense to me. When I replace all ErrorResultType with AnyObject, the error won't appear any more.
Anything I missed?
For now the method in the extension must be marked with #objc to allow overriding it in subclasses.
extension UIViewController: XProtocol {
#objc
func dealError(error: ErrorResultType) {
// do something
}
}
But that requires all types in the method signature to be Objective-C compatible which your ErrorResultType is not.
Making your ErrorResultType a class instead of a struct should work though.
If i am not making mistake this is connected with Swift official extension mechanism for adding methods to classes.
Conclusion :
At the moment, it's not possible to override entities declared in
extension by subclassing, like so:
class Base { }
extension Base {
var foo: String { return "foo" }
}
class Sub: Base {
override var foo: String { return "FOO" } // This is an error
}
Please check this resource for more information : https://github.com/ksm/SwiftInFlux/blob/master/README.md#overriding-declarations-from-extensions

Subclass of subclass of PFObject not being registered

I have a class called Attendee which inherits from PFObject. I also have another class called Speaker which is a child of Attendee.
Below are the class definition for Attendee & Speaker
class Attendee: PFObject, PFSubclassing {
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
class func parseClassName() -> String {
return "Attendee"
}
}
class Speaker: Attendee {
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
}
I register both in my applicationDidFinishLaunch method before using any parse features:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Speaker.initialize()
Attendee.initialize()
Parse.setApplicationId(ObjectManager.appID, clientKey: ObjectManager.clientKey)
...
}
However, when I try to use the Attendee and Speaker classes later on in my code, I get this error:
The class MedConf.Attendee must be registered with registerSubclass before using Parse.
The code that triggers the error is:
var attendee = Attendee()
I don't understand why this is happening, as I clearly register both the subclasses before I do anything with them.
My bet is you can't subclass any class the implements a PFSubclassing protocol. Try making the Speaker it's own class and build the relationship use the relationship API in Parse. If you need a resource to build relational data, check here. https://www.parse.com/docs/ios/guide#objects-relational-data

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

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