0

I'm trying to reproduce this pattern in Swift

#define mustBeKindOfClassFailedReturn(object, objectClass, ret) \
if(![object isKindOfClass:objectClass]) { \
NSLog(([NSString stringWithFormat:@"%@ must be kind of %@ class, current class is %@", object, NSStringFromClass(objectClass), NSStringFromClass([object class])])) \
return ret; }

used like this

 - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
                    layout:(UICollectionViewLayout*)collectionViewLayout
    insetForSectionAtIndex:(NSInteger)section {

mustNotBeNilFailedReturn(self.adapter, UIEdgeInsetsZero)
mustBeKindOfClassFailedReturn(self.adapter, [WBCollectionViewSectionAdapter class], UIEdgeInsetsZero)

Does anyone know a good solution to this?

Edit

I know about guard but using guard i have to rewrite a lot of code each time i'll have to override a method in my subclasses for exemple

/**
* mustOverride
*/
#define mustOverride \
{ NSLog(@"You must override this function") }

#define mustOverrideFailedReturn(ret) \
{ mustOverride \
return ret; }

EDIT 2

I've ended with solution is it the optimal one ?

func needOverride(function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__) -> Bool {

    REVLogManager.SharedInstance.logErrorMessage("You must override this function", function: function, file: file, line: line, exception: nil, error: nil)

    return false

}

func doesObject(function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__, matchingObject: AnyObject!, matchesClass matchingClass: AnyClass) -> Bool {

    guard matchingObject.isKindOfClass(matchingClass) else {

        let message = "\(matchingObject) must be kind of \(matchingClass) class, current class is \(matchingObject.dynamicType)"
        REVLogManager.SharedInstance.logErrorMessage(message, function: function, file: file, line: line, exception: nil, error: nil)

        return false

    }

    return true

}

I'm calling the method like this

public func actualScrollOffsetDistanceWithScrollView(scrollView: UIScrollView!) -> Float {

    guard needOverride() else { return 0.0 }
    return 0.0

}

And

guard doesObject(matchingObject: self, matchesClass: REVListSectionAdapter.classForCoder()) else { return }
Pwyll28
  • 117
  • 11

3 Answers3

0

My answer follows the same legacy pattern using in Swift. You can use classForCoder, but I prefer dynamicType for type checking.

func mustBeKindOfClassFailedReturn(object: AnyObject, objectClass: AnyClass, returnVal: Any?) {
    if !object.isKindOfClass(objectClass) {
        print("\(object) must be kind of \(objectClass) class, current class is \(object.dynamicType)")
        return returnVal
    }
    return nil // or UIEdgeInsetsZero, or whatever
}

You may also get similar results with !(object is objectClass), but things get tricky when comparing certain number types. See Swift type inference and type checking issue

You can also replace AnyObject and Any with NSObject if you know you're dealing with Objective-C types. From your comment it seems like ret could be a value type, so that's why I make it an Any type.

Community
  • 1
  • 1
JAL
  • 41,701
  • 23
  • 172
  • 300
  • But like this even if my object is the correct class i'll return "returnVal" which is not ok to me – Pwyll28 Mar 22 '16 at 17:18
  • @Pwyll28 ah I misread your macro, edited my answer to return nil if the class doesn't match. – JAL Mar 22 '16 at 17:19
  • Is this doing anything other than re-writing "if let" or "guard let" ? – GetSwifty Mar 22 '16 at 17:24
  • @PEEJWEEJ it keeps the logging and the legacy style. If the OP wants more Swift-y code, they can go with an `if let` or `guard`. – JAL Mar 22 '16 at 17:25
  • I suppose if you're going to write Objective-C in Swift that's fine, but good swift code (IMO) should be written to either handle those kind of cases implicitly or, ideally, written so they won't happen at all. (yay!) :-) – GetSwifty Mar 22 '16 at 17:28
  • @PEEJWEEJ I think that's up to the OP to decide which way they want to write their code. – JAL Mar 22 '16 at 17:29
  • Your solution does not suit me, in my old macros, when the condition is correct my macro simply "does nothing" – Pwyll28 Mar 22 '16 at 17:33
  • @Pwyll28 you're going to have to refactor your code a little bit. Your macro takes care of the return for you in Objective-C. In Swift, there's really no way to do that. You can import this macro through a bridging header if you really need the exact functionality. – JAL Mar 22 '16 at 17:36
  • @PEEJWEEJ can you check my new edit and tell if it is the correct implementation ? – Pwyll28 Mar 22 '16 at 18:16
0

Swift has a better solution for this kind of problem, the guard statement. In this case, just put this in your method.

guard let adapter = self.adapter as? WBCollectionViewSectionAdapter else {
/* return value if the adapter is not setup properly. */
    return .Zero
}
/* continue with valid and correctly classed adapter */

If you're looking for a way to handle this for sub classes, you may want to consider generics. It will allow you implicit type safety without having to rewrite logic in your subclasses.

GetSwifty
  • 7,568
  • 1
  • 29
  • 46
0

Swift doesn't have a macro capability of this kind.

In terms of accomplishing your goal. The pattern you are following would most correctly be handled using guard statements. A function containing a guard statement must exit the enclosing scope (or trap) and may do so with a return.

Please check the Swift documentation for information about Control Flow and Early Exit control

Scott Thompson
  • 22,629
  • 4
  • 32
  • 34