How to declare a property as a function in Swift? - objective-c

Here is my code:
import Cocoa
class VC1: NSViewController {
let aFunctionVar ()->Void
}
The compiler however tells me: "Class VC1 has no initializers"
According to the swift example in Apple Swift iBook, they did their examplle like so:
var mathFunction: (Int, Int) -> Int = addTwoInts
But in my case, I'm trying to create a property variable. It is not yet known what the variable will be, so i can't set it there. Any help?
Edit - I already know how to make variables optional and lazy when it comes to simple String/Array/Dictionary types etc. But this is a function type property variable. It is meant to hold a function of type ()->Void. Any help on how this can be done?
In objectiveC this can be done by making a block property like this:
#property (nonatomic, copy) void (^aFunctionVar)();

Declare projectLaunchData as an optional var:
import Cocoa
class VC1: NSViewController {
var projectLaunchData: (()->Void)?
}
Then you can assign a value later:
func test() {
print("this works")
}
let myVC = VC1()
// assign the function
myVC.projectLaunchData = test
// Call the function using optional chaining. This will safely do nothing
// if projectLaunchData is nil, and call the function if it has been assigned.
// If the function returns a value, it will then be optional because it was
// called with the optional chaining syntax.
myVC.projectLaunchData?()

If the value will be known after the object is setup, you can use a lazy variable:
class LazyTester {
lazy var someLazyString: String = {
return "So sleepy"
}()
}
var myLazyTester = LazyTester()
myLazyTester.someLazyString
The compiler is giving you that error because you are defining a mandatory stored variable, projectLaunchData, but not giving it a value. If you know the variables value at init time, you can set it at init time.

Related

Indirect initialization of memory via UnsafeMutablePointer types

I encountered an unfamiliar pattern of initialization from Objective-C that I'm struggling to replicate in Swift.
Objective-C
In the example code, they defined a C struct such as this (abbreviated, original here):
struct AQPlayerState {
AudioFileID mAudioFile;
}
Here's an example that uses AQPlayerState:
AQPlayerState aqData; // 1
OSStattus result =
AudioFileOpenURL(
audioFileURL,
fsRdPerm,
0,
&aqData.mAudioFile // 2
);
The key takeaway from above is that aqData currently has uninitialized properties, and AudioFileOpenURL is initializing aqData.mAudioFile on it's behalf.
Swift
I'm trying to replicate this behaviour in Swift. Here's what I've tried so far:
Models:
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Foo {
var person: Person?
}
My idea was to replicate the Objective-C code by passing a reference of Foo.person into a function that would instantiate it on it's behalf.
Initialization Function:
func initializeWithBob(_ ptr: UnsafeMutablePointer<Person?>) {
ptr.pointee = Person(name: "Bob")
}
initializeWithBob takes a pointer to an address for a Person? type and initializes it with a Person(name: "Bob") object.
Here's my test code:
let foo = Foo()
let ptr = UnsafeMutablePointer<Person?>.allocate(capacity: 1)
ptr.initialize(to: foo.person)
defer {
ptr.deinitialize()
ptr.deallocate(capacity: 1)
}
initializeWithBob(ptr)
print(foo.person) // outputs nil
initializeWithBob failed to "install" an instance of type Person in my Foo instance. I presume some of my assumptions are wrong. Looking for help in correcting my assumptions and understanding of this situation.
Thanks in advance!
You can achieve what you are looking for via withUnsafeMutablePointer(to:_:) like so:
let foo = Foo()
withUnsafeMutablePointer(to: &foo.person) { (ptr) -> Void in
initializeWithBob(ptr)
}
print(foo.person!.name) // outputs Bob
However, I wouldn't recommend this approach. IMHO it makes more sense to wrap the APIs you are working with in a C function that you can make 'nice' to call from Swift. The problem with your current approach is that this type of Swift is hard to read for Swift developers and also hard to read for Audio Toolbox developers.
#kelvinlau Is this what you were thinking of trying to achieve?
func initializeWithBob(_ ptr: UnsafeMutablePointer<Foo>) {
ptr.pointee.person = Person(name: "Bob")
}
let foo = Foo()
let ptr = UnsafeMutablePointer<Foo>.allocate(capacity: 1)
ptr.initialize(to: foo)
initializeWithBob(ptr)
print(foo.person?.name ?? "nil")
ptr.deinitialize()
ptr.deallocate(capacity: 1)
print(foo.person?.name ?? "nil")
The code pattern you have in Objective-C is for out parameters, that is parameters which return a value, or in out parameters, that is parameters which both pass a value in and return one. Objective-C does not directly support these so pointers are used to produce the semantics.
Swift has in out parameters indicated by the keyword inout in the function declaration. Within the function an assignment to an inout parameters effectively assigns a value to the variable that was passed as the argument. At the function call site the variable must be prefixed by & to indicate it is the variable itself and not its value which is effectively being passed.
Keeping your Person and Foo as is your function becomes:
func initializeWithBob(_ ptr: inout Person?)
{
ptr = Person(name: "Bob")
}
and it may be used, for example, like:
var example = Foo()
initializeWithBob(&example.person)
Using inout in Swift is better than trying to build the same semantics using pointers.
HTH
Note: You can skip this unless you are curious
"Effectively" was used a few times above. Typically out parameters are implemented by the parameter passing method call-by-result, while in out use call-by-value-result. Using either of these methods the returned value is only assigned to the passed variable at the point the function returns.
Another parameter passing method is call-by-reference, which is similar to call-by-value-result except that each and every assignment to the parameter within the function is immediately made to passed variable. This means changes to the passed variable may be visible before the function returns.
Swift by design does not specify whether its inout uses call-by-value-result or call-by-reference. So rather than specify the exact semantics in the answer "effectively" is used.

How to use pointer from other classes in swift

I'm immigrating my old ObjectiveC code to swift. In ObjcC had a separate class to handle my Admob activity.
In this class I've created a pointer in the init func, and when changing scene, I could use this pointer to change the location of the ads banner.
#implementation MyAdsSupport
+(id)ShowAds:(My_Ads_Position)posIndex
{
if (_adsBannerPointer == nil)
{
_adsBannerPointer = [[KTFAdsSupport alloc]initAds:posIndex];
}
else
{
[_adsBannerPointer setAdsPosition:posIndex];
}
return _adsBannerPointer;
}
In Swift I created the Admob class, and managed to present ads on screen but when I try to call the pointer from another class it returns always nil.
Here is my Swift Code:
var adsPointer: My_Ads_Support!
func initAds(myView: UIViewController, atPos: My_Ads_Position) -> KTF_Ads_Support {
if adsPointer == nil {
adsPointer = self
adsPointer.ShowAds(myView: myView, atPos: atPos)
}
else
{
print("adsPointer ALIVE")
adsPointer.setAdsPos( atPos: atPos)
}
return self.adsPointer!
}
How can I set a pointer in Swift to be able to reach the ads banner from any scene?
In your Objective-C code you have three methods, the instance methods initAds: and setAdsPosition:, and the class method ShowAds:. The latter uses a variable, presumably declared static, called _adsBannerPointer.
Your Swift code is not the same. It has two methods, the instance methods initAds and setAdsPos, and one variable, the instance variable adsPointer.
In Swift class methods are termed type methods (as they can belong to classes, structs and enumerations) and are indicated by the use of the keyword static, type (class) variables are also indicated with static. So to follow your Objective-C model you need something along the lines of:
static var adsPointer: My_Ads_Support!
// instance init
init(startingPos : My_Ads_Position) { ... }
// instance set position
fun setAdsPos(atPos : My_Ads_Position) { ... }
static func showAds(myView: UIViewController, atPos: My_Ads_Position) -> KTF_Ads_Support { ... }
HTH

Why can I not use KVC from an Objective-C object to a Swift Property?

My team has decided that new files should be written in swift, and I am seeing an odd problem with using KVC in an Objective-C object to set a property on a Swift object.
My Objective-C sets a property like so: [textObject setValue:0.0 forKey:#"fontSize"]
My Swift object (textObject) has a custom setter/getter for this property.
var fontSize: CGFloat? {
get {
return internalTextGraphic?.fontSize
}
set {
internalTextGraphic?.fontSize = newValue
}
}
However, if I set a breakpoint in the set, it never gets hit.
I have Objective-C objects that also get this same call, and I just implement
-setFontSize, and the execution enters properly.
Why can't I seem to get into my set method through -setValueForKey?
I have 100% confirmed the textObject is exists and is the correct type.
EDIT:
Martin R is correct, I had to make the type a non-optional. This is my working code:
var fontSize: CGFloat {
get {
var retFontSize: CGFloat = 0.0
if let fontSize = internalTextGraphic?.fontSize {
retFontSize = fontSize
}
return retFontSize
}
set {
if let textGraphic = internalTextGraphic {
textGraphic.fontSize = newValue
}
}
}
The reason is that a Swift optional struct or enum (in your case CGFloat?)
is not representable in Objective-C (and you won't see that property
in the generated "Project-Swift.h" header file). That becomes more obvious
if you mark the property
explicitly with #objc, then you'll get the error message
error: property cannot be marked #objc because its type cannot be represented in Objective-C
If you change the property type to the non-optional CGFloat
then KVC works as expected. It would also work with an optional
class type, such as NSNumber?.

Get an element in a Set with "member"

I'm quite new to Swift and I'm trying to re-write a piece of Objective-C code into the Swift language. The original code is modifying an element in a Set:
#property (nonatomic, strong, nonnull) NSMutableSet<CustomItem *> *itemsSet;
...
CustomItem *item = [[CustomItem alloc] initWithSomething:something];
CustomItem *existingItem = [self.itemsSet member:item];
if (existingItem) {
existingItem.property = 3
}
The Swift code looks something like this:
var itemsSet = Set<CustomItem>()
...
let item = CustomItem(something: something)
let existingItem = self.itemsSet...?
Is it possible to get the Set's element in a similar fashion using Swift?
I unfortunately have no idea as to why this does not work as expected. I would have assumed that the Swift Set has a member function as well - apparently it does not.
In the documentation of the Swift module the following is written:
/// A shadow for the "core operations" of NSSet.
///
/// Covers a set of operations everyone needs to implement in order to
/// be a useful `NSSet` subclass.
#objc public protocol _NSSetCoreType : _NSCopyingType, _NSFastEnumerationType {
public init(objects: UnsafePointer<AnyObject?>, count: Int)
public var count: Int { get }
public func member(object: AnyObject) -> AnyObject?
public func objectEnumerator() -> _NSEnumeratorType
public func copyWithZone(zone: _SwiftNSZone) -> AnyObject
public func countByEnumeratingWithState(state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>, objects: UnsafeMutablePointer<AnyObject>, count: Int) -> Int
}
which sounds like all those functions are not available in Swift (I might be wrong about this one but it seems to be true).
One workaround for this is to use a NSMutableSet (the same type as in Objective-C):
var itemsSet : NSMutableSet = NSMutableSet()
itemsSet.addObject("bla")
let existingItem = itemsSet.member("bla")
The other one is to write slightly more code but stay in "Swift territory":
let item = CustomItem(something: "")
if let index = itemsSet.indexOf(item) {
let existingItem = itemsSet[index]
existingItem.something = "somethingElse"
}
Using indexOf() and subscripting – as suggested in luk2302's answer –
seems a good and Swifty solution to me.
But just for the sake of completeness: You can use the member()
method of NSSet, you just have to cast Set to NSSet
explicitly:
if let existingItem = (itemsSet as NSSet).member(item) as? CustomItem {
existingItem.something = "somethingElse"
}

Error in Swift class: Property not initialized at super.init call

I have two classes, Shape and Square
class Shape {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
With the implementation above I get the error:
property 'self.sideLength' not initialized at super.init call
super.init(name:name)
Why do I have to set self.sideLength before calling super.init?
Quote from The Swift Programming Language, which answers your question:
“Swift’s compiler performs four helpful safety-checks to make sure
that two-phase initialization is completed without error:”
Safety check 1 “A designated initializer must ensure that all of the
“properties introduced by its class are initialized before it
delegates up to a superclass initializer.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11
Swift has a very clear, specific sequence of operations that are done in initializers. Let's start with some basic examples and work our way up to a general case.
Let's take an object A. We'll define it as follows.
class A {
var x: Int
init(x: Int) {
self.x = x
}
}
Notice that A does not have a superclass, so it cannot call a super.init() function as it does not exist.
OK, so now let's subclass A with a new class named B.
class B: A {
var y: Int
init(x: Int, y: Int) {
self.y = y
super.init(x: x)
}
}
This is a departure from Objective-C where [super init] would typically be called first before anything else. Not so in Swift. You are responsible for ensuring that your instance variables are in a consistent state before you do anything else, including calling methods (which includes your superclass' initializer).
From the docs
Safety check 1
A designated initializer must ensure that all of the properties
introduced by its class are initialized before it delegates up to a
superclass initializer.
Why do we need a safety check like this?
To answer this lets go though the initialization process in swift.
Two-Phase Initialization
Class initialization in Swift is a two-phase process. In the first
phase, each stored property is assigned an initial value by the class
that introduced it. Once the initial state for every stored property
has been determined, the second phase begins, and each class is given
the opportunity to customize its stored properties further before the
new instance is considered ready for use.
The use of a two-phase initialization process makes initialization
safe, while still giving complete flexibility to each class in a class
hierarchy. Two-phase initialization prevents property values from
being accessed before they are initialized, and prevents property
values from being set to a different value by another initializer
unexpectedly.
So, to make sure the two step initialization process is done as defined above, there are four safety checks, one of them is,
Safety check 1
A designated initializer must ensure that all of the properties
introduced by its class are initialized before it delegates up to a
superclass initializer.
Now, the two phase initialization never talks about order, but this safety check, introduces super.init to be ordered, after the initialization of all the properties.
Safety check 1 might seem irrelevant as,
Two-phase initialization prevents property values from being accessed before they are initialized can be satisfied, without this safety check 1.
Like in this sample
class Shape {
var name: String
var sides : Int
init(sides:Int, named: String) {
self.sides = sides
self.name = named
}
}
class Triangle: Shape {
var hypotenuse: Int
init(hypotenuse:Int) {
super.init(sides: 3, named: "Triangle")
self.hypotenuse = hypotenuse
}
}
Triangle.init has initialized, every property before being used. So Safety check 1 seems irrelevant,
But then there could be another scenario, a little bit complex,
class Shape {
var name: String
var sides : Int
init(sides:Int, named: String) {
self.sides = sides
self.name = named
printShapeDescription()
}
func printShapeDescription() {
print("Shape Name :\(self.name)")
print("Sides :\(self.sides)")
}
}
class Triangle: Shape {
var hypotenuse: Int
init(hypotenuse:Int) {
self.hypotenuse = hypotenuse
super.init(sides: 3, named: "Triangle")
}
override func printShapeDescription() {
super.printShapeDescription()
print("Hypotenuse :\(self.hypotenuse)")
}
}
let triangle = Triangle(hypotenuse: 12)
Output :
Shape Name :Triangle
Sides :3
Hypotenuse :12
Here if we had called the super.init before setting the hypotenuse, the super.init call would then have called the printShapeDescription() and since that has been overridden it would first fallback to Triangle class implementation of printShapeDescription(). The printShapeDescription() of Triangle class access the hypotenuse a non optional property that still has not been initialised. And this is not allowed as Two-phase initialization prevents property values from being accessed before they are initialized
So make sure the Two phase initialization is done as defined, there needs to be a specific order of calling super.init, and that is, after initializing all the properties introduced by self class, thus we need a Safety check 1
The "super.init()" should be called after you initialize all your instance variables.
In Apple's "Intermediate Swift" video (you can find it in Apple Developer video resource page https://developer.apple.com/videos/wwdc/2014/), at about 28:40, it is explicit said that all initializers in super class must be called AFTER you initialize your instance variables.
In Objective-C, it was the reverse. In Swift, since all properties need to be initialized before it's used, we need to initialize properties first. This is meant to prevent a call to overrided function from super class's "init()" method, without initializing properties first.
So the implementation of "Square" should be:
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
self.sideLength = sideLength
numberOfSides = 4
super.init(name:name) // Correct position for "super.init()"
}
func area () -> Double {
return sideLength * sideLength
}
}
Sorry for ugly formatting.
Just put a question character after declaration and everything will be ok.
A question tells the compiler that the value is optional.
class Square: Shape {
var sideLength: Double? // <=== like this ..
init(sideLength:Double, name:String) {
super.init(name:name) // Error here
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Edit1:
There is a better way to skip this error. According to jmaschad's comment there is no reason to use optional in your case cause optionals are not comfortable in use and You always have to check if optional is not nil before accessing it. So all you have to do is to initialize member after declaration:
class Square: Shape {
var sideLength: Double=Double()
init(sideLength:Double, name:String) {
super.init(name:name)
self.sideLength = sideLength
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Edit2:
After two minuses got on this answer I found even better way. If you want class member to be initialized in your constructor you must assign initial value to it inside contructor and before super.init() call. Like this:
class Square: Shape {
var sideLength: Double
init(sideLength:Double, name:String) {
self.sideLength = sideLength // <= before super.init call..
super.init(name:name)
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
Good luck in learning Swift.
swift enforces you to initialise every member var before it is ever/might ever be used. Since it can't be sure what happens when it is supers turn, it errors out: better safe than sorry
Edward,
You can modify the code in your example like this:
var playerShip:PlayerShip!
var deltaPoint = CGPointZero
init(size: CGSize)
{
super.init(size: size)
playerLayerNode.addChild(playerShip)
}
This is using an implicitly unwrapped optional.
In documentation we can read:
"As with optionals, if you don’t provide an initial value when you
declare an implicitly unwrapped optional variable or property, it’s
value automatically defaults to nil."
Swift will not allow you to initialise super class with out initialising the properties, reverse of Obj C. So you have to initialise all properties before calling "super.init".
Please go to http://blog.scottlogic.com/2014/11/20/swift-initialisation.html.
It gives a nice explanation to your problem.
Add nil to the end of the declaration.
// Must be nil or swift complains
var someProtocol:SomeProtocol? = nil
// Init the view
override init(frame: CGRect)
super.init(frame: frame)
...
This worked for my case, but may not work for yours
its should be this:
init(sideLength:Double, name:String) {
self.sideLength = sideLength
super.init(name:name)
numberOfSides = 4
}
look at this link:
https://swiftgg.gitbook.io/swift/swift-jiao-cheng/14_initialization#two-phase-initialization
You are just initing in the wrong order.
class Shape2 {
var numberOfSides = 0
var name: String
init(name:String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class Square2: Shape2 {
var sideLength: Double
init(sideLength:Double, name:String) {
self.sideLength = sideLength
super.init(name:name) // It should be behind "self.sideLength = sideLength"
numberOfSides = 4
}
func area () -> Double {
return sideLength * sideLength
}
}
#Janos if you make the property optional, you don't have to initialise it in init. –
This worked for me.