Optionals/Nullables with Kotlin native and ObjectiveC/Swift - objective-c

I have a kotlin function returning a Nullable like the following:
fun getBoolOrNil(): Boolean? {
if (something){
return true
}
return null
}
I then crosscompile this with kotlin native to objectiveC for iOs, but the method in the resulting Library that I use with objC/swift returns
__attribute__((swift_name("KotlinBoolean")))
#interface MyLibBoolean : MyLibNumber
- (instancetype)initWithBool:(BOOL)value;
+ (instancetype)numberWithBool:(BOOL)value;
#end;
whilst a MyLibNumber is just a
#interface MyLibNumber : NSNumber
...
Is this the crosscompilers try to give me something I can use like an Optional/Nullable or is this Object unusable for that purpose? I know that ObjectiveC does not support Optionals/Nullables, but I do not understand this Object that the Crosscompiler gives me here.

The closest your function translates to Objective C is something like:
- (NSNumber * _Nullable)getBoolOrNil {
if(something) {
return [NSNumber numberWithBool:YES];
}
return NULL;
}
The usage of NSNumber with initWithBool is a common pattern of expressing the nullable boolean in Objective-C.
So that's what your cross-compiler did, except it also created a wrapper for NSNumber, and now the same function at the top, could be expressed as:
- (MyLibBoolean * _Nullable)getBoolOrNil {
if(something) {
return [MyLibBoolean numberWithBool:YES];
}
return NULL;
}
Of course in Swift, this whole wrapping is not needed, the function would look almost exactly the same as in kotlin:
func getBoolOrNil() -> Bool? {
if something {
return true
}
return nil
}

Related

How do we return an optional enum value from Swift to Obj-C?

We know how to expose Swift enums to Obj-C:
#objc enum Animal: Int {
case Cat, Dog
}
But the compiler complains that the following "cannot be represented in Obj-C":
func myAnimal() -> Animal? {
if hasPet() {
return .Cat
} else {
return nil
}
}
Ideas?
Optional Ints in Swift don't map to a type in Objective-C. The issue is that your myAnimal() method is returning a type that can't be represented in Objective-C.
The way I see it, you have two options...
Option1: Change your method's return type:
func myAnimal() -> NSNumber? {
// method body goes here.
}
This doesn't really seem great since in Objective-C you'd have to do something like this:
if (myAnimal().integerValue == AnimalCat) {
// do stuff here
}
Option 2: Add a catch-all case in your enum
#objc enum Animal: Int {
case none = 0
case cat = 1
case dog = 2
init(rawValue: Int) {
switch rawValue {
case 1: self = Cat
case 2: self = Dog
default: self = None
}
}
}
// Updated method signature that makes the compiler happy.
func myAnimal() -> Animal {
if hasPet() {
return .Cat
} else {
return .None
}
}
This way, you can change your method signature to not return an optional, and the compiler will be happy.

SWIFT 3.0 migration error - Extension of a generic Obj-C class cannot access the class' generic parameter at runtime

I have this code working fine in Swift 2.
extension PHFetchResult: Sequence {
public func makeIterator() -> NSFastEnumerationIterator {
return NSFastEnumerationIterator(self)
}
}
Since I upgraded to Swift 3
Extension of a generic Objective-C class cannot access the class's generic parameters at runtime
I have no idea on how to fix this. Any help is much appreciated!
Problem was reported here: https://bugs.swift.org/browse/SR-1576
But in the end you can't use for in with PHFetchResult in Swift 3.0. Let's see some examples:
let collections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
let collectionLists = PHCollectionList.fetchCollectionLists(with: .momentList, subtype: .momentListYear, options: nil)
let assets = PHAsset.fetchAssets(with: .image, options: nil)
You can either use the built-in enumeration of PHFetchResult (my recommended solution):
collections.enumerateObjects(_:) { (collection, count, stop) in
//...
}
collectionLists.enumerateObjects(_:) { (collectionList, count, stop) in
//...
}
assets.enumerateObjects(_:) { (asset, count, stop) in
//...
}
Or access each object by its index:
for idx in 0 ..< collections.count {
let collection = collections[idx]
// ...
}
for idx in 0 ..< collectionLists.count {
let collectionList = collectionLists[idx]
// ...
}
for idx in 0 ..< assets.count {
let asset = assets[idx]
// ...
}
Pulling from Realm, you may be able to get around this by extending a subclass of what you want to conform to Sequence and put the makeIerator function there.
// Sequence conformance for ClassA is provided by ProtocolX's `makeIterator()` implementation.
extension ClassA: Sequence {}
extension ProtocolX {
// Support Sequence-style enumeration
public func makeIterator() -> RLMIterator {
return RLMIterator(collection: self)
}
}
You can see the full code at https://github.com/realm/realm-cocoa/blob/master/Realm/Swift/RLMSupport.swift
You can use a wrapper type as suggested by Swift engineer Jordan Rose on the bug report:
import Photos
struct ResultSequence<Element: AnyObject>: Sequence {
var result: PHFetchResult<Element>
init(_ result: PHFetchResult<Element>) {
self.result = result
}
func makeIterator() -> NSFastEnumerationIterator {
return NSFastEnumerationIterator(self.result)
}
}
func test(_ request: PHFetchResult<PHCollection>) {
for elem in ResultSequence(request) {
print(elem)
}
}
There is no way to fix that, you might need to refactor your code or use some other techniques.
you can refer to this:
How to use a swift class with a generic type in objective c
docs:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_78

Shouldn't every .swift file be a class?

I have some experience in ObjC and I just started learning Swift. In Objc everything is a class through #interface in .h and #implementation of .m, or in other Swift classes that I have seen everything is usually in some form of
class MyCustomClassInhertingfrom: SomeFoundationClass { //methods & properties}
Yet here in some class named pancakeHouse.Swift there is no mention of the keyword class WHY? Isn't this a Model Class? Doesn't this break the MVC design pattern? Is this happening because of new powerful features of enums& structs vs class in Swift?_____I am confused obviously!
import UIKit
import CoreLocation
enum PriceGuide : Int {
case Unknown = 0
case Low = 1
case Medium = 2
case High = 3
}
extension PriceGuide : CustomStringConvertible {
var description : String {
switch self {
case .Unknown:
return "?"
case .Low:
return "$"
case .Medium:
return "$$"
case .High:
return "$$$"
}
}
}
enum PancakeRating {
case Unknown
case Rating(Int)
}
extension PancakeRating {
init?(value: Int) {
if value > 0 && value <= 5 {
self = .Rating(value)
} else {
self = .Unknown
}
}
}
extension PancakeRating {
var ratingImage : UIImage? {
guard let baseName = ratingImageName else {
return nil
}
return UIImage(named: baseName)
}
var smallRatingImage : UIImage? {
guard let baseName = ratingImageName else {
return nil
}
return UIImage(named: "\(baseName)_small")
}
private var ratingImageName : String? {
switch self {
case .Unknown:
return nil
case .Rating(let value):
return "pancake_rate_\(value)"
}
}
}
struct PancakeHouse {
let name: String
let photo: UIImage?
let thumbnail: UIImage?
let priceGuide: PriceGuide
let location: CLLocationCoordinate2D?
let details: String
let rating: PancakeRating
}
extension PancakeHouse {
init?(dict: [String : AnyObject]) {
guard let name = dict["name"] as? String,
let priceGuideRaw = dict["priceGuide"] as? Int,
let priceGuide = PriceGuide(rawValue: priceGuideRaw),
let details = dict["details"] as? String,
let ratingRaw = dict["rating"] as? Int,
let rating = PancakeRating(value: ratingRaw) else {
return nil
}
self.name = name
self.priceGuide = priceGuide
self.details = details
self.rating = rating
if let imageName = dict["imageName"] as? String where !imageName.isEmpty {
photo = UIImage(named: imageName)
} else {
photo = nil
}
if let thumbnailName = dict["thumbnailName"] as? String where !thumbnailName.isEmpty {
thumbnail = UIImage(named: thumbnailName)
} else {
thumbnail = nil
}
if let latitude = dict["latitude"] as? Double,
let longitude = dict["longitude"] as? Double {
location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
} else {
location = nil
}
}
}
extension PancakeHouse {
static func loadDefaultPancakeHouses() -> [PancakeHouse]? {
return self.loadPancakeHousesFromPlistNamed("pancake_houses")
}
static func loadPancakeHousesFromPlistNamed(plistName: String) -> [PancakeHouse]? {
guard let path = NSBundle.mainBundle().pathForResource(plistName, ofType: "plist"),
let array = NSArray(contentsOfFile: path) as? [[String : AnyObject]] else {
return nil
}
return array.map { PancakeHouse(dict: $0) }
.filter { $0 != nil }
.map { $0! }
}
}
extension PancakeHouse : CustomStringConvertible {
var description : String {
return "\(name) :: \(details)"
}
}
extension PancakeHouse: Equatable {
}
func ==(lhs: PancakeHouse, rhs: PancakeHouse) -> Bool {
return lhs.name == rhs.name
}
Note: I would appreciate an answer that also includes comparison of the .swift vs .h + .m ie don't just consider this as a specific question, consider it as a general question and explain or link the prerequisites's details needed to understand this question)
pancakeHouse.swift defines PancakeHouse and all the various things that go with it. That is perfectly good Swift style. There is no class here because PancakeHouse happens to be a struct, which is also perfectly good Swift style (and mildly preferred). Structs are much like classes in Swift, in that they can have data and methods (and extensions).
ObjC does not require that each class be defined in its own .h/.m pair, but it is fairly typical ObjC style to do so. That said, even in ObjC there are exceptions. It is common to have mutable subclasses defined in the same file as their base class (NSArray and NSMutableArray are both defined in NSArray.h). Swift style has evolved towards lumping related things together more closely. One style makes it easier to find something if you know its name. The other makes it easier to find related concepts together. Both have their advantages, and once people are used to one they tend to believe that that one is obviously correct. But they're just different ways of organizing.
Note that this file also makes use of Swift extensions to break up related methods. That is also common and good Swift. Old ObjC did that with categories, but it's much less common to organize ObjC code that way today (categories are used for other things now). Again, neither is deeply correct. It's just the styles that have evolved.
Swift files just need valid swift code. That's all. In this case, your swift file is just defining two enums. Generally it's better practice to put each enum (and its extension) in it's own .swift file, but that's really just personal opinion when you get right down to it.
A .m is the implementation of the .h interface. It's a pain in the !##$ to keep those two things always up to date. Swift makes this much easier by just merging them into one thing/file, and your entire project automatically sees it based on the access you've set. If you don't set any access, like the example above, then you have 'internal' which means the whole project.
There are two philosophies the compiler writers could take on this question:
Each public class needs a file with the matching name - this is Java's way of doing it. Although it is minimally restrictive, the logic behind it is to let the compiler find class references without looking through all the files. It also helps programmers organize their code.
Do it your own way - this is the road taken by Swift, along with C, C#, Objective-C. Essentially, compiler writers tell you that their compiler will find your classes no matter where you put them, letting you organize your code in a way that you find the most intuitive for you and your team.

Trying port Obj-C code to Swift that checks if keys exist in dict and values have correct type

Okay, here is some Objective C code I've been using in the past to quickly check if a dictionary contains certain key/value pairs and if the values are of the right type (have the expected class).
First I need a little helper class:
#interface TypeCheck : NSObject
#property NSString * name;
#property Class type;
#end
#implementation TypeCheck
+ (instancetype)typeCheckWitName:(NSString *)name type:(Class)type {
TypeCheck * tc = [[self alloc] init];
tc.name = name;
tc.type = type;
return tc;
}
#end
And then I can define keys and types like this:
NSArray<TypeCheck *> * model = #[
[TypeCheck typeCheckWitName:#"firstName" type:[NSString class]],
[TypeCheck typeCheckWitName:#"lastName" type:[NSString class]],
[TypeCheck typeCheckWitName:#"age" type:[NSNumber class]],
[TypeCheck typeCheckWitName:#"image" type:[NSImage class]]
// ... Many more follow ...
];
I was able to use structs for all that prior to ARC but with ARC the compiler doesn't like objects in structs, so I now use objects for everything. The final check code just looks like this:
for (TypeCheck * typeCheck in model) {
id value = dict[typeCheck.name];
if (!value) {
// BAD... Value must be there
// Throw error
return;
}
if (![value isKindOfClass:typeCheck.type]) {
// BAD... Value must be of right type
// Throw error
return;
}
// Do something with value
}
This used to be pretty nice code IMHO. Gets even nicer if you use a C function to create the TypeCheck objects:
#define TypeString [NSString class]
#define TypeNumber [NSNumber class]
#define TypeImage [NSImage class]
static TypeCheck * makeTypeCheck ( NSString * name, Class type ) {
return [TypeCheck typeCheckWitName:name type:type];
}
And then:
NSArray<TypeCheck *> * model = #[
makeTypeCheck(#"firstName", TypeString),
makeTypeCheck(#"lastName", TypeString),
makeTypeCheck(#"age", TypeNumber),
makeTypeCheck(#"image", TypeImage)
// ... and so on ...
One could even make makeTypeCheck a macro.
And now I'm trying to do the same or similar in Swift... and I fail horribly! I tried with a struct but how can I store class types there? I don't want to use Any as a type qualifier. Then I tried with a generic struct, like struct TypeCheck<T> so I could set the type, but I cannot put multiple of these into a single array as when T is different, as these are effectively different types (and again, I don't want to use Array<Any> despite that how can I cast? I cannot cast to TypeCheck without a generic type). I don't have to use a struct, an object will be fine but that doesn't really solve any of my problems. I cannot believe that this is so hard to do in Swift. I'm not really a Swift expert yet, so I guess i must be missing something important here.
While Obj-C was good at dynamic type inspection, Swift is much better of you allow the compiler to do the type checking at compile time. Since I don't know what your specific use case is, I'd encourage you to look for a more Swifty way to do what you want instead of just converting Obj-C to Swift. But since I don't know your use case, I'll just answer your question as written.
The only way I could get something like you describe was to make the struct conform to a protocol. Then when you put it in an array, you set the arrays type to contain instances of the protocol.
protocol TypeCheckable {
var name: String { get }
func matches(thing: Any) -> Bool
}
struct TypeCheck<T>: TypeCheckable {
let name: String
init(name: String, type: T.Type) {
self.name = name
}
func matches(thing: Any) -> Bool {
return thing is T
}
}
let array: [TypeCheckable] = [
TypeCheck(name: "test", type: String.self),
TypeCheck(name: "other", type: Int.self)
]
If you define this:
typealias TypeChecker = ([String: AnyObject]) -> Bool
struct makeTypeCheck<T> {
let name: String
func checker() -> TypeChecker {
return { (dict: [String: AnyObject]) -> Bool in
guard let v = dict[self.name] else {
return false
}
if let _ = v as? T {
return true
}
return false
}
}
}
func checkDictionary(dict: [String: AnyObject], model: [TypeChecker]) -> Bool {
for m in model {
if !m(dict) {
return false
}
}
return true
}
This test passes
func testChecker() {
let model = [
makeTypeCheck<String>(name: "firstName").checker(),
makeTypeCheck<Int>(name: "age").checker(),
]
XCTAssertTrue(checkDictionary(["firstName": "Jane", "age": 35], model: model))
XCTAssertFalse(checkDictionary(["firstName": 21, "age": 35], model: model))
XCTAssertFalse(checkDictionary(["age": 35], model: model))
}

How to access private members of an Objective-C class from a Swift extension?

I'm trying to extend an Objective-C class in Swift and make it conform to the Equatable protocol. This requires to access some private members of the extended class, which the compiler doesn't let me do. What is the correct way to do it without making the private members public?
My Swift code:
import Foundation
extension ShortDate : Equatable { }
public func == (lhs: ShortDate, rhs: ShortDate) -> Bool {
if (lhs.components.year == rhs.components.year)
&& (lhs.components.month == rhs.components.month)
&& (lhs.components.day == rhs.components.day) {
return true;
}
return false;
}
Objective-C:
#interface ShortDate : NSObject<NSCopying, NSCoding> {
NSDate *inner;
NSDateComponents *components; // The date split into components.
}
...
#end
The error I'm getting:
ShortDate.swift:26:9: 'ShortDate' does not have a member named 'components'
I came across this question while trying to find a way to access a private variable of a class from one of the SDKs we use. Since we don't have or control the source code we can't change the variables to properties. I did find that the following solution works for this case:
extension ObjcClass {
func getPrivateVariable() -> String? {
return value(forKey: "privateVariable") as? String
}
open override func value(forUndefinedKey key: String) -> Any? {
if key == "privateVariable" {
return nil
}
return super.value(forUndefinedKey: key)
}
}
Overriding value(forUndefinedKey:) is optional. value(forKey:) will crash if the private variable doesn't exist on the class unless you override value(forUndefinedKey:) and provide a default value.
I believe that there is no way to access Objective-C instance variables from Swift. Only Objective-C properties get mapped to Swift properties.