'PFObject' does not have a member named 'subscript' - ios

I understand, this particular error was already posted here and there and the code is somewhat basic, but I myself still unable to figure this one out & I need advice.
The thing is when I add the first two lines of code provided on parse.com for saving objects
var gameScore = PFObject(className:"GameScore")
gameScore["score"] = 1337
I get the following error for the second line:
'PFObject' does not have a member named 'subscript'
I'm on Xcode 6.3 beta 2.
All required libraries are linked with binary, <Parse/Parse.h> imported via BridgeHeader.
What syntax should I use?

This is happening due to the 1.6.4 version of the parse sdk which added Objective-C Nullability Annotations to the framework. In particular the file Parse/PFObject.h defines:
- (PF_NULLABLE_S id)objectForKeyedSubscript:(NSString *)key;
which is causing the Swift compile error. Removing the PF_NULLABLE_S fixes the problem.
On the other hand it seems correct that an object for a keyed subscript might be nil, so I suspect this is a Swift bug...

The problem seems to be the changed method signature, as kashif suggested. Swift doesn't seem to be able to bridge to the Objective-C method because the signature no longer matches the subscript method names.
Workaround 1
You can work around this without modifying the framework by calling the subscript method directly, instead of using the [] operator:
Instead of using the instruction below for getting the value of a particular key:
let str = user["key-name"] as? Bool
Please use the following instruction:
let str = user.objectForKey("key-name") as? Bool
and
Instead of using the instruction below for setting the value of a particular key:
user["key-name"] = "Bla bla"
Please use the following instruction:
user.setObject("Bla bla", forKey: "key-name")
Workaround 2
Another solution is to add an extension on PFObject that implements the subscript member and calls setValue:forKey::
extension PFObject {
subscript(index: String) -> AnyObject? {
get {
return self.valueForKey(index)
}
set(newValue) {
if let newValue: AnyObject = newValue {
self.setValue(newValue, forKey: index)
}
}
}
}
Note that this second workaround isn't entirely safe, since I'm not sure how Parse actually implements the subscript methods (maybe they do more than just calling setValue:forKey - it has worked in my simple test cases, so it seems like a valid workaround until this is fixed in Parse/Swift.

I've successfully run your exact code.
First, make sure you are indeed saving the object in the background, after you set the new value:
gameScore.save()
I would double check for misspellings in the class name and subclass; if they are incorrect, it will not work.
If that's not the problem, verify in Parse that the "score" subclass is set to be a number. If you accidentally set it to be a string, setting it as an integer will not work.
If these suggestions have not hit the solution, then I'm not sure what the problem is. Hope I helped.

I encountered a similar error with Parse 1.6.4 and 1.6.5 in the PFConstants.h header file with method parameters.
Xcode 6.3 beta 4 has a note in the "New in..." section regarding nullability operators.
Moving the nullability operator between the pointer operator and the variable name seems to comply/compile.
Changing:
PF_NULLABLE_S NSError *error
to:
NSError * PF_NULLABLE_S error
(i.e., NSError* __nullable error)
... resolved the compiler error.
This also worked for block parameters defined with id. So...
PF_NULLABLE_S id object
becomes:
id PF_NULLABLE_S object
In the above case, perhaps:
- (id PF_NULLABLE_S)objectForKeyedSubscript:(NSString *)key;
I.e., the nullability operator is after the pointer type.

I know its been a while but I encountered a similar problem when I was starting my first Parse application with SDK version 1.9.1.
The Quickstart guide had the code below as an example as to how to save values:
let testObject = PFObject(className: "TestObject")
testObject["foo"] = "bar"
testObject.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved.")
}
But, the line testObject["foo"] = "bar" returned the error 'PFObject' does not have a member named 'subscript'. I was able to work around the problem by changing the line to testObject.setValue("bar", forKey: "foo"). (As suggested by a tutorial video on YouTube: https://www.youtube.com/watch?v=Q6kTw_cK3zY)

Related

Objective-C pointer and swift

I'm following an apple document, but unfortunately the examples are written on objective-c, but I have confidence with Swift language and can not understand the meaning of some things, in particular, in this example:
void RunLoopSourcesPerformRoutine (void *info){
RunLoopSource* obj = (RunLoopSource*)info;
[obj sourceFired];
}
this line: RunLoopSource* obj = (RunLoopSource*)info;
the parameter: void *info indicates that info is a pointer to void, then I can put the address of any type of data structure, following various apple documents I saw that the translation of this : void *info into swift language is :
info: UnsafeMutableRawPointer?
Now, the RunLoopSource* obj = (RunLoopSource*)info; line indicates that obj is a variable of type: RunLoopSource, and to this is assigned the value of (RunLoopSource *) info, but precisely What does it mean this statement? : (RunLoopSource *) info, and how it translates in swift language ?
Swift really hates pointer. These 2 lines of code can be converted to Swift as
func RunLoopSourcesPerformRoutine(info: UnsafeMutableRawPointer) {
let obj = info.assumingMemoryBound(to: RunLoopSource.self)
obj.pointee.sourceFired()
}
This specific expression is a "typecast": it's saying that info, which is declared to be a pointer-to-unknown-anything (void *) is actually known by the programmer to be a pointer to a RunLoopSource. This forcibly changes the type of the expression to make the compiler happy as it is assigned to obj.
It is equivalent to using as! in Swift and is idiomatic when you know the semantics of a void * but the syntax doesn't capture it.
(This attempts to answer your question as stated but I'm not sure if you are looking for more information. If so, please clarify and me or someone more expert in unsafe pointers in Swift can help out.)
What you are dealing with (void *info) is a C pointer-to-void, which arrives into Swift as a form of UnsafeRawPointer. This means that type info has been cast away and that memory is being managed elsewhere.
In order to work with this thing as what you believe it to be, i.e. a RunLoopSource, you need to characterize it explicitly as a RunLoopSource. In C, you would cast, as in the example code you posted: (RunLoopSource*)info. In Swift, you rebind.
Observe that in your case this whole thing has been made just a little more complicated by the fact that this UnsafeMutableRawPointer has been wrapped in an Optional, and will have to be unwrapped before you can do anything at all.
Assuming, then, in your case, that info is really an UnsafeMutableRawPointer? bound to a RunLoopSource, you can say:
let rlsptr = info!.assumingMemoryBound(to: RunLoopSource.self)
let rls = rlsptr.pointee
Now rls is a RunLoopSource and you can work with it however you like. Keep in mind, however, that the memory is unmanaged, so you should work with it only here and now.
EDIT By the way, Apple has a really nice document on this entire matter: https://swift.org/migration-guide/se-0107-migrate.html

Parse local datastore doesn't work - Swift 2

I am currently using the latest version of Parse 1.14.2 and Bolts 1.8.4.Parse is implemented correctly and I have been using it for a long time now. The problem I'm facing now is when I try to use Parse's local datastore. I have the following code in my AppDelegate.swift:
Parse.enableLocalDatastore()
Parse.setApplicationId("ID",
clientKey: "Client_Key")
I have the following code to create and save a string named firstName in a class named contact:
let contact = PFObject(className: "contact")
contact["firstName"] = "Jack"
contact.pinInBackground()
Here is the code to retrieve objects from the created class:
let query = PFQuery(className: "contact")
query.fromLocalDatastore()
query.getFirstObjectInBackgroundWithBlock({ (object, error) -> Void in
if error == nil {
if let contact = object {
print(contact.objectForKey("firstName"))
}
}
})
I have added libsqlite3.dylib to my project. My app doesn't crash when I run this code but it simply gives me the following message when I try to retrieve objects:
2016-08-29 11:31:38.049 App_Demo[14436:3504319] [Bolts] Warning: `BFTask` caught an exception in the continuation block.
This behavior is discouraged and will be removed in a future release.
Caught Exception: Method requires Pinning enabled.
Can anyone help me to work around this issue? I am guessing the issue is that this version of Bolts cannot pin Parse objects in the background and I need to work my way around this bug. Any help would be appreciated as I have been stuck at this for a while and can't find too much info online.
Edited: I have tried downgrading Bolts, but then Parse downgrades with it in Cocoapod and it causes errors in Xcode.
it's not objectforkey.
You need to call object["UsedName"] "UsedName" being the key. Hope that helps.

AnyObject not working in Xcode8 beta6?

In Xcode8 beta6, the following code will cause a warning: 'is' test is always true. But it won't print pass.
struct TestStruct {
}
//warning: 'is' test is always true
if TestStruct() is AnyObject {
print("pass")
}
And the following code will cause a warning: Conditional cast from 'T' to 'AnyObject' always succeeds
public static func register<T>(_ protocolType: T.Type, observer: T) {
//Warning: Conditional cast from 'T' to 'AnyObject' always succeeds
guard let object = observer as? AnyObject else {
fatalError("expecting reference type but found value type: \(observer)")
}
//...
}
The warning works as intended: the false return of TestStruct() is AnyObject, however, does not
The prior version of this answer perceived the warning,
'is' test is always true
as the bug, and contained some discussion as to why this perceived buggy warning would manifest itself. That TestStruct() is AnyObject evaluated to false at runtime, however, was perceived as expected behaviour.
Given the comments to the bug report filed by the OP (SR-2420), it seems the situation is the reverse: since Xcode 8/beta 6, the is test should always evaluate to true, and the bug the OP:s post is the fact that TestStruct() is AnyObject evaluates to false during runtime.
Joe Groff writes:
This is correct, because everything bridges to AnyObject now.
...
is/as AnyObject always succeed for all types now. It's behaving
as intended.
The new SwiftValue box for conversion from Swift values to Obj-C objects
(for additional details, see discussion in the comments below, thanks #MartinR)
It seems as if Swift values that are not explicitly implemented to be bridgeable to Obj-C objects via e.g. conformance to _ObjectiveCBridgeable (see e.g. the following Q&A for details regarding _ObjectiveCBridgeable), will instead automatically make use of the new SwiftValue box to allow conversion to Obj-C objects.
The initial commit message for swift/stdlib/public/runtime/SwiftValue.mm reads:
Runtime: Implement an opaque 'SwiftValue' ObjC class to hold bridged values
If there's no better mapping for a Swift value into an Objective-C
object for bridging purposes, we can fall back to boxing the value in
a class. This class doesn't have any public interface beyond being
NSObject-conforming in Objective-C, but is recognized by the Swift
runtime so that it can be dynamically cast back to the boxed type.
Long story short.
To check if value has a reference type:
if type(of: value) is AnyClass {
// ...
}
To check if type is a reference type:
if SomeType.self is AnyClass {
// ...
}
More helpful answers:
https://stackoverflow.com/a/39185374/746347
https://stackoverflow.com/a/39546887/746347

Swift model class error handling

I have come with below approach for Model class initialisation with error handling. But whenever make init method as throwable in swift, I am not able to access from Obj C code. Declaration doesn't get create in projectname-Swift.h header file. Without "throws" it works perfect.
init?(dictionary: NSDictionary?)throws {
if let dictionary = dictionary {
// Parsing
}
else {
throw MyError.DictionaryNil("Nil Dictionary")
}
}
Am I missing anything here? My model class is subclass of NSObject and it has only one init method.
Well nullable & throwing isn't supported. You can remove ? and it will give you an error parameter.
Also you can't overload a throwing init as failable.
So it leaves you with the one with error parameter. Maybe you can call that from a convenience initializer in objective-c to send a nil error parameter.
If you'll want to use this with Objective C, I think you'll have to resort to the lowest common denominator in error handling: an NSError parameter.

Xcode error "value of type '[UIViewController]' has no member 'firstObject'"

I am attempting to create a Swift version of this GitHub repository. I started by doing a literal transfer of all the code from Objective-C to Swift, which caused a lot of errors (60 something in a 321 line file).
For this specific line of code
self.currentViewController = self.childViewControllers.firstObject
I am getting this error:
Value of type '[UIViewController]' has no member 'firstObject'
I have tried adding as! UIViewController, and placing exclamation points and question marks at most of locations possible. None of the things I tried from the suggested posts helped.
It's just called first on Swift arrays.
self.currentViewController = self.childViewControllers.first

Resources