What does #objc dynamic var mean in Swift 4 - dynamic

Could you briefly explain what #objc and dynamic mean in Swift 4 using Xcode 9.x?
With tries and errors and following articles in the stackoverflow, I have eventually achieved this snippet to work. But I would like to know a little bit about those magical keywords.
class SampleViewController: NSViewController {
#objc class Parameters : NSObject {
#objc dynamic var value1: Double = 0 // bound to Value of a NSTextfield with NumberFormatter
#objc dynamic var value2: Double = 0 // as "parameters.value1" for the Model Key Path
}
#objc dynamic var parameters = Parameters()
#objc dynamic var value3: Double { // in the similar way as "value3" for the Model Key Path
get {
return parameters.value1 + parameters.value2
}
}
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
switch key {
case "value3" :
return Set(["parameters.value1", "parameters.value2"])
default:
return super.keyPathsForValuesAffectingValue(forKey: key)
}
}
}

Having fun with Xcode and its disassembler, I have found some. Thanks to Mike Henderson's comment.
Firstly, adding a #objc modifier seems to have the compiler write its corresponding symbol name in a __OBJC segment of executables and/or library files, which will be then used by the Objective-C run-time system.
otool -o filename command shows us the contents of __OBJC segment.
Secondly, adding a dynamic modifier seems to have the compiler insert additional assembler codes to interact with the Objective-C run-time system. The additional code realizes that accessing dynamic properties will be done through objc_msgSend() and its related functions. Similarly, calling dynamic methods also will be done through objc_msgSend().
Now, in my understandings, the jargon dynamic dispatch implies use of objc_msgSend() while static dispatch does no use of it. In the latter case, both accessing variables and calling functions will be done without intervention of the Objective-C run-time system, which is in the similar, but not exactly same, way of C++ ABI.
Apparently, static one is faster than dynamic one. But static one is incapable of Objective-C's magical benefits, though. With the programming language Swift, we have opportunities to utilize both aspects by choosing either static or dynamic dispatch depending on the situation, by omitting or adding those magical keywords, respectively.
Thanks!
Further readings:
Objective-C Runtime
Using Swift with Cocoa and Objective-C (Swift 4.0.3)

#objc means you want your Swift code (class, method, property, etc.) to be visible from Objective-C.
dynamic means you want to use Objective-C dynamic dispatch.
Swift 3 - dynamic vs #objc

Related

Would it have any bad influence to add #objc to a Swift method or variable?

I got some Objective-C files imported in Swift project, and tried to access some Swift classes.
According to Apple's Guideline, I added the #objc to the method I wanted it to be exposed to Objective-C files.
But the question is, does this "#objc" have any side effect to my Swift project?
The following code is a singleton local data manager.
#objc class LocalDataManager {
#objc public static let shared = LocalDataManager()
private init() {}
#objc var nickName: String {
get { return loadData("nickName") } // loadData is a convenience access method to UserDefaults
set { UserDefaults.standard.set(newValue, forKey: "nickName") }
}
}
The #objc keyword does severely affect performance. The Apple docs states:
applying the #objc attribute can increase the compiled size of an app and adversely affect performance.
Therefore, only use #objc if you really need too.

Swift class properties not initialized when constructed by Objective C code

I'm attempting to create a class in Swift 3 to implement a Cordova plugin. I have this building and running, but the application crashes whenever any properties of the class are accessed. I've tried two ways of initializing the class:
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players = [UUID:DSFPlayerHandler] ();
...
}
and
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players :[UUID:DSFPlayerHandler];
override init () {
players = [:];
}
...
}
However, when my players property is used, the result is a EXC_BAD_ACCESS exception, with an address that looks like a null pointer dereference.
The object is being created by Objective C code, which is a language I have no familiarity with at all, but I think this is the line that creates it:
obj = [[NSClassFromString(className)alloc] initWithWebViewEngine:_webViewEngine];
The CDVPlugin class contains a comment stating that initWithWebViewEngine should not be overridden (and indeed I do not seem to be able to override this method, because while it is declared in the CDVPlugin.m file, it isn't mentioned in CDVPlugin.h, so the Swift compiler doesn't seem to know about it), but rather initialization code should be placed in a method called pluginInitialize instead. However, if I do that I get a compiler error ("Class DSFMediaCentre has no initializers").
Furthermore, if I put my init() method back in and set it to call pluginInitialize(), like this:
override init () {
super.init(); // necessary otherwise next line is an error
pluginInitialize();
}
override func pluginInitialize() {
players = [:];
}
the error then changes to "Property 'self.players' not initialized at super.init call".
How do I make this class initialize correctly?
You have a mismatch between the strict initialization system required by the language and the procedure used by the framework you're working with.
Swift demands that a) properties be initialized as part of object construction, and b) that construction be chained to the type's supertype. But the CDVPlugin type is doing the construction on your behalf; you don't have the ability to customize it. (This makes more sense in ObjC, because it doesn't have the same compile-time restrictions as Swift.)
The situation is similar to unpacking an object from a nib file. In that case too, because it's the nib loading system that's constructing your object, you don't have the ability to customize the initializer. Your type will always be constructed by init(coder:). In a certain sense, your initialization point moves further down, to awakeFromNib(), and among other things, that forces outlets to other objects in the archive to be declared as optional, usually implicitly unwrapped.
The same solution should avail you here. You should consider pluginInitialize() to be your initialization point. The language then requires that properties be optional, since they are not filled at its initialization point. Therefore, make the property an IUO:
#objc(DSFMediaCentre)
class DSFMediaCentre : CDVPlugin
{
var players :[UUID:DSFPlayerHandler]!
override func pluginInitialize() {
players = [:];
}
}
and all should be well.
The other solution is to use lazy keyword
lazy var players :[UUID:DSFPlayerHandler] = [:]
So, you don't need to initialize players in initializer but still make sure players always non-nulable

How to decipher "objc_method_description" from protocol method description list?

I have some Swift 3 code to decode an iOS Objective-C protocol (which has a Swift counterpart). After concluding Swift 3 reflection was not developed enough to accomplish what I needed, I stumbled on the objc runtime method protocol_copyMethodDescriptionList(), which returns an array of the following C structs:
struct objc_method_description
SEL name;
char *types;
};
The code fetches a list of protocol selector names, but not sure what's being returned in the type field. I'm confused about how to properly decode objc_method_description.type values.
What I'm getting in the the type fields are in a cryptic format, for example, "B48#0:8#16{_NSRange=QQ}24#40" At first I thought it was a problem in how I was converting C strings, but after some study, I suspect it actually is an encoding of the parameters, similar how Java's JVM passes around method signatures. But I still have no reference by which to decode it.
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var noteView : UITextView!
func decodeProto() {
var methodCount : UInt32 = 1
if let methodList = protocol_copyMethodDescriptionList(UITextViewDelegate.self,
false, true, UnsafeMutablePointer<UInt32>(&methodCount)) {
for i in 0..<Int(methodCount) {
let methodDesc = methodList[i];
let name = methodDesc.name
let types = String(validatingUTF8: methodDesc.types)
print("\(name) \(types)")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
decodeProto()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The output of that in the XCode console is:
Optional(textViewDidBeginEditing:) Optional("v24#0:8#16")
Optional(textViewDidEndEditing:) Optional("v24#0:8#16")
Optional(textViewShouldBeginEditing:) Optional("B24#0:8#16")
Optional(textViewShouldEndEditing:) Optional("B24#0:8#16")
Optional(textView:shouldChangeTextInRange:replacementText:) Optional("B48#0:8#16{_NSRange=QQ}24#40")
Optional(textView:shouldChangeTextInRange:replacementText:) Optional("B48#0:8#16{_NSRange=QQ}24#40")
.
.
.
What is returned objc_method_description.type field?
A method signature encoding scheme
... which includes an obsolete stack offset representation
In other words, to get a usable method signature encoding from the type field, just just extract the alpha-symbolic characters in left-to-right order discarding the digits.
Supporting documentation:
Objective-C Method Signature Type Encoding
Meaning of the numeric elements in signature encoding, explained by an Apple developer
Playing around with Type Encodings
class_addmethod (according to #Richard_J_Ross, this is the root of it)
Objective-C Runtime Programming Guide
Objective-C Runtime Reference

Extending a Swift class with Objective-C category

Im in a situation where I need to use Objective-C category to extend a Swift class. I've done something as follows:
In "SomeClass.swift":
class SomeClass: NSObject {
}
In "SomeClass+Extension.h":
#import "Project-Swift.h"
#interface SomeClass (Extension)
-(void)someMethod();
#end
This has worked well. And if I try to use the SomeClass extension in my Objective C code, it is fine.
The problem is, if I want to use someMethod() in a another Swift class, I will need to put the SomeClass+Extension.h file into my ObjC-BridgingHeader.h file.
But doing this will cause a circular dependency, because SomeClass+Extension.h also imports Project-Swift.h.
Does anyone have a good way to get around this?
Please note that simply forward declaring the class in the category header will not work, as categories cannot use forward declarations for it's own implementation as so:
#class SomeClass without importing Project-Swift.h will give a compile error.
The Bad
i too have been fighting this issue a bunch. unfortunately the documentation pretty explicitly states that this pattern is not allowed:
To avoid cyclical references, don’t import Swift code into an
Objective-C header (.h) file. Instead, you can forward declare a Swift
class or protocol to reference it in an Objective-C interface.
Forward declarations of Swift classes and protocols can only be used
as types for method and property declarations.
also throughout the the linked page you will notice it keeps mentioning to import the generated header specifically into the .m file:
To import Swift code into Objective-C from the same target
Import the Swift code from that target into any Objective-C .m file
within that target
The Good
one solution that may work for you is to create a swift extension that redefines each method you need in the category. it is fragile and ugly, but arguably the cleanest solution.
/**
Add category methods from objc here (since circular references prohibit the ObjC extension file)
*/
extension SomeClass {
#nonobjc func someMethod() {
self.performSelector(Selector("someMethod"))
}
}
adding the #noobjc to the front allows the
same method signature to be used w/o overriding the ObjC implementation
now the import "SomeClass+Extension.h" from the bridging
header can be removed
if support for more than two input params is needed, or tighter type coupling is desired i would recommend using the runtime to call the underlying function. a great description is here.
From the Interoperability guide, we cannot directly access the subclassed / categorized / extensioned Objc-objects for the .swift [SomeClass] class.
But as a turn-around, we can do this:
For Variables , we can do this:
extension Class {
private struct AssociatedKeys {
static var DescriptiveName = "sh_DescriptiveName"
}
var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
}
For Methods, we can use method_swizzling which is not recommended.
As one simple solution, you can move the extension to your Swift code. Then you won't have any dependency problems.

Is it possible to use Swift's Enum in Obj-C?

I'm trying to convert some of my Obj-C class to Swift. And some other Obj-C classes still using enum in that converted class. I searched In the Pre-Release Docs and couldn't find it or maybe I missed it. Is there a way to use Swift enum in Obj-C Class? Or a link to the doc of this issue?
This is how I declared my enum in my old Obj-C code and new Swift code.
my old Obj-C Code:
typedef NS_ENUM(NSInteger, SomeEnum)
{
SomeEnumA,
SomeEnumB,
SomeEnumC
};
#interface SomeClass : NSObject
...
#end
my new Swift Code:
enum SomeEnum: NSInteger
{
case A
case B
case C
};
class SomeClass: NSObject
{
...
}
Update: From the answers. It can't be done in Swift older version than 1.2. But according to this official Swift Blog. In Swift 1.2 that released along with XCode 6.3, You can use Swift Enum in Objective-C by adding #objc in front of enum
As of Swift version 1.2 (Xcode 6.3) you can. Simply prefix the enum declaration with #objc
#objc enum Bear: Int {
case Black, Grizzly, Polar
}
Shamelessly taken from the Swift Blog
Note: This would not work for String enums or enums with associated values. Your enum will need to be Int-bound
In Objective-C this would look like
Bear type = BearBlack;
switch (type) {
case BearBlack:
case BearGrizzly:
case BearPolar:
[self runLikeHell];
}
To expand on the selected answer...
It is possible to share Swift style enums between Swift and Objective-C using NS_ENUM().
They just need to be defined in an Objective-C context using NS_ENUM() and they are made available using Swift dot notation.
From the Using Swift with Cocoa and Objective-C
Swift imports as a Swift enumeration any C-style enumeration marked with the NS_ENUM macro. This means that the prefixes to enumeration value names are truncated when they are imported into Swift, whether they’re defined in system frameworks or in custom code.
Objective-C
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
Swift
let cellStyle: UITableViewCellStyle = .Default
From the Using Swift with Cocoa and Objective-C guide:
A Swift class or protocol must be marked with the #objc attribute to
be accessible and usable in Objective-C. [...]
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics Tuples / Enumerations defined in Swift / Structures defined in
Swift / Top-level functions defined in Swift / Global variables defined in
Swift / Typealiases defined in Swift / Swift-style variadics / Nested types /
Curried functions
So, no, you can't use a Swift enum in an Objective-C class.
Swift 4.1, Xcode 9.4.1:
1) Swift enum must be prefixed with #objc and be Int type:
// in .swift file:
#objc enum CalendarPermission: Int {
case authorized
case denied
case restricted
case undetermined
}
2) Objective-C name is enum name + case name, eg CalendarPermissionAuthorized:
// in .m file:
// point to something that returns the enum type (`CalendarPermission` here)
CalendarPermission calPermission = ...;
// use the enum values with their adjusted names
switch (calPermission) {
case CalendarPermissionAuthorized:
{
// code here
break;
}
case CalendarPermissionDenied:
case CalendarPermissionRestricted:
{
// code here
break;
}
case CalendarPermissionUndetermined:
{
// code here
break;
}
}
And, of course, remember to import your Swift bridging header as the last item in the Objective-C file's import list:
#import "MyAppViewController.h"
#import "MyApp-Swift.h"
If you prefer to keep ObjC codes as-they-are, you could add a helper header file in your project:
Swift2Objc_Helper.h
in the header file add this enum type:
typedef NS_ENUM(NSInteger, SomeEnum4ObjC)
{
SomeEnumA,
SomeEnumB
};
There may be another place in your .m file to make a change: to include the hidden header file:
#import "[YourProjectName]-Swift.h"
replace [YourProjectName] with your project name. This header file expose all Swift defined #objc classes, enums to ObjC.
You may get a warning message about implicit conversion from enumeration type... It is OK.
By the way, you could use this header helper file to keep some ObjC codes such as #define constants.
If you (like me) really want to make use of String enums, you could make a specialized interface for objective-c. For example:
enum Icon: String {
case HelpIcon
case StarIcon
...
}
// Make use of string enum when available:
public func addIcon(icon: Icon) {
...
}
// Fall back on strings when string enum not available (objective-c):
public func addIcon(iconName:String) {
addIcon(Icon(rawValue: iconName))
}
Of course, this will not give you the convenience of auto-complete (unless you define additional constants in the objective-c environment).
After researching this, I kept finding only partial answers, so I created an entire example of a Swift App bridged to Objective C that has Swift enums used by Objective C code and Objective C enums used by Swift code. It is a simple Xcode project that you can run and experiment with. It was written using Xcode 10.3 with Swift 5.0
Example Project
In case you are trying to observe an enum which looks like this:
enum EnumName: String {
case one = "One"
case two = "Two"
}
this workaround helped me.
Observable Class:
create #objc dynamic var observable: String?
create your enum instance like this:
private var _enumName: EnumName? {
didSet {
observable = _enumName!.rawValue
}
}
Observer Class:
create private var _enumName: EnumName?
create private let _instance = ObservableClass()
create
private var _enumObserver: NSKeyValueObservation = _instance.observe(\.observable, options: .new, changeHandler: { [weak self] (_, value) in
guard let newValue = value.newValue else { return }
self?._enumName = EnumName(rawValue: period)!
})
Than's it. Now each time you change the _enumName in the observable class, an appropriate instance on the observer class will be immediately updated as well.
This is of course an oversimplified implementation, but it should give you an idea of how to observe KVO-incompatible properties.
this might help a little more
Problem statement :- I have enum in swift class, which I am accessing form other swift classes, and Now I need to access it form my one of the objective C class.
Before accessing it from objective-c class :-
enum NTCType {
case RETRYNOW
case RETRYAFTER
}
var viewType: NTCType?
Changes for accessing it from objective c class
#objc enum NTCType :Int {
case RETRYNOW
case RETRYAFTER
}
and add a function to pass it on the value
#objc func setNtc(view:NTCType) {
self.viewType = view; // assign value to the variable
}