I'm trying to capture initialisation of InputStream with file URL in Swift. I've successfully implemented such capture in Objective-C for NSInputStream class.
The issue is that after exchanging initialisers, swizzled method is not triggered. Furthermore, after method exchange, calling swizzled method directly does not lead to its execution, meaning, its implementation was successfully exchanged. I'd like to know, what are the reasons of such odd behaviour, as method is swizzled successfully, but it's not clear, which method was replaced by swizzled one. Provided sample with current implementation of InputStream swizzling in Swift.
private let swizzling: (AnyClass, Selector, Selector) -> () = { forClass, originalSelector, swizzledSelector in
if let originalMethod = class_getInstanceMethod(forClass, originalSelector),
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
extension InputStream {
#objc dynamic func swizzledInit(url: URL) -> InputStream? {
print("Swizzled constructor")
return self.swizzledInit(url: url)
}
static func Swizzle() {
swizzling(InputStream.self, #selector(InputStream.init(url:)), #selector(swizzledInit(url:)))
}
}
From the InputStream documentation:
NSInputStream is an abstract superclass of a class cluster consisting of concrete subclasses of NSStream that provide standard read-only access to stream data.
The key here: when you create an InputStream, the object you get back will not be of type InputStream, but a (private) subclass of InputStream. Much like the Foundation collection types (NSArray/NSMutableArray, NSDictionary/NSMutableDictionary, etc.), the parent type you interface with is not the effective type you're working on: when you +alloc one of these types, the returned object is usually of a private subclass.
In most cases, this is irrelevant implementation detail, but in your case, because you're trying to swizzle an initializer, you do actually care about the value returned from +alloc, since you're swizzling an initializer which is never getting called.
In the specific case of InputStream, the value returned from +[NSInputStream alloc] is of the private NSCFInputStream class, which is the effective toll-free bridged type shared with CoreFoundation. This is private implementation detail that may change at any time, but you can swizzle the initializer on that class instead:
guard let class = NSClassFromString("NSCFInputStream") else {
// Handle the fact that the class is absent / has changed.
return
}
swizzling(class, #selector(InputStream.init(url:)), #selector(swizzledInit(url:)))
Note that if you're submitting an app for App Store Review, it is possible that the inclusion of a private class name may affect the review process.
Related
A class variable that contains the class itself, for example, the main property of the class DispatchQueue
Question 1
What kind of design pattern it is? Is this related to Singleton?
Question 2
How does the object initialized when referring this property?
Yes, it is the singleton pattern. It is all over swift like UserDefaults.standard, NotificationCenter.default, FileManager.default, ... For instance, URLSession.shared is defined in the apple docs as:
The shared singleton session object.
The singleton pattern makes sure that only one instance of the class is created. The class lazily creates its sole instance the first time it is requested and thereafter ensures that no other instance can be created.
Here is some sample code:
class MyClass {
static let shared = MyClass()
//Make the initializer private
private init(){}
//Here is a sample method
func doSomething() {
print("Doing something")
}
}
Making the initializer prevents the possibility of creating many instances, and thus the following line would yield an error:
let instance = MyClass() //MyClass initializer is inaccessible due to private protection level
To access the singleton use MyClass.shared:
let instance = MyClass.shared
instance.doSomething() //Prints: Doing something
For more details on the singleton pattern in Swift, you may look here and here.
This question already has answers here:
Custom class clusters in Swift
(7 answers)
Closed 6 years ago.
Objective-C has a peculiar, but very helpful ability to make an initializer return a different instance than the one the initializer is being called on. You can read about that here.
It's great for things like class clusters, but even for things as simple as ensuring specific instances are returned based on specific conditions (i.e. newing up an Account class with an account number will always return the same instance of the Account class for that number.)
However, in our app, we're using Swift but I don't see any such provisions in this language. Is there such a thing?
Normally we'd simply create a class method to return the instance we're interested in, but while we own the class in question, we do not own the calling code which uses the standard initializer syntax. e.g.
let foo = OurClass()
We want to control which instance is handed back to them based on some external conditions (omitted for clarity here.)
So does Swift have any such mechanisms?
While not as easy or clean as objective-c, hopefully you should be able to achieve your goal using convenience initializers or failable initializers (or both).
A convenience initializer allows you to delegate initialization to another initializer, essentially giving you control of how the object is initialized:
class RecipeIngredient {
var quantity: Int
init(quantity: Int) {
self.quantity = quantity
}
convenience init() {
// You control how the properties are initialized here
if (/* Some external or internal condition */) {
self.init(quantity: 1)
}
else {
self.init(quantity: 2)
}
}
}
// The caller
let mysteryFood = Food()
The failable initializer allows you to return nil in a designated initializer if a condition isn't met or if an error occurs during initialization:
class Account {
var id: Int
// Failable initializers have '?' after init
init?(id: Int) {
// You control if the initialization succeeds or not
if (/* check your db if id is duplicate, for example */) {
return nil
}
// Succeeds
self.quantity = quantity
}
}
// The caller
let account = Account(id: 5)
if (account == nil) {
// It failed
}
else {
// It succeeded
}
In your case, based on your comments, it sounds like you would want to use a failable initializer that returns nil if the caller is attempting to create a duplicate instance.
Hopefully this is along the lines of what you are looking for!
As far as I can tell it's not possible with classes, but you can do this with struct initializers (self = otherValue). If it's possible the class can be converted to a struct that would probably be the way to go.
The obvious alternative (and arguably better design) is a factory method, and when working with an already existing API, you could deprecate that initializer or make it fallible to indicate the factory method should instead be used. Otherwise there are a few options that come to mind.
Copy the properties of the instance you would like to return to the new instance you're creating.
As suggested, drop down to Objective-C, for that initializer.
Use a subclass only for Swift and enforce usage of the constructor.
Consider the following test case, which contains a 'factory' class which is able to call a closure it contains, providing a new instance of some 'defaultable' type:
protocol Defaultable {
init()
}
extension Int: Defaultable { }
extension Double: Defaultable { }
extension String: Defaultable { }
class Factory<T : Defaultable> {
let resultHandler: (T) -> ()
init(resultHandler: (T) -> ()) {
self.resultHandler = resultHandler
}
func callResultHandler() {
resultHandler(T.init())
}
}
Now, this works well when I use it on its own, where I can keep track of the generic type:
// Create Int factory variant...
let integerFactory = Factory(resultHandler: { (i: Int) in print("The default integer is \(i)") })
// Call factory variant...
integerFactory.callResultHandler()
Unfortunately, it doesn't work so well if I want to use factories in a way where I can't keep track of the generic type:
// Create a queue of factories of some unknown generic type...
var factoryQueue = [Factory]()
// Add factories to the queue...
factoryQueue.append(integerFactory)
factoryQueue.append(doubleFactory)
factoryQueue.append(stringFactory)
// Call the handler for each factory...
for factory in factoryQueue {
factory.callResultHandler()
}
I understand the error I get (Generic parameter 'T' could not be inferred), but I don't understand why I can't do this, because when I interact with the array, I don't need to know what the generic parameter is (I don't interact with any of the generic things in the Factory instance). Is there any way I can achieve the above?
Note that the above is a simplified example of what I'm trying to do; in actuality I'm designing a download manager where it can infer what type of file I want (JSON, image, etc.) using generics; the protocol actually contains an init(data:) throws initialiser instead. I want to be able to add the download objects to a queue, but I can't think of any way of adding them to a queue because of the generic nature of the download objects.
The problem is that Swift's strict type safety means you cannot mix two instances of the same class with different generic parameters. They are effectively seen as completely different types.
However in your case, all you're doing is passing a closure to a Factory instance that takes a T input, and then invoking it at any given time with T.init(). Therefore, you can create a closed system in order to contain the type of T, meaning that you don't actually need your generic parameter to be at the scope of your class. You can instead restrict it to just the scope of the initialiser.
You can do this by defining your resultHandler as a Void->Void closure, and create it by wrapping the passed closure in the initialiser with another closure – and then passing in T.init() into the closure provided (ensuring a new instance is created on each invocation).
Now whenever you call your resultHandler, it will create a new instance of the type you define in the closure that you pass in – and pass that instance to the closure.
This doesn't break Swift's type safety rules, as the result of T.init() is still known due to the explicit typing in the closure you pass. This new instance is then being passed into your closure that has a matching input type. Also, because you never pass the result of T.init() to the outside world, you never have to expose the type in your Factory class definition.
As your Factory class itself no longer has a generic parameter, you can mix different instances of it together freely.
For example:
class Factory {
let resultHandler: () -> ()
init<T:Defaultable>(resultHandler: (T) -> ()) {
self.resultHandler = {
resultHandler(T.init())
}
}
func callResultHandler() {
resultHandler()
}
}
// Create Int factory variant...
let integerFactory = Factory(resultHandler: { (i: Int) in debugPrint(i) })
// Create String factory variant...
let stringFactory = Factory(resultHandler: { (i: String) in debugPrint(i) })
// Create a queue of factories of some unknown generic type...
var factoryQueue = [Factory]()
// Add factories to the queue...
factoryQueue.append(integerFactory)
factoryQueue.append(stringFactory)
// Call the handler for each factory...
for factory in factoryQueue {
factory.callResultHandler()
}
// prints:
// 0
// ""
In order to adapt this to take an NSData input, you can simply modify the resultHandler closure & callResultHandler() function to take an NSData input. You then just have to modify the wrapped closure in your initialiser to use your init(data:) throws initialiser, and convert the result to an optional or do your own error handling to deal with the fact that it can throw.
For example:
class Factory {
let resultHandler: (NSData) -> ()
init<T:Defaultable>(resultHandler: (T?) -> ()) {
self.resultHandler = {data in
resultHandler(try? T.init(data:data)) // do custom error handling here if you wish
}
}
func callResultHandler(data:NSData) {
resultHandler(data)
}
}
I recently came back to needing a better answer for this question—as I was performing some refactoring—and thought that it would be really useful to have generic properties of the class, which of course would mean that the class itself would have to be generic as well.
I'm not sure why it didn't occur to me before, but I can simply create a protocol which mirrors the non-generic methods of the class. Using the example I originally had in my question, I could create a FactoryProtocol like so:
protocol FactoryProtocol {
func callResultHandler()
}
Make the class conform to it:
class Factory<T : Defaultable>: FactoryProtocol
And then use the protocol rather than the class when I define my array:
var factoryQueue = [FactoryProtocol]()
This allows me to add any type of specialised Factory to the array and interact with the non-generic methods as I please.
I am afraid this is not possible. The reason for this is that Swift doesn't have first class metatypes. I can imagine all sorts of Monads and Functors being built if this was possible. Unfortunately, this is a limitation. Welcome to Swift.
The golden rule is that in Swift, you cannot nail a type down to a protocol. Swift needs a concrete type.
Check this article out for more details around the subject.
I feel like the answer is obvious, but I haven't been able to figure this out and it seems to be a recurring problem for me. Basically I want to do something like this:
extension NSData {
convenience init(JSONObject: AnyObject) {
do {
self = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: [])
}
catch {
self = nil
}
}
}
However it won't let me simply assign a value to self. I do this all the time with enums, but it won't let me do it with classes. Is there any way implement at convenience initializer using an instance of the class created in the initializer implementation?
Saying that factory initializers are "not supported yet" in Swift is fallacious. Their exclusion is a design decision, and their use intended to be covered by failable initializers; quoting the following Apple Swift blog post
Failable initializers eliminate the most common reason for factory
methods in Swift, which were previously the only way to report failure
when constructing this object.
...
Using the failable initializer allows greater use of Swift’s uniform
construction syntax, which simplifies the language by eliminating
the confusion and duplication between initializers and factory
methods.
So in your case, you're probably looking for a convenience failable initializer. E.g., something along the lines
extension NSData {
convenience init?(JSONObject: AnyObject) {
do {
let foo = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: [])
self.init(data: foo)
}
catch {
return nil
}
}
}
/* Example usage */
let foo : AnyObject = ["Foo":"bar"]
let bar = NSData.init(JSONObject: foo)
In the title of your question you include "... instead of calling an existing init". When making use of convenience initializer, a designated initializer (of same class) must be called at some point (even via other convenience initializers). From the Swift Language Guide - Initialization - Class Inheritance and Initialization:
...
Rule 2
A convenience initializer must call another initializer from the same
class.
Rule 3
A convenience initializer must ultimately call a designated
initializer.
The example code above, however, allows an early escape (failure) of the convenience initializer if NSJSONSerialization.dataWithJSONObject(...) fails, but if it succeeds, sooner or later a designated initializer needs to be called (in this case init(data:) designated initializer).
For details on failable initializers, see the Swift Language Guide - Initialization - Failable Initializers. For an additional remark regarding the initializer chain (convenience -> ... -> designated initializer), see rickster:s comment below.
If I understand you right you want a factory initializer. In Swift they are not supported yet. The best you could do is to use static factory method.
Calling a method from its name (in a String format) can be sometimes useful.
In Swift it is recomended to change behavior and to use closures to do something "dynamically", so for example you can have a dictionary of functions, with the name as the key, and the implementation as the value.
However, sometimes you want to simply know "how to do it", and this is the reason of this question.
So, how to call dynamically a Swift method starting from it's name as string?
In Objective C it was simple:
[self performSelector:NSSelectorFromString(#"aSelector")];
But performSelector is banned in Swift
Is there any alternative?
In Swift, you should use closures and change your approach.
However, if you want to use performSelector to dynamically call a method given only it's String signature, altough it's not supported natively, I've found how to do it.
It is possible to create a C alternative to performSelector that:
works even on native swift classes (non objective-c)
takes a selector from string
However it's not so straightforward to implement a complete version of it, and it's necessary to create the method in C.
in C we have dlsym(), a function that returns a pointer to a function given the char symbol.
Well, reading this interesting post:
http://www.eswick.com/2014/06/inside-swift/
I've learned a lot of interesting things about swift.
Swift instance methods are plain functions with a specific signature, like this
_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString
where the "self" value is passed as the last parameter
in short you can call it directly from the c side without any kind of bridging, it is sufficient to rebuild the correct function signature.
In the signature above, there is the name of the project (FirstSwiftTest) and the lenght (14), the name of the class (ASampleClass) and the lenght (12), the name of the function (aTestFunction) and the lenght (13), then other values as the return type ecc ecc. For other details look at the previous link
The function above, is the representation of this:
class ASampleClass
{
func aTestFunction() -> NSString
{
println("called correctly")
return NSString(string: "test")
}
}
Well, on the c side, I was able to create this function
#include <stdio.h>
#include <dlfcn.h>
typedef struct objc_object *id;
id _performMethod(id stringMethod, id onObject)
{
// ...
// here the code (to be created) to translate stringMethod in _TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString
// ...
id (*functionImplementation)(id);
*(void **) (&functionImplementation) = dlsym(RTLD_DEFAULT, "_TFC14FirstSwiftTest12ASampleClass13aTestFunctionfS0_FT_CSo8NSString");
char *error;
if ((error = dlerror()) != NULL) {
printf("Method not found \n");
} else {
return functionImplementation(onObject); // <--- call the function
}
return NULL
}
And then called it on the swift side
let sampleClassInstance = ASampleClass()
println(_performMethod("aTestFunction", sampleClassInstance))
The function resulted in these statement printed on the log:
called correctly
test
So it should be not so difficult to create a _performMethod() alternative in C that:
creates automatically the function signature (since it seems to have a logic :-)
manages different return value types and parameters
EDIT
In Swift 2 (and maybe in Beta3, I didn't try) It seems that performSelector() is permitted (and you can call it only on NSObject subclasses). Examining the binary, It seems that now Swift creates static functions that can be specifically called by performSelector.
I created this class
class TestClass: NSObject {
func test() -> Void {
print("Hello");
}
}
let test = TestClass()
let aSel : Selector = NSSelectorFromString("test")
test.performSelector(aSel)
and now in the binary I find
000000010026d830 t __TToFC7Perform9TestClass4testfT_T_
At this time, I don't understand well the reasons behind this, but I'll investigate further
You could call a method from a String this way:
let foo = <some NSObject subclass instance>
let selectorName = "name"
foo.perform(Selector(selectorName))
It is available only when your foo class is subclass of NSObject
swift3 version
class MyClass:NSObject
{
required public override init() { print("Hi!") }
public func test(){
print("This is Test")
}
public class func static_test(){
print("This is Static Test")
}
}
if let c: NSObject.Type = NSClassFromString("TestPerformSelector.MyClass") as? NSObject.Type{
let c_tmp = c.init()
c_tmp.perform(Selector("test"))
c.perform(Selector("static_test"))
}