I use this auto coding lib a lot through my projects.
https://github.com/nicklockwood/AutoCoding
I think it is very useful. So I am wondering how I can implement the same extension in Swift?
Thanks.
Additional question:
Is it possible to write AnyObject into a file? Or it has to inherit from NSObject and conform NSCoding protocol?
From looking at this project, the object does not have to conform to NSCoding. But only Objective-C properties are saved. So for a Swift object, it will only work with the properties that are visible to Objective-C (with #objc and/or dynamic, and the type of the property is one that can be bridged to an Objective-C type).
I wrote a little utility that simplifies NSCoding in Swift: https://github.com/mustafa/SwiftCoding
Related
I read this documentation for importing swift code into objective c.
https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c
I have a few questions.
Can I put #objc annotation for a Struct?
Do I need to inherit all the class that I want to export to obj to be child of NSobject ? I am getting error 'error: only classes that inherit from NSObject can be declared #objc'
When I export a swift class with #objc, I need to add #objc to all its parent classes, protocol and interface and also class and structure in all its methods, is that correct?
No. Objective-C cannot see a native Swift struct.
Yes. Objective-C classes must basically be derived from NSObject. Objective-C can be made aware of the existence of other classes, but it cannot do anything useful with them.
You can mark the class with #objcMembers, in which case you will give everything within it full visibility to Objective-C.
I am running project with lots of legacy code with objc and swift.
I've been using objc MPOldKeychainManager, which is now deprecated and swift's NewKeychainManager is to be used.
The problem is following: MPOldKeychainManager had some categories written and I don't want to rewrite them as swift extensions.
What I've done is:
naming the class derived from the NewKeychainManager (visible in "myTarget-Swift.h") to "MPOldKeychainManager"
removing the objc declaration of MPOldKeychainManager
...hoping that the categories will still work.
objc(MPOldKeychainManager)
class NewKeychainManager: KeychainManager {
}
Unfortunately, old extensions can't see the MPOldKeychainManager (derived from swift), even though I've updated the imported header to myTarget-Swift.h
#import "myTarget-Swift.h" //previously - objc "MPOldKeychainManager.h"
#interface MPOldKeychainManager (Authentication)
Question: is it possible to use categories for objc classes derived from swift?
I have already tried totally new naming
I have already tried loads of clean-builds
In case you haven't seen it, here is a useful resource for migrating from Objective-C to Swift: https://developer.apple.com/documentation/swift/migrating_your_objective_c_code_to_swift . Among other things, it states that one cannot subclass a Swift class in Objective-C. What you are trying to do is specify a different Objective-C name, MPOldKeychainManager, for the NewKeychainManager Swift class.
This will, actually, work if you add an ampersand before objc, like so:
#objc(MPOldKeychainManager)
class NewKeychainManager: KeychainManager {
}
You can then use all your existing categories in Objective-C. You will, however, have a problem using them in Swift, because to be usable in Swift they need to be available in the bridging header, and you won't be able to use the class' Objective-C name (MPOldKeychainManager) in the bridging header.
You can, however, write an Objective-C wrapper class that will have a method corresponding to each category method and also taking a NewKeychainManager pointer. A wrapper method can then delegate to the category method, which is available to Objective-C code, so you won't have to re-implement your category methods in Swift.
Let's say an Objective-C category has method authenticateUser::
#interface MPOldKeychainManager (Authentication)
-(void)authenticateUser:(int32_t)uid;
#end
The method could be wrapped as follows:
#interface OldKCMWrapper : NSObject
+(void)authenticateUser:(int32_t)uid withManager:(NewKeychainManager*)inst;
#end
This interface declaration must be available, directly or indirectly, via the bridging header. Then, somewhere in your Objective-C code, the wrapper could be implemented thus:
#implementation OldKCMWrapper
+(void)authenticateUser:(int32_t)uid withManager:(MPOldKeychainManager*)inst {
[inst authenticateUser:uid];
}
#end
The wrapper can then be used in Swift code, e.g.:
let kcm = NewKeychainManager()
OldKCMWrapper.authenticateUser(321, with: kcm)
In fact, the wrapper could be used in a Swift extension of NewKeychainManager. You would still have a Swift extension with equivalents of all the Objective-C category methods, but you would not have to re-implement their code in Swift: methods in the extension would simply delegate to the wrapper.
Hopefully this is helpful. There are other ways of implementing this idea, possibly more elegant.
I know that Objective-C categories are called extension in Swift.
But recently i bumped in to this :
extension MyClass : GMSMapViewDelegate
{
func mapView(mapView: GMSMapView, idleAtCameraPosition position: GMSCameraPosition)
{
print("idleAtCameraPosition")
self.getAddressFromLocation(mapView.camera.target)
}
}
This looked like they are implementing delegate functions.This seemed a very good and clean way of implementing delegate functions. So I personally tried it and this works but I think I may be wrong because categories i.e. extensions are not supposed to do this.They were used to add extra functionality to other classes with out subclassing.
So my question is can we use extensions for such purpose or not?
If we can do this then extensions are more than just categories.Because i don't think we could achieve this by categories in Objective-C.
Thanks
From the official Swift Language Reference:
Extensions in Swift can:
Add computed instance properties and computed type properties
Define instance methods and type methods
Provide new initializers
Define subscripts
Define and use new nested types
Make an existing type conform to a protocol
Yes, extensions are a valid method to make a class conform to a delegate protocol.
Not only are extensions valid for this purpose, but they allow for better code organization and follow good style practice in Swift.
Yes you can, and should, do that. Putting the methods that implement a protocol into a class extension is considered good style in Swift. It separates out groups of methods based on what they do. I would suggest making liberal use of extensions in your coding.
Yes, extensions are meant to be able to allow existing classes to conform to new protocols, in addition to just adding functions to a class.
When I declare static parameter in extension of class then I have to write #nonobjc before variable like:
#nonobjc static let test = "test"
and sometimes I have to write #objc before method, so what is use of #objc and #nonobjc in Swift.
Can anyone help me for this problem?
This is explained in the Apple's official documentation about Objective-C - Swift interoperability:
When you use the #objc(name) attribute on a Swift class, the class is
made available in Objective-C without any namespacing. As a result,
this attribute can also be useful when migrating an archivable
Objective-C class to Swift. Because archived objects store the name of
their class in the archive, you should use the #objc(name) attribute
to specify the same name as your Objective-C class so that older
archives can be unarchived by your new Swift class.
Conversely, Swift also provides the #nonobjc attribute, which makes a
Swift declaration unavailable in Objective-C. You can use it to
resolve circularity for bridging methods and to allow overloading of
methods for classes imported by Objective-C. If an Objective-C method
is overridden by a Swift method that cannot be represented in
Objective-C, such as by specifying a parameter to be a variable, that
method must be marked #nonobjc.
To summarize, use #objc when you want to expose a Swift attribute to Objective-C without a namespace . Use #nonobjc if you want to keep the attribute available and accessible only in Swift code.
(Addendum/additional official details to #bontoJR well summarizing answer)
From the Swift Language Reference - Attributes [emphasis mine]:
objc
Apply this attribute to any declaration that can be represented in
Objective-C — for example, non-nested classes, protocols, nongeneric
enumerations (constrained to integer raw-value types), properties and
methods (including getters and setters) of classes and protocols,
initializers, deinitializers, and subscripts. The objc attribute tells
the compiler that a declaration is available to use in Objective-C
code.
...
nonobjc
Apply this attribute to a method, property, subscript, or initializer
declaration to suppress an implicit objc attribute. The nonobjc
attribute tells the compiler to make the declaration unavailable in
Objective-C code, even though it is possible to represent it in
Objective-C.
...
Here you can find more details in this Swift Documentation : InteractingWithObjective-C
As an answer of your question, overview from attached link is as below.
#objc : You can use attribute to change the name of a class, property, method, enumeration type, or enumeration case declaration in
your interface as it’s exposed to Objective-C code.
Example : if the name of your Swift class contains a character that isn’t supported by Objective-C, you can provide an alternative name to use in Objective-C.
#nonobjc : It makes a swift declaration unavailable in Objective-C. You can use it to resolve circularity for bridging
methods and to allow overloading of methods for classes imported by
Objective-C.
I am trying to write a registry in Swift that maps from API's (Protocols) to Implementations (Classes). I would like to be able to provide the registry an API and receive back an instance of the class that implements it. In Objective-C this was fairly trivial - just call NSStringFromProtocol on the protocol and then use that as a key for a dictionary containing the classes that implement them. In Swift, however, we do not have this introspective capability. When I try to do the same I am told that MyAPI.protocol does not have a member "mirrorType". My question to you is how, in Swift, without using #objc protocols, I can map from a protocol itself to the class that implements it. Thanks!
By now it's not possible without using #objc. The solution I've found in this case is using the protocol name (string) as a key for the dictionary for this implementations (In my case I'll always have only one instance per Protocol).
Using #objc will force you to have all your implementations returning AnyObject the equivalent (id) in objective-C (if your function does not return a native objective-C type).
Hope that helps.