Cannot access swift object's function from objc even adding the #objcMembers tag while running UI testing - objective-c

I'm working on UI testing code for iOS app recently,
my UI testing code is using swift, but the project itself has some objective-c code, and I create a singleton object for launch arguments settings, which also written by swift, so I adding the #objcMembers tag to the class.
but while running the UI testing code, it seems not capable to reach the variables and functions, the singleton itself also not exists?
something I missed?
import UIKit
#if DEBUG
#objcMembers
class EALaunchArugments: NSObject {
static let sharedInstance = EALaunchArugments()
// key for property
static private let UITestingKey = "-UITesting"
var isUITesting: Bool {
let value = self.getLaunchArgumentValue(key: EALaunchArugments.UITestingKey)
return Bool(value ?? "") ?? false
}
private func getLaunchArgumentValue(key: String) -> String? {
let arguments = ProcessInfo.processInfo.arguments
let indexOfKey = arguments.firstIndex(of: key)
guard let index = indexOfKey, index != NSNotFound else {
return nil
}
guard index + 1 >= arguments.count else {
return nil
}
return arguments[index + 1]
}
func setAllLaunchArgumentsSettingsForUITesting() {
// some work
}
}
#endif

Related

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.

Assigning a Delegate to NSOpenPanel in Nested Scope Causes EXC_BAD_ACCESS

Consider the following simplified code example. It's presented here in Swift, but the same behavior occurs in objective-c.
import Foundation
import Cocoa
class MainWindow : NSWindow {
#IBAction func onClick_openFile(sender : AnyObject?) {
let path = runOpenPanel(false);
NSLog(path as String)
}
#IBAction func onClick_crashyByeBye(sender : AnyObject?) {
let path = runOpenPanel(true);
NSLog(path as String)
}
private func runOpenPanel(useCrashyDelegate : Bool) -> NSString {
let openPanel = NSOpenPanel.init()
openPanel.canChooseDirectories = false
openPanel.canChooseFiles = true
openPanel.allowsMultipleSelection = false
let safeDelegate = MyOpenPanelDelegate.init() //same scope as openPanel.runModal()--works fine
if (useCrashyDelegate) {
let crashyDelegate = MyOpenPanelDelegate.init() //falls out of scope before openPanel.runModal() and crashes
openPanel.delegate = crashyDelegate
} else {
openPanel.delegate = safeDelegate
}
if (openPanel.runModal() == NSFileHandlingPanelOKButton && openPanel.URLs.count == 1) {
return openPanel.URLs[0].path!
}
return ""
}
}
class MyOpenPanelDelegate : NSObject, NSOpenSavePanelDelegate {
func panel(sender: AnyObject, shouldEnableURL url: NSURL) -> Bool {
var isDir : ObjCBool = false
if (NSFileManager.defaultManager().fileExistsAtPath(url.path!, isDirectory: &isDir)) {
return isDir || (url.path! as NSString).lastPathComponent.lowercaseString == "foo.txt"
}
return false
}
}
When the useCrashyDelegate argument to runOpenPanel is true, crashyDelegate is instantiated in a nested scope and falls out of scope before the call to openPanel.runModal(). Since the open panel assigns crashyDelegate as its delegate, I would expect crashyDelegate's reference count to be incremented. However, the application crashes with an EXC_BAD_ACCESS when useCrashyDelegate is true. If useCrashyDelegate is false, safeDelegate, which is instantiated in the same scope as the call to openPanel.runModal(), is assigned to the open panel and there is no EXC_BAD_ACCESS.
This is leading me to believe that NSOpenPanel is not incrementing its delegate's reference count. Is this the expected behavior, or might this be a bug?
This is leading me to believe that NSOpenPanel is not incrementing its delegate's reference count. Is this the expected behavior, or might this be a bug?
It is expected. Check the type of the property and you will see it is assign (Objective-C) or unsafe (Swift), a strong reference is not kept. This is a common design pattern for Cocoa delegates.
HTH

Objective-C calling parameterized Swift method crashes Swift compiler

I have a simple Swift extension on NSManagedObject, in which I have a parametrized method for finding a single object - the signature looks like:
public class func findFirst<T:NSManagedObject>(inContext context : NSManagedObjectContext? = .None) -> T?
I'm trying to call this from Objective-C, but it seems like it cannot be seen. If I create a non-parameterized version I can see and call it just fine from Objective-C:
public class func findFirstUntypedWithPredicate(predicate:NSPredicate?, inContext context : NSManagedObjectContext? = .None) -> NSManagedObject?
Is there any way for ObjectiveC to be able to reach the parameterized version of the call?
I would use Self like so:
public class func findFirst(inContext context : NSManagedObjectContext? = .None) -> Self?
using the technique found here:
How can I create instances of managed object subclasses in a NSManagedObject Swift extension?
However, that causes the Swift compiler to segfault when compiling the code (Xcode 6.3.1, or Xcode 6.4 beta 2).
Edit: Here's a link with the full source of the framework I'm trying to build, including bonus Swift compiler crashes caused by templated methods:
https://www.dropbox.com/s/fixaj9ygdoi4arp/KiGiCoreData.zip?dl=0
Generic methods are not visible from Objective-C. However you can use
the ideas from How to use generic types to get object with same type to define a findFirst() class method
which returns Self? (the Swift equivalent of instancetype) without
being generic:
// Used to cast `AnyObject?` to `Self?`, `T` is inferred from the context.
func objcast<T>(obj: AnyObject?) -> T? {
return obj as! T?
}
extension NSManagedObject
{
class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = split(classString) { $0 == "." }
return components.last ?? classString
}
// Return any matching object, or `nil` if none exists or an error occurred
class func findFirst(context : NSManagedObjectContext, withPredicate pred : NSPredicate?) -> Self? {
let name = entityName()
let request = NSFetchRequest(entityName: name)
request.predicate = pred
var error : NSError?
let result = context.executeFetchRequest(request, error: &error)
if let objects = result {
return objcast(objects.first)
} else {
println("Fetch failed: \(error?.localizedDescription)")
return nil
}
}
}
This can be used from Swift
if let obj = YourEntity.findFirst(context, withPredicate: nil) {
// found
} else {
// not found
}
and from Objective-C:
YourEntity *obj = [YourEntity findFirst:context withPredicate:nil];

Alternative to load method in Swift

I am working on developing an application in Swift. I wanted to design a system for the application that allowed for loose coupling between objects, and one strategy (which I have used successfully in other languages) was to create something I call an instance factory. It is pretty simple and here is the basic implementation I came up with in Swift:
import Foundation
private var typeGenerators = Dictionary<String, InstanceFactory.GeneratorCallback>()
public class InstanceFactory: NSObject {
public typealias GeneratorCallback = () -> AnyObject!
public class func registerGeneratorFor(typeName: String, callback: GeneratorCallback) {
typeGenerators[typeName] = callback
}
public class func instanceOf(typeName: String) -> AnyObject! {
return typeGenerators[typeName]?()
}
}
The idea is that when an object instance needs access to another object instance, rather than creating that instance outright which would more tightly couple the two objects, the first object would defer to the factory to provide the needed instance by calling the instanceOf method. The factory would know how to provide various instance types because those types would register with the factory and provide a closure that could generate the instance.
The trick is how to get the classes to register with the factory. I had previously made a similar factory in Objective-C and the way I got registration to work was to override the +load method for each class that needed to register with the factory. This worked great for Objective-C, and I figured it could work for Swift as well since I would be restricting the factory to only provide objects that are derived from NSObject. It appeared I got this to work and I spent a significant about of effort designing classes to make use of the factory.
However, after upgrading to Xcode 6.3, I discovered Apple has disallowed the usage of the load class method in Swift. Without this, I am unaware of a mechanism to allow classes to automatically register themselves with the factory.
I am wondering if there some other way to get the registration to work.
What alternatives are available that could allow classes to register with the factory, or what other techniques could be use to accomplish the same kind of loose coupling the factory provides?
I've found a possible solution to your problem after I wanted to register all ViewControllers that would be implementing a certain Protocol in my application and I ran into both this question and a possible answer.
The original was posted here: How to list all classes conforming to protocol in Swift?
I adapted it to Swift 3 and made it a bit more Swift-y and generic:
import UIKit
class ContextRoute: NSObject {
}
#objc protocol ContextRoutable {
static var route: ContextRoute { get }
}
class ContextRouter: NSObject {
private static var storedRoutes: [ContextRoute]?
static var routes: [ContextRoute] {
get {
if let storedRoutes = storedRoutes {
return storedRoutes
}
let routables: [ContextRoutable.Type] = classes(implementing: ContextRoutable.self)
let newRoutes = routables.map { routable in routable.route }
storedRoutes = newRoutes
return newRoutes
}
}
private class func classes<T>(implementing objcProtocol: Protocol) -> [T] {
let classes = classList().flatMap { objcClass in objcClass as? T }
return classes
}
private class func classList() -> [AnyObject] {
let expectedClassCount = objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))
let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
let actualClassCount:Int32 = objc_getClassList(autoreleasingAllClasses, expectedClassCount)
var classes = [AnyObject]()
for i in 0 ..< actualClassCount {
if let currentClass: AnyClass = allClasses[Int(i)],
class_conformsToProtocol(currentClass, ContextRoutable.self) {
classes.append(currentClass)
}
}
allClasses.deallocate(capacity: Int(expectedClassCount))
return classes
}
}
I tried it in my application and it works. I clocked it in the simulator and it takes 0.05s for an application that has about 12000 classes.
Consider taking the Swift approach using a protocol instead. I think the solution is actually simpler than the Objective-C approach. There are variations of this with Self constraints which are even better if you have more control over the classes.
// define a protocol to create an instance of a class
protocol FactoryInstantiable {
static func makeFactoryInstance() -> AnyObject
}
// Factory for generating new instances
public class InstanceFactory: NSObject {
public class func instanceOf(typeName: String) -> AnyObject! {
if let ProductType = NSClassFromString(typeName) as? FactoryInstantiable.Type {
return ProductType.makeFactoryInstance()
} else {
return nil
}
}
}
// your class which generally could be defined somewhere else
class MyClass {
var counter : Int
init(counter: Int) {
self.counter = 0
}
}
// extension of your class to conform to the FactoryInstantiable protocol
extension MyClass : FactoryInstantiable {
static func makeFactoryInstance() -> AnyObject {
return MyClass(counter: 0)
}
}

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.