I've created a wizard for user's to sign up using my app, however, I'm having some doubts as to how I should store their information along the way.
I have a User model, which is filled out when users are pulled from the database, however, there are some required fields on this model that wouldn't be filled out if I were to use it as the object that is passed along as the user goes through the the wizard.
Here is my User model:
final class User: NSObject, ResponseObjectSerializable, ResponseCollectionSerializable {
let id: Int
var facebookUID: String?
var email: String
var firstName: String
var lastName: String
var phone: String?
var position: String?
var thumbnail: UIImage?
var timeCreated: CVDate
init?(response: NSHTTPURLResponse, var representation: AnyObject) {
if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [String: AnyObject]) {
representation = dataRepresentation
}
self.id = representation.valueForKeyPath("id") as! Int
self.facebookUID = (representation.valueForKeyPath("facebook_UID") as? String)
self.email = (representation.valueForKeyPath("email") as? String) ?? ""
self.firstName = (representation.valueForKeyPath("first_name") as? String) ?? ""
self.lastName = (representation.valueForKeyPath("last_name") as? String) ?? ""
self.phone = (representation.valueForKeyPath("phone") as? String)
self.position = (representation.valueForKeyPath("position_name") as? String)
self.thumbnail = UIImage(named: "ThomasBaldwin")
if let timeCreated = representation.valueForKeyPath("time_created") as? String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
if let date = formatter.dateFromString(timeCreated) {
self.timeCreated = CVDate(date: date)
} else {
self.timeCreated = CVDate(date: NSDate())
}
} else {
self.timeCreated = CVDate(date: NSDate())
}
}
static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [User] {
var users: [User] = []
if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [NSDictionary]) {
if let dataRepresentation = dataRepresentation as? [[String: AnyObject]] {
for userRepresentation in dataRepresentation {
if let user = User(response: response, representation: userRepresentation) {
users.append(user)
}
}
}
}
return users
}
}
Notice the variables id and timeCreated. These are both generated when a new row is added to the Users table in the database, therefore, I wouldn't have values for those variables until the user is actually created.
Also, I would like to add some methods to the model, such as validateUser which will be a method that makes sure all the fields are filled out, and validateEmail which will be a method that makes sure the email is in proper syntax, and so on...
My question is, should I
A. just make those constants optional and add those methods to my current User model
B. make another model called CreateUserModel that only has variables for the information the user will be filling out and put the extra methods in there
UPDATE
I updated my User class to use a dictionary as the storage mechanism and it already looks a lot cleaner. However, the issue that comes to mind is, how will another programmer know which fields he can grab from the User model since I'm not individually storing them as variables anymore. Would they just have to check the DB and look at the structure of the table?
Here's my updated User class:
final class User: NSObject, ResponseObjectSerializable, ResponseCollectionSerializable {
var properties = NSDictionary()
init?(response: NSHTTPURLResponse, representation: AnyObject) {
if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [String: AnyObject]) {
properties = dataRepresentation
}
properties = representation as! NSDictionary
}
static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [User] {
var users: [User] = []
if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [NSDictionary]) {
if let dataRepresentation = dataRepresentation as? [[String: AnyObject]] {
for userRepresentation in dataRepresentation {
if let user = User(response: response, representation: userRepresentation) {
users.append(user)
}
}
}
}
return users
}
}
I would make them Optionals. That is the beauty of Optionals - you can use nil to mean exactly "no data here".
The other grand strategy that comes to mind is to use a dictionary as the storage mechanism inside your model, because that way either it has a certain key or it doesn't. You could make your User object key-value coding compliant, and thus effectively transparent, by passing keys on to the dictionary.
Related
I want to put the information I get from the API into the corresponding Label inside, I use the Alamofire to get the API information and put the corresponding Label inside, but I found that my Label text has not been changed, would like to ask this happen What's the problem? Who can answer me for me? Thank you
Here is my Information class:
import Foundation
import Alamofire
class Information {
var account:String?
var date:String?
var name:String?
var sex:String?
var born:String?
var phoneNumber:String?
var email:String?
init(account:String,date:String,name:String,sex:String,born:String,phoneNumber:String,email:String) {
self.account = account
self.date = date
self.name = name
self.sex = sex
self.born = born
self.phoneNumber = phoneNumber
self.email = email
}
typealias DownlaodComplete = () -> ()
func downlaodInformation(completion:#escaping DownlaodComplete) {
Alamofire.request("http://163.18.22.78:81/api/Information/A1001a").responseJSON { response in
print(response)
if let json = response.result.value as? Dictionary<String,Any> {
guard let account = json["Account"] as? String ,let date = json["Date"] as? String , let name = json["Name"] as? String , let sex = json["Sex"] as? String , let born = json["Born"] as? String , let phoneNumber = json["PhoneNumber"] as? String , let email = json["Email"] as? String else {
return
}
self.account = account
self.date = date
self.name = name
self.sex = sex
self.born = born
self.phoneNumber = phoneNumber
self.email = email
completion()
}
}
}
}
And here is my ViewController:
var information:Information?
override func viewDidLoad() {
super.viewDidLoad()
if let currentInformation = information {
currentInformation.downlaodInformation {
self.accountLabel.text = currentInformation.account
self.dateLabel.text = currentInformation.date
self.nameLabel.text = currentInformation.name
self.sexLabel.text = currentInformation.sex
self.bornLabel.text = currentInformation.born
self.phoneNumberLabel.text = currentInformation.phoneNumber
self.emailLabel.text = currentInformation.email
}
}
You need to use your completion block which will be called whenever Alamofire has finished the data request. You can also improve your code a bit by for example have a onCompletion block that passes an Information object and an onError block to display if you have any errors. Example below:
func downlaodInformation(parameterOne: String, parameterTwo: Int, onCompletion: #escaping (Information) -> Void, onError: #escaping(NSError) -> Void) {
Alamofire.request("http://163.18.22.78:81/api/Information/A1001a").responseJSON { response in
if let json = response.result.value as? Dictionary<String,Any> {
let account = json["Account"] as? String
let date = json["Date"] as? String
let name = json["Name"] as? String
let sex = json["Sex"] as? String
let born = json["Born"] as? String
let phoneNumber = json["PhoneNumber"] as? String
let email = json["Email"] as? String
let information = Information(account: account, date: date, name: name, sex: sex, born: born, phoneNumber: phoneNumber, email: email)
onCompletion(information)
} else {
onError(NSError(domain: "Error while getting data", code: 0, userInfo: nil))
}
}
}
Usage:
downlaodInformation(parameterOne: "someParam", parameterTwo: 123, onCompletion: { (currentInformation) in
print(currentInformation.account)
self.accountLabel.text = currentInformation.account
self.dateLabel.text = currentInformation.date
self.nameLabel.text = currentInformation.name
self.sexLabel.text = currentInformation.sex
self.bornLabel.text = currentInformation.born
self.phoneNumberLabel.text = currentInformation.phoneNumber
self.emailLabel.text = currentInformation.email
}) { (error) in
print(error.domain)
}
Here you declare information to be an Information optional
var information:Information?
But you don't give it an initial value, meaning that it is nil
In your viewDidLoad you do the right thing and check whether information has a value:
if let currentInformation = information
But I'm guessing it hasn't, because you haven't created an instance of it. Therefore you don't end up inside your if let loop and never calls downlaodInformation
So you need to create a new instance of Information before you can use it.
However
This leads to a problem with your Information class.
If I was to instantiate an Information object, I'd need to have:
account
date
name
sex
born
phoneNumber
email
Or..since you've created them as optionals, pass nil.
But that is not what you want, is it?
I'm guessing you'd like to do something along the lines of this in your ViewController:
let information = Information()
and then in viewDidLoad
information.downloadInformation( currrentInformation in
self.accountLabel.text = currentInformation.account
....
}
To do so you could change your Information to not take parameters to its constructor and then create another struct which would hold your data.
Something like:
struct Information {
var account:String?
var date:String?
var name:String?
var sex:String?
var born:String?
var phoneNumber:String?
var email:String?
}
class InformationLoader {
func downloadInformation(completion: (Information?) -> ()) {
Alamofire.request("http://163.18.22.78:81/api/Information/A1001a").responseJSON{ response in
print(response)
if let json = response.result.value as? Dictionary<String,Any> {
guard let account = json["Account"] as? String,
let date = json["Date"] as? String,
let name = json["Name"] as? String,
let sex = json["Sex"] as? String,
let born = json["Born"] as? String,
let phoneNumber = json["PhoneNumber"] as? String,
let email = json["Email"] as? String else {
completion(nil)
return
}
let information = Information(account: account, date: date, name: name, sex: sex, born: born, phoneNumber: phoneNumber, email: email)
completion(information)
}
}
}
And you'd need to change your code in the ViewController to:
let informationLoader:InformationLoader()
In viewDidLoad
informationLoader.downloadInformation{ currentInformation in
if let currentInformation = currentInformation {
//populate your textfields
self.accountLabel.text = currentInformation.account
....
}
}
Hope that makes sense and helps you.
Your code has a lot of mistakes, so here is a working variant. Better to call an updateUI or something like that from the closure. I hope this will help:
ViewController.swift:
class ViewController: UIViewController
{
#IBOutlet weak var accountLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var nameLabel: UILabel!
var information: Information?
override func viewDidLoad()
{
super.viewDidLoad()
information = Information.init(account: "aaaa", date: "dddd", name: "nnnn", sex: "ssss", born: "bbbb", phoneNumber: "pppp", email: "eeee")
information?.downlaodInformation(completion:
{
self.updateUI()
})
}
func updateUI()
{
print("called")
self.accountLabel.text = information?.account
self.dateLabel.text = information?.date
self.nameLabel.text = information?.name
/*self.sexLabel.text = currentInformation.sex
self.bornLabel.text = currentInformation.born
self.phoneNumberLabel.text = currentInformation.phoneNumber
self.emailLabel.text = currentInformation.email*/
}
}
Information.swift:
class Information
{
var account:String?
var date:String?
var name:String?
var sex:String?
var born:String?
var phoneNumber:String?
var email:String?
typealias DownlaodComplete = () -> ()
init(account:String,date:String,name:String,sex:String,born:String,phoneNumber:String,email:String) {
self.account = account
self.date = date
self.name = name
self.sex = sex
self.born = born
self.phoneNumber = phoneNumber
self.email = email
}
func downlaodInformation(completion:#escaping DownlaodComplete) {
Alamofire.request("http://163.18.22.78:81/api/Information/A1001a").responseJSON { response in
print(response)
completion()
if let json = response.result.value as? Dictionary<String,Any>
{
print("Dictionary done")
guard
let account = json["Account"] as? String,
let date = json["Date"] as? String ,
let name = json["Name"] as? String else
{
print("Parse error!")
return
}
self.account = account
self.date = date
self.name = name
/*self.sex = sex
self.born = born
self.phoneNumber = phoneNumber
self.email = email*/
completion()
}
}
}
}
Tested, and got the following response:
SUCCESS: {
Account = A1001a;
Born = 841031;
CarParking = "";
Date = "0001/1/1 \U4e0a\U5348 12:00:00";
Email = "zxc#gmail.com";
Name = Ray;
Phone = 09361811111;
Sex = "\U7537"; } called Dictionary done called
I'm new to iOS development and I understand that allowing optional values when an object is initialized is not a 'good citizen' technique. That being said, I've read that it is good practice to always have values set, like this:
class Item{
var name: String
var color: String
init(name: String, color: String) {
self.name = name
self.color = color
}
}
This looks nice and tidy but how can I do something like that working with Firebase? Look what I've got so far:
private func loadPosts(){
databaseHandle = ref.child("users/\(self.user.uid)/posts").observe(.value, with:{(snapshot) in
var newPosts = [Post]()
for itemSnapShot in snapshot.children {
let post = Post(snapshot: itemSnapShot as! FIRDataSnapshot)
newPosts.append(post!)
}
self.posts = newPosts
self.tableView.reloadData()
})
}
This guy is placed in my PostsViewController where I have my table view. This is my model:
class Post {
var ref: FIRDatabaseReference?
var title: String?
var answer: String?
var contentUrl: String?
var photoUrl: String?
var createdAt: String?
var feeling: String?
var kind: String?
var text: String?
var uid: String?
var measurements: Dictionary<String, String>?
//MARK: Initialization
init?(snapshot: FIRDataSnapshot){
ref = snapshot.ref
let data = snapshot.value as! Dictionary<String, Any>
title = data["title"]! as? String
answer = data["answer"] as? String
contentUrl = data["content_url"] as? String
photoUrl = data["photo_url"] as? String
createdAt = data["created_at"] as? String
feeling = data["feeling"] as? String
kind = data["kind"] as? String
text = data["text"] as? String
uid = data["uid"] as? String
measurements = data["measurements"] as? Dictionary<String, String>
}
}
I don't know exactly why but those question marks doesn't feel quite right and now and then I get some nil pointer error, which I think I should be able to avoid by using the 'good citizen' technique.
So, does anybody know how can I use Firebase following Swift best practices?
Either you wish to allow the properties of your Post class to be nil or you don't.
If you do, that's fine. The code you posted allows any of them to be nil. You just need to safely access each property every time you need it.
If you don't, then don't make them optional. Then in your init you need to ensure none of the properties are set to nil by giving each a default if there is no value in the snapshot.
class Post {
var ref: FIRDatabaseReference
var title: String
var answer: String
var contentUrl: String
var photoUrl: String
var createdAt: String
var feeling: String
var kind: String
var text: String
var uid: String
var measurements: [String : String]
//MARK: Initialization
init?(snapshot: FIRDataSnapshot) {
if let data = snapshot.value as? [String : Any] {
self.ref = snapshot.ref
title = data["title"] as? String ?? ""
answer = data["answer"] as? String ?? ""
contentUrl = data["content_url"] as? String ?? ""
photoUrl = data["photo_url"] as? String ?? ""
createdAt = data["created_at"] as? String ?? ""
feeling = data["feeling"] as? String ?? ""
kind = data["kind"] as? String ?? ""
text = data["text"] as? String ?? ""
uid = data["uid"] as? String ?? ""
measurements = data["measurements"] as? [String : String] ?? [:]
} else {
return nil
}
}
}
Note how this ensures there is a proper snapshot. Note how a default value is set to each property if there is no value in the snapshot. Obviously you can assign any default you wish. I use the empty string as an example.
Even if you want to allow the properties to be nil, you should at least update your code to check for a valid snapshot like in the code above.
Of course you can have a combination where some properties can't be nil and some can. That's up to your needs.
First it is fine for you to have optionals in your data model, as long as you assign value to it later on in the future.
I would recommend to use ObserveSingleEvent() and you should make use of completion handler to make it easy. If you don't know completion handler: Link
I recommend:
• not to put database ref in your class model, and instead of using Dictionary<String, String>? just use [String: AnyObject]?
• make your post array public so that it can be accessed into the tableview.
Here's example:
class func getPosts(uid: String, _ completion: #escaping (_ posts: [Post]?, _ error: Error?) -> Void) {
//update inside users node
var posts = [Post]()
Firebase.databaseRef.child("users").child(uid).child("posts").observeSingleEvent(of: FIRDataEventType.value, with: { (dataSnapshot) in
guard let postsDictionary = dataSnapshot.value as? [String: AnyObject] else {
completion(nil, nil)
return
}
let n = postsDictionary.count
for postDictionary in postsDictionary {
let post = Post()
post.userID = uid
if let content = postDictionary.value["content"] as? String {
post.content = content
}
if let imageURL = postDictionary.value["imageURL"] as? String {
post.imageURL = imageURL
}
if let timeStamp = postDictionary.key as String! {
if let date = timeStamp.convertToDate() {
post.timeStamp = date
}
post.postIdentifier = timeStamp
}
posts.append(post)
if posts.count == n {
// Sort the array by the newest post
let sortedPosts = posts.sorted(by: { $0.timeStamp.compare($1.timeStamp) == .orderedDescending })
completion(sortedPosts, nil)
}
}
}) { (error) in
completion(nil, error)
}
}
Assigning to tableview be like:
getPosts(uid: Current.user.userID!) { (posts, error) in
guard error == nil else {
print(error.debugDescription)
return
}
cell.label.text = posts[indexPath.item].content
I am trying to retrieve the data from firebase database. However, I cannot get my local variables assigned to the values of the database. I am using the following classes and methods.
class Episode {
var title: String?
var description: String?
var location: String?
var discount: String?
var star: Int?
init() {
self.title = ""
self.description = ""
self.location = ""
self.discount = ""
self.star = 0
}
This is my method for pulling the data from the databse
func getValues() -> Episode {
let rootRef = FIRDatabase.database().reference().child("Restaurants").child("The Kafe")
let descriptionRef = rootRef.child("Description")
let discountRef = rootRef.child("Discount")
let locationRef = rootRef.child("Location")
let starRef = rootRef.child("Star")
let episode = Episode()
descriptionRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
episode.description = snap.value as? String
}
discountRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
episode.discount = snap.value as? String
}
locationRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
episode.location = snap.value as? String
}
starRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
episode.star = snap.value as? Int
print(episode.description!)
}
return episode
}
When I print out the values of the returned episode, they are all empty. However, when I print the values within the closure itself (Eg. if I do print(episode.description) within the obserEventType closure, it works fine. But if I print it outside it is empty.
I think I am missing something fundamental about swift or firebase. I am new to iOS programming so any help would be greatly appreciated.
Only inside the first observer you will have the value the return will always be nil, that is because only the return is trying to work in a sync way while firebase will always work in an async way
rootRef.observeEventType(.Value, withBlock: {(snap) in
let ep: Dictionary<String,AnyObject?> = [
"title": snap.childSnapshotForPath("Title").value as? String,
"description": snap.childSnapshotForPath("Description").value as? String,
"location": snap.childSnapshotForPath("Location").value as? String,
"discount": snap.childSnapshotForPath("Discount").value as? String,
"star": (snap.childSnapshotForPath("Star").value as? NSNumber)?.integerValue,
]
//Here you have your data in your object
episode = Episode(key: snap.key, dictionary: ep)
})
rootRef.observeEventType(.Value) { (snap: FIRDataSnapshot) in
print(snap.childSnapshotForPath("Title").value as? String)
}
return episode!
Also if you want to get it from a function like that you should probably use observeSingleEventType.
You need to rethink flow of your code because you are expecting firebase to work synchronously when it is always asynchronous. The way you have your getValues function will never work.
To solve this issue you should read about async execution and callbacks in swift.
All Firebase events are asynchronous so they are executed in a non-sequential way, that is why you only have access to the data inside the context of the callback...if you put a print outside the callback it is executed in a synchronous way so it gets executed before the callback, that is why it is in its initial status
1) You only need the rootRef, delete the rest
let ref = FIRDatabase.database().reference().child("Restaurants").child("The Kafe")
2) You only need one observer
var episode:Episode? = nil
rootRef.observeEventType(.Value,withBlock: {(snap) in
let ep:Dictionary<String,AnyObject?> = [
"title":snap.childSnapshotForPath("title").value as? String,
//Etc...
"star":(snap.childSnapshotForPath("price").value as? NSNumber)?.integerValue,
]
//Here you have your data in your object
episode = Episode(key:snap.key,dictionary:ep)
}
3) your episode class can be like this
class Episode {
private var _key:String!
private var _title:String?
//Etc.....
private var _star:Int?
var key:String!{
return _key
}
var title:String?{
return _title
}
//Etc....
var star:Int?{
return _star
}
init(key:String!, title:String?,//etc...., star:Int?){
self._key = key
self._title = title
//Etc....
}
init(key:String,dictionary:Dictionary<String,AnyObject?>){
_key = key
if let title = dictionary["title"] as? String{
self._title = title
}
//Etc...
if let star = dictionary["star"] as? Int{
self._star = star
}
..
}
}
I'm using Alamofire and am parsing the returned JSON into an object as shown below:
final class User: NSObject, ResponseObjectSerializable {
var id: Int
var facebookUID: String?
var email: String
var firstName: String
var lastName: String
var phone: String?
var position: String?
var timeCreated: CVDate
init?(response: NSHTTPURLResponse, var representation: AnyObject) {
if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [String: AnyObject]) {
representation = dataRepresentation
}
if let id = representation.valueForKeyPath("id") as? Int {
self.id = id
} else {
self.id = 0
}
if let facebookUID = representation.valueForKeyPath("facebook_UID") as? String {
self.facebookUID = facebookUID
}
if let email = representation.valueForKeyPath("email") as? String {
self.email = email
} else {
self.email = ""
}
if let firstName = representation.valueForKeyPath("first_name") as? String {
self.firstName = firstName
} else {
self.firstName = ""
}
if let lastName = representation.valueForKeyPath("last_name") as? String {
self.lastName = lastName
} else {
self.lastName = ""
}
if let phone = representation.valueForKeyPath("phone") as? String {
self.phone = phone
}
if let position = representation.valueForKeyPath("position_name") as? String {
self.position = position
}
if let timeCreated = representation.valueForKeyPath("time_created") as? String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
if let date = formatter.dateFromString(timeCreated) {
self.timeCreated = CVDate(date: date)
} else {
self.timeCreated = CVDate(date: NSDate())
}
} else {
self.timeCreated = CVDate(date: NSDate())
}
}
}
My question is, is this style the best way to decode JSON and set the non-optional instance variables? For example, in this statement:
if let id = representation.valueForKeyPath("id") as? Int {
self.id = id
}
I am required by the compiler to add an else clause and set the id to something otherwise xCode throws an error saying: self.id is not initialized at implicitly generated super.init call.
But at the same time, intializing self.id with a value of 0 is wrong and doesn't help me at all.
But at the same time, intializing self.id with a value of 0 is wrong and doesn't help me at all.
If having a default value for self.id feels wrong, then you should make this property an Optional. That way you wouldn't have to add an else clause:
final class User: NSObject, ResponseObjectSerializable {
var id: Int?
var facebookUID: String?
var email: String
var firstName: String
var lastName: String
var phone: String?
var position: String?
var timeCreated: CVDate
init?(response: NSHTTPURLResponse, var representation: AnyObject) {
if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [String: AnyObject]) {
representation = dataRepresentation
}
if let id = representation.valueForKeyPath("id") as? Int {
self.id = id
}
...
Update
You said in the comments:
I always need to have an id for the user object though.
If you have to have this id property then the question is moot, you just have to do
let id = representation.valueForKeyPath("id") as! Int
and guarantee earlier that this value will exist.
Because if your object needs an ID, then you can't initialize it anyway if this value doesn't exist and if you don't want a default value.
You could use ?? to provide default values like this:
self.id = (representation.valueForKeyPath("id") as? Int) ?? 0
While the ResponseObjectSerializable code is a great example from the Alamofire project, it's really a better idea to use a dedicated JSON parsing library that has actual error states. This is far better than using optionals to represent error states, or having to provide a default value for every field just in case the response isn't correctly formed.
Although it has a bit of learning curve, I prefer to use Argo for my JSON parsing. Once you get the hang of it it makes JSON parsing practically bulletproof. Better yet, it's easy to integrate with Alamofire, especially version 3 that was released today.
To address your concern about not having an ID being an error condition, you could use a failable initializer. I did that in a recent project. Looks something like this:
let id: Int!
init? (inputJson: NSDictionary) {
if let id = inputJson["id"] as? Int {
self.id = id
} else {
// if we are initing from JSON, there MUST be an id
id = nil
cry(inputJson) // this logs the error
return nil
}
}
Of course, this means your code will need to accept that the initialization of your entire object may fail ..
I'm developping an app, which make an request to a webservice, once the webservice answer (in JSON) I decode the result in an other view (repository.swift)
I just need to use the value which are stored in repository, in one of my other view (Swype.swift)
This is my code to decode JSON :
if let json = NSJSONSerialization.JSONObjectWithData(urlData!, options: nil, error: nil) as? [String:AnyObject] {
for (_, value) in json {
if let dict = value as? [String:AnyObject] {
let repo = Repository(jsonData: dict)
repos.append(repo)
}
}
}
And my "repository" class :
class Repository {
var idobjet: String?
var title: String?
var price: String?
var description: String?
var added: String?
var userid: String?
var user_name: String?
var user_zipCode: String?
var category_id: String?
var category_label: String?
var subcategory_id: String?
var subcategory_label: String?
var picture_url: String?
init(jsonData: [String:AnyObject]) {
self.idobjet = jsonData["id"] as? String
self.title = jsonData["title"] as? String
self.price = jsonData["price"] as? String
self.description = jsonData["description"] as? String
self.added = jsonData["addedDate"] as? String
self.userid = jsonData["userid"] as? String
self.user_name = jsonData["user_name"] as? String
self.user_zipCode = jsonData["user_zipCode"] as? String
self.category_id = jsonData["category_id"] as? String
self.category_label = jsonData["category_label"] as? String
self.subcategory_id = jsonData["subcategory_id"] as? String
self.subcategory_label = jsonData["subcategory_label"] as? String
self.picture_url = jsonData["picture_url"] as? String
}
}
var repos = [Repository]()
I need to get "picture_url", "title" and "Description" value in my other view, to create an Imageview and some other fields :
//Construct the imgUrl to get an image URL for the pages
let urlString: NSString = "picture_url" Values
if let url = NSURL(string: urlString as String) {
if let data = NSData(contentsOfURL: url) {
newCard.image = UIImage(data: data)!
newCard.content = "title" value
newCard.desc = "price" value
self.data.append(newCard)
NSLog("fetch new data")
}
}
}
Depending on how many view controllers make use of this fields, probably the simplest solution is to define a property that holds your Repository object in your destination view controller and then set it in the parent view controller before the segue (using the (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender method).
On the other hand, if these fields have to be accessed frequently, it's probably more convenient to set a data model class which view controllers can rely on.
Okay, so there are multiple ways to handle this.
Here is what I suggest:
initialize a Repository object as a global variable.
var repo:Repository?
class SomeViewControllerOne: UIViewController {
func initializeRepository() {
repo = Repository(....)
// continue with initialization
}
}
class AnotherViewControllerTwo: UIViewController {
func useRepository() {
if let repository = repo {
print(repository.title)
}
}
}
the object "repo" is global, and can be used application wide. just be carful, because the object is an optional.