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;
}
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'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
I am translating an Obj-C app to Swift and having trouble dealing with some syntax. I believe I have declared the variable types correctly so I don't know why I'm be getting these errors. Maybe some blocks are located incorrectly inside classes/functions when they should be outside or something. I would love it if you could review my code. I'm new to programming so what may be a clear and explicit explanation to you probably will still be vague for me so please show with examples using existing names.
Thanks
"Unary operator '++' cannot be applied to an operand of type 'Int?'"
and
"Binary operator '<' cannot be applied to an operand of type 'Int? and Float'"
and
"Use of unresolved identifier '=-'"
import UIKit
import Foundation
import AVFoundation
let minFramesForFilterToSettle = 10
enum CurrentState {
case statePaused
case stateSampling
}
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
let session = AVCaptureSession()
var camera : AVCaptureDevice?
var validFrameCounter: Int = 0
var pulseDetector: PulseDetector!
var filter: Filter!
var currentState = CurrentState.stateSampling // Is this initialized correctly?
override func viewDidLoad() {
super.viewDidLoad()
self.pulseDetector = PulseDetector()
self.filter = Filter()
// TO DO startCameraCapture() // call to un-used function.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
let NZEROS = 10
let NPOLES = 10
class Filter {
var xv = [Float](count: NZEROS + 1, repeatedValue: 0)
var yv = [Float](count: NPOLES + 1, repeatedValue: 0)
func processValue(value: Float) -> Float {
let gain: Float = 1.894427025e+01
xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; xv[5] = xv[6]; xv[6] = xv[7]; xv[7] = xv[8]; xv[8] = xv[9]; xv[9] = xv[10]; xv[10] = value / gain;
yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; yv[5] = yv[6]; yv[6] = yv[7]; yv[7] = yv[8]; yv[8] = yv[9]; yv[9] = yv[10];
yv[10] = (xv[10] - xv[0]) + 5 * (xv[2] - xv[8]) + 10 * (xv[6] - xv[4])
+ ( -0.0000000000 * yv[0]) + ( 0.0357796363 * yv[1])
+ ( -0.1476158522 * yv[2]) + ( 0.3992561394 * yv[3])
+ ( -1.1743136181 * yv[4]) + ( 2.4692165842 * yv[5])
+ ( -3.3820859632 * yv[6]) + ( 3.9628972812 * yv[7])
+ ( -4.3832594900 * yv[8]) + ( 3.2101976096 * yv[9]);
return yv[10];
}
}
let maxPeriod = 1.5 // float?
let minPeriod = 0.1 // float?
let invalidEntry:Double = -11
let maxPeriodsToStore:Int = 20
let averageSize:Float = 20
class PulseDetector {
var upVals: [Float] = [averageSize]
var downVals: [Float] = [averageSize]
var upValIndex: Int?
var downValIndex: Int?
var lastVal: Float?
var periodStart: Float?
var periods: [Double] = []
var periodTimes: [Double] = []
var periodIndex: Int?
var started: Bool?
var freq: Float?
var average: Float?
var wasDown: Bool?
func reset() {
for var i=0; i < maxPeriodsToStore; i++ {
periods[i] = invalidEntry
}
for var i=0; i < averageSize; i++ { // why error when PulseDetector.h said averageSize was an Int?
upVals[i] = invalidEntry
downVals[i] = invalidEntry
}
freq = 0.5
periodIndex = 0
downValIndex = 0
upValIndex = 0
}
func addNewValue(newVal:Float, atTime:Double) -> Float {
// we keep track of the number of values above and below zero
if newVal > 0 {
upVals[upValIndex!] = newVal
upValIndex++
if upValIndex >= averageSize {
upValIndex = 0
}
}
if newVal < 0 {
downVals[downValIndex] =- newVal
downValIndex++
if downValIndex >= averageSize {
downValIndex = 0
}
}
// work out the average value above zero
var count: Float
var total: Float
for var i=0; i < averageSize; i++ {
if upVals[i] != invalidEntry {
count++
total+=upVals[i]
}
}
var averageUp = total/count
// and the average value below zero
count=0;
total=0;
for var i=0; i < averageSize; i++ {
if downVals[i] != invalidEntry {
count++
total+=downVals[i]
}
}
var averageDown = total/count
// is the new value a down value?
if newVal < (-0.5*averageDown) {
wasDown = true
}
// original Objective-C code
PulseDetector.h
#import <Foundation/Foundation.h>
#define MAX_PERIODS_TO_STORE 20 // is this an Int?
#define AVERAGE_SIZE 20 // is this a Float?
#define INVALID_PULSE_PERIOD -1 // done
#interface PulseDetector : NSObject {
float upVals[AVERAGE_SIZE];
float downVals[AVERAGE_SIZE];
int upValIndex;
int downValIndex;
float lastVal;
float periodStart;
double periods[MAX_PERIODS_TO_STORE]; // this is an array!
double periodTimes[MAX_PERIODS_TO_STORE]; // this is an rray !!
int periodIndex;
bool started;
float freq;
float average;
bool wasDown;
}
#property (nonatomic, assign) float periodStart; // var periodStart = float?
-(float) addNewValue:(float) newVal atTime:(double) time; // declaring a method called addNewValue with 2 arguments called atTime and time that returns a float
-(float) getAverage; // declaring a method called getAverage that returns a float
-(void) reset; // declaring a method that returns nothing
#end
PulseDetector.m
#import <QuartzCore/QuartzCore.h>
#import "PulseDetector.h"
#import <vector>
#import <algorithm>
#define MAX_PERIOD 1.5
#define MIN_PERIOD 0.1
#define INVALID_ENTRY -100 // is this a double?
#implementation PulseDetector
#synthesize periodStart;
- (id) init
{
self = [super init];
if (self != nil) {
// set everything to invalid
[self reset];
}
return self;
}
-(void) reset {
for(int i=0; i<MAX_PERIODS_TO_STORE; i++) {
periods[i]=INVALID_ENTRY;
}
for(int i=0; i<AVERAGE_SIZE; i++) {
upVals[i]=INVALID_ENTRY;
downVals[i]=INVALID_ENTRY;
}
freq=0.5;
periodIndex=0;
downValIndex=0;
upValIndex=0;
}
-(float) addNewValue:(float) newVal atTime:(double) time {
// we keep track of the number of values above and below zero
if(newVal>0) {
upVals[upValIndex]=newVal;
upValIndex++;
if(upValIndex>=AVERAGE_SIZE) {
upValIndex=0;
}
}
if(newVal<0) {
downVals[downValIndex]=-newVal;
downValIndex++;
if(downValIndex>=AVERAGE_SIZE) {
downValIndex=0;
}
}
// work out the average value above zero
float count=0;
float total=0;
for(int i=0; i<AVERAGE_SIZE; i++) {
if(upVals[i]!=INVALID_ENTRY) {
count++;
total+=upVals[i];
}
}
float averageUp=total/count;
// and the average value below zero
count=0;
total=0;
for(int i=0; i<AVERAGE_SIZE; i++) {
if(downVals[i]!=INVALID_ENTRY) {
count++;
total+=downVals[i];
}
}
float averageDown=total/count;
// is the new value a down value?
if(newVal<-0.5*averageDown) {
wasDown=true;
}
// is the new value an up value and were we previously in the down state?
if(newVal>=0.5*averageUp && wasDown) {
wasDown=false;
// work out the difference between now and the last time this happenned
if(time-periodStart<MAX_PERIOD && time-periodStart>MIN_PERIOD) {
periods[periodIndex]=time-periodStart;
periodTimes[periodIndex]=time;
periodIndex++;
if(periodIndex>=MAX_PERIODS_TO_STORE) {
periodIndex=0;
}
}
// track when the transition happened
periodStart=time;
}
// return up or down
if(newVal<-0.5*averageDown) {
return -1;
} else if(newVal>0.5*averageUp) {
return 1;
}
return 0;
}
-(float) getAverage {
double time=CACurrentMediaTime();
double total=0;
double count=0;
for(int i=0; i<MAX_PERIODS_TO_STORE; i++) {
// only use upto 10 seconds worth of data
if(periods[i]!=INVALID_ENTRY && time-periodTimes[i]<10) {
count++;
total+=periods[i];
}
}
// do we have enough values?
if(count>2) {
return total/count;
}
return INVALID_PULSE_PERIOD;
}
#end
Your problem is that you didn't copied the defines:
#define MAX_PERIODS_TO_STORE 20 // is this an Int?
#define AVERAGE_SIZE 20 // is this a Float?
#define INVALID_PULSE_PERIOD -1 // done
You have to change your defines so they work in your Swift code.
Check this answer how to replace the Objective-C #define to make Swift-Workable.
Also you could just change the defines to variables and initialize your variables with them.
First, a bit on optionals. Variables that end with a '?' are Optional, meaning that they are allowed to be nil (basically not exist). The compiler will not know at compile time whether this variable exists or not, because you are allowed to set it to nil.
"Unary operator '++' cannot be applied to an operand of type 'Int?'"
You seem to have read that last word as Int, but it is Int? which is significant. Basically, since it is an optional (as indicated by the question mark), the compiler knows it can be nil. You cannot use ++ on nil, and since optionals can be nil, you cannot use ++ on optionals. You must forcibly unwrap it first:
downValIndex!++ //note the exclamation point for unwrapping
"Use of unresolved identifier '=-'"
=- isnt a thing. -= is a thing. So
downVals[downValIndex] -= newVal
downVals[downValIndex] = downVals[downValIndex]-newVal //equivalent to above
"Binary operator '>=' cannot be applied to an operand of type 'Int? and Float'"
The compiler thinks you have an optional int on the left of the < and a Float on the right. Assuming you want two Ints, you must unwrap the left and make sure the right is cast to be an int (something like this). If you want two floats instead, cast or define as floats instead of ints.
if downValIndex! >= averageSize as! Int { //casting to Int
You should just be defining averageSize as an int though
var averageSize:Int = 10 //or whatever number
Also, you have lots of optionals. If any of them can be defined to something at compile time, it will make your life easier as you won't need to unwrap them everywhere. Alternately you could implicitly unwrap them (only do this if you are absolutely sure they will never be nil).
var implicitlyUnwrappedOptional:Int!
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
I'm trying to figure out something with the new Apple Swift language. Let's say I used to do something like the following in Objective-C. I have readonly properties, and they cannot be individually changed. However, using a specific method, the properties are changed in a logical way.
I take the following example, a very simple clock. I would write this in Objective-C.
#interface Clock : NSObject
#property (readonly) NSUInteger hours;
#property (readonly) NSUInteger minutes;
#property (readonly) NSUInteger seconds;
- (void)incrementSeconds;
#end
#implementation Clock
- (void)incrementSeconds {
_seconds++;
if (_seconds == 60) {
_seconds = 0;
_minutes++;
if (_minutes == 60) {
_minutes = 0;
_hours++;
}
}
}
#end
For a specific purpose, we cannot touch the seconds, minutes and hours directly, and it's only allowed to increment second by second using a method. Only this method could change the values by using the trick of the instance variables.
Since there are no such things in Swift, I'm trying to find an equivalent. If I do this:
class Clock : NSObject {
var hours: UInt = 0
var minutes: UInt = 0
var seconds: UInt = 0
func incrementSeconds() {
self.seconds++
if self.seconds == 60 {
self.seconds = 0
self.minutes++
if self.minutes == 60 {
self.minutes = 0
self.hours++
}
}
}
}
That would work, but anybody could change directly the properties.
Maybe I already had a bad design in Objective-C and that's why the potential new Swift equivalent is not making sense. If not and if someone have an answer, I would be very grateful ;)
Maybe the future Access Control Mechanisms promised by Apple is the answer?
Thanks!
Simply prefix the property declaration with private(set), like so:
public private(set) var hours: UInt = 0
public private(set) var minutes: UInt = 0
public private(set) var seconds: UInt = 0
private keeps it local to a source file, while internal keeps it local to the module/project.
private(set) creates a read-only property, while private sets both, set and get to private.
There are two basic ways of doing what you want. First way is by having a private property and and a public computed property which returns that property:
public class Clock {
private var _hours = 0
public var hours: UInt {
return _hours
}
}
But this can be achieved in a different, shorter way, as stated in the section "Access Control" of the "The Swift Programming Language" book:
public class Clock {
public private(set) var hours = 0
}
As a side note, you MUST provide a public initialiser when declaring a public type. Even if you provide default values to all properties, init() must be defined explicitly public:
public class Clock {
public private(set) var hours = 0
public init() {
hours = 0
}
// or simply `public init() {}`
}
This is also explained in the same section of the book, when talking about default initialisers.
Since there are no access controls (meaning that you can't make an access contract that differs depending on who the caller is), here's what I would do for now:
class Clock {
struct Counter {
var hours = 0;
var minutes = 0;
var seconds = 0;
mutating func inc () {
if ++seconds >= 60 {
seconds = 0
if ++minutes >= 60 {
minutes = 0
++hours
}
}
}
}
var counter = Counter()
var hours : Int { return counter.hours }
var minutes : Int { return counter.minutes }
var seconds : Int { return counter.seconds }
func incrementTime () { self.counter.inc() }
}
This merely adds a level of indirection, as it were, to direct access to the counter; another class can make a Clock and then access its counter directly. But the idea — i.e., the contract we're trying to make — is that another class should use the Clock's top-level properties and methods only. We can't enforce that contract, but actually it was pretty much impossible to enforce in Objective-C too.
Actually access control (which does not exist yet in Swift) is not as enforced as you may think in Objective C. People can modify your readonly variables directly, if they really want to. They just do not do it with the public interface of the class.
You can do something similar in Swift (cut&paste of your code, plus some modifications, I did not test it):
class Clock : NSObject {
var _hours: UInt = 0
var _minutes: UInt = 0
var _seconds: UInt = 0
var hours: UInt {
get {
return _hours
}
}
var minutes: UInt {
get {
return _minutes
}
}
var seconds: UInt {
get {
return _seconds
}
}
func incrementSeconds() {
self._seconds++
if self._seconds == 60 {
self._seconds = 0
self._minutes++
if self._minutes == 60 {
self._minutes = 0
self._hours++
}
}
}
}
which is the same as what you have in Objective C except that the actual stored properties are visible in the public interface.
In swift you can also do something more interesting, which you can also do in Objective C, but it's probably prettier in swift (edited in the browser, I did not test it):
class Clock : NSObject {
var hours: UInt = 0
var minutes: UInt {
didSet {
hours += minutes / 60
minutes %= 60
}
}
var seconds: UInt {
didSet {
minutes += seconds / 60
seconds %= 60
}
}
// you do not really need this any more
func incrementSeconds() {
seconds++
}
}