I have problem adding annotations to mapView. I had success with this code:
func placeAnnotations() {
for _ in placeDetails {
let multipleAnnotations = MKPointAnnotation()
multipleAnnotations.title = place.address
multipleAnnotations.subtitle = place.phone
multipleAnnotations.coordinate = CLLocationCoordinate2D(latitude: place.lat, longitude: place.lng)
mapView.addAnnotation(multipleAnnotations)
}
}
Problem is, it is not conforming to my Place class, thus not showing custom Title, Subtitle and MKAnnotationView. This is code inside viewDidLoad(), where I'm trying to put all the annotations, but it keeps adding only last one. I understood that it overrides all the previous ones from array, but haven't found any other way/method to implement.
var placeDetails = [Place]()
var places = [Place]()
override func viewDidLoad() {
super.viewDidLoad()
downloadPlaceID {
for obj in places {
place.downloadDetails(input: obj.placeId, completed: {
self.placeDetails.append(obj)
//self.placeAnnotations()
self.mapView.addAnnotations(self.placeDetails)
})
}
}
}
And this is my class with all the data conforming to MKAnnotation
protocol, and functions, downloadPlaceID() and downloadDetails()
class Place: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var placeId: String!
var vicinity: String!
var phone: String!
var workHours: Bool!
var lat: Double!
var lng: Double!
var address: String!
var subtitle: String? {
return phone
}
var title: String? {
return address
}
var _placeId: String {
if placeId == nil {
placeId = ""
}
return placeId
}
var _vicinity: String {
if vicinity == nil {
vicinity = ""
}
return vicinity
}
var _phone: String {
if phone == nil {
phone = ""
}
return phone
}
var _workHours: Bool {
if workHours == nil {
workHours = false
}
return workHours
}
var _lat: Double {
if lat == nil {
lat = 0.0
}
return lat
}
var _lng: Double {
if lng == nil {
lng = 0.0
}
return lng
}
var _address: String {
if address == nil {
address = ""
}
return address
}
init(place: [String:Any]) {
if let ids = place["place_id"] as? String {
self.placeId = ids
}
if let vicinities = place["vicinity"] as? String {
self.vicinity = vicinities
}
self.coordinate = CLLocationCoordinate2DMake(0.0, 0.0)
}
func downloadDetails(input: String, completed: #escaping DownloadComplete) {
let details = "\(detailsBaseURL)\(detailsPlaceId)\(input)\(detailsKey)\(detailsSearchAPIKey)"
Alamofire.request(details).responseJSON { response in
let result = response.result
if let dictionary = result.value as? [String:Any] {
if let result = dictionary["result"] as? [String:Any] {
if let phoneNumber = result["formatted_phone_number"] as? String {
self.phone = phoneNumber
}
if let geometry = result["geometry"] as? [String:Any] {
if let location = geometry["location"] as? [String:Any] {
if let latitude = location["lat"] as? Double {
self.lat = latitude
}
if let longitude = location["lng"] as? Double {
self.lng = longitude
}
self.coordinate = CLLocationCoordinate2DMake(self.lat, self.lng)
}
}
if let openingHours = result["opening_hours"] as? [String:Any] {
if let openNow = openingHours["open_now"] as? Bool {
self.workHours = openNow
}
}
if let addressComponents = result["address_components"] as? [[String:Any]] {
let longName = addressComponents[1]["long_name"] as? String
let shortName = addressComponents[0]["long_name"] as? String
self.address = "\(longName!),\(shortName!)"
}
}
}
completed()
}
}
}
func downloadPlaceID (completed: #escaping DownloadComplete) {
let placeURL = URL(string: nearbyURL)
Alamofire.request(placeURL!).responseJSON { (response) in
let result = response.result
if let dictionary = result.value as? [String:Any] {
if let results = dictionary["results"] as? [[String:Any]] {
if let status = dictionary["status"] as? String {
if status == "OK" {
for obj in results {
place = Place(place: obj)
places.append(place)
}
} else {
print("jede govna")
}
}
}
}
completed()
}
There's some odd mixing of variable names and concepts here that makes your code somewhat hard to understand.
As an example, assigning a variable you call vicinities, plural, to an attribute called vicinity, singular. Or not separating your downloadDetails function from your data model.
That aside, it looks to me like you're unnecessarily adding your MKAnnotations many times to your map, by adding your array of [MKAnnotation] to your map in each loop.
I suspect you've done this because you've made it hard for yourself to know when the whole array is done updating its details.
As a quick fix, I'd suggest changing your downloadDetails function to call the completed function with the place you've just downloaded details for. Here's a really simplified, but working, version of what you are trying to do. First your Place class:
class Place: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
override init() {
coordinate = CLLocationCoordinate2DMake(0, 0)
}
func downloadDetails(completed: #escaping (Place) -> Void) {
// Instead of downloading details we are just creating random positions
self.coordinate = CLLocationCoordinate2DMake(CLLocationDegrees(arc4random_uniform(50)), CLLocationDegrees(arc4random_uniform(50)))
// Return the object you've just built
completed(self)
}
}
Now in your view controller, here I am starting with an array of 50 Place objects, getting the details for them and placing them on the map:
var places = [Place]()
for _ in 1...50 {
places.append(Place())
}
for place in places {
place.downloadDetails(completed: { (placeWithDetails) in
self.mapView.addAnnotation(placeWithDetails)
})
}
This results in the map being populated with 50 random places:
Related
I am trying to fetch the data from firebase. I am calling a function which has a databaseReference but it is not calling up.Basically this function is also called from databaseRefernce only of another function.
Here is my code:
func prepareCoursesFromCourseNodewithAllCoursesList(allCourseList: [Any]){
var courseDetailsNode = String()
var courseCount = allCourseList.count
weak var weakSelf = self
var courseId = String()
var localCoursesAndCardDictionary = [AnyHashable: Any]()
for singleCourse in allCourseList as! [[String:Any]] {
if singleCourse["elementId"] != nil {
courseId = "\(String(describing: singleCourse["elementId"]!))"
}else{
if singleCourse["currentCourseId"] != nil {
courseId = "\(String(describing: singleCourse["currentCourseId"]!))"
}
}
if singleCourse["parentNodeName"] != nil {
if singleCourse["parentNodeName"] as! String == "course" {
//this is course
courseDetailsNode = IMERCOURSE_URL
}
else {
//this is course Collection
courseDetailsNode = IMERCOURSECOLLECTION_URL
}
}
var reference = DatabaseReference()
let courseURL = "\(FIREBASE_URL)\(courseDetailsNode.replacingOccurrences(of: "{Id}", with: courseId))"
reference = Database.database().reference(fromURL: courseURL)
self.addIntoListFirebaseRef(firebaseRef: ref)
reference.observe(DataEventType.value, with: { (snapShot:DataSnapshot) in
courseCount -= 1
if snapShot.value != nil {
let singleCourseDictionary = snapShot.value as? [String: Any]
localCoursesAndCardDictionary[courseId] = singleCourseDictionary
self.settingUserDetailsViewData()
(Constants.sharedTools() as AnyObject).hideProgressIndicator()
}
}
}, withCancel: { (error:Error) in
})
}
}
}
this prepareCoursesFromCourseNodewithAllCoursesList(allCourseList: [Any]) is called from another databaseRefernce.ObserveEevnt
value.
So I have a section in my app which displays events that your followers are going to. I successfully pull the followers and there corresponding events. I then add those events to a bigger array of event objects. I am currently having trouble removing the duplicates I have tried many extensions but it doesn't seem to be working. I figure it is because the array contains different objects with different memory addresses so when I compare them they aren't the same regardless of the fact that the content is. I have been messing with this for a while now and can't really figure anything out. if anyone could help me I would appreciate it.
This is the method grabs the events and returns them to an array in my main VC.
static func showFollowingEvent(for followerKey: String,completion: #escaping (Event) -> Void) {
//getting firebase root directory
let ref = Database.database().reference()
ref.child("users").child(followerKey).child("Attending").observeSingleEvent(of: .value, with: { (attendingSnapshot) in
print(attendingSnapshot)
guard var eventKeys = attendingSnapshot.children.allObjects as? [DataSnapshot] else{return}
for event in eventKeys{
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
EventService.show(forEventKey: event.key, completion: { (event) in
dispatchGroup.leave()
completion(event!)
})
}
}) { (err) in
print("couldn't grab event info",err)
}
} print("couldn't grab event info",err)
}
}
This is the function that receives the events and reloads the collectionView upon getting them.
#objc func grabFriendsEvents(){
print("Attempting to see where your friends are going")
UserService.following { (user) in
for following in user {
print(following.username as Any)
PostService.showFollowingEvent(for: following.uid, completion: { (event) in
self.friendsEvents.append(event)
// self.friendsEvents.append(contentsOf: event)
// leave here
self.allEvents2["Friends Events"] = self.friendsEvents.removeDuplicates()
self.collectionView?.reloadData()
})
}
}
}
I tried these extensions which I saw in previous questions and nothing worked.
extension Array where Element: Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
mutating func removeDuplicatesTwo() {
var result = [Element]()
for value in self {
if !result.contains(value) {
result.append(value)
}
}
self = result
}
}
import Foundation
import FirebaseDatabase.FIRDataSnapshot
#objc(Event)
class Event:NSObject{
var key: String?
let currentEventName: String
let currentEventImage: String
let currentEventPromo: String?
let currentEventDescription: String
//nested properties
let currentEventStreetAddress: String
let currentEventCity: String
let currentEventState: String
let currentEventDate: String?
let currentEventEndDate: String?
let currentEventTime: String?
let currentEventEndTime: String?
let currentEventZip: Int
var category: String
//nested properties stop
var currentAttendCount: Int
var isAttending = false
var eventDictionary: [String: Any]{
let dateDict = ["start:date":currentEventDate, "start:time": currentEventTime,"end:time":currentEventEndTime, "end:date": currentEventEndDate]
return ["event:name":currentEventName,"event:imageURL" : currentEventImage,
"event:description": currentEventDescription, "attend:count": currentAttendCount,
"event:street:address": currentEventStreetAddress,"event:zip": currentEventZip,
"event:state": currentEventState, "event:city": currentEventCity, "event:promo": currentEventPromo ?? "", "event:date": dateDict, "event:category":category]
}
init(currentEventKey: String, dictionary: [String:Any]) {
self.key = currentEventKey
self.currentEventName = dictionary["event:name"] as? String ?? ""
self.currentEventImage = dictionary["event:imageURL"] as? String ?? ""
self.currentEventDescription = dictionary["event:description"] as? String ?? ""
self.currentEventPromo = dictionary["event:promo"] as? String ?? ""
self.currentAttendCount = dictionary["attend:count"] as? Int ?? 0
self.category = dictionary["event:category"] as? String ?? ""
//nested properties
self.currentEventStreetAddress = dictionary["event:street:address"] as? String ?? ""
self.currentEventCity = dictionary["event:city"] as? String ?? ""
self.currentEventState = dictionary["event:state"] as? String ?? ""
self.currentEventZip = dictionary["event:zip"] as? Int ?? 0
let eventDate = dictionary["event:date"] as? [String: Any]
self.currentEventDate = eventDate?["start:date"] as? String ?? ""
self.currentEventTime = eventDate?["start:time"] as? String ?? ""
self.currentEventEndTime = eventDate?["end:time"] as? String ?? ""
self.currentEventEndDate = eventDate?["end:date"] as? String ?? ""
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String : Any],
let currentEventName = dict["event:name"] as? String,
let currentEventImage = dict["event:imageURL"] as? String,
let currentEventDescription = dict["event:description"] as? String,
let currentEventPromo = dict["event:promo"] as? String,
let category = dict["event:category"] as? String,
let currentEventStreetAddress = dict["event:street:address"] as? String,
let currentEventCity = dict["event:city"] as? String,
let currentEventState = dict["event:state"] as? String,
let currentEventZip = dict["event:zip"] as? Int,
let currentAttendCount = dict["attend:count"] as? Int,
let eventDate = dict["event:date"] as? [String: Any],
let currentEventDate = eventDate["start:date"] as? String,
let currentEventEndDate = eventDate["end:date"] as? String,
let currentEventTime = eventDate["start:time"] as? String,
let currentEventEndTime = eventDate["end:time"] as? String
else { return nil }
self.key = snapshot.key
self.currentEventName = currentEventName
self.currentEventImage = currentEventImage
self.currentEventDescription = currentEventDescription
self.currentEventStreetAddress = currentEventStreetAddress
self.currentEventCity = currentEventCity
self.currentEventState = currentEventState
self.currentEventZip = currentEventZip
self.currentAttendCount = currentAttendCount
self.currentEventPromo = currentEventPromo
self.currentEventDate = currentEventDate
self.currentEventTime = currentEventTime
self.currentEventEndTime = currentEventEndTime
self.category = category
self.currentEventEndDate = currentEventEndDate
}
static func ==(lhs: Event, rhs: Event) -> Bool {
return lhs.key == rhs.key
}
}
Classes of type NSObject will automatically call isEqual() for the contains() method. You can override the superclass's implementation to fit your logic.
If your class HAS to inherit NSObject, then use:
class Event: NSObject {
var key: String?
init(key: String) {
self.key = key
}
override func isEqual(_ object: Any?) -> Bool {
guard let event = object as? Event else { return false }
return self.key == event.key
}
}
var event = Event(key: "abc")
var eventCopy = Event(key: "abc")
extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
return reduce(into: []) { result, element in
if !result.contains(element) {
result.append(element)
}
}
}
}
var events = [event, eventCopy]
events = events.removeDuplicates()
print(events.count)
If your class does not inherit NSObject, make it conform to the Equatable protocol.
class Event: Equatable {
var key: String?
init(key: String) {
self.key = key
}
static func ==(lhs: Event, rhs: Event) -> Bool {
return lhs.key == rhs.key
}
}
var event = Event(key: "abc")
var eventCopy = Event(key: "abc")
extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
}
var events = [event, eventCopy]
events = events.removeDuplicates()
print(events.count)
You have to make Event class conform to Equatable protocol
So i have this model
class Event: NSObject {
var _eventName: String!
var _venueName : String!
var _eventImage: String!
var eventName: String {
if _eventName == nil {
_eventName = ""
}
return _eventName
}
var venueName: String {
if _venueName == nil {
_venueName = ""
}
return _venueName
}
var eventImage: String {
if _eventImage == nil {
_eventImage = ""
}
return _eventImage
}
init(eventsDict: Dictionary<String, AnyObject>) {
if let venue = eventsDict["venue"] as? Dictionary<String, AnyObject> {
if let venuname = venue["name"] as? String{
self._venueName = venuname
}
if let eventname = eventsDict["name"] as? String {
self._eventName = eventname
}
if let eventimage = eventsDict["coverPicture"] as? String {
self._eventImage = eventimage
}
}
}
And i make it IGListDiffable with this extension.
extension NSObject: IGListDiffable {
public func diffIdentifier() -> NSObjectProtocol {
return self
}
public func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
return isEqual(object)
}
}
So when I'm loading data from hardcoded code like this
var entries = [Event]()
func loadFakeEvents() {
let entries = [
Event(
eventName: "Ζωρζ Πιλαλι Και Η Soufra Band Στο AN Groundfloor - Live Stage!",
venueName: "AN Groundfloor - live stage",
eventImage: "https://scontent.xx.fbcdn.net/v/t31.0-8/s720x720/15936729_1867160333520142_8855370744955080264_o.jpg?oh=8198bc10a8ea61011d7ec1902b34aa01&oe=593D6BC4"
),
Event(
date: "2017-02-18T21:30:00+0200",
name: "Διονύσης Σαββόπουλος at Gazarte I Main Stage 18/02",
venuename: "Gazarte",
eventImage: "https://scontent.xx.fbcdn.net/v/t1.0-9/s720x720/16265335_1262826863809003_3636661375515976849_n.jpg?oh=5bb342321a65d33dbc1cc41de266b45e&oe=5907857C"
)
]
self.entries = entries
}
The events are loading fine. As they have to.
But when i'm making an alamofire request, of course, it takse some time to load the data and append them to the empty array of events.
This is the function that I have to call the events
func loadEvents() {
let parameters: Parameters = [
"Some" : "Parameters",
"Some" : "Parameters"
]
Alamofire.request(baseurl, method: .get, parameters: parameters)
.responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let result = responseData.result
if let dict = result.value as? Dictionary<String, AnyObject>{
print(dict) // <-- Check this out
if let list = dict["events"] as? [Dictionary<String, AnyObject>] {
for obj in list {
let event = Event(eventsDict: obj)
self.entries.append(event)
}
}
}
}
}
}
So in the above code i have a print, which prints the json.
And in my
extension LocationViewController: IGListAdapterDataSource {
func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {
let items: [IGListDiffable] = loader.entries as [IGListDiffable]
print(items.count) // <--- another print of items that should be displayed
return items
}
func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
return NormalSectionController()
}
func emptyView(for listAdapter: IGListAdapter) -> UIView? { return nil }
}
Adapter i also print the items that should be displayed.
So when i load the fakeEvents function it prints 2 but when i load them with the normal function it prints 0 and then the JSON from the dict var from the previous code.
Normally i would reloadData() of the collection view.
But with IGListKit what is the trick of sending the Event Class to the CollectionView?
Thanks a lot for your time and i hope i'm not off topic !
Pasting my answer from this same issue on Github in case anyone finds this.
https://github.com/Instagram/IGListKit/issues/468
It looks like you're missing a call to self.adapter.performUpdates(animated: true) after the for-loop when appending to the entries dict:
func loadEvents() {
// ...
Alamofire.request(baseurl, method: .get, parameters: parameters)
.responseJSON { (responseData) -> Void in
if responseData.result.value != nil {
let result = responseData.result
if let dict = result.value as? Dictionary<String, AnyObject>{
if let list = dict["events"] as? [Dictionary<String, AnyObject>] {
for obj in list {
let event = Event(eventsDict: obj)
self.entries.append(event)
}
// missing this!
self.adapter.performUpdates(animated: true)
// missing that!
}
}
}
}
}
I want to save the response from JSON in a file and fetch from it when the network is not available. However on trying to fetch idea by disabling the wifi, the app always crashes. Are there any other ways for offline fetching in swift except saving in database??
This is the error I am getting : Could not cast value of type 'Swift._NSContiguousString' (0x109e22320) to 'NSArray'
This is what I have done so far:
Create a model
class Directory : NSObject, NSCoding {
var data : [AnyObject]
var tid : String
var vid : String
var name : String
var imgThumbnail : String
var imgMedium : String
var imgLarge : String
var child : String
// MARK: Archiving Paths
init(data:[AnyObject],tid:String,vid:String,name:String,imgThumbnail:String,imgMedium:String,imgLarge:String,child:String) {
self.data = data ?? []
self.tid = tid ?? ""
self.vid = vid ?? ""
self.name = name ?? ""
self.imgThumbnail = imgThumbnail ?? ""
self.imgMedium = imgMedium ?? ""
self.imgLarge = imgLarge ?? ""
self.child = child ?? ""
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(data, forKey:Constants.kData)
aCoder.encodeObject(name, forKey:Constants.Directory.kName )
aCoder.encodeObject(tid, forKey: Constants.Directory.tid)
aCoder.encodeObject(vid, forKey: Constants.Directory.vid)
aCoder.encodeObject(imgThumbnail, forKey:Constants.Directory.kImageThumbnail)
aCoder.encodeObject(imgMedium, forKey: Constants.Directory.kImageMedium)
aCoder.encodeObject(imgLarge, forKey: Constants.Directory.kImageLarge)
aCoder.encodeObject(child, forKey: Constants.Directory.kChild)
}
required convenience init?(coder aDecoder: NSCoder) {
let data = aDecoder.decodeObjectForKey(Constants.kData) as! [AnyObject]
let name = aDecoder.decodeObjectForKey(Constants.Directory.kName) as! String
let tid = aDecoder.decodeObjectForKey(Constants.Directory.tid) as! String
let vid = aDecoder.decodeObjectForKey(Constants.Directory.vid) as! String
let imgThumbnail = aDecoder.decodeObjectForKey(Constants.Directory.kImageThumbnail) as! String
let imgMedium = aDecoder.decodeObjectForKey(Constants.Directory.kImageMedium) as! String
let imgLarge = aDecoder.decodeObjectForKey(Constants.Directory.kImageLarge) as! String
let child = aDecoder.decodeObjectForKey(Constants.Directory.kChild) as! String
// Must call designated initializer.
self.init(data:data,tid:tid,vid:vid,name:name,imgThumbnail:imgThumbnail,imgMedium: imgMedium,imgLarge: imgLarge, child: child)
}
}
Code for saving and loading the data from file
class func loadSavedFile(fileName: String) -> AnyObject? {
let pathString: String = Utility.fetchFilePathString(fileName)
print("Here the pathString is \(pathString)")
if NSFileManager.defaultManager().fileExistsAtPath(pathString) {
return NSKeyedUnarchiver.unarchiveObjectWithFile(pathString)!
} else {
return "File doesn't exist"
}
return ""
}
class func saveObject(object: AnyObject, toFile fileName: String) {
let pathString: String = Utility.fetchFilePathString(fileName)
NSKeyedArchiver.archiveRootObject(object, toFile: pathString)
}
class func fetchFilePathString(fileName: String) -> String {
let pathAray: [AnyObject] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.AllDomainsMask, true)
let pathString = pathAray.last!
return NSURL(fileURLWithPath: pathString as! String).URLByAppendingPathComponent(fileName).absoluteString
}
Checking for network connection in the view controller
var directoryArr = [Directory]()
override func viewDidLoad() {
super.viewDidLoad()
if Utility.isNetworkReachable() {
Utility.saveObject([], toFile: Constants.File.kDirectory)
self.serviceCallDirectory()
} else {
self.directorie = (Utility.loadSavedFile(Constants.File.kDirectory) as? [Directory])!
self.tableView.reloadData()
}
Service Call
func serviceCallDirectory() -> Void {
let stringUrl = Constants.baseUrl + Constants.kDirectoryUrl
WebService.getRequestAPI(stringUrl, withSuccess: {(responseDic, Statusflag,error) in
if Statusflag {
self.tableView.backgroundColor = UIColor.clearColor()
self.tableView.hidden = false
let tempInfo = responseDic![Constants.kData] as! [AnyObject]
var imgthumbnail : String = ""
var imgmedium : String = ""
var imglarge : String = ""
var name : String = ""
var child : String = ""
if tempInfo.count != 0 {
for info in tempInfo {
let tid = info[Constants.Directory.tid] as! String
let vid = info[Constants.Directory.vid] as! String
if let names = info[Constants.Directory.kName] as? String {
name = names
}
if let childs = info[Constants.Directory.kChild] as? String {
child = childs
}
if let imgthumb = info[Constants.Directory.kImageThumbnail] as? String {
imgthumbnail = imgthumb
} else {
imgthumbnail = ""
}
if let imgmediumd = info[Constants.Directory.kImageMedium] as? String {
imgmedium = imgmediumd
} else {
imgmedium = ""
}
if let imglarges = info[Constants.Directory.kImageLarge] as? String {
imglarge = imglarges
}
let myModel = Directory(
data: tempInfo,
tid: tid,
vid: vid,
name: name,
imgThumbnail: imgthumbnail,
imgMedium: imgmedium,
imgLarge: "",
child: child
)
self.directorie.append(myModel)
}
I don't know that this is the only issue, but this code
class func loadSavedFile(fileName: String) -> AnyObject? {
let pathString: String = Utility.fetchFilePathString(fileName)
print("Here the pathString is \(pathString)")
if NSFileManager.defaultManager().fileExistsAtPath(pathString) {
return NSKeyedUnarchiver.unarchiveObjectWithFile(pathString)!
} else {
return "File doesn't exist"
}
return ""
}
Either returns an object or a string. That's not very sensible. It should return a success flag or a tuple or use a completion block. When you call this function your code expects to get back an array of directory, which in a number of cases won't happen
self.directorie = (Utility.loadSavedFile(Constants.File.kDirectory) as? [Directory])!
The error in your question indicates a different kind of data mismatch. You should try not to use AnyObject, let swift help you by type checking what you're doing...
I'm testing Realm, but I cant find a easy way to convert my object to JSON.
I need to push the data to my REST interface.
How can I do it using swift?
class Dog: Object {
dynamic var name = ""
}
class Person : Object {
dynamic var name = ""
let dogs = List<Dog>()
}
I'm trying something like this, but I can't iterate unknown objects (List)
extension Object {
func toDictionary() -> NSDictionary {
let props = self.objectSchema.properties.map { $0.name }
var dicProps = self.dictionaryWithValuesForKeys(props)
var mutabledic = NSMutableDictionary()
mutabledic.setValuesForKeysWithDictionary(dicProps)
for prop in self.objectSchema.properties as [Property]! {
if let objectClassName = prop.objectClassName {
if let x = self[prop.name] as? Object {
mutabledic.setValue(x.toDictionary(), forKey: prop.name)
} else {
//problem here!
}
}
}
return mutabledic
}
}
**sorry for ugly code.
I am also new to Realm but I think the easiest way is to reflect on Object's schema:
class Person: Object {
dynamic var name = ""
dynamic var age = 0
}
let person = Person()
let schema = person.objectSchema
let properties = schema.properties.map() { $0.name }
let dictionary = person.dictionaryWithValuesForKeys(properties) // NSDictionary
println(properties)
println(dictionary)
I think that I found the solution.
I'm not reliant about performance.
extension Object {
func toDictionary() -> NSDictionary {
let properties = self.objectSchema.properties.map { $0.name }
let dicProps = self.dictionaryWithValuesForKeys(properties)
var mutabledic = NSMutableDictionary()
mutabledic.setValuesForKeysWithDictionary(dicProps)
for prop in self.objectSchema.properties as [Property]! {
if let objectClassName = prop.objectClassName {
if let nestedObject = self[prop.name] as? Object {
mutabledic.setValue(nestedObject.toDictionary(), forKey: prop.name)
} else if let nestedListObject = self[prop.name] as? ListBase {
var objects = [AnyObject]()
for index in 0..<nestedListObject._rlmArray.count {
if let object = nestedListObject._rlmArray[index] as? Object {
objects.append(object.toDictionary())
}
}
mutabledic.setObject(objects, forKey: prop.name)
}
}
}
return mutabledic
}
}
Here is my solution. use unsafeBitCast to avoid cast fail warning.
extension Object {
func toDictionary() -> [String:AnyObject] {
let properties = self.objectSchema.properties.map { $0.name }
var dicProps = [String:AnyObject]()
for (key, value) in self.dictionaryWithValuesForKeys(properties) {
if let value = value as? ListBase {
dicProps[key] = value.toArray()
} else if let value = value as? Object {
dicProps[key] = value.toDictionary()
} else {
dicProps[key] = value
}
}
return dicProps
}
}
extension ListBase {
func toArray() -> [AnyObject] {
var _toArray = [AnyObject]()
for i in 0..<self._rlmArray.count {
let obj = unsafeBitCast(self._rlmArray[i], Object.self)
_toArray.append(obj.toDictionary())
}
return _toArray
}
}