Check protocol conformance when protocol contains an enum in Swift? - enums

In Swift, in order to check protocol conformance with is or as? downcasting you must mark the protocol with the #objc attribute. Once you mark a protocol with that attribute it seems you can not have a protocol with an enum as a property because enums cannot be represented in Objective-C.
enum Language:String {
case English = "English"
case Spanish = "Spanish"
case German = "German"
}
#objc protocol Humanizable {
var language:Language { get set }
}
You'll get an error: error: property cannot be marked #objc because its type cannot be represented in Objective-C
Here is full example: http://swiftstub.com/475659213/
In the example if you change the Language to String then it works fine.

This is not an answer, but I did spot a compile error in your 'swift stub', Human should be defined as follows:
class Human:Humanizable {
var name:String = "Frank"
var language:Language = .English
}
You were trying to create an enum instance from a string literal.
I am a little surprised that protocol conformance checking requires #obj - that's just ugly!

Related

Not able to extract particular field from "MBRecognitionResult*" in BlinkIDUI-iOS sdk?

I am using your new BlinkIDUI sdk for iOS and I can have the list of all the scanned fields from "recognitionResult.resultEntries" like Secondary ID = Jason", "Primary ID = Bourne", "Sex = F", "Date Of Birth = 3/23/83", "Nationality = UAE", "Document Code = P" from the delegate method "- (void)didScanEntireDocumentWithRecognitionResult:(MBRecognitionResult * _Nonnull)recognitionResult successFrame:(UIImage * _Nullable)successFrame". My query is How to get value for particular key like “"Document Code” ?
Additional Details are:
The Framework addition in Project: Manual.
Xcode version : 10.1.
Language: Objective-C (ARC OFF).
Device: iPhone8 / iOS(11.1.1)
That's because resultEntries is an array not a dictionary,
Use like this:
for (MBField *field in recognitionResult.resultEntries) {
if (field.key == MBFieldKeyDocumentCode) {
}
}
If you are using it in ObjectiveC project then also check if #objc tag is there in front of MBFieldKey public property in "MBField" class, if it is not there just put it as:
public class MBField: NSObject {
#objc public let key: MBFieldKey
#objc public let value: String
.....
}

Migrating Custom Push Row to Eureka 4.1

I'm having quite a hard time migrating a custom row from a previous version of Eureka (around 3.0) to Eureka 4.1. The custom row is a custom push row so the label can have multiple lines and so the view controller that is pushed can have a custom section header. There may be a better way to accomplish that than having a custom row, so that is one possible solution, but I'm not sure if that's a possibility.
So there are 2 problems: The custom row and the the custom selector view controller. Here's the custom row:
open class _StackedPushRow: SelectorRow<StackedPushCell, CustomSelectorViewController<String>> {
public typealias StackedRow = PushRow<String>
var dontClearWhenDisabled: Bool = false
required public init(tag: String?) {
super.init(tag: tag)
cellProvider = CellProvider(nibName: "StackedPushCell")
presentationMode = .show(controllerProvider: ControllerProvider.callback {
return CustomSelectorViewController<Cell.Value> { _ in } },
onDismiss: { vc in
self.cell.update()
_ = vc.navigationController?.popViewController(animated: true)
})
}
}
/// A generic inline row where the user can pick an option from a presented view controller
public final class StackedPushRow: _StackedPushRow, RowType {
required public init(tag: String?) {
super.init(tag: tag)
}
}
and here's the custom selector view controller:
public final class CustomSelectorViewController<T: Equatable>: SelectorViewController<T> {
open override func viewDidLoad() {
super.viewDidLoad()
form.first?.header = HeaderFooterView.caliberStyleSectionHeader(title: row.title ?? "")
}
static func presentationMode<T: Equatable>() -> PresentationMode<SelectorViewController<T>> {
return .show(controllerProvider: ControllerProvider.callback {
return CustomSelectorViewController<T> { _ in }
}, onDismiss: { vc in
_ = vc.navigationController?.popViewController(animated: true)
})
}
}
Here are the errors for the custom row:
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:78:29:
Generic type 'SelectorRow' specialized with too many type parameters
(got 2, but expected 1)
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:85:9:
'super' members cannot be referenced in a root class
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:86:9:
Use of unresolved identifier 'cellProvider'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:88:9:
Use of unresolved identifier 'presentationMode'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:98:20:
Type 'StackedPushRow' does not conform to protocol 'BaseRowType'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:98:20:
Type 'StackedPushRow' does not conform to protocol 'RowType'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:98:20:
Candidate has non-matching type '(String?, (Self) -> Void)'
(Eureka.RowType)
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:98:20:
Type 'StackedPushRow' does not conform to protocol 'Taggable'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:98:20:
Type 'StackedPushRow' does not conform to protocol 'TypedRowType'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:111:9:
Use of unresolved identifier 'validationOptions'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomRows/StackedPushRow.swift:141:9:
'StackedPushRow' is not a subtype of 'BaseRow'
Here are the errors for the custom selector view controller:
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomSelectorViewController.swift:12:64:
Type 'T' does not conform to protocol 'OptionsProviderRow'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomSelectorViewController.swift:14:24:
Method does not override any method from its superclass
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomSelectorViewController.swift:15:9:
'super' members cannot be referenced in a root class
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomSelectorViewController.swift:17:9:
Use of unresolved identifier 'form'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomSelectorViewController.swift:17:80:
Use of unresolved identifier 'row'
/Users/rod/Documents/Development/Caliber/CaliberiOS/H2O/_iOS/H2O/UI/Common/CustomSelectorViewController.swift:22:52:
Argument passed to call that takes no arguments
I've read the issues section on Github and seen examples of others running into this, but every time I try to put the examples in to practice, I run into other problems and I just keep running in circles. I could really use some help.
Thanks.
SelectorRow now only takes a generic Cell type but no view controller. The SelectorViewController is defined by the presentationMode. You should declare your row like:
open class _StackedPushRow: SelectorRow<StackedPushCell>
For your CustomSelectorViewController you need to pass the rowType you are going to use as generic type. That row type must conform to OptionsProviderRow, like ListCheckRow for example.

How to implement two way binding with Swift?

How to implement a two way data binding using Swift 2.0?
Let's assume I have the following model class (using couchbase lite):
#objc(Person)
class Person: NSObject{
#NSManaged var firstName: NSString?
#NSManaged var lastName: NSString?
}
And I like to bind the properties to a given formItemDescriptor like this:
class FormItemDescriptor {
var tag: String?
var value: Any?
}
How would I bind the FormItemDescriptor.value property to the Person.firstName property using 2-way-binding?
So changes in the model appear in the form and vice versa?
Swift does not support bindings out of the box, but you can achieve them with frameworks like ReactiveKit.
For example, when you declare an observable property you can bind it to another observable property.
let numberA: Property<Int>
let numberB: Property<Int>
numberA.bindTo(numberB)
To do bi-directional, just bind in other direction too:
numberB.bindTo(numberA)
Now, whenever you change any of them, the other will change.
Your example is bit harder though because you need to do bindings from #NSManaged properties that cannot be made Observable. It's not impossible, but to work the properties of Person should support key-value observing (KVO).
For example, given a Person object and a label, you could take an Property from KVO-enabled property with the method rValueForKeyPath and then bind that observable to the label, like this:
let person: Person
let nameLabel: UILabel
person.rValueForKeyPath("firstName").bindTo(nameLabel)
If you have an intermediately object like your FormItemDescriptor, then you'll have to make its properties observable.
class FormItemDescriptor {
var tag: Property<String?>
var value: Property<Any?>
}
Now you could establish binding
let person: Person
let descriptor: FormItemDescriptor
person.rValueForKeyPath("firstName").bindTo(descriptor.value)
Because firstName is not observable, you cannot do binding in another direction so you'll have to update it manually whenever value changes.
descriptor.value.observeNext { value in
person.firstName = value as? NSString
}
We also had to do cast to NSString because value is of type Any?. You should rethink that though and see if you can make it a stronger type.
Note that UIKit bindings are coming from ReactiveUIKit framework. Check our documentation of ReactiveKit for more info.

Enum values as parameter default values in Haxe

Is there a way to use enum default parameters in Haxe? I get this error:
Parameter default value should be constant
enum AnEnum {
A;
B;
C;
}
class Test {
static function main() {
Test.enumNotWorking();
}
static function enumNotWorking(e:AnEnum = AnEnum.A){}
}
Try Haxe link.
Update: this feature has been added in Haxe 4. The code example from the question now compiles as-is with a regular enum.
Previously, this was only possible if you're willing to use enum abstracts (enums at compile time, but a different type at runtime):
#:enum
abstract AnEnum(Int)
{
var A = 1;
var B = 2;
var C = 3;
}
class Test3
{
static function main()
{
nowItWorks();
}
static function nowItWorks(param = AnEnum.A)
{
trace(param);
}
}
There's nothing special about the values I chose, and you could choose another type (string, or a more complex type) if it better suits your use case. You can treat these just like regular enums (for switch statements, etc.) but note that when you trace it at runtime, you'll get "1", not "A".
More information: http://haxe.org/manual/types-abstract-enum.html
Sadly enums can't be used as default values, because in Haxe enums aren't always constant.
This piece of trivia was on the old website but apparently hasn't made it into the new manual yet:
http://old.haxe.org/ref/enums#using-enums-as-default-value-for-parameters
The workaround is to check for a null value at the start of your function:
static function enumNotWorking(?e:AnEnum){
if (e==null) e=AnEnum.A;
}
Alternatively, an Enum Abstract might work for your case.

Swift – "Cannot find object class with name"

I'm a newcomer to Cocoa programming, having never got into Objective-C. Now, I am trying to learn it with Swift by going through the Aaron Hillegrass book “Cocoa Programming for Mac OS X, 4e" and implementing everything there in Swift instead of Obj-C. It has been going okay so far, but I hit a roadblock in Chapter 8 (the RaiseMan application).
Here is the Objective-C code from the book:
The header:
#import <Foundation/Foundation.h>
#interface Person : NSObject {
NSString *personName;
float expectedRaise;
}
#property (readwrite, copy) NSString *personName;
#property (readwrite) float expectedRaise;
#end
And here is the implementation
#import "Person.h"
#implementation Person
#synthesize personName;
#synthesize expectedRaise;
- (id)init
{
self = [super init];
if (self) {
expectedRaise = 0.05;
personName = #"New Person";
}
return self;
}
#end
You are then supposed to go to Interface Builder, get an Array Controller, specify Person as its class, and add personName and expectedRaise as its keys.
I rewrote the Person class in Swift as follows:
import Cocoa
class Person: NSObject {
var personName = String()
var expectedRaise = Float()
}
and connected it to the ArrayController as the book told me to.
There is also some code in the Document file:
init() {
employees = NSMutableArray()
println("hi")
super.init()
// Add your subclass-specific initialization here.
var p = Person()
p.personName = "New Person"
p.expectedRaise = 0.05
}
Here is what the interface looks like (SO won't let me post it directly) https://www.dropbox.com/s/e5busxes9ex3ejd/Screenshot%202014-06-13%2019.02.37.png
When I try to run the app and click the "add employees" button, I get this error in the console:
"RaiseMan[9290:303] Cannot find object class with name Person"
So, my question is: what am I doing wrong?
Short answer: you need to specify the class namespace (module name) in Interface Builder: RaiseMan.Person
Details and other options:
This is because Swift adds a prefix to the name of every class injected into the Objective-C runtime in order to avoid name collisions. The prefix follows this convention: _TtC$$AppName%%ClassName, where $$ is the length of AppName and %% is the length of ClassName (see this other SO question for more info).
So in order for the array controller to be able to instantiate the Person class, you need to provide the mangled name in Interface Builder: _TtC8RaiseMan6Person.
Another option is to provide an explicit Objective-C name for your Swift class by using the #objc(<#name#>) attribute:
#objc(Person)
class Person: NSObject {
}
In that case, you can provide the name specified in your #objc attribute to Interface Builder (e.g. Person). See the Using Swift with Cocoa and Objective-C guide for more details.
If I'm reading your example right, and assuming your init() method belongs in the Person class, you need to put the init() method inside the class braces.
Also, declare variables of a certain type with the following format:
var/let (name): (type)(optional)
So, for your example, you would instead have for the Person class:
class Person {
var personName: String
var expectedRaise: Float?
init() {...}
}
You don't necessarily have to subclass from NSObject if you don't need to; in Swift objects don't have to subclass from NSObject (or another parent) if they don't require it. And the "Float?" means that it is of type Optional Float, meaning that it is a Float that may have a value, or it may have no value, depending if the person is expected to get a raise or not (or you may not want it Optional and just set the value to 0).
Keep in mind, if you have multiple build targets, every new class you create needs to be added to that build target. If you don't have its target membership set to the build target, Swift will not find the class file.

Resources