Swift : Parse subclass dynamic var dont get included when retrieving object - dynamic

I canĀ“t get the object properties when retrieving an object from Parse Data Browser. This happened after I changed from "#NSManaged var friends" to "dynamic var friends". Even "name" show nil in User.logInWithUsernameInBackground block which is crazy because the login succeeds. The ACL for User is set to "public read".
User object:
class User : PFUser, PFSubclassing {
dynamic var friends:[User]!
dynamic var name:String!
override class func load() {
self.registerSubclass()
}
}
Retrieving the User along with the friends. println showing nil
var query = User.query()
query.includeKey("friends")
query.getObjectInBackgroundWithId(currentUser.objectId) {
(pfObject: PFObject!, error: NSError!) -> Void in
if pfObject != nil {
var user = pfObject as User
var friends = user.friends as [User]
println("friends: \(friends)") //nil
} else {
println(error)
}
}
Login. println showing nil
User.logInWithUsernameInBackground(USERNAME, password:PASSWORD) {
(user: PFUser!, error: NSError!) -> Void in
if user != nil {
println("Logged in with user: \(user.name)") //nil
} else {
println(error)
}
}

Looking a little deeper for you, it seems the hurdle is a misunderstanding of what the dynamic modifier in Swift does. Apparently, dynamic in Swift is used for Key-Value observing, not declaring a variable's accessors to be defined at runtime (what #dynamic does in Objective-C)
See this for a description of dynamic in Swift https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-XID_8
and this for the description of why #NSManaged works the way #dynamic does in Objective-C
https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/WritingSwiftClassesWithObjective-CBehavior.html#//apple_ref/doc/uid/TP40014216-CH5-XID_66

With xCode 6.1.1 I was able to get this working without the bridging header but I did need to use #NSManaged. Here's how... Just:
import Parse
at the top of the calling module. For the class declaration .swift file dynamic didn't work so I needed to use #NSManaged for the variable types to get them to link to the Parse class variables successfully. Like this:
class PSCategory : PFObject, PFSubclassing {
override class func load() {
self.registerSubclass()
}
class func parseClassName() -> String! {
return "Category"
}
#NSManaged var Name: String
}
Then in my query all the names are dynamically linked:
var query = PSCategory.query() // PFQuery(className: "Category")
query.cachePolicy = kPFCachePolicyCacheElseNetwork // kPFCachePolicyNetworkElseCache
query.maxCacheAge = 60 * 60 * 24 // One day, in seconds.
query.findObjectsInBackgroundWithBlock {
(categories: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for abstractCategory in categories {
let category = abstractCategory as PSCategory
NSLog("Category Name: %#", category.Name)
}
} else {
NSLog("Unable to retrieve categories from local cache or network")
}
}

Related

Could not cast value of type XX to XX in swift 3

In Swift 3 contains method is always giving error. In the below code if annotation is MKAnnotation is passed and goest to next line. Then it gives error. I have searched a lot but not able to find the problem. Any solution for this issue?
Class declaration:
open class FBAnnotation : NSObject {
open var coordinate = CLLocationCoordinate2D(latitude: 52.0936440, longitude: 4.3592340)
open var title: String? = ""
open var annotationIndex: Int?
}
extension FBAnnotation : MKAnnotation {
}
Usage:
do {
if annotation is MKAnnotation {
if try node.annotations.contains(where: annotation as! (MKAnnotation) throws -> Bool) {
try node.annotations.remove(at: node.annotations.index(where: annotation as! (MKAnnotation) throws -> Bool)!)
node.count -= 1
return true
}
}
} catch {
return false
}
This works in a playground, no casting required. You cannot pass an annotation as the where parameter. You must pass in a function that declares whether or not the annotation is the one you are looking for. Here I consider them a match if they have the same coordinates, although your match criteria may be different.
var annotations = [MKAnnotation]()
var annotation: Any? = nil
if let annotation = annotation as? MKAnnotation {
if let index = annotations.index(where: {
$0.coordinate.latitude == annotation.coordinate.latitude &&
$0.coordinate.longitude == annotation.coordinate.longitude
}) {
annotations.remove(at: index)
}
}

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

Swift extension - Parse Json to NSDictionary Model (By Using Category Like In Obj-C)

I have API request to get json string, something like that:
{
"data":[
{
"id":123,
"name":"Felix"},
{
"id":122,
"name":"Mary"},
{
"id":111,
"name":"Jason"},
]
}
and in my Obj-C NSDictionary+user is
-(NSString *)usrId
{
return self[#"id"];
}
-(NSString *)usrName
{
return self[#"name"];
}
...
and when I use this I import the category class, and access it like dict.name...
I can't find a good answer related to this question. How can I achieve this in Swift?
UPDATED [2017.07.21]
I've chose to use SwiftyJSON library to settle all the json-dictionary-object-mapping works. Save tons of time.
SwiftyJSON
Best and safest way to parse json response to nsdictionary and use it in swift is:
var responseRecord<datastruct> = Array<datastruct>()
struct datastruct
{
var id: String?
var name:String?
init(add: NSDictionary)
{
id = add["id"] as? String
name = add["name"] as? String
}
}
And to access it in program:
print(self.responseRecord.id)
print(self.responseRecord.name)
Simply create a empty Swift class with required properties (Swift 2.2):-
class myClass {
var id:Int?
var name:String?
init(id:Int,name:String)
{
self.id= id
self.name = name
}
}
Then to access in required VC :-
var requiredData:[String]?
var data:[myClass]?
override fun viewDidLoad()
{
super.viewDidLoad()
requiredData = data!.enumerate().map({ (index, category) in
return ("\(category.name!)")
})
print(requiredData)
}
Note: I am assuming you have already populated data in the mentioned "data" variable of myClass type. If not, then you can do so as follows:-
data = [myClass(id:1,name:"aaa"),myClass(id:2,name:"bob"),myClass(id:3,name:"ccc")]

Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c

Is it possible to call methods defined in a protocol extension in Swift from Objective-C?
For example:
protocol Product {
var price:Int { get }
var priceString:String { get }
}
extension Product {
var priceString:String {
get {
return "$\(price)"
}
}
}
class IceCream : Product {
var price:Int {
get {
return 2
}
}
}
The price string of an instance of IceCream is '$2' and can be accessed in Swift, however the method is not visible in Objective-C. The compiler throws the error 'No visible #interface for 'IceCream' declares the selector ...'.
In my configuration, if the method is defined directly in the Swift object's implementation, everything works as expected. i.e.:
protocol Product {
var price:Int { get }
var priceString:String { get }
}
class IceCream : Product {
var price:Int {
get {
return 2
}
}
var priceString:String {
get {
return "$\(price)"
}
}
}
I am nearly certain the answer to this is "no", although I haven't found official Apple documentation that spells it out.
Here is a message from the swift-evolution mailing list discussing the proposal to use dynamic dispatch for all method calls, which would provide calling semantics more like Objective-C:
Again, the sole exception to this is protocol extensions. Unlike any other construct in the language, protocol extension methods are dispatched statically in a situation where a virtual dispatch would cause different results. No compiler error prevents this mismatch. (https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001707.html)
Protocol extensions are a Swift-only language feature, and as such are not visible to objc_msgSend().
If it is ok to remove the priceString from the protocol, and only have it in your extension, you can call the protocol extension by casting IceCream to a Product in a helper extension.
#objc protocol Product {
var price:Int { get }
}
extension Product {
var priceString:String {
return "$\(price)"
}
}
// This is the trick
// Helper extension to be able to call protocol extension from obj-c
extension IceCream : Product {
var priceString:String {
return (self as Product).priceString
}
}
#objc class IceCream: NSObject {
var price: Int {
return 2
}
}
Protocol extension does not work with #objc protocol, however you can extend the class in swift as a workaround.
#objc protocol Product {
var price: NSNumber? { get }
var priceString:String { get }
}
...
// IceCream defined in Objective-C that does not extend Product
// but has #property(nonatomic, strong, nullable) (NSNumber*)price
// to make it confirm to Product
...
extension IceCream: Product {
var priceString:String {
get {
return "$\(price ?? "")"
}
}
}
This code is not clean at all, but it works.

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];