How to Override Internal Framework Method in Application (Outside Framework)

Overriding methods in Swift extensions

Extensions cannot/should not override.

It is not possible to override functionality (like properties or methods) in extensions as documented in Apple's Swift Guide.

Extensions can add new functionality to a type, but they cannot override existing functionality.

Swift Developer Guide

The compiler is allowing you to override in the extension for compatibility with Objective-C. But it's actually violating the language directive.

That just reminded me of Isaac Asimov's "Three Laws of Robotics" /p>

Extensions (syntactic sugar) define independent methods that receive their own arguments. The function that is called for i.e. layoutSubviews depends on the context the compiler knows about when the code is compiled. UIView inherits from UIResponder which inherits from NSObject so the override in the extension is permitted but should not be.

So there's nothing wrong with grouping but you should override in the class not in the extension.

Directive Notes

You can only override a superclass method i.e. load() initialize()in an extension of a subclass if the method is Objective-C compatible.

Therefore we can take a look at why it is allowing you to compile using layoutSubviews.

All Swift apps execute inside the Objective-C runtime except for when using pure Swift-only frameworks which allow for a Swift-only runtime.

As we found out the Objective-C runtime generally calls two class main methods load() and initialize() automatically when initializing classes in your app’s processes.

Regarding the dynamic modifier

From the Apple Developer Library (archive.org)

You can use the dynamic modifier to require that access to members be dynamically dispatched through the Objective-C runtime.

When Swift APIs are imported by the Objective-C runtime, there are no guarantees of dynamic dispatch for properties, methods, subscripts, or initializers. The Swift compiler may still devirtualize or inline member access to optimize the performance of your code, bypassing the Objective-C runtime. /p>

So dynamic can be applied to your layoutSubviews -> UIView Class since it’s represented by Objective-C and access to that member is always used using the Objective-C runtime.

That's why the compiler allowing you to use override and dynamic.

Is there a way to override an app delegate in a framework in swift?

There is no universal way to do that. For example there is no iOS default notification which is posted in this case and can be observed.

I see 2 ways of implementing it:

1) Redirect method call (Probably as you did). VK SDK implemented redirection this way.

For example:

func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool
{
let result = MySDK.sharedInstance().openURL(url, options: options)
reutrn result
}

Pros: Explicit, developers will know where your SDK handles "open url".

Cons: You need developers to know that they need to add this line of code

2) Create Base App Delegate (Facebook SDK uses this approach). So you create base class which implements this method and force developers to use it as a super class of their AppDelegate

class BaseAppDelegate: UIResponder, UIApplicationDelegate {

func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool
{
let result = MySDK.sharedInstance().openURL(url, options: options)
reutrn result
}
}

class MyAppDelegate: BaseAppDelegate {

// Implement some custom app delegate

}

Pros: Easier to implement by developers.

Cons: Not so explicit as previous approach, if this method will be overriden by MyAppDelegate, super needs to be called.

Swift override method didPressSend

override func didPressSend(_ button: UIButton?, withMessageText text: String?, senderId: String?, senderDisplayName: String?, date: Date?) {
// Check to make sure we are sending something no need for blank messages.
guard let text = text else {
return
}
print("working")
// add logic here.
}

practice safe code out there.

Is overriding Objective-C framework methods ever a good idea?

If you're extending the question from mere swizzling to actual library modification then I can think of useful examples.

As of iOS 5, NSURLConnection provides sendAsynchronousRequest:queue:completionHandler:, which is a block (/closure) driven way to perform an asynchronous load from any resource identifiable with a URL (local or remote). It's a very useful way to be able to proceed as it makes your code cleaner and smaller than the classical delegate alternative and is much more likely to keep the related parts of your code close to one another.

That method isn't supplied in iOS 4. So what I've done in my project is that, when the application is launched (via a suitable + (void)load), I check whether the method is defined. If not I patch an implementation of it onto the class. Henceforth every other part of the program can be written to the iOS 5 specification without performing any sort of version or availability check exactly as if I was targeting iOS 5 only, except that it'll also run on iOS 4.

In Java or C++ I guess the same sort of thing would be achieved by creating your own class to issue URL connections that performs a runtime check each time it is called. That's a worse solution because it's more difficult to step back from. This way around if I decide one day to support iOS 5 only I simply delete the source file that adds my implementation of sendAsynchronousRequest:.... Nothing else changes.

As for method swizzling, the only times I see it suggested are where somebody wants to change the functionality of an existing class and doesn't have access to the code in which the class is created. So you're usually talking about trying to modify logically opaque code from the outside by making assumptions about its implementation. I wouldn't really support that as an idea on any language. I guess it gets recommended more in Objective-C because Apple are more prone to making things opaque (see, e.g. every app that wanted to show a customised camera view prior to iOS 3.1, every app that wanted to perform custom processing on camera input prior to iOS 4.0, etc), rather than because it's a good idea in Objective-C. It isn't.

EDIT: so, in further exposition — I can't post full code because I wrote it as part of my job, but I have a class named NSURLConnectionAsyncForiOS4 with an implementation of sendAsynchronousRequest:queue:completionHandler:. That implementation is actually quite trivial, just dispatching an operation to the nominated queue that does a synchronous load via the old sendSynchronousRequest:... interface and then posts the results from that on to the handler.

That class has a + (void)load, which is the class method you add to a class that will be issued immediately after that class has been loaded into memory, effectively as a global constructor for the metaclass and with all the usual caveats.

In my +load I use the Objective-C runtime directly via its C interface to check whether sendAsynchronousRequest:... is defined on NSURLConnection. If it isn't then I add my implementation to NSURLConnection, so from henceforth it is defined. This explicitly isn't swizzling — I'm not adjusting the existing implementation of anything, I'm just adding a user-supplied implementation of something if Apple's isn't available. Relevant runtime calls are objc_getClass, class_getClassMethod and class_addMethod.

In the rest of the code, whenever I want to perform an asynchronous URL connection I just write e.g.

[NSURLConnection sendAsynchronousRequest:request
queue:[self anyBackgroundOperationQueue]
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *blockError)
{
if(blockError)
{
// oh dear; was it fatal?
}

if(data)
{
// hooray! You know, unless this was an HTTP request, in
// which case I should check the response code, etc.
}

/* etc */
}

So the rest of my code is just written to the iOS 5 API and neither knows nor cares that I have a shim somewhere else to provide that one microscopic part of the iOS 5 changes on iOS 4. And, as I say, when I stop supporting iOS 4 I'll just delete the shim from the project and all the rest of my code will continue not to know or to care.

I had similar code to supply an alternative partial implementation of NSJSONSerialization (which dynamically created a new class in the runtime and copied methods to it); the one adjustment you need to make is that references to NSJSONSerialization elsewhere will be resolved once at load time by the linker, which you don't really want. So I added a quick #define of NSJSONSerialization to NSClassFromString(@"NSJSONSerialization") in my precompiled header. Which is less functionally neat but a similar line of action in terms of finding a way to keep iOS 4 support for the time being while just writing the rest of the project to the iOS 5 standards.

Cannot override open method declared in class extension in another module swift

Short answer: The doStuff method in

public extension UIView {
open func doStuff() {...}
}

has an effective access level "public" because the extension is
marked public. Therefore it cannot be overridden in a subclass.

Note that Xcode warns

warning: declaring instance method in PUBLIC extension

and the warning text should be (see below)

warning: declaring OPEN instance method in PUBLIC extension

To solve the problem, remove the public access modifier of the extension:

extension UIView {
open func doStuff() {...}
}

Now the extension has the access level "open" (inherited from open class UIView)
and the effective access level of doStuff is "open" and it can be subclassed.

Longer answer:

Access Control in the Swift reference
states that

... you can mark an extension with an explicit access-level modifier ... to set a new
default access level for all members defined within the extension. This new default
can still be overridden within the extension for individual type members.

but actually you can only restrict type members within the extension to the same
or a lower access. Unfortunately, I could not find a definite reference for this
fact in the documentation.

SE-0117 Allow distinguishing between public access and public overridability
states that

For example, the true access level of a type member is computed as the minimum of the
true access level of the type and the declared access level of the member. If the class
is public but the member is open, the true access level is public.

but does not explain how this applies to extensions.

The check can be seen in the compiler source code
TypeCheckAttr.cpp.
If the access level of an item is larger then the access level of the containing
extension then the
diag::access_control_ext_member_more
diagnostic message is emitted:

WARNING(access_control_ext_member_more,none,
"declaring %select{PRIVATE|a fileprivate|an internal|a public}0 %1 in "
"%select{a private|a fileprivate|an internal|PUBLIC}2 extension",
(Accessibility, DescriptiveDeclKind, Accessibility))

Note that the "open" level is missing in the selection, and that is why it is missing in

warning: declaring instance method in PUBLIC extension

Allowing overriding zero-initialized object with internal linkage

Use of static

You may actually be able to use static instead of external.
Making a quick test with gcc under Ubuntu:

#include "test_suite.h"

static const char *const test_suite_name = "huhu";

int main() {
run_all_tests();
return 0;
}

If I compile with:

gcc -Wall -Wpedantic -Wextra mytest.c -o mytest

it gives as output:

Running huhu suite

Omitting static

If you accidentally forget to specify static it should give a compile time error then. So if I change this line to:

const char *const test_suite_name = "huhu";

and try to compile it like this:

gcc -Wall -Wpedantic -Wextra mytest2.c -o mytest2

this error message will be displayed:

mytest2.c:3:19: error: non-static declaration of ‘test_suite_name’ follows static declaration
const char *const test_suite_name = "huhu";
^~~~~~~~~~~~~~~
In file included from mytest2.c:1:
test_suite.h:3:26: note: previous declaration of ‘test_suite_name’ was here
static const char *const test_suite_name;

Since it is an error it is also output if you compile with:

gcc mytest2.c -o mytest2

Screenshot of Error Message

Screenshot with Error Message

Write a class method visible to one project and hidden in other?

As mentioned by Mindaugas in one of the comment, you can use class extension for this purpose.

Class extensions are often used to extend the public interface with additional private methods or properties for use within the implementation of the class itself.

For example
.h contains

@interface Person : NSObject
@property(nonatomic, copy) NSString *firstName;
@property(nonatomic, copy) NSString *lastName;

- (void)setDateOfBirth:(NSDate *)date;
@end

.m will have

@interface Person()
@property(nonatomic, strong) NSDate *dateOfBirth;

- (void)calculateMyAge;
@end

@implementation Person
- (void)setDateOfBirth:(NSDate *)date
{
self.dateOfBirth = date;
//Some more code goes here
}

- (void)calculateMyAge
{
//Do something with self.dateOfBirth here
}

@end

Here we made @property dateOfBirth and a method calculateMyAge private to the interface Person and implemented it under @implementation block.

@interface Person()

is nothing but class extension (or nameless category or anonymous category).

Now coming back to original question, you want to make it public to your framework and private for outside world.

Let's say you want Person class to have a method that will return full name (first and last name appended). However you want for internal purpose of your framework.

You can create a separate header file for this.

Person+InternalHeader.h

which will have declaration for the method

@interface Person()
- (NSString *)fullName;
@end

You can import

#import "Person+InternalHeader.h"

into your Person.m file and under @implementation block you can implement body for the method.

@implementation Person
//other code
- (NSString *)fullName
{
return [self.firstName stringByAppendingFormat:@" %@",self.lastName];
}
@end

You can always import Person+InternalHeader.h into your other classes and make use of fullName method.

Why extension and not category?

Extensions enable you to override the property or add new property to the existing parent class. Also you can always override the default behaviour of method defined in extension, in child class. With category, it simply adds the method to your existing object.

For example, lets there is class Doctor which inherits from class Person.

@interface Doctor : Person

Now you want the fullName method to return "Dr." prepended. You can simply override the fullName behaviour in your Doctor.m file.

@implementation Doctor
- (NSString *)fullName
{
NSString *fullName = [super fullName];
return [@"Dr. " stringByAppendingString:fullName];
}
@end


Related Topics



Leave a reply



Submit