Is RawOptionSetType compatible Objective-C? - ios

I recently discovered the way to create NS_OPTIONS equivalent in Swift, however I cannot use them from Objective-C code in a Objective-C/Swift project.
Here is a sample project I did:
ObjcObject.h & ObjcObject.m
typedef NS_OPTIONS(NSUInteger, MyObjcOption)
{
MyOptionNone = 0,
MyObjcOptionCase01 = 1 << 0,
MyObjcOptionCase02 = 1 << 1,
MyObjcOptionCaseAll = MyObjcOptionCase01 | MyObjcOptionCase02
};
#interface ObjcObject : NSObject
+ (void)printMyObjcOption:(MyObjcOption)option;
#end
#implementation ObjcObject
+ (void)printMyObjcOption:(MyObjcOption)option
{
if (option == 0)
NSLog(#"None");
if (option & MyObjcOptionCase01)
NSLog(#"MyObjcOptionCase01");
if (option & MyObjcOptionCase02)
NSLog(#"MyObjcOptionCase02");
}
#end
SwiftObject.swift
struct MySwiftOption: RawOptionSetType, BooleanType {
private var value: UInt = 0
var rawValue: UInt { return self.value }
init(_ value: UInt) { self.value = value }
init(rawValue value: UInt) { self.value = value }
init(nilLiteral: ()) { self.value = 0 }
var boolValue: Bool { return value != 0 }
static var allZeros: MySwiftOption { return self(0) }
static var None: MySwiftOption { return self(0) }
static var All: MySwiftOption { return self.Case01 | self.Case02 }
static var Case01: MySwiftOption { return self(1 << 0) }
static var Case02: MySwiftOption { return self(1 << 1) }
}
public class SwiftObject: NSObject {
class func printMySwiftOption(option: MySwiftOption) {
if option.rawValue == 0 {
println("None")
}
if option & MySwiftOption.Case01 {
println(".Case01")
}
if option & MySwiftOption.Case02 {
println(".Case02")
}
}
class func sayHello() {
println("Hello")
}
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
MyObjcOption objcOption = MyObjcOptionCase02;
[ObjcObject printMyObjcOption:objcOption];
[SwiftObject sayHello];
// MySwiftOption swiftOption = MySwiftOptionCase02; // Error: Use of undeclared identifier 'MySwiftOption'
// [SwiftObject printMySwiftOption:swiftOption];
return YES;
}
In the Objective-C code, I always get an error Use of undeclared identifier 'MySwiftOption'.
Is it a known issue? Is there a workaround?

You can't expose structs from Swift to ObjectiveC. Actually, have a look at the -Swift.h file to see what you can access from Objective. You won't find MySwiftOptions in there.
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
I, for one, am living with these limitations on a daily bases.
Using Swift with Cocoa and Objective-C at Apple.

Related

Inherit MGLPolygon in Mapbox iOS SDK 3.3.1

I'm trying to define a class Building which inherits MGLPolygon.
MGLPolygon is defined as:
public class MGLPolygon : MGLMultiPoint, MGLOverlay {
public var interiorPolygons: [MGLPolygon]? { get }
public convenience init(coordinates coords: UnsafeMutablePointer<CLLocationCoordinate2D>, count: UInt)
public convenience init(coordinates coords: UnsafeMutablePointer<CLLocationCoordinate2D>, count: UInt, interiorPolygons: [MGLPolygon]?)
}
MGLPolygon's designated initializer is hidden in the swift version of SDK. The following would fail:
class Building: MGLPolygon {
let name: String
init(name: String, coordinates: [CLLocationCoordinate2D]){
self.name = name
super.init(coordinates: &coordinates, count: UInt(coordinates.count))
// Must call a designated initializer of the superclass 'MGLPolygon'
}
}
I checked the original SDK code in Objective-C:
#implementation MGLPolygon
#dynamic overlayBounds;
+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count {
return [self polygonWithCoordinates:coords count:count interiorPolygons:nil];
}
+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MGLPolygon *> *)interiorPolygons {
return [[self alloc] initWithCoordinates:coords count:count interiorPolygons:interiorPolygons];
}
- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(NSArray<MGLPolygon *> *)interiorPolygons {
if (self = [super initWithCoordinates:coords count:count]) {
if (interiorPolygons.count) {
_interiorPolygons = interiorPolygons;
}
}
return self;
}
- (mbgl::LinearRing<double>)ring {
NSUInteger count = self.pointCount;
CLLocationCoordinate2D *coordinates = self.coordinates;
mbgl::LinearRing<double> result;
result.reserve(self.pointCount);
for (NSUInteger i = 0; i < count; i++) {
result.push_back(mbgl::Point<double>(coordinates[i].longitude, coordinates[i].latitude));
}
return result;
}
- (mbgl::Annotation)annotationObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate {
mbgl::Polygon<double> geometry;
geometry.push_back(self.ring);
for (MGLPolygon *polygon in self.interiorPolygons) {
geometry.push_back(polygon.ring);
}
mbgl::FillAnnotation annotation { geometry };
annotation.opacity = [delegate alphaForShapeAnnotation:self];
annotation.outlineColor = [delegate strokeColorForShapeAnnotation:self];
annotation.color = [delegate fillColorForPolygonAnnotation:self];
return annotation;
}
#end
However, unfortunately I'm not good with Objective-C and I don't understand the code.
What am I asking?
What is the designated initializer of MGLPolygon in Swift? What does it take as parameters?
Extra question
Why is the designated initializer hidden?
I think you will need to create a class method in your subclass (e.g. +buildingWithCoordinates:count:) which calls super's implementation in order to handle this.
My solution according to #incanus's advice:
class Building: MGLPolygon {
var name: String?
afterInit(name: String){
self.name = name
}
}
func initBuilding(name: String, coordinates: [CLLocationCoordinate2D]) -> Building {
let building = Building(coordinates: &coordinates, count: UInt(coordinates.count))
building.afterInit(name)
return building
}
It's not elegant but it works.

Static variables in protocols

I have a protocol A which has a static variabe x. B is a implementation of A. In Class C I pass an instance of B and assigned it to a. How can I access 2 (value of x in class B) from it ?
protocol A {
static var x : Int { get }
}
class B : A {
static var x: Int {
return 2
}
}
class C {
// instance of B is assigned to a.
let a: A
print(a.x)
}
A static variable belongs to the class, not the instance. You can refer to the class by calling dynamicType:
print(a.dynamicType.x)
So things have changed a little bit since this answer was posted
type(of: a).x
Within class C a is a property that holds an instance of a type adhering to protocol A. A static variable (otherwise known as a class variable) however is not accessible from an instance, it is accessible from the class, so you will access the value in the following way:
B.x
An instance variable would be a different matter and the code for this would be:
protocol A {
var x : Int { get }
}
class B : A {
var x: Int {
return 2
}
}
class C {
// instance of B is assigned to a.
let a: A
init() {
a = B()
}
}
C().a.x
These can happily co-exist with the same variable names:
protocol A {
static var x : Int { get }
var x : Int { get }
}
class B : A {
static var x: Int {
return 2
}
var x: Int {
return 2
}
}
class C {
// instance of B is assigned to a.
let a: A
init() {
a = B()
}
}
C().a.x
B.x

How to iterate over CustomCollection generic type in Swift

I have a custom collection class with an embedded array written in Obj-c. The class implements NSFastEnumerator protocol in order to be iterable in Obj-c.
For my Swift classes I had to add the following code based on apporaches on SOF.
extension CustomCollection: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
Which again makes it iterable in Swift classes.
All is good until I need to use this class as a Generic type in one of my Swift base classes.
class SomeBaseClass<T: CustomCollection> {
typealias Collection = T
var model: Collection?
// Implementation goes here
}
When I try to iterate over my 'model' property, I get Command Signal Failure Error during build.
Any idea how this needs to be done and whether it's even possible to be done?
Running XCode 7 beta 6 and Swift 2.0
Thanks.
Here is what I came up with Xcode 7.0.1:
First the CustomCollection class. I've keep it simple since I don't know what yours is doing.
public class CustomCollection: NSFastEnumeration
{
var array: NSMutableArray = []
#objc public func countByEnumeratingWithState(state: UnsafeMutablePointer<NSFastEnumerationState>, objects buffer: AutoreleasingUnsafeMutablePointer<AnyObject?>, count len: Int) -> Int {
var index = 0
if state.memory.state != 0 {
index = Int(state.memory.state)
}
if index >= self.array.count {
return 0
}
var array = Array<AnyObject?>()
while (index < self.array.count && array.count < len)
{
array.append(self.array[index++])
}
let cArray: UnsafeMutablePointer<AnyObject?> = UnsafeMutablePointer<AnyObject?>.alloc(array.count)
cArray.initializeFrom(array)
state.memory.state = UInt(index)
state.memory.itemsPtr = AutoreleasingUnsafeMutablePointer<AnyObject?>.init(cArray)
return array.count
}
}
Then there's the code you provided.
extension CustomCollection: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
class SomeBaseClass<T: CustomCollection>
{
typealias Collection = T
var model: Collection?
}
With all this, I'm able to run the following
var myModel = CustomCollection()
myModel.array.addObject("This")
myModel.array.addObject("is")
myModel.array.addObject("a")
myModel.array.addObject(["complex", "test"])
var myVar = SomeBaseClass()
myVar.model = myModel
for myObject in myVar.model!
{
print(myObject)
}
And the console prints
This
is
a
(
complex,
test
)
Hope it helps!

set associated objects for literal value in Swift

Well, this possibly a duplicated question. I've found some questions like this one:
Is there a way to set associated objects in Swift?
However, I want to add an Int property into swift's extension and these answers in the link above doesn't work.
Here's my code:
import ObjectiveC
var xoAssociationKey: UInt8 = 0
extension NSData {
var position: Int {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as Int
}
set {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
override convenience init() {
self.init()
position = 0
}
}
And I get fatal error: unexpectedly found nil while unwrapping an Optional value everytime I access position
FYI, I did find a solution for this error in Objective C and I'm looking for a swift solution. Here's my code in objective C if you interested:
static char PROPERTY_KEY;
#implementation NSData (Extension)
#dynamic position;
- (NSInteger)position {
return [objc_getAssociatedObject(self, &PROPERTY_KEY) integerValue];
}
- (void)setPosition:(NSInteger)position {
// Must convert to an object for this trick to work
objc_setAssociatedObject(self, &PROPERTY_KEY, #(position), OBJC_ASSOCIATION_COPY);
}
- (instancetype)init {
self = [super init];
if (self) {
self.position = 0;
}
return self;
}
NSData is part of a class cluster, so your custom init method is not necessarily called,
e.g.
let d = NSMutableData()
does not use your init method. The next problem is that your init method calls
itself recursively, therefore
let d = NSData()
crashes with a stack overflow. Note also that the Objective-C code relies on
undefined behaviour, because it replaces a method in a class extension.
So better remove your custom initialization, and change the getter to
return a default value if the associated object has not been set.
This can easily be achieved with an optional cast (as? Int) and the
nil-coalescing operator (??):
extension NSData {
var position: Int {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? Int ?? 0
}
set {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
}
}

NSFastEnumeration in Swift

I am trying to convert an Objective-C project to swift, but I am unable to find how to use NSFastEnumeration for an object of a class that conforms to NSFastEnumeration.
Here is the code in ObjC:
// get the decode results
id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];
ZBarSymbol *symbol = nil;
for(symbol in results)
// just grab the first barcode
break;
so far I tried to find how to do this, but this doe not seems work, here is the swift code:
var results: ZBarSymbolSet = infoDictionary?.objectForKey(ZBarReaderControllerResults) as ZBarSymbolSet
var symbol : ZBarSymbol? = nil;
for symbol in results
{ //just grab first barcode
break;
}
the error comes in for condition - "ZBarSymbolSet" does not have a member named "Generator"
What am I doing wrong?
Here is the screen shot
After a while poking around the swift framework files, I finally found this nice class called NSFastGenerator. NSSet and friends seem to be using the same Generator.
For ZBarSymbolSet, here's how you'd extend it to support for-in loops:
extension ZBarSymbolSet: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
Update: Looks like Swift 2.0's protocol extensions fixed this for us!
Your defined class ZBarSymbolSet needs to implement the Swift SequenceType interface in order to be usable in for <identifier> in <sequence> syntax. The SequenceType interface is
protocol SequenceType : _Sequence_Type {
typealias Generator : GeneratorType
func generate() -> Generator
}
and thus you see the mention of Generator as reported in your error message.
Also in the syntax:
for <identifier> in <sequence> {
<statements>
}
the <identifer> is only in scope for <statements>. Thus your second use of symbol in the if will be out of scope and an error. One proper idiom would be:
var symbolFound : ZBarSymbol?
for symbol in result {
symbolFound = symbol
break
}
if symbolFound ...
If course, but the time ZBarSymbolSet implements SequenceType it would also implement CollectionType with subscript and thus the whole 'find the first element' code would be var symbol = result[0]
Here is John Estropia's answer for Swift 3:
extension ZBarSymbolSet: Sequence {
public typealias Iterator = NSFastEnumerationIterator
public func makeIterator() -> NSFastEnumerationIterator {
return NSFastEnumerationIterator(self)
}
}
Then you for-in loop would look like this:
for element in results {
let symbol = element as! ZBarSymbol
// ...
}
This answer could be improved by also adopting the IteratorProtocol so you can specify the element associated type as ZBarSymbol. I have not figured out how to do this yet.
Step1:
extension ZBarSymbolSet: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
Step2:
var results: NSFastEnumeration = info.objectForKey(ZBarReaderControllerResults) as NSFastEnumeration
var symbolFound : ZBarSymbol?
for symbol in results as ZBarSymbolSet {
symbolFound = symbol as? ZBarSymbol
break
}
resultString = NSString(string: symbolFound!.data)
Here's a way to do it without writing an extension
var iterator = NSFastEnumerationIterator(collection)
while let element = iterator.next() {
// use element
}
Also, if you know that all objects in your ZBarSymbolSet are ZBarSymbol objects (since ObjC doesn't enforce all objects being ZBarSymbol objects), you can do the following:
extension ZBarSymbolSet {
public struct ZBarSymbolSetIterator {
public typealias Element = ZBarSymbol
private let enumerator: NSFastEnumerationIterator
init(_ symbols: ZBarSymbolSet) {
self.enumerator = NSFastEnumerationIterator(symbols)
}
public mutating func next() -> ZBarSymbol {
if let object = self.enumerator.next() {
return object as? ZBarSymbol
}
else { return nil }
}
}
public func makeIterator() -> ZBarSymbolSetIterator {
return ZBarSymbolSetIterator(self)
}
}
Now your for-loop will look like this:
for element in results {
// element is a ZBarSymbol
}

Resources