Is there any equivalent to gson in Objective-C?
Thanks.
DCKeyValueObjectMapping https://github.com/dchohfi/KeyValueObjectMapping is not a JSON parser but an object-json mapper to inject NSObject properties from NSDictionary/NSArray.
I recently used Mantle which works great and is very similar to GSON (which is use for android projects)
https://github.com/Mantle/Mantle
In Objective-C the functionality of GSON is sort of built in. Say I have a class defined like so:
#interface MyModel : NSObject
#property(nonatomic,strong) NSString *name;
#property(nonatomic,strong) NSString *address;
#end
And lets say that I have a JSON object defined like so
{
"name":"marc",
"address":"1234 Some Street"
}
Then I can use AFNetowrking to get an NSDictionary of the JSON object which is pretty easy. Finally you can just do a loop like so where dict is the dictionary returned by AFNetworking parsing the JSON and self is an instance of MyModel.
for (NSString *key in dict) {
[self setObject:dict[key] forKey:key];
}
In Java GSON uses reflection to achieve the same effect as the above loop. Its just a lot easier in objective-c so no need for a library to do it. If you have nested objects maybe AFNetworking with DCKeyValueObjectMapping.
OCMapper is very similar to Gson and easy to use
https://github.com/aryaxt/OCMapper
Json
{
"firstName" : "FirstName",
"lastName" : "LastName",
"age" : 26,
"dateOfBirth" : "01/01/2013",
"address" : {
"city" : "San Diego",
"country" : "US"
},
"posts" : [
{
"title" : "Post 1 title",
"datePosted : "04/15/2013",
},
{
"title" : "Post 2 title",
"datePosted : "04/12/2013",
}
]
}
Model
#objc public class User: NSObject {
var firstName: String?
var lastName: String?
var age: NSNumber?
var dateOfBirth: NSDate?
var address: Address?
var posts: [Post]?
}
Usage Swift
let user = ObjectMapper.sharedInstance().objectFromSource(dict, toInstanceOfClass:User.self) as User
or
let User = User.objectFromDictionary(dictionary)
Usage Objective C
User *user = [[ObjectMapper sharedInstance] objectFromSource:dictionary toInstanceOfClass:User.class];
or
User *user = [User objectFromDictionary:dictionary];
At WWDC 2017, Apple has introduced the new feature in Swift to parse JSON without any pain using Swift Codable protocol
struct YourStructure: Codable {
let name: String?
let avatarUrl: URL?
private enum CodingKeys: String, CodingKey {
case name
case avatarUrl = "avatar_url"
}
}
decoder:
let decoder = JSONDecoder()
parsedData = decoder.decode(YourStructure.self, from: YourJsonData)
encode:
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(data)
more info: Encoding and Decoding Custom Types
OCMapper is the best i know and the easiest library and it have reverse mapping as well and map complex objects without the need of configuration , and work with realmObjects as well
Yes - see http://psionides.jogger.pl/2010/03/04/cocoa-json-parsing-libraries/
I think I have found few libraries which can server this purpose but most important one seems to be RestKit
Related
I need to write test case and in the test case, I need to mock an object of the following class and put them into an array. e.g. moviesArray = [MovieCoreData]. I am wondering how I can instantiate this?
#objc(MovieCoreData)
class MovieCoreData {
static var genre: Genre = "Comedy"
#NSManaged var actor: String
#NSManaged var released: Bool
#NSManaged var name: String?
public static func mapping() -> [FieldName: String] {
return [
"actor": "actor",
"released": "released",
"name": "name"
]
}
}
For example, I tried to write
moviesArray = [MovieCoreData(actor: "Johnny Depp", released: True, name: "Pirate Captain"]
and
moviesArray.first.actor = "Johnny Depp"
moviesArray.first.released = "true"
moviesArray.first.name = "Pirate Captain"
none of them worked, I admit I don't know much about Objective-C and NSManaged, can someone tell me how to create an array of MovieCoreData with fake data?
The #NSManaged attribute belongs to Core Data.
The class must be a subclass of NSManagedObject and instances must be created with regard to the NSManagedObjectContext to take advantage of its functionality
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")]
Okay, here is some Objective C code I've been using in the past to quickly check if a dictionary contains certain key/value pairs and if the values are of the right type (have the expected class).
First I need a little helper class:
#interface TypeCheck : NSObject
#property NSString * name;
#property Class type;
#end
#implementation TypeCheck
+ (instancetype)typeCheckWitName:(NSString *)name type:(Class)type {
TypeCheck * tc = [[self alloc] init];
tc.name = name;
tc.type = type;
return tc;
}
#end
And then I can define keys and types like this:
NSArray<TypeCheck *> * model = #[
[TypeCheck typeCheckWitName:#"firstName" type:[NSString class]],
[TypeCheck typeCheckWitName:#"lastName" type:[NSString class]],
[TypeCheck typeCheckWitName:#"age" type:[NSNumber class]],
[TypeCheck typeCheckWitName:#"image" type:[NSImage class]]
// ... Many more follow ...
];
I was able to use structs for all that prior to ARC but with ARC the compiler doesn't like objects in structs, so I now use objects for everything. The final check code just looks like this:
for (TypeCheck * typeCheck in model) {
id value = dict[typeCheck.name];
if (!value) {
// BAD... Value must be there
// Throw error
return;
}
if (![value isKindOfClass:typeCheck.type]) {
// BAD... Value must be of right type
// Throw error
return;
}
// Do something with value
}
This used to be pretty nice code IMHO. Gets even nicer if you use a C function to create the TypeCheck objects:
#define TypeString [NSString class]
#define TypeNumber [NSNumber class]
#define TypeImage [NSImage class]
static TypeCheck * makeTypeCheck ( NSString * name, Class type ) {
return [TypeCheck typeCheckWitName:name type:type];
}
And then:
NSArray<TypeCheck *> * model = #[
makeTypeCheck(#"firstName", TypeString),
makeTypeCheck(#"lastName", TypeString),
makeTypeCheck(#"age", TypeNumber),
makeTypeCheck(#"image", TypeImage)
// ... and so on ...
One could even make makeTypeCheck a macro.
And now I'm trying to do the same or similar in Swift... and I fail horribly! I tried with a struct but how can I store class types there? I don't want to use Any as a type qualifier. Then I tried with a generic struct, like struct TypeCheck<T> so I could set the type, but I cannot put multiple of these into a single array as when T is different, as these are effectively different types (and again, I don't want to use Array<Any> despite that how can I cast? I cannot cast to TypeCheck without a generic type). I don't have to use a struct, an object will be fine but that doesn't really solve any of my problems. I cannot believe that this is so hard to do in Swift. I'm not really a Swift expert yet, so I guess i must be missing something important here.
While Obj-C was good at dynamic type inspection, Swift is much better of you allow the compiler to do the type checking at compile time. Since I don't know what your specific use case is, I'd encourage you to look for a more Swifty way to do what you want instead of just converting Obj-C to Swift. But since I don't know your use case, I'll just answer your question as written.
The only way I could get something like you describe was to make the struct conform to a protocol. Then when you put it in an array, you set the arrays type to contain instances of the protocol.
protocol TypeCheckable {
var name: String { get }
func matches(thing: Any) -> Bool
}
struct TypeCheck<T>: TypeCheckable {
let name: String
init(name: String, type: T.Type) {
self.name = name
}
func matches(thing: Any) -> Bool {
return thing is T
}
}
let array: [TypeCheckable] = [
TypeCheck(name: "test", type: String.self),
TypeCheck(name: "other", type: Int.self)
]
If you define this:
typealias TypeChecker = ([String: AnyObject]) -> Bool
struct makeTypeCheck<T> {
let name: String
func checker() -> TypeChecker {
return { (dict: [String: AnyObject]) -> Bool in
guard let v = dict[self.name] else {
return false
}
if let _ = v as? T {
return true
}
return false
}
}
}
func checkDictionary(dict: [String: AnyObject], model: [TypeChecker]) -> Bool {
for m in model {
if !m(dict) {
return false
}
}
return true
}
This test passes
func testChecker() {
let model = [
makeTypeCheck<String>(name: "firstName").checker(),
makeTypeCheck<Int>(name: "age").checker(),
]
XCTAssertTrue(checkDictionary(["firstName": "Jane", "age": 35], model: model))
XCTAssertFalse(checkDictionary(["firstName": 21, "age": 35], model: model))
XCTAssertFalse(checkDictionary(["age": 35], model: model))
}
I have a question regarding object oriented design principles and Swift. I am pretty familiar with Java and I am currently taking a udacity course to get a first hands on in Swift.
In the Java community (basically in every community that follows OOP) it is very common to use information hiding techniques such as hiding or encapsulating data within classes to make sure it cannot be manipulated from outside. A common principle is to declare all attributes of a class as being private and use getters for retrieving an attribute's value and setters for manipulation.
I tried to follow this approach when writing a class that was part of the course and it looks like this:
//
// RecordedAudio.swift
// Pitch Perfect
//
import Foundation
class RecordedAudio: NSObject {
private let filePathUrl: NSURL!
private let title: String?
init(filePathUrl: NSURL, title: String?)
{
self.filePathUrl = filePathUrl
self.title = title
}
func getFilePathUrl() -> NSURL
{
return filePathUrl
}
func getTitle() -> String
{
if title != nil
{
return title!
}
else
{
return "none"
}
}
}
The code works and my private attributes cannot be accessed from outside my class, which is exactly the behavior I wanted to achieve. However, the following questions came to my mind:
1.) The course instructor decided to leave the attributes' access control level at the default "internal" and not use getters/setters but rather access the attributes directly from outside. Any thoughts on why developers might do that in swift? Is my approach not "swift" enough???
2.) In conclusion: Is there a "swifter" way to implement encapsulation when writing your own class? What are swift's native techniques to achieve the information hiding I am aiming for?
You can restrict external property manipulation, by marking the property public for reading and private for writing, as described in the documentation:
class RecordedAudio: NSObject {
public private(set) let filePathUrl: NSURL!
public private(set) let title: String?
init(filePathUrl: NSURL, title: String?) {
self.filePathUrl = filePathUrl
self.title = title
}
}
// in another file
let audio = RecordedAudio(filePathUrl: myUrl, title: myTitle)
let url = audio.filePathUrl // works, returns the url
audio.filePathUrl = newUrl // doesn't compile
I do it a bit like in Obj-C:
class MyClass
private var _something:Int
var something:Int {
get {return _something}
// optional: set { _something = newValue }
}
init() { _something = 99 }
}
...
let c = MyClass()
let v = c.something
Above is a primitive example, but handled stringent it works as a good pattern.
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")
}
}