Use swift global variable (singleton) from Interface Builder - xcode

I'm using a global swift variable to create a Singleton like instance. Due to global variables being dispatch_once by default in Swift it works pretty well.
/// LPGlobal.swift
import Foundation
let mySingleton : LPSingleton = LPSingleton()
/// LPSingleton.swift
import Foundation
class LPSingleton {
let myConstant = 10.0
}
Reference from Swift:
/// LPAnySwiftClass.swift
import Foundation
class LPSwiftClass {
init () {
println("my singleton constant: \(mySingleton.myConstant)")
}
}
The Question is: how can I access this LPSingleton class from within Interface Builder?. There is no "Swift class" in the Object library. Do I need to create an Objective-C singleton in order to "act" as a bridge?.
Note: The LPSingleton class is not a subclass of NSObject !!!
Thanks
Luis

Edit: As discussed in the comments, this works only if you subclass NSObject. At the time of this answer there isn't a way to access "pure" Swift classes through IB.
You can drag an "Object" item into IB and assign it a Swift class.
Pick "Object" from the IB palette:
Drag it under your view controller on the left sidebar:
And assign it to your class in the object inspector on the right:

Related

How can I use a Transformable attribute as its actual type in a NSManagedObject subclass?

Just starting a project. The data-model file has one entity, which has a single attribute that is Transformable. It's supposed to be a NS/CGRect. I had Xcode create corresponding NSManagedObject subclass files. For "MyThing.swift", I got:
import Foundation
import CoreData
class MyThing: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
}
And I got a "MyThing+CoreDataProperties.swift":
import Foundation
import CoreData
extension MyThing {
#NSManaged var myBounds: NSObject?
}
I want the property to be an actual CGRect, so I need a NSData <-> NSValue <-> NSRect conversion chain. I already have "NSKeyedUnarchiveFromDataTransformer" as the Name under the Attribute Type in Interface Builder. What do I add (and/or change) to pass CGRect values around?
Or do I not do this, and just pass NSValue-wrapped CGRects around instead? (I hope that CoreData will take care of any NSData <-> NSValue conversions.)
As long as your transformable type complies to NSCoding, Core Data will take care of the rest for you. I've used NSAttributedString in my NSManagedObjects, and only changed the CoreData provided id-type that was generated at runtime.
If you want to have a property of your own type, i.e. MyAwesomeObject, then make sure you implement initWithCoder: and encodeWithCoder:.
So, for your case, in order to store CGRects with CoreData, you would need to wrap them in a class that implements the above mentioned methods. This is because CGRect doesn't comply to the NSCoding protocol, and thus cannot be stored directly as a transformable attribute.
Your other option is of course to store x,y,width,height as properties, and either have a transient property wich you compute in awakeFromFetch and awakeFromInsert or just a convenience method.
Because CGRect is not a class but struct i would suggest creating another property which will access the raw variable with proper conversions. And then use only that property.
class MyThing {
var boundsValue : NSValue?
}
extension MyThing {
var bounds : CGRect {
get {
if let value = boundsValue {
value.CGRectValue()
}
return CGRectNull
}
set {
boundsValue = NSValue(CGRect: newValue)
}
}
}

Xcode 7.1: Property with retain or strong attribute must be of object type

I have this variable in a swift file:
var adbk: ABAddressBook!
Which has always been fine, until Xcode 7.1. Now it complains "Property with retain or strong attribute must be of object type." The error is in the -Swift.h file. Any idea what got changed that would cause this and how to fix it?
This error occurs if Swift class declares some of the AdressBook properties and this class is part of the mixed Swift / ObjC project. Xcode then generate Swift bridging header, where this property becomes (nonatomic, strong), which is applicable to objects only, not structures.
I have encountered similar issue when I needed to pass ABRecordRef from Objective-C class to Swift class: Xcode didn't like my ABRecordRef property in Swift. So I've ended up making that property private, so that it is not exported to the bridging header, and adding new method in Swift class to receive ABRecordRef:
class: PersonDetails {
private var selectedPerson: ABRecorfRef?
func setPerson(person: ABRecordRef) {
selectedPerson = person
}
}
And then you can call
[personDetails setPerson: person];
from Objective-C class.
ABAddressBook is deprecated
#available(iOS, introduced=2.0, deprecated=9.0, message="use CNContactStore")
public typealias ABAddressBookRef = ABAddressBook
so i think you have to use CNContactStore

NSWindowController in Swift. Subclassing and initializing with Nib

In a test Swift project, I am subclassing NSWindowController. My NSWindowController subclass is designed to work with a particular Nib file. It is desirable, then, that when my window controller is initialized, the nib file is automatically loaded by the window controller instance. In Objective-C, this was achieved by doing:
#implementation MyWindowController
- (id)init {
self = [super initWithWindowNibName:"MyWindowNib"]
if (self) {
// whatever
}
return self
}
#end
Now, in Swift this is not possible: init() cannot call super.init(windowNibName:), because the later is declared not as a designated initializer, but as a convenience one by NSWindowController.
How can this be done in Swift? I don't see a strightforward way of doing it.
P.S.: I have seen other questions regarding this topic, but, as long as I've been able to understand, the solutions all point to initialize the Window Controller by calling init(windowNibName:). Please note that this is not the desired beheaviour. The Window Controller should be initialized with init(), and it should be the Window Controller itself who "picks up" its Nib file and loads it.
If you use the init() just to call super.init(windowNibName:), you could instead just override the windowNibName variable.
override var windowNibName: String {
get {
return "MyWindowNib"
}
}
Then there should be no need to mess with the initializers.
You can create your own convenience initializer instead:
override convenience init() {
self.init(windowNibName: "MyWindowNib")
}
You should instead opt in to replacing all designated initializers in your subclass, simply delegating to super where appropriate. Confer https://stackoverflow.com/a/24220904/1460929

Custom NSValueTransformer in xcode 6 with swift

Did anyone successfully implement a custom NSValueTransformer in xcode 6 beta with swift?
I have the following swift class:
import Foundation
class myTransformer: NSValueTransformer {
let amount = 100
override class func transformedValueClass() -> AnyClass!
{
return NSNumber.self
}
override func transformedValue(value: AnyObject!) -> AnyObject! {
return value.integerValue + amount
}
}
So all this transformer should do is, adding 100 to a given value in the gui.
As you can see, the transformer class appears now in the Value Transformer drop down in IB.
But if I choose this transformer the application crashes with:
2014-08-27 20:12:17.686 cdTest[44134:303]
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Cannot find value transformer with name newTransformer'
Is it right to register this transformer in the AppDelegate with
override class func initialize() {
let newTransformer = myTransformer()
}
Does anyone know how this whole stuff should work?
kind regards!
martin
From Xcode release notes:
If you set a Swift subclass of NSValueTransformer as a binding’s value
transformer, the XIB or storyboard will contain an invalid reference
to the class, and the binding will not work properly at runtime. You
can either enter a mangled class name into the Value Transformer field
or add the #objc(…) attribute to the NSValueTransformer subclass to
solve this problem. (17495784)
From Swift guide:
To make your Swift class accessible and usable back in Objective-C,
make it a descendant of an Objective-C class or mark it with the #objc
attribute. To specify a particular name for the class to use in
Objective-C, mark it with #objc(<#name#>), where <#name#> is the name
that your Objective-C code will use to reference the Swift class. For
more information on #objc, see Swift Type Compatibility.
Solution:
Declare your class as #objc(myTransformer) class myTransformer: NSValueTransformer and then you can use "myTransformer" as name...
After you initialise newTransformer you should also include the line:
NSValueTransformer.setValueTransformer(newTransformer, forName: "myTransformer")
Then in your Interface Builder you should use myTransformer instead of newTransformer under the Value Transformer dropdown.

How to initialize a NSWindowController in Swift?

I want to initialize a window controller object from a nib file, quite easy right? But I simply can't get it to work.
According to my previous experience in ObjC, I've written down the following code:
init() {
super.init(windowNibName: "SplitWindowController")
}
And in the app delegate file, I simply init and displays the window:
var myWindowController: MyWindowController = MyWindowController()
myWindowController.showWindow(self)
myWindowController.window.makeKeyAndOrderFront(nil)
But the compiler gives me this error: Must call a designated initializer of the superclass 'NSWindowController'. And according to the Swift version of NSWindowController definition, there are only 3 designated initializers, namely init(), init(window), init(coder). I don't know what to do next. Shall I build a NSCoder from a nib file, which I don't know how to do?
You were almost there. You can indeed override init() as a convenience initialiser in a manner that is equivalent to the Obj-C code you got used to:
import Cocoa
class MyWindowController: NSWindowController {
override convenience init() {
self.init(windowNibName: "<xib name>")
}
}
Note that you are calling init(windowNibName:) on self, because init() being a convenience initialiser, you still inherit all the initialisers from the superclass. From documentation:
Rule 1: A designated initializer must call a designated initializer
from its immediate superclass.
Rule 2: A convenience initializer must call another initializer from
the same class.
Rule 3: A convenience initializer must ultimately call a designated
initializer.
Also, as #weichsel mentioned above, make sure you set the class of the File's Owner to your subclass of NSWindowController (in the example above, that would be MyWindowController) and then connect its window outlet with the window itself.
That being said, I'm not sure why is compiler asking for the override keyword to be added. Though NSWindowController is a subclass of NSResponder, which defines an init(), the following code compiles without issue even though it implements an equivalent inheritance hierarchy:
class A {
init() { }
}
class B: A {
init(Int) {
super.init()
}
convenience init(String) {
self.init(5)
}
}
class C: B {
convenience init() {
self.init("5")
}
}
NSWindowController has 2 designated initializers:
init(window: NSWindow!)
init(coder: NSCoder!)
When creating a subclass, you should invoke the designated initializer of its superclass. Recent versions of Xcode enforce this. Either via built-in language mechanism (Swift) or via NS_DESIGNATED_INITIALIZER macro (Objective-C).
Swift additionally requires that you call the superclasses designated initializer when you override a convenience initializer.
From the "Initialization: Designated Initializers and Convenience Initializers" section of Swift Programming Guide:
If the initializer you are overriding is a convenience initializer,
your override must call another designated initializer from its own
subclass, as per the rules described above in Initializer Chaining.
In your case, you should probably override init(window: NSWindow!) and call super's counterpart from there.

Resources