I am trying to add some dictionaries into an array and save it using UserDefaults, but the problem is the array doesn't append new dictionary but only replaces the dictionary. How can I fix this issue?
here is the code:
class Bookmark: NSObject {
var bookmark: [[String:Any]] = []
override init() {
super.init()
}
func setBookmark(imageURL:String, title:String, description:String, summary:String, date:String, link:String) {
bookmark.append(["imageURL":imageURL , "title":title , "description":description, "summary":summary , "date":date, "link":link])
UserDefaults.standard.set(bookmark, forKey: "bookmark")
}
func getBookrmark() -> [Any] {
let loadedBookmark = UserDefaults.standard.array(forKey: "bookmark")
return (loadedBookmark)!
}
}
Using Bookmark class:
class ReadViewController: UIViewController {
var bookmark = Bookmark()
func save() {
bookmark.setBookmark(imageURL: nImageURL.absoluteString, title: ntitle!, description: nDescription.htmlToString , summary: nSummary!, date: nDate!, link: nLink.absoluteString)
}
There is not enough information but I can guess you where you have made mistake.
Your class Bookmark is not singleton class so,every time Bookmark() create new instance every time. that means it will create new bookmark object for every instance.
What I suggest you is inside func func setBookmark(imageURL:String, title:String, description:String, summary:String, date:String, link:String)
Method 1 : Fetch the latest bookmarks in separate variable and append new object inside it and write to User Default as well as update the global object
Method 2 Or you can make it singleton and use shared instance for every time you perform operation.
Method 3 Another solution can be create global object of Bookmark in AppDelegate or Your singleton class and then use that object
However
func setBookmark(imageURL:String, title:String, description:String, summary:String, date:String, link:String) {
bookmark.append(["imageURL":imageURL , "title":title , "description":description, "summary":summary , "date":date, "link":link])
UserDefaults.standard.set(bookmark, forKey: "bookmark")
}
This is the bad practice to follow. You are directly replacing User default with new value however your intension is to append new element not replace existing data in userdefault.
You should always fetch latest and update it Like method 1 show as well as If you wanted to fetch the data in userdefault there is no use of your global object var bookmark: [[String:Any]] = [] because you have already getBookrmark method there
Hope it is clear to you
I've tried using the above code with an example and its working completely fine.
class ViewController: UIViewController {
let b = Bookmark()
override func viewDidLoad() {
super.viewDidLoad()
b.setBookmark(imageURL: "a", title: "a", description: "a", summary: "a", date: "a", link: "a")
b.setBookmark(imageURL: "b", title: "b", description: "b", summary: "b", date: "b", link: "b")
print(b.getBookrmark())
}
}
Output:
[{
date = a;
description = a;
imageURL = a;
link = a;
summary = a;
title = a;
}, {
date = b;
description = b;
imageURL = b;
link = b;
summary = b;
title = b;
}]
Check if you are using the same way or anything else.
Related
I have an array which gets instantiated in viewDidLoad like var bookingsArray : [[String]] = []
I am adding elements to it in this way:
var configuration: [String] = []
configuration.append(textfieldFacility.text!)
configuration.append((pickSlotTF.text?.components(separatedBy: " ")[0])!)
configuration.append((pickSlotTF.text?.components(separatedBy: " ")[1])!)
bookingsArray.append(configuration as [String])
bookingsArray looks like :
[["A", "20-08-2017", "14:00"], ["B", "20-08-2017", "14:00"]]
While adding new elements to bookingsArray, I want to check if the new element is already present in the array. How do I do it in this case of multi-dimensional array?
First of all, if you want unique objects use a Set.
If this is not possible I highly recommend to use a custom struct which can conform to Equatable rather than a nested array , for example
struct Booking : Equatable {
let facilty : String
let date : String
let time : String
static func ==(lhs : Booking, rhs : Booking) -> Bool {
return lhs.facilty == rhs.facilty && lhs.date == rhs.date && lhs.time == rhs.time
}
}
Then declare the array as
var bookingsArray = [Booking]()
and create an object with
let dateArray = pickSlotTF.text!.components(separatedBy: " ")
let configuration = Booking(facility: textfieldFacility.text!,
date: dateArray[0],
time = dateArray[1])
bookingsArray.append(configuration)
The huge benefit is that you can easily check
if bookingsArray.contains(item)
You can simply search for it with contains().
var configuration: [String] = []
configuration.append(textfieldFacility.text!)
configuration.append((pickSlotTF.text?.components(separatedBy: " ")[0])!)
configuration.append((pickSlotTF.text?.components(separatedBy: " ")[1])!)
if !bookingsArray.contains(where: {$0 == configuration}) {
bookingsArray.append(configuration)
}
I have a Profile Data singleton class as follows.
I am trying to store data into an empty array in a dictionary .
After appending data to the array also the count of the array is 0.
class ProfileData{
static let sharedInstance = ProfileData()
var artistProfileDict = [String : Profile]()
var loggedInUserProfile = Profile(artistName: "John Smith", artistDescription: "Admiral of New England, English soldier, explorer, and author.", totalLikes: "174", totalViews: "200", totalFollowing: "100",totalFollowers:"50",imageUrl:"image_singer", feeds:[] )
private init() {
getProfilesDictionary()
}
func getProfilesDictionary()->[String: Profile]{
artistProfileDict["John Smith"] = loggedInUserProfile
return artistProfileDict
}
func add(array: Feed, artistName: String) {
artistProfileDict[artistName]!.feeds.append(array)
}
}
In another view Controller I am trying to add an array to the empty array in the dictionary as follows
let newFeed = Feed(profilePicture: "image",artistName: "New",
videoUrl: "url",videoTitle:"New", videoViews: "160",likes:
"200",shoutouts: "200",comments: [],votes: "50", dateCreated: Date(),
userActivity :"This user liked your video")
ProfileData.sharedInstance.add(array: newFeed,artistName:"John Smith")
After appending the array to the empty array in the dictionary I still get the count of the array as 0.
I am not able to figure out the problem here. Any help will appreciated . Thank you.
Profile class
struct Profile {
var artistName: String
var artistDescription: String
var totalLikes: String
var totalViews: String
var totalFollowing: String
var totalFollowers: String
var imageUrl: String
var feeds : [Feed]
init(artistName: String,artistDescription:String,totalLikes:String,totalViews:String,totalFollowing:String,totalFollowers:String,imageUrl:String, feeds:[Feed]) {
self.artistName = artistName
self.artistDescription = artistDescription
self.totalLikes = totalLikes
self.totalViews = totalViews
self.totalFollowing = totalFollowing
self.totalFollowers = totalFollowers
self.imageUrl = imageUrl
self.feeds = feeds
}
}
It's working fine
ProfileData.sharedInstance.add(array: newFeed,artistName:"John Smith")
ProfileData.sharedInstance.artistProfileDict["John Smith"]?.feeds.count // 1
Probably you are using wrong class ArtistProfileData instead of ProfileData.
I have this class
class InboxInterests {
var title = ""
var eventID = 0
var count = ""
var added = 0
init(title : String, eventID : NSInteger, count: String, added : NSInteger) {
self.title = title
self.eventID = eventID
self.count = count
self.added = added
}
}
And i use it like this
var array: [InboxInterests] = [InboxInterests]()
Add item
let post = InboxInterests(title: "test",eventID : 1, count: "test", added: 0)
self.array.append(post)
I want to find the index by eventID key and change the value of added key in the same index
How is that possible?
For me, the above answer did not work. So, what I did was first find the index of the object that I want to replace then using the index replace it with the new value
if let row = self.upcoming.index(where: {$0.eventID == id}) {
array[row] = newValue
}
In Swift 5.0:
if let row = self.upcoming.firstIndex(where: {$0.eventID == id}) {
array[row] = newValue
}
Since you are using a class, use filter and first to find the value:
array.filter({$0.eventID == id}).first?.added = value
In this you:
filter the array down to elements that match the event ID
pick the first result, if any
then set the value
This works since classes are pass by reference. When you edit the return value from array.filter({$0.eventID == id}).first?, you edit the underlying value. You'll need to see the answers below if you are using a struct
EDIT: In Swift 3 you can save yourself a couple of characters
array.first({$0.eventID == id})?.added = value
EDIT: Swift 4.2:
array.first(where: { $0.eventID == id })?.added = value
array.filter {$0.eventID == id}.first?.added = value
The filter operator is not the best in this case, it works for some of you because classes are passed by reference.
Explanation: (You can copy the following code in a playground if you want to verify it).
class Book {
let id: Int
var title = "default"
init (id: Int) {
self.id = id
}
}
var arrayBook = [Book]()
arrayBook.append(Book(id: 0))
arrayBook.append(Book(id:1))
arrayBook.forEach { book in
print(book.title)
}
arrayBook.filter{ $0.id == 1 }.first?.title = "modified"
arrayBook.forEach { book in
print(book.title)
}
Arrays are copied by value not reference, so when you are using filter you are creating a new array (different than the initial), but when you modify the new one, the initial one gets modified too because both are pointing to the same class (classed are passed by reference), so after the filter your array will have changed and the new one gets deallocated. So in this case it will print "default", "default" and then "default, "modified".
What happens if you change class for struct, the value will be passed by value not reference so you will have 2 arrays in memory with different values, so if you go through arrayBooks again it will print before the filter "default","default", and then "default", "default" again. Because when you are using the filter you are creating and modifying a new array that will get deallocated if you do not store it).
The solution is using map, creating a new array with all the values but with the modified items or fields that we want and then replace our array with the new one. This will print "default", "default" before the map, and then "default", "modified"
This will work with structs, classes and everything that you want :).
struct Book {
let id: Int
var title = "default"
init (id: Int) {
self.id = id
}
}
var arrayBook = [Book]()
arrayBook.append(Book(id: 0))
arrayBook.append(Book(id:1))
arrayBook.forEach { book in
print(book.title)
}
arrayBook = arrayBook.map{
var mutableBook = $0
if $0.id == 1 {
mutableBook.title = "modified"
}
return mutableBook
}
arrayBook.forEach { book in
print(book.title)
}
array = array.map { $0.eventID == id ? newValue : $0 }
If you conform your class to Equatable then this would work:
extension Array where Element: Equatable {
#discardableResult
public mutating func replace(_ element: Element, with new: Element) -> Bool {
if let f = self.firstIndex(where: { $0 == element}) {
self[f] = new
return true
}
return false
}
}
Use like this:
array.replace(prev, with: new)
I have this class:
class MainView:UIView{
var categories:[Category]!
}
i want to set the categories arg, but i need to pass it by reference not value. because it's more efficient and better.
so if i did this:
let mainView = MainView()
mainView.categories = categoriesData.
then it pass it by value.
if i need to pass it by reference i could do that by using function inside the MainView()
class MainView:UIView{
var categories:[Category]!
fun setCategories(inout categories: Int){
self.categories = categories;
}
}
but if i don't want to use set function, How could i pass it by reference.
e.g
mainView.categories = &categoriesData. but that doesn't work ?thanks
Swift uses ARC (Automatic Reference Counting) when dealing with arrays, and it delays copying arrays until one of the copies is modified:
For example:
var a = [1, 2, 3, 4, 5]
let b = a
let c = a // 1
a.append(6) // 2
print(a.count)
print(b.count)
print(c.count)
At step 1 above, there is only one copy of [1, 2, 3, 4, 5] in memory, and a, b, and c are references to it.
When a is modified in step 2, Swift gives a and new copy of the array and b and c continue to reference the original array. So now there are 2 copies of the array in memory.
Let's look at a much more involved example:
class Person: CustomStringConvertible {
let name: String
var friends: [Person] = []
init(name: String) {
self.name = name
}
var description: String { return name }
}
func createFredsFriends() -> [Person] {
let barney = Person(name: "Barney")
let wilma = Person(name: "Wilma")
let betty = Person(name: "Betty")
let friends = [barney, wilma, betty] // 1
return friends
}
func createFred() -> Person {
let fred = Person(name: "Fred")
let friends = createFredsFriends() // 2
fred.friends = friends // 3
return fred
}
let fred = createFred() // 4
print(fred.friends) // [Barney, Wilma, Betty]
At step 1, the array of friends is created. It is referenced by the local variable friends.
This reference goes away when createFredsFriends() returns, and the only reference to the array is held then by the local variable friends at step 2. Ownership of the array has been passed.
At step 3, a second reference to the array has been assigned to the friends property of fred.
At step 4, createFred() has returned, so the local variable friends is gone and no longer references the array. The only reference is in the property of the fred object which is held by the variable fred.
So the array was created once, several references to it were created, and in the end there is a single reference to the array and all of this was done without a single copy operation.
Since Swift arrays are value types and copied when changed, you can't pass an array and then expect the original to be updated when the copy is. If you need that level of functionality, you can create a class wrapper for the array and then always access that array through the instance of that class.
Here I've modified the previous example to show how that would work:
// Class to wrap array so that everyone references the same copy
class FriendsWrapper {
var friends: [Person]
init(friends: [Person]) {
self.friends = friends
}
}
class Person: CustomStringConvertible {
let name: String
var friendsWrapper: FriendsWrapper?
init(name: String) {
self.name = name
}
func addFriend(friend: Person) {
if let wrapper = friendsWrapper {
wrapper.friends.append(friend)
} else {
friendsWrapper = FriendsWrapper(friends: [friend])
}
}
var description: String { return name }
}
func createFredsFriends() -> [Person] {
let barney = Person(name: "Barney")
let wilma = Person(name: "Wilma")
let betty = Person(name: "Betty")
let friends = [barney, wilma, betty]
return friends
}
func createFred() -> Person {
let fred = Person(name: "Fred")
let friendsWrapper = FriendsWrapper(friends: createFredsFriends())
fred.friendsWrapper = friendsWrapper
// Add friend to Fred object
fred.addFriend(Person(name: "Bam Bam"))
// Copy of array in local friendsWrapper is updated
print(friendsWrapper.friends) // [Barney, Wilma, Betty, Bam Bam]
return fred
}
let fred = createFred()
is there a possibility to get an object from an array with an specific property? Or do i need to loop trough all objects in my array and check if an property is the specific i was looking for?
edit: Thanks for given me into the correct direction, but i have a problem to convert this.
// edit again: A ok, and if there is only one specific result? Is this also a possible method do to that?
let imageUUID = sender.imageUUID
let questionImageObjects = self.formImages[currentSelectedQuestion.qIndex] as [Images]!
// this is working
//var imageObject:Images!
/*
for (index, image) in enumerate(questionImageObjects) {
if(image.imageUUID == imageUUID) {
imageObject = image
}
}
*/
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
var imageObject = questionImageObjects.filter( { return $0.imageUUID == imageUUID } )
// this is not working - NSArray is not a subtype of Images- so what if there is only 1 possible result?
You have no way to prove at compile-time that there is only one possible result on an array. What you're actually asking for is the first matching result. The easiest (though not the fastest) is to just take the first element of the result of filter:
let imageObject = questionImageObjects.filter{ $0.imageUUID == imageUUID }.first
imageObject will now be an optional of course, since it's possible that nothing matches.
If searching the whole array is time consuming, of course you can easily create a firstMatching function that will return the (optional) first element matching the closure, but for short arrays this is fine and simple.
As charles notes, in Swift 3 this is built in:
questionImageObjects.first(where: { $0.imageUUID == imageUUID })
Edit 2016-05-05: Swift 3 will include first(where:).
In Swift 2, you can use indexOf to find the index of the first array element that matches a predicate.
let index = questionImageObjects.indexOf({$0.imageUUID == imageUUID})
This is bit faster compared to filter since it will stop after the first match. (Alternatively, you could use a lazy sequence.)
However, it's a bit annoying that you can only get the index and not the object itself. I use the following extension for convenience:
extension CollectionType {
func find(#noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
return try indexOf(predicate).map({self[$0]})
}
}
Then the following works:
questionImageObjects.find({$0.imageUUID == imageUUID})
Yes, you can use the filter method which takes a closure where you can set your logical expression.
Example:
struct User {
var firstName: String?
var lastName: String?
}
let users = [User(firstName: "John", lastName: "Doe"), User(firstName: "Bill", lastName: "Clinton"), User(firstName: "John", lastName: "Travolta")];
let johns = users.filter( { return $0.firstName == "John" } )
Note that filter returns an array containing all items satisfying the logical expression.
More info in the Library Reference
Here is a working example in Swift 5
class Point{
var x:Int
var y:Int
init(x:Int, y:Int){
self.x = x
self.y = y
}
}
var p1 = Point(x:1, y:2)
var p2 = Point(x:2, y:3)
var p3 = Point(x:1, y:4)
var points = [p1, p2, p3]
// Find the first object with given property
// In this case, firstMatchingPoint becomes p1
let firstMatchingPoint = points.first{$0.x == 1}
// Find all objects with given property
// In this case, allMatchingPoints becomes [p1, p3]
let allMatchingPoints = points.filter{$0.x == 1}
Reference:
Trailing Closure
Here is other way to fetch particular object by using object property to search an object in array.
if arrayTicketsListing.contains({ $0.status_id == "2" }) {
let ticketStatusObj: TicketsStatusList = arrayTicketsListing[arrayTicketsListing.indexOf({ $0.status_id == "2" })!]
print(ticketStatusObj.status_name)
}
Whereas, my arrayTicketsListing is [TicketsStatusList] contains objects of TicketsStatusList class.
// TicketsStatusList class
class TicketsStatusList {
internal var status_id: String
internal var status_name: String
init(){
status_id = ""
status_name = ""
}
}