Swift: write a function that allocates an instance of (any) class - objective-c

I'm looking for the equivalent of the following Obj-C code in Swift:
- newInstanceOf:(id)classRef {
return [classRef new];
}
and then to use it:
id instance = [whatever newInstanceOf:NSArray.class]
[instance isKindOfClass:NSArray.class] == YES
I have tried using a Swift template:
func newSomething<T>(classRef:T.Type) -> T {
return classRef()
}
I get the error: error: 'T' cannot be constructed because it has no accessible initializers

You could create a protocol to act as a type constraint for objects initializable by a void-argument initializer, and thereafter extend your types of choice to this protocol.
protocol SimplyInitializable {
init()
}
extension Int : SimplyInitializable { }
extension Double : SimplyInitializable { }
extension String : SimplyInitializable { }
struct MyStruct {
var myInt : Int
init() {
myInt = 0
}
}
extension MyStruct : SimplyInitializable { }
func newSomething<T: SimplyInitializable>(classRef: T.Type) -> T {
return classRef.init()
}
/* Examples */
var a = newSomething(Int)
var b = newSomething(Double)
var c = newSomething("".dynamicType)
var d = newSomething(MyStruct)
var e = newSomething(a.dynamicType)
print(a.dynamicType) // Int
print(b.dynamicType) // Double
print(c.dynamicType) // String
print(d.dynamicType) // MyStruct
print(e.dynamicType) // Int

Actually in Swift, not all classes are guaranteed to have an init() initializer. It can work with NSObject classes though because NSObject does have that requirement.
func newInstanceOf<T:NSObject>(aClass:T.Type) -> NSObject
{ return aClass.init() }
let string = newInstanceOf(NSString) // ""
let date = newInstanceOf(NSDate) // 2016-01-15 05:27:29 +0000

Related

Expose Swift Array Of Enums in Objective-C

I have this enum in swift
#objc(PaymentMethods)
public enum PaymentMethods: Int, RawRepresentable {
public typealias RawValue = String
case card
case account
case paypal
public var rawValue: RawValue {
switch self {
case .card:
return "CARD"
case .account:
return "ACCOUNT"
case .paypal:
return "PAYPAL"
}
}
public init(rawValue: RawValue){
switch rawValue {
case "CARD":
self = .card
case "ACCOUNT":
self = .account
case "PAYPAL":
self = .paypal
default:
self = .card
}
}
}
And this property in a class.
#objc public class SomeClass: ExtendingSomeOtherStuffs {
var supportedPaymentMethods:[PaymentMethods]!
}
my problem is how to bridge supportedPaymentMethods into Objective-C and use it.
I have looked at this post and this but still can't figure it out.
can someone help me out with an example at least.
Am trying to use this in Native-script and I need to expose that property from Swift to Objective
You can do it like so:
#objc public class SomeClass: NSObject {
var supportedPaymentMethods: [PaymentMethods]
#objc init(supportedPaymentMethods: [String]) {
self.supportedPaymentMethods = supportedPaymentMethods.map { .init(rawValue: $0) }
}
}
and use it like this in your Objective-C code:
[[SomeClass alloc] initWithSupportedPaymentMethods:#[#"ACCOUNT", #"CARD", #"PAYPAL"]];
If you don't want your class initializer to take any String I would suggest doing the following:
Make your init(rawValue:) failable and return nil when the argument is invalid:
public init?(rawValue: RawValue) {
switch rawValue {
case "CARD":
self = .card
case "ACCOUNT":
self = .account
case "PAYPAL":
self = .paypal
default:
return nil
}
}
Use compactMap instead of map in the init of the SomeClass like so:
#objc init(supportedPaymentMethods: [String]) {
self.supportedPaymentMethods = supportedPaymentMethods.compactMap { .init(rawValue: $0) }
}
(it will eliminate the nil values)

Swift 3 - Singleton

class MyManager {
private static var __once: () = {
Static.instance = MyManager()
}()
class var sharedInstance: MyManager {
struct Static {
static var onceToken: Int = 0
static var instance: MyManager? = nil
}
_ = MyManager.__once
return Static.instance!
}
fileprivate init() {
print("MyManager init");
}
....... etc
calling it
aManager = MyManager.sharedInstance
results in
MyManager init fatal error: unexpectedly found nil while unwrapping an Optional value
_ = MyManager.__once isn't calling your __once function, it's assigning it to nothing. You forgot the ():
MyManager.__once()
That's the whole purpose of requiring _ =, to make you realize you're dealing with the function itself, not a function call.
Regardless, this is an unnecessarily convoluted and messy implmentation of a singleton. All you need is:
class MyManager {
static let instance = MyManager()
}
It's lazy, thread-safe, and sane.
For me this is the best way, make init private.
// MARK: - Singleton
final class Singleton {
// Can't init is singleton
private init() { }
// MARK: Shared Instance
static let shared = Singleton()
// MARK: Local Variable
var emptyStringArray : [String] = []
}

Swift pass block from Objective-C to Swift

I have a method in Objective-C that takes a parameter and returns a result in a block. How do I return this result through a bridging header to a swift block? Note I have other non-block functions returning and this works fine.
Objective-C
- (void)performMethod:(NSString*)myString
{
[self.class
doSomething:myString
onSuccess:^(NSArray * results) {
// Return results
}
onFailure:^(NSError * error) {
// Return error
}];
}
Bridging header
?
Swift
class.performMethod(myString:String)->(results) in NSArray {
}
the trouble is that your Objective C function return void, not some block
how to deal with block in swift ? see the example below
// this block takes an Int as parameter and returns an Int
let myBlock: (Int)->Int = { i in
return i
}
let i = myBlock(10)
print(i) // 10
// this block takes no parameters and return an empty Array<Int>
let myBlock2: ()->Array<Int> = { Void in
return [Int]()
}
print(myBlock2.dynamicType) // () -> Array<Int>
let arr = myBlock2()
print(arr, arr.dynamicType) // [] Array<Int>
from what i can see in your code, you try to do something like
import Foundation
func bar(str: String, onSucces: ()-> NSArray, onError: ()->NSError) {
// do something with str
if str == "alfa" {
// success
let arr = onSucces()
print(arr)
} else {
let err = onError()
print(err)
}
}
func foo(str: String) -> Void {
bar(str, onSucces: { () -> NSArray in
return NSArray(array: [str])
}) { () -> NSError in
return NSError(domain: "error", code: -1, userInfo: nil)
}
}
foo("alfa")
/*
(
alfa
)
*/
foo("")
/*
Error Domain=error Code=-1 "(null)"
*/

May I implement one function of protocol in subclass?

I just modified it. Another problem is that if I want to have a subclass inherit from BaseParticipant, may I re-implement func performEvent inside the subclass?
For example:
class CyclingParticipant: BaseParticipant, Participant
{
init(name: String)
{
super.init(name: name, preferredEvent: Event.CYCLING)
}
func performEvent(event: Event, distance: Distance) throws
{
}
}
but the compiler said "redundant conformance of CyclingParticipant to protocol Participant .
class BaseParticipant: Participant
{
var name: String
var preferredEvent: Event
var raceTime: Int
var couldNotFinish: Bool
//var performedEvent: Event
// in swift, the class accepts protocol must impletment all funcs inside protocol
init(name: String, preferredEvent: Event)
{
self.name = name
self.preferredEvent = preferredEvent
self.raceTime = 0
self.couldNotFinish = false
}
func getName() -> String
{
return self.name
}
func getPreferredEvent() -> Event
{
return self.preferredEvent
}
func isDisqualified() -> Bool
{
return self.couldNotFinish
}
func addTime(addtionalRaceTime:Int) -> Void
{
self.raceTime += addtionalRaceTime
}
func setCouldNotFinish() -> Void
{
self.couldNotFinish = true
}
func performEvent(event: Event, distance: Distance) throws -> Int
{
return 1
}
func getTime() throws
{
}
}
The code of protocol Participant:
protocol Participant
{
func getName() -> String
func getPreferredEvent() -> Event
func isDisqualified() -> Bool
func performEvent(event: Event,distance: Distance) throws ->Int
func addTime(addtionalRaceTime: Int)
func setCouldNotFinish()
func getTime() throws
}
You're missing an implementation of the getTime() function as listed in your Protocol. Also, you should post such questions on Piazza. :P
[Updating to answer reworded question]
The BaseParticipant class already adopts the Participant protocol, so the CyclingParticipant subclass should not declare that it adopts it also, this is causing the redundant conformance error. Because BaseParticipant is already a Participant, any subclass of BaseParticipant will also be a Participant.
Change:
class CyclingParticipant: BaseParticipant, Participant
to:
class CyclingParticipant: BaseParticipant
All declared methods in a Swift protocol are required by default.
getTime() is not implemented

Custom equality in swift objects preserving compatibility with legacy Objective-C code

In Objective-C you would do something along the lines of
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return [self.customProperty isEqual:other.customProperty];
}
My first naive attempt in swift goes as follows
func isEqual(other: AnyObject) -> Boolean {
if self === other {
return true
}
if let otherTyped = other as? MyType {
return self.myProperty == otherTyper.myProperty
}
return false
}
But I'm far from being happy with it. I don't even know whether the signature is right or whether we're supposed to use anything different than isEqual.
Any thoughts?
EDIT:
I'd also like to keep Objective-C compatibility (my class is used in both legacy Obj-C code and new Swift code). So I think only overriding == isn't enough. Am I wrong?
Yes, you need to override isEqual (and hash) to make your objects fully Objective-C compatible. Here's a Playground-ready example for the syntax:
import Foundation
class MyClass: NSObject {
var value = 5
override func isEqual(object: AnyObject?) -> Bool {
if let object = object as? MyClass {
return value == object.value
} else {
return false
}
}
override var hash: Int {
return value.hashValue
}
}
var x = MyClass()
var y = MyClass()
var set = NSMutableSet()
x.value = 10
y.value = 10
set.addObject(x)
x.isEqual(y) // true
set.containsObject(y) // true
(syntax current as of Xcode 6.3)
You could also implement a custom equatable, for instance:
func == (lhs: CustomClass, rhs: CustomClass) -> Bool {
return lhs.variable == rhs.variable
}
This will allow you to simply check equality like this:
let c1: CustomClass = CustomClass(5)
let c2: CustomClass = CustomClass(5)
if c1 == c2 {
// do whatever
}
Be sure your custom equatable is outside the class scope!
swift3 sig:
open override func isEqual(_ object: Any?) -> Bool {
guard let site = object as? PZSite else {
return false
}
....
}
In Swift you can override infix operators (and even make your own). See here.
So rather than using isEqual you could do:
myType == anotherType
One more example
public class PRSize: NSObject {
public var width: Int
public var height: Int
public init(width: Int, height: Int) {
self.width = width
self.height = height
}
static func == (lhs: PRSize, rhs: PRSize) -> Bool {
return lhs.width == rhs.width && lhs.height == rhs.height
}
override public func isEqual(_ object: Any?) -> Bool {
if let other = object as? PRSize {
if self === other {
return true
} else {
return self.width == other.width && self.height == other.height
}
}
return false
}
override public var hash : Int {
return "\(width)x\(height)".hashValue
}
}
To archive Objective-C compatibility you have to override isEqual method as described on page 16 of this document: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/BuildingCocoaApps.pdf