set associated objects for literal value in Swift - ios

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))
}
}
}

Related

Why i can not assign true to a BOOL in objective C?

Created a BOOL *myBool variable in implementation file as below:
#interface myClass ()
{
BOOL *myBool;
}
- (void)viewDidLoad {
[super viewDidLoad];
myBool = false; // no error
}
- (IBAction)myBtnClickd:(UIButton *)sender {
if (!myBool) {
myBool = true; //error: incompatible integer to pointer conversion assigning to BOOL from int.
}
else {
myBool = false;
}
}
why I can not assign true to it and I am not assigning any int as we can see in the code, I do not want to make it a property.
You can't assign true to BOOL, because you are not trying to assign to BOOL, but to BOOL*. A pointer to BOOL. Assigning false to a BOOL* works for some weird reasons deeply hidden in the C standard.
Anyway, this is Objective-C. Why are you using true and false in Objective-C? It's either YES or NO.
Anyway, what is that nonsense code? Just write myBool = ! myBool.
Anyway, what are you doing having instance variables in an Objective-C class that don't start with an underscore, and why are you not using properties? That code should be either
self.myBool = ! self.myBool;
or
_myBool = ! _myBool;
And of course the BOOL* should be a BOOL. BOOL is not a reference type, it's a value type.

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.

Error message when defining struct

I am writing a struct in Swift:
struct LevelDictionary {
let kNumberOfSegments: Int = 10
static func loadLevelData() -> NSDictionary {
for var segmentNumber = 0; segmentNumber < kNumberOfSegments; ++segmentNumber {
//My code here
}
return dictionary
}
}
For some reason I get an error on compiling: Instance member 'kNumberOfSegments' cannot be used on type 'LevelDictionary'. What am I missing? I get the same error when I set up LevelDictionary as a Class.
loadLevelData() is a static function which is called on "class" level
LevelDictionary.loadLevelData()
To use kNumberOfSegments in the static function it must be static as well
static let kNumberOfSegments: Int = 10
The direct answer to your question is that you can't use a property in class scope.
A different answer is that you seem to want a static function that returns a dictionary after doing something a certain number of times; which is why you have kNumberOfSegments in the first place. But do you really need to have a variable for something that you aren't going to use again. Another way to do this is to have a default variable in your class method:
struct LevelDictionary {
static func loadLevelData(numberOfSegments: Int = 10) -> NSDictionary {
for segment in 0 ..< numberOfSegments {
// your code here
}
return dictionary
}
}
Now you can call the method without an argument to use the default
let dictionary = LevelDictionary.loadLevelData() // Will use 10 segments
Or you can use a parameter to override the default
let dictianary = LevelDictionary.loadLevelData(20) // Will use 20 segments
You can't use instance member variables/constants inside the static function. (In terms of Objective C you can't use instance member objects inside class function)
Either you should declare the kNumberOfSegments as static or make that function as non-static. I prefer the first option,
struct LevelDictionary
{
static let kNumberOfSegments: Int = 10
static func loadLevelData() -> NSDictionary
{
for var segmentNumber = 0; segmentNumber < kNumberOfSegments; ++segmentNumber
{
//My code here
}
return dictionary
}
}

Is RawOptionSetType compatible Objective-C?

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.

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