Swift extension - Parse Json to NSDictionary Model (By Using Category Like In Obj-C) - objective-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")]

Related

Unable to Parse property name "description" in Json file in realm swift

I am trying to parse JSON through realm Swift Here is my Json. response
{
code = 1;
data = {
subjects = (
{
code = 786;
description = "not a good deal";
id = 13;
name = "Mechine Learning in iOS";
}
);
};
message = success;
}
And here it is my swift file to parse this json.
import UIKit
import RealmSwift
import Realm
class Subject: Object
{
#objc dynamic var id: Int = 0
#objc dynamic var code: String!
#objc dynamic var name: String!
#objc dynamic var description: String!
override class func primaryKey() -> String? {
return "id"
}
}
We know that description is keyword in swift, so it shows this error:
Getter for 'description' with Objective-C selector 'description' conflicts with getter for 'description' from superclass 'NSObject' with the same Objective-C selector.
Is there any way to parse description here, without using key value explicitly?
use another parameter for description (myDescription) and override init
import UIKit
import RealmSwift
import Realm
class Subject: Object{
func parseDescription(data : [String: Any]) {
guard let desc:String = data["description"] as? String else{
return
}
myDescription = desc
}
override init(value: Any) {
super.init(value: value)
guard let data: [String: Any] = value as? [String: Any] else {
return
}
parseDescription(data: data)
}
required init(realm: RLMRealm, schema: RLMObjectSchema) {
super.init(realm: realm, schema: schema)
}
required init() {
super.init()
}
required init(value: Any, schema: RLMSchema) {
super.init(value: value, schema: schema)
guard let data: [String: Any] = value as? [String: Any] else {
return
}
parseDescription(data: data)
}
#objc dynamic var id: Int = 0
#objc dynamic var code: String!
#objc dynamic var name: String!
#objc dynamic var myDescription: String!
override class func primaryKey() -> String? {
return "id"
}
}
Found 2 soutions:
Update the date of your machine
Use alternate USB port
To update the datetime of your machine you can open Utilities->Terminal in the installer, check the current datetime with the command date and if it is wrong use the command ntpdate -u time.apple.com
Note that you has to be connected to the internet or use the command date with timestamp to set the datetime manually

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.

Call generic function from non-generic function in Swift

MMCondition is a protocol defined in Swift, but interoperates with Objective-C (annotated with #objc).
#objc public protocol MMCondition {
static var name: String { get }
static var isMutuallyExclusive: Bool { get }
}
I have the following code:
// addCondition cannot be generic as I want it to be accessible from Objective-C as well.
public func addCondition(condition: MMCondition) {
// How do I initialize OperationConditionImplementer here?
let operationCondition = OperationConditionImplementer(condition: condition) // doesn't compile
// Error: Cannot invoke initializer for type 'OperationConditionImplementer<T>' with an argument list of type '(condition: MMCondition)'
// Can I use condition.dynamicType to init OperationConditionImplementer somehow?
}
struct OperationConditionImplementer<T: MMCondition> {
let condition: T
static var name: String {
return "Silent<\(T.name)>"
}
static var isMutuallyExclusive: Bool {
return T.isMutuallyExclusive
}
init(condition: T) {
self.condition = condition
}
}
From Objective-C, you can't use generics as stated in the documentation.
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics
...
So you need to remove completely the generics code. One possible solution might be:
#objc protocol MMCondition {
static var name: String { get }
static var isMutuallyExclusive: Bool { get }
}
struct OperationConditionImplementer {
let condition: MMCondition
var name: String {
return "Silent<\(condition.dynamicType.name)>"
}
var isMutuallyExclusive: Bool {
return condition.dynamicType.isMutuallyExclusive
}
init(condition: MMCondition) {
self.condition = condition
// Here decide comparing types
if condition.dynamicType === ExampleCondition.self {
print(condition.dynamicType.name)
}
}
}
So for instance, if you try it out in a playground:
class ExampleCondition: NSObject, MMCondition {
static var name: String = "ExampleCondition"
static var isMutuallyExclusive: Bool = false
}
let example = OperationConditionImplementer(condition: ExampleCondition())
You'll see "ExampleCondition" printed.
If you eventually switch to pure Swift, you need to specify T when initializing a OperationConditionImplementer.
You can achieve that defining the addCondition method as:
func addCondition<T: MMCondition>(condition: T) {
let a = OperationConditionImplementer<T>(condition: condition)
}
Since Swift 2.0 instances of generic classes can implement Objective-C protocols. What won't be possible I believe is having a struct implement the protocol. In fact I expect that your protocol may need to inherit from NSObjectProtocol to be usable in Objective-C which would then prevent you from implementing the protocol with structs or enums.
You also rightly mention that you can't access generic functions from Objective-C.
For a concrete example of using a generic to fulfil an Objective-C protocol have a look at this blog post.

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

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")
}
}