Objective-C interface Named "Category" cannot be imported into swift via the bridging header - swift

The below swift code throws this error ('Category' is ambiguous for type lookup in this context)
// ctx is the UIContext, fetchCategories is an Obj-C function that queries CoreData
//for the Categories, the cast shouldn't even be necessary but swift will not
//recognize my obj-c interface
if let cats = self.fetchCategories(ctx) {
let array = cats as! [Category] //error happens on this line
return array
}
This is the implementation in my bridging header,
there are many other imports but I removed them for this post, so note that it is a fully functional bridging header for almost 40 other .h files
#ifndef AppDataRoom_Bridging_Header_h
#define AppDataRoom_Bridging_Header_h
#import "Category.h"
#endif /* AppDataRoom_Bridging_Header_h */
Below is the interface implementation
#interface Category : _Category <QLPreviewItem> {
UIImage *squareThumb;
}
I realize that changing the name of the Category class would be the best option since this is probably due to the naming convention being common, However when I change the code to this :
if let cats = self.fetchCategories(ctx) {
let array = cats as! [<ModuleName>.Category] //error happens on this line
return array
}
Is still says it is too ambiguous to lookup. Any ideas on other things I could try besides changing the interface name, because that really isn't an option and I will spare you all the reasons why.

Related

Swift is using the wrong subscript overload. How can I get it to use the right one?

I'm writing UI testing code using XCTest. Here is my test method:
func testLandingUI() {
let app = XCUIApplication()
let headerExpectedMessage = "Title"
let headerLabel = app.staticTexts[headerExpectedMessage]
XCTAssert(headerLabel.exists)
}
I'm getting this error:
ExampleUITests.swift:38:19: error: value of type 'Any?' has no member 'exists'
XCTAssert(headerLabel.exists)
^~~~~~~~~~~ ~~~~~~
What's strange about this error is that I expected headerLabel to be of type XCUIElement, not Any?.
XCUIApplication.staticTexts is an XCUIElementQuery, which has a subscript method declared thusly:
open class XCUIElementQuery : NSObject, XCUIElementTypeQueryProvider {
...
open subscript(key: String) -> XCUIElement { get }
...
}
What I believe is happening is the subscript method in XCUIElementQuery is not being selected by Swift's overload resolution. Instead, it's selecting this category on NSObject:
#interface NSObject (MyCategory)
- (id)objectForKeyedSubscript:(NSString*)key;
- (void)setObject:(id)obj forKeyedSubscript:(NSString*)key;
#end
I verified that if I remove that category from my project, the error goes away. Assume that removing that category is not possible (because it's not). Is there any way to get Swift to use the correct subscript method?
Minimal test case that shows the problem: https://www.dropbox.com/s/f0fm5ennco7t2ua/SubscriptCategoryTest.zip?dl=1
(note that the error is in the UI tests, so press command-shift-U to see the error)
EDIT: It looks like the problem only shows up if the category defines setObject:forKeyedSubscript:. Interestingly, I get a slightly different error if both getter and setter are defined vs. just the setter.
Since the compiler is confused, you need to help it....
Given this example:
let example1: Any? = "1"
let example2: Any = "2"
You have two issues (it looks like you have an optional...)
if example1.exists { // won't work - I got your error message
}
if (example2 as AnyObject).exists { // works with a cast
}
I believe if you correct identify the type of the variable it will solve your problem. It is matching the NSObject category only because it didn't match something more specific.

How to use swift function that returns a value - in objective C?

I am using IBM Watson APIs - Alchemy Data news
The problem is, I am using swift - objective C bridging and in between I am stuck with the function that returns a value. How do I use that value in my objective C code?
Here is my swift class
#objc class alchemyNews : NSObject {
func getNewsList() -> NewsResponse {
let apiKey = "api-key"
let alchemyDataNews = AlchemyDataNews(apiKey: apiKey)
let start = "now-1d" // yesterday
let end = "now" // today
let query = [
"q.enriched.url.title": "O[IBM^Apple]",
"return": "enriched.url.title,enriched.url.entities.entity.text,enriched.url.entities.entity.type"
]
let failure = { (error: NSError) in print(error) }
alchemyDataNews.getNews(start, end: end, query: query, failure: failure) { news in
print(news)
}
let response : NewsResponse = alchemyDataNews.getNews(start, end: end) { news in
return news
}
return response
}
}
I want to have alchemyDataNews.getNews print value to be display. So I am calling this function in my Objective C class in this way.
#property (strong, nonatomic) AlchemyDataNews *getnews;
-(void)loadNews
{
self.getnews = [[AlchemyDataNews alloc]init];
[self.getnews getNewsList];
}
But what to do now? This will just call the function and not give me the response so that I can put it in array and display in tableview.
I think the issue is getNewsList returns an instance of NewsResponse, you should store it in a variable and then use it.
self.getnews = [[AlchemyDataNews alloc]init];
NewsResponse *newResponse = [self.getnews getNewsList];
// now you can use 'newResponse'
// ...
Hope that helped.
The NewsResponse type (and its primary property, NewsResult) is a struct. Unfortunately, Swift's struct types do not bridge to Objective-C. To use the Swift-to-Objective-C bridging, you will need to write Swift code to further process the NewsResponse type.
For example, you might choose the information from each NewsResponse that you're interested in and store those values in a Swift Dictionary (which bridges to an Objective-C NSDictionary). Alternatively, you could expose the table view to Swift and write Swift code to populate it.
Hope that helps!

"Ambiguous use of 'children'" when trying to use NSTreeController.arrangedObjects in Swift 3.0

I get an Ambiguous use of 'children' error in XCode 8.0/Swift 3.0 when trying to send a message to the opaque NSTreeController.arrangedObjects object.
Here is a bare playground showing the use case :
import AppKit
extension NSTreeController {
func whatever () {
let k = (self.arrangedObjects as AnyObject).children // error here
}
}
I try to use AnyObject as a bridge to the underlying ObjC object, which is supposed to be able to get through any method call, I guess.
Xcode signals that it found two candidates that could respond to a "children" message: Foundation.XMLNode and AppKit.NSTreeNode.
Of course the obvious solution (casting to NSTreeNode) is not working because arrangedObjects returns an opaque, proxy object not a real NSTreeNode
Any suggestion on how we're supposed to use NSTreeController.arrangedObjects.children in Swift 3 ?
The two candidates for the children property differ by their type:
Foundation.XMLNode:137:14: note: found this candidate
open var children: [XMLNode]? { get }
^
AppKit.NSTreeNode:12:14: note: found this candidate
open var children: [NSTreeNode]? { get }
^
You can resolve the ambiguity by casting the value of the property
to the expected type. In your case:
let k = (self.arrangedObjects as AnyObject).children as [NSTreeNode]?
Another solution: Adding an Obj-C category to NSTreeController
.h
#import <Cocoa/Cocoa.h>
#interface NSTreeController (RootNodes_m)
- (NSArray *) rootNodes;
#end
.m
#import "NSTreeController+RootNodes_m.h"
#implementation NSTreeController (RootNodes_m)
- (NSArray *) rootNodes {
NSObject * arranged = self.arrangedObjects;
if ([arranged respondsToSelector: #selector(childNodes)]) {
return [arranged performSelector:#selector(childNodes)];
}
return nil;
}
#end
Now in your code you can use it like this:
return treeController.rootNodes() as? [NSTreeNode]
I had problem with the above answer: The compiler refused to compile when "whole module optimization" was turned on. A swift extension didn't help. I'm using Xcode 8.2.1.

Parse snapshot to model - FirebaseUI

I am currently moving from Android dev to Swift dev with Firebase.
In Android, it was very easy to parse a snapshot retrieved by Firebase to an object (e.g. dataSnapshot.getvalue(EventModel.class))
How can we achieve the same in Swift? It seems that there is no such functionality. I am using Firebase-ui to retrieve objects for a list, there is a parameter "modelClass", but there is no information on how to define that class.
How do we have to define the following objects:
EventModel:
name : string
description : string
messages: MessageModel
MessageModel:
author: string
content: string
Is there a way to easily define this and parse the snapshot in Swift? Or do we have to use custom functions/classes like ObjectWrapper?
Kind regards,
Henry
Author of FirebaseUI here, good question!
The short answer is that the Objective-C client for Firebase has no similar functionality for the Android/Java snapshot.getValue(Object.class). This is due to several reasons but essentially Java has several things that Objective-C doesn't have (type annotations and generics being the two relevant ones in this case) that prevent us from doing this easily in the client. Typically, people end up building/using a JSON <--> Object mapper (which I assume ObjectWrapper is) given that Objective-C doesn't provide this functionality out of the box.
The longer answer is that doing this is sort of possible by using the Objective-C runtime and using reflection to transform the dictionary received into a model object. I hacked together a modelClass implementation that's semi-undocumented in FirebaseUI, as it only works in a limited set of circumstances. See below for my quick pass at documentation :)
The current feature simply creates an instance of the class then populates the fields directly from the dictionary provided from snapshot.value (see implementation here). This means that the properties must exactly match the keys in Firebase. This also means that it needs to receive a Dictionary back from Firebase--it won't automatically convert Strings, Numbers, Booleans, or Arrays. It also means that nested objects are unlikely to work, given that Objective-C doesn't have generics (at least not mainstream Obj-C, yet). Specifically, we currently only support single level Javascript objects with values that are primitive types.
If you have a JSON object in Firebase like:
{
message: {
name: String,
text: String
}
}
Your Objective-C (or Swift) class would look like:
// Obj-C
#interface MyMessageClass : NSObject
#property (nonatomic) NSString *name;
#property (nonatomic) NSString *text;
#end
// Swift
class MyMessageClass {
let name: String!
let text: String!
}
You can then use the modelClass:(Class)class property passing in either [MyModelClass class] or MyModelClass.self in the constructors. Take a look at ViewController.m and Message.h for a simple implementation of how to use this feature.
I'm still testing how well I can make a more robust version that could be merged into the client library (it might look like -valueAsObject:(Class)class so you can do something like MyClass *customClass = [snapshot valueAsObject:[MyClass class]];) No word on if that's even really possible, or if it ever sees the light of day.
I have written a library with similar functionality as the android version.
It's a generic parser which allows you to easily convert a snapshot into a swift object(or a list).
For your question the following code would do the job:
func main(){
let model=SnapshotParser().parse(snap: Snapshot, type: EventModel.self)
}
class EventModel: ParsableObject {
var name:String?=nil
var description:String?=nil
var messages:[MessageModel]?=nil
required init(){}
func bindProperties(binder: SnapshotParser.Binder) {
binder.bindField(name: "name", field: &name)
binder.bindField(name: "description", field: &description)
binder.bindList(name: "messages", list: &messages)
}
}
class MessageModel: ParsableSnapshot {
var id: String?
var author:String?=nil
var content:String?=nil
required init(){}
func bindProperties(binder: SnapshotParser.Binder) {
binder.bindField(name: "id", field: &id)
binder.bindField(name: "author", field: &author)
binder.bindField(name: "content", field: &content)
}
}
The json would be something like:
"eventModel":{
"name":"bob",
"description":"this is bob",
"messages":{
"id1":{
"author":"alice",
"content":"long text"
},
"id2":{
"author":"alice",
"content":"long text"
}
}
}

How can one use XCTAssertNil with optional structs?

Update 3/23/2016 I just tested my original sample code below and it all compiles fine in XCode 7.3. Looks like XCTAssertNil was updated along the way to take an expression of type () throws -> Any? Therefore this question and answer may be no longer needed (except for a while with older versions of the compiler.)
I'm writing my first unit tests in XCode with XCTest. I'm unsure how one can take advantage of XCTAssertNil as it seems to only compile when using certain types. It appears it will work with optionals made from classes and built-in primitives, but not structs. How would one go about using this method?
For structs the compiler gives the following error (assuming 'SimpleStruct' is the name of your type):
'SimpleStruct' is not identical to 'AnyObject'
Here's a simple test class to illustrate some of the types that compile okay and other's that don't.
import Cocoa
import XCTest
struct SimpleStruct {
}
class SimpleClass {
}
class Tests: XCTestCase {
func testl() {
var simpleStruct:SimpleStruct? = nil;
var simpleClass:SimpleClass? = nil;
var i:Int? = nil;
var s:String? = nil;
var tuple:(Int,String)? = nil;
XCTAssertNil(simpleStruct); // compile error
XCTAssertNil(simpleClass); // OK
XCTAssertNil(i); // OK
XCTAssertNil(s); // OK
XCTAssertNil(tuple); // compile error
}
}
Update 3/23/2016 Updated for XCode 7.3 (however if you see my edit to the question, it would appear this workaround is no longer needed)
Here is a workaround. I created my own generic function:
func AssertNil<T>(#autoclosure expression: () -> T?, message: String = "",
file: StaticString = #file, line: UInt = #line) {
if (expression() != nil) {
XCTFail(message, file:file, line:line)
}
}
Doesn't seem like this should be necessary. Is this just a result of XCTest originally targeting Objective-C and not being updated/bridged enough for Swift yet?
Edit: I've done enough research to see that AnyObject can be used to represent any class but not structs. However, that then doesn't explain why the code in my original post compiles for Int types and String types. (I read somewhere else that Xcode may auto convert these to NSNumber and NSString for you which might explain why. See http://www.scottlogic.com/blog/2014/09/24/swift-anyobject.html and http://www.drewag.me/posts/swift-s-weird-handling-of-basic-value-types-and-anyobject. I'll try removing my import of Cocoa which imports Foundation to see what happens)
Edit2: XCTest also imports Foundation so I can't test what I wanted to. I could create my own methods and test this. For now, I assume that the auto-conversions are what are allowing the int and string optionals to compile. Seems like XCTest isn't quite ready for prime time with Swift.
Update 8/13/2015: Edited the function to be compatible with XCode 7 beta