I'm trying to convert this example here:
https://github.com/NilStack/NKWatchChart
to swift2-code. I'm having problems with exactly this part:
data01.getData = ^(NSUInteger index) {
CGFloat yValue = [data01Array[index] floatValue];
return [NKLineChartDataItem dataItemWithY:yValue];
};
getData is defined as follows:
public class NKLineChartData : NSObject {
public var color: UIColor!
public var alpha: CGFloat
public var itemCount: UInt
public var getData: LCLineChartDataGetter!
The LCLineChartDataGetter is a type alias:
public typealias LCLineChartDataGetter = (UInt) -> NKLineChartDataItem!
I'm pretty new to objective-c and swift, so maybe someone can point me to how the assignment can be done using swift!
I found the solution myself. The equivalent in Swift is:
data01.getData = {(index : UInt) -> NKLineChartDataItem in
let yValue : CGFloat = CGFloat(data01Array[Int(index)] as! NSNumber)
return NKLineChartDataItem.init(y: yValue)
}
Maybe someone has a better/shorter solution but the above seems to work ;)
The thing being assigned to getData in Obj-C is a block that takes an integer parameter and returns a NKLineChartDataItem. (Check something like http://goshdarnblocksyntax.com if you need help remembering these.)
In Swift, the formal syntax for the corresponding closure looks like this:
data01.getData = { (index: UInt) -> NKLineChartDataItem! in
// body
}
But you can also use type inference to shorten it:
data01.getData = { index in
// body
}
Looking at more of the example from the NKWatchChart readme you linked to:
NSArray * data01Array = #[#60.1, #160.1, #126.4, #0.0, #186.2, #127.2, #176.2];
NKLineChartData *data01 = [NKLineChartData new];
data01.color = NKGreen;
data01.alpha = 0.9f;
data01.itemCount = data01Array.count;
data01.inflexionPointStyle = NKLineChartPointStyleTriangle;
data01.getData = ^(NSUInteger index) {
CGFloat yValue = [data01Array[index] floatValue];
return [NKLineChartDataItem dataItemWithY:yValue];
};
You can probably shorten this even more through native Swift types:
let data01Array: [CGFloat] = [60.1, 160.1, 126.4, 0.0, 186.2, 127.2, 176.2]
let data01 = NKLineChartData()
data01.color = NKGreen
data01.alpha = 0.9
data01.itemCount = data01Array.count
data01.inflexionPointStyle = .Triangle;
data01.getData = { NKLineChartDataItem(y: data01Array[$0]) }
You don't need to convert swift to objective-c to make it run in your project. Just drag the swift into your project and Xcode will automatically generate a bridging header for you
Related
Before you vote on this question I would like to how dumb what I am trying to do is. Maybe I still don't understand properly enums.
So, I am working on a project that uses an Obj-C framework. This framework contains enums:
typedef enum : NSInteger
{
kImageSizeUnknown = 0,
kImageSize75,
kImageSize110,
kImageSize170,
kImageSize220,
kImageSize300,
kImageSize450,
kImageSize720,
kImageSize1080,
/* Size aliases */
kImageSizeThumbnail = kImageSize75,
kImageSizeSmall = kImageSize170,
kImageSizeMedium = kImageSize450,
kImageSizeLarge = kImageSize720,
kImageSizeXLarge = kImageSize1080
} GnImageSize;
I want somehow be able to declare a Swift enum that returns values of the Obj-C enum (That might be the silly part).
That is how I have at the moment.
enum GNImageSize:Int, CaseIterable{
case thumbnail
case sizeSmall
func toGnImageSize() -> GnImageSize {
switch self {
case .thumbnail:
return kImageSizeThumbnail
case .sizeSmall:
return kImageSizeSmall
}
}
static func toGnImageSize(sizeType:GNImageSize) -> GnImageSize {
switch sizeType {
case .thumbnail:
return kImageSizeThumbnail
case .sizeSmall:
return kImageSizeSmall
}
}
}
However when I do:
enum GNImageSize:Int, CaseIterable{
case thumbnail = GnImageSize.kImageSizeThumbnail
case sizeSmall = GnImageSize.kImageSizeSmall
}
I get the following error message:
Raw value for enum case must be a literal
Thank you.
EDIT
Function that comunicates with Obj-c functions
func getArtworkURL(forImageType imageType:GNImageSize, shouldFindAlternatives:Bool, highQualityFirst:Bool)->URL?{
if let asset = coverArt()?.asset(GnImageSize(rawValue: imageType.rawValue)), let assetURL = asset.urlHttp(){
return URL(string:assetURL)
}
else{
if shouldFindAlternatives{
if highQualityFirst{
for size in GNImageSize.allCases.reversed(){
if let asset = coverArt()?.asset(GnImageSize(rawValue: size.rawValue)), let assetURL = asset.urlHttp(){
return URL(string:assetURL)
}
}
}
else{
for size in GNImageSize.allCases{
if let asset = coverArt()?.asset(GnImageSize(rawValue: size.rawValue)), let assetURL = asset.urlHttp(){
return URL(string:assetURL)
}
}
}
}
}
return nil
}
Where
-(nullable GnAsset*) asset: (GnImageSize)imageSize;
The five size aliases have the (raw) values 1, 3, 6, 7, 8 so declare a Swift enum
enum GNImageSize : Int {
case thumbnail = 1
case small = 3
case medium = 6
case large = 7
case xLarge = 8
}
To use the Int value in Swift use for example
GNImageSize.thumbnail.rawValue
Alternatively create a custom enum with static properties to map the types
enum GNImageSize {
static let thumbnail = GnImageSize(0)
static let small = GnImageSize(3)
static let medium = GnImageSize(6)
static let large = GnImageSize(7)
static let xLarge = GnImageSize(8)
}
I don't understand that in 2018 ObjC frameworks still use the Stone-age syntax typedef enum : NSInteger { ... } Foo; rather than Swift compliant syntax typedef NS_ENUM (NSInteger, Foo) { ... }; The latter syntax exists for 6 years (iOS 6, macOS 10.8).
That raw-value style enum grammar error.
raw-value-assignment → = raw-value-literal
raw-value-literal → numeric-literal | static-string-literal | boolean-literal
So, only numeric(numbers like -7, 0x10, 0b010), static string(characters in quotes, like "foo") and boolean(true or false) literals are allowed there.
Anything else won't work.
I've been having issues converting an Objective-C snippet to Swift that uses NSData and CoreBluetooth. I have looked at this question and a couple others dealing with NSData in Swift but haven't had any success.
Objective-C Snippet:
- (CGFloat) minTemperature
{
CGFloat result = NAN;
int16_t value = 0;
// characteristic is a CBCharacteristic
if (characteristic) {
[[characteristic value] getBytes:&value length:sizeof (value)];
result = (CGFloat)value / 10.0f;
}
return result;
}
What I have so far in Swift (not working):
func minTemperature() -> CGFloat {
let bytes = [UInt8](characteristic?.value)
let pointer = UnsafePointer<UInt8>(bytes)
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
value = Int16(fPointer.pointee)
result = CGFloat(value / 10) // not correct value
return result
}
Does the logic look wrong here? Thanks!
One error is in
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
because the rebound pointer $0 is only valid inside the closure and must
not be passed to the outside. Also the capacity should be 1 for a
single Int16 value. Another problem is the integer division in
result = CGFloat(value / 10)
which truncates the result (as already observed by the4kman).
Creating an [UInt8] array from the data is not necessary, the
withUnsafeBytes() method of Data can be used instead.
Finally you could return nil (instead of "not a number") if no
characteristic value is given:
func minTemperature() -> CGFloat? {
guard let value = characteristic?.value else {
return nil
}
let i16val = value.withUnsafeBytes { (ptr: UnsafePointer<Int16>) in
ptr.pointee
}
return CGFloat(i16val) / 10.0
}
You should make the return value optional and check if characteristic is nil in the beginning with a guard. You should also explicitly convert the value to CGFloat, then divide it by 10.
func minTemperature() -> CGFloat? {
guard characteristic != nil else {
return nil
}
let bytes = [UInt8](characteristic!.value)
let pointer = UnsafePointer<UInt8>(bytes)
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
let value = Int16(fPointer.pointee)
result = CGFloat(value) / 10
return result
}
I'm converting a swift project to objective c, but i get some trouble because i don't know how to convert follow code. Please help me. Thanks!
public enum UPCarouselFlowLayoutSpacingMode {
case fixed(spacing: CGFloat)
case overlap(visibleOffset: CGFloat)
}
and
fileprivate var currentPage: Int = 0 {
didSet {
let character = self.items[self.currentPage]
self.infoLabel.text = character.name.uppercased()
self.detailLabel.text = character.movie.uppercased()
}
}
The first (an enum with associated values) has no direct equivalent in Objective-C. For your particular example, you could use something like this:
typedef NS_ENUM(NSInteger, UPCarouselFlowLayoutSpacingMode) {
UPCarouselFlowLayoutSpacingModeFixed,
UPCarouselFlowLayoutSpacingModeOverlap
};
typedef struct {
UPCarouselFlowLayoutSpacingMode mode;
CGFloat amount;
} UPCarouselFlowLayoutSpacing;
You would just pass around values of type UPCarouselFlowLayoutSpacing. You could create helper functions to make these easier to create, e.g.
UPCarouselFlowLayoutSpacing UPCarouselFlowLayoutSpacingMakeFixed(CGFloat spacing) {
UPCarouselFlowLayoutSpacing value;
value.mode = UPCarouselFlowLayoutSpacingModeFixed;
value.amount = spacing;
return value;
}
UPCarouselFlowLayoutSpacing UPCarouselFlowLayoutSpacingMakeOverlap(CGFloat visibleOffset) {
UPCarouselFlowLayoutSpacing value;
value.mode = UPCarouselFlowLayoutSpacingModeOverlap;
value.amount = visibleOffset;
return value;
}
For the second, you can override the setter method of your Objective-C class's currentPage property:
- (void)setCurrentPage:(NSInteger)page {
_page = page;
MovieCharacter *character = self.items[page];
self.infoLabel.text = character.name.localizedUppercaseString;
self.detailLabel.text = character.movie.localizedUppercaseString;
}
Guys i am new to iOS development hardly 2 weeks, i want to convert custom object to NSDictionary or NSData. I want to pass that complex object to web API to get and save.
class customClass{
var firstName:String?
var lastName:String?
var services:Services
}
class Services{
var list:[String] = [String]()
}
can somebody help me to resolve this please.
A bit late... try this in playground. Please note that it will not handle NSData correctly. Also, for the code to work, you need to make sure that your custom class inherits from NSObject and adheres to protocol Parsable. That is because you need to make use of the Objective C runtime for this solution to work with classes. Furthermore all your objects need to be subclasses of NSObject (e.g. NSArray instead of Swift type Array).
import UIKit
protocol Parsable {
}
// Define classes as NSObjects
class customClass: NSObject, Parsable {
var firstName:String?
var lastName:String?
var services:Services?
}
class Services: NSObject, Parsable {
var list:NSMutableArray = NSMutableArray()
}
// Create objects
var obj = customClass()
obj.firstName = "My"
obj.lastName = "Name"
var serv = Services()
serv.list = NSMutableArray()
serv.list.addObject("AAA")
serv.list.addObject("BBB")
obj.services = serv
// The magic
func parse(o: NSObject) -> AnyObject {
let aClass : AnyClass? = o.dynamicType
var propertiesCount : CUnsignedInt = 0
let propertiesInAClass : UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(aClass, &propertiesCount)
let propertiesDictionary : NSMutableDictionary = NSMutableDictionary()
if propertiesCount == 0 {
if let a = o as? [NSObject] {
return a.map( {parse($0)} )
} else {
// Take into account NSData here!!!
return o
}
} else {
for var i = 0; i < Int(propertiesCount); i++ {
let property = propertiesInAClass[i]
let propertyName = NSString(CString: property_getName(property), encoding: NSUTF8StringEncoding) as! String
let propertyValue : AnyObject = o.valueForKey(propertyName)!;
if propertyValue is NSObject && propertyValue is protocol<Parsable> {
propertiesDictionary.setValue(parse(propertyValue as! NSObject), forKey: propertyName)
} else {
propertiesDictionary.setValue(propertyValue, forKey: propertyName)
}
}
return propertiesDictionary
}
}
print(parse(obj))
//print(parse(obj.services!))
//print(parse(obj.services!.list))
I'm going through Jastor's documentation:
There's an Objective-C implementation for returning arrays:
+ (Class)categories_class {
return [ProductCategory class];
}
This is my attempt at converting it to Swift, however it ends up not returning anything so I don't think it's implemented correctly:
#<_TtC4TestApp4Room: id = (null) {
resultCount = 50; // 50 is returning fine
results = ( // results is not
);
}>
NSDictionary response:
{
"resultCount" : 50,
"results" : [
{
"collectionExplicitness" : "notExplicit",
"discCount" : 1,
"artworkUrl60" : "http:\/\/a4.mzstatic.com\/us\/r30\/Features\/2a\/b7\/da\/dj.kkirmfzh.60x60-50.jpg",
"collectionCensoredName" : "Changes in Latitudes, Changes in Attitudes (Ultmate Master Disk Gold CD Reissue)"
}
]
}
Music.swift (not quite sure how to implement the results_class() method)
class Music : Jastor {
var resultCount: NSNumber = 0
var results: NSArray = []
class func results_class() -> AnyClass {
return Author.self
}
}
Author.swift
class Author {
var collectionExplicitness: NSString = ""
var discCount: NSNumber = 0
var artworkUrl60: NSString = ""
var collectionCensoredName: NSString = ""
}
I'm using the following syntax (adapted to your example):
static let results_class = Author.self
and everything works for me.
Other differences that may or may not have an effect:
I'm using Int instead of NSNumber and String instead of NSString (except for arrays).
I'm using implicitly wrapped optionals rather than assigning a default value to each field