4

How can I call

print(NSDate()) 

and instead of receiving the usual response, obtain the one I have in a function named getString() that is part of an extension of NSDate.

Here's my extension:

extension NSDate {
    
   //NSDate to String
    public func getString() -> String {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        dateFormatter.locale = NSLocale.currentLocale()
        
        return dateFormatter.stringFromDate(self)
    }
}

Please, note that I don't want to just use:

NSDate().getString()

I want to override original description of this class.

UPDATE:

So, everything looks like the only option is Method Swizzling, if even possible.

Anyone interested in the bounty?

HEADS UP

I'm doing this just for personal growth and to get to understand the concept, not planning on shipping an App using it in this scenario, not even sure right now in what scenarios I could be using it.

Community
  • 1
  • 1
Hugo Alonso
  • 6,684
  • 2
  • 34
  • 65
  • Show your extension `NSDate().getString()` ? – Leo Dabus Feb 02 '16 at 17:16
  • I don't think is necessary, I'm not adding anything else but this function. I tried overriding description var or func, but I get an error complaining about obj selector for description. Nevertheless, I will update my answer with it – Hugo Alonso Feb 02 '16 at 17:19
  • That's the point, I don't want that. I want to call it the other way. – Hugo Alonso Feb 02 '16 at 17:23
  • 2
    I don't think that you can override the `description` method of `NSDate`, at least not easily. You cannot override methods in an extension, only in a subclass. You can replace it with method swizzling. Both ways are difficult (if possible at all) because NSDate is part of a class cluster. – Martin R Feb 02 '16 at 17:23
  • @MartinR I want to know how to do it. It's my curiosity talking over here. What are the steps you would recommend? – Hugo Alonso Feb 02 '16 at 17:25
  • 1
    @HugoAlonso: My recommendation is: don't do it :) – Seriously, I don't have a solution. About subclassing, see e.g. http://stackoverflow.com/questions/2657275/how-do-i-subclass-nsdate. – Martin R Feb 02 '16 at 17:36
  • @MartinR see my answer. I think, i have a solution :-) – user3441734 Mar 04 '16 at 23:13
  • you don't need to override description, you don't need swizzling. see my answer, please. – user3441734 Mar 04 '16 at 23:42

5 Answers5

8
import Foundation

extension NSDate: Streamable {
    public func writeTo<Target : OutputStreamType>(inout target: Target) {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        dateFormatter.locale = NSLocale.currentLocale()

        print("without swizzling", dateFormatter.stringFromDate(self), toStream: &target)
    }
}

let date = NSDate()

print(date) // without swizzling 2016-03-05 00:09:34 +0100

to print 'default' / original behavior / use

print(date.description)

if you are worry about using print in your extension just replace it with

//print("without swizzling", dateFormatter.stringFromDate(self), toStream: &target)
let str = dateFormatter.stringFromDate(self)
str.writeTo(&target)
user3441734
  • 16,722
  • 2
  • 40
  • 59
3

I'm pretty sure this is a terrible, bad, no-good, horrible idea. But here you go:

extension NSDate {
    private static let dateFormatter = NSDateFormatter()
    private static var once = dispatch_once_t()
    static func swizzleDescription() {
        dispatch_once(&once) {
            dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
            dateFormatter.locale = NSLocale.currentLocale()

            let originalMethod = class_getInstanceMethod(self, "description")
            let replacementMethod = class_getInstanceMethod(self, "description_terribleIdea")
            method_exchangeImplementations(originalMethod, replacementMethod)
        }
    }

    func description_terribleIdea() -> String {
        return NSDate.dateFormatter.stringFromDate(self)
    }
}

let date = NSDate()
print(date)
NSDate.swizzleDescription()
print(date)

Output:

2016-03-04 22:17:20 +0000
2016-03-04 16:17:20 -0600
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • I'm doing this just for personal growth and to get to understand the concept, not planning on shipping an App using it. Thanks, I'll be reviewing it and testing it next monday. – Hugo Alonso Mar 04 '16 at 22:24
  • 1
    Upvoting the fact that this is a horrible idea. :) – Rob Napier Mar 04 '16 at 22:24
  • @RobNapier it is not so horrible idea, but it is NOT available for structs, non objc classes etc. The solution exist! Just implement Streamable protocol and you done. see my answer, please. – user3441734 Mar 04 '16 at 23:18
2

could define your own version of print:

func print(date: NSDate) {
    print(date.getString())
}

extension NSDate {

    //NSDate to String
    public func getString() -> String {
        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        dateFormatter.locale = NSLocale.currentLocale()

        return dateFormatter.stringFromDate(self)
    }
}

both of these calls will now print the same thing:

let date = NSDate()
print(date.getString())
print(date)
Casey
  • 6,531
  • 24
  • 43
  • the trouble is, that it works ONLY if use print with exactly one parameter with type NSDate. try print(NSDate(), NSDate()) and see the result ... or simply print("now is: ", NSDate()) etc . – user3441734 Mar 08 '16 at 07:32
  • yup, but it does answer the question ;) – Casey Mar 08 '16 at 07:47
1

You could define an override of NSDate in your module with the same class name. You have to follow the guidelines for overriding NSDate, which is a bit tedious (but it works in the playground at least)

The compiler should use your override instead of the foundation one when in scope:

 public class NSDate:Foundation.NSDate
 {
     override public var description:String { return getString() }

     private var dateValue:NSTimeInterval = Foundation.NSDate().timeIntervalSinceReferenceDate

     override public var timeIntervalSinceReferenceDate:NSTimeInterval { return dateValue }

     override public init()
     { super.init() }

     override public init(timeIntervalSinceReferenceDate ti: NSTimeInterval)
     { 
       super.init()
       dateValue = ti 
     }

     required public init?(coder aDecoder: NSCoder)
     { super.init(coder:aDecoder) }

     //NSDate to String
     public func getString() -> String 
     {
         let dateFormatter = NSDateFormatter()
         dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
         dateFormatter.locale = NSLocale.currentLocale()
         return dateFormatter.stringFromDate(self) + " This is the Overriden One"
     }
 }

 print("\(NSDate())")

prints: 2016-03-04 18:16:22 -0500 This is the Overriden One

note that this actually overrides the description variable and not merely trick the print function.

Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • That's becauseI'm prescient :) No, actually I was referring to the answer that extended NSDate with writeTo which does essentially the same thing (i.e. only apply to printing) – Alain T. Mar 08 '16 at 13:01
  • Perhaps "trick" was a poor choice of word. What I meant was that the scope of the approach is limited to "printing" (or more generally "output") as opposed to an actual override of the description variable which was how I felt the OP was stated. – Alain T. Mar 08 '16 at 13:22
  • Subclassing IS how you override variables and functions of a class. Extension is a different concept. – Alain T. Mar 08 '16 at 16:13
-2

By definition Extensions can add functionalities but not override them. Souce: The Swift Programming Language - Extensions

What you can do is implement the protocol CustomStringConvertible and replace that behaviour:

extension NSDate : CustomStringConvertible {
   var description: String {
     let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
        dateFormatter.locale = NSLocale.currentLocale()

        return dateFormatter.stringFromDate(self)
  }
}
piyuj
  • 163
  • 7
  • That does not compile, and you cannot replace/override methods in an extension (of the same class). – Martin R Feb 02 '16 at 17:40