Swift ios store Data and String in Array - ios

I wonder if its possible to store both Data and String in a array?
I need a way to store images that user picks together with the image names inside a array so I later can append it to my API request when I upload it.
Now I am using a array tuple that I later append Data and String to
var imgArray = [(Data, String)]()
Then I add data to that array tuple like this:
if let firstImage = self.firstImage {
if let firstImageData = firstImage.compressImage() {
self.imgArray.append(firstImageData, self.randomImageName(length: 15))
}
I use the code above for everyimage the user uploads and it works, imgArray gets populated with both Data and String which I later send to my API.
But is there a way to use a array to store Data and String values to?
I am not sure if tuples is the best solution
}

Apple discourages from using tuples as data source.
Tuples are useful for temporary groups of related values. They are not suited to the creation of complex data structures. If your data structure is likely to persist beyond a temporary scope, model it as a class or structure, rather than as a tuple
The (object oriented) Swift way is a struct:
struct ImageData {
var data : Data
var name : String
}
var imgArray = [ImageData]()
if let firstImage = self.firstImage {
if let firstImageData = firstImage.compressImage() {
self.imgArray.append(ImageData(data:firstImageData, name:self.randomImageName(length: 15))
}
The benefit is to get the members by name
let imageName = imgArray[0].name

Related

How to access differently nested string array swift 5

and good job , well basically i have array like this :
let imageArray = [ ["image0"] , ["image11","image12"] , ["image2"], ["image31","image32","image33"] ]
In this point i want to put for example first item of each nested array into a new array like this :
var newArray = ["image0","image11","image2","image31"]
And also i want to have a condition for example if "image31" clicked we have a new page that show ervery images of that first array show us, like ["image31", "image32", "image33"]
So could you tell any idea how can implement like this?
You can try
let res = imageArray.map { $0.first! }
When clicked use the index to access other elements , say from above index 3 image is clicked then use
let resImages = imageArray[clickedIndex]
Edit:
let arr = ["images/product/2021-05-02T09-47-17.699Z-download (2).jpg"]
let res = arr.map { $0[$0.range(of: "images/product/")!.upperBound...] }
print(res)
Using simple data structures (like arrays) to model complex data is rarely the best approach.
In this case I would create a struct ImageList and then create an array of this struct
struct ImageList {
let images: [String]
var firstImage: String? {
return self.images.first
}
}
You can use map to create the array from your current source, if required
imageListArray = imageArray.map { ImageList(images:$0) }
Now you can have an index that is associated with an ImageList struct - imageListArray[i].firstImage! is the value you want for your list and iageListArray[i].images is the array you want for your second requirement.

How to maintain the order of the data in Dictionary in Swift?

I want to display the data in the sequence order. I am getting the json string as the response and converted into the Dictionary. I am not able to maintain the order after converted dictionary.
I know it's not guaranteed the order when we use dictionary.
How do I maintain the actual order of the data.
Note: I can't hard code the key since data's are coming from the server response.
func setupData(responseString : String)
{
// Convert string to dictionary for extracting the keys and values
// Need to maintain the order while adding the data
let content = convertToDictionary(text: responseString)
var text : String = ""
if let contentDataDict = content
{
for (key, value) in contentDataDict
{
// want to append the key and values in the sequence order
content += \(key) \n \(value)
}
}
textView.attributedText = text.htmlToAttributedString
}
.
Short answer:
You can not maintain order in the Dictionary.
Long answer:
Apple says:
Dictionaries are unordered collections of key-value associations.
Please refer this
To maintain the order you need to use the Array or create sorted Array(Ascending, Descending or by using specific key) from the Dictionary and use it.
Thank you.

How to convert string to array in Swift 3

I am inserting an Array into my database as a String and after fetching it I want it to convert it again to Array. So that I can fetch my values again and I can do next operation.
Here below is my array inserting into database(TestQuestion) as a String:
let testQuestionModel : TestQuestion = NSEntityDescription.insertNewObject(forEntityName: "TestQuestion", into: AppDelegate.getContext()) as! TestQuestion
testQuestionModel.optionsArray = "\(question["options"] as! NSArray)"
Example: String Array I am getting from Database
(\n \"Rahul Abhyankar\",\n \"Pinkesh Shah\",\n \"Ramanan
Ganesan\",\n \"Dr. Marya Wani\",\n \"\",\n \"\"\n)".
Here is 4 options you can see this is my string after fetching from Database.
1) Rahul Abhyankar.
2) Pinkesh Shah.
3) Ramanan Ganesan.
4) Dr. Marya Wani.
Now how can I convert it into array?
I tried some methods.
let arr = NSArray(object: quetion.optionsArray!).
But I am getting only one object. How can I get my array values same as previous from this string array?
I don't know about the actual type of the "option" in your code, so I set up a fake Elem struct to represent it. The remaining logic is independent of the type as long as you provide a conversion logic to and from String.
struct Elem {
// let's say this is your element type in your array
let foo: Int;
}
extension Elem: CustomStringConvertible {
var description: String {
// provide a logic to convert your element to string
return "\(foo)";
}
}
let arrayToSave = [
Elem(foo: 1),
Elem(foo: 2),
Elem(foo: 3)
]
extension Elem {
init(string: String) {
// provide a function to construct your element type from a string
self.init(foo: Int(string)!)
}
}
let stringToSave = arrayToSave.map { $0.description }.joined(separator: "|")
// save this string
// at some point retrieve it from database, which hopefully same as the saved one
let retrivedString = stringToSave;
let retrivedArray = retrivedString.split(separator: "|").map { Elem(string: String($0)) }
print(retrivedArray) // [1, 2, 3]
Here below is my array inserting into database (TestQuestion) as a
String :
let testQuestionModel : TestQuestion = NSEntityDescription.insertNewObject(forEntityName: "TestQuestion", into: AppDelegate.getContext()) as! TestQuestion
testQuestionModel.optionsArray = "\(question["options"] as! NSArray)"
No, and No.
You are using -description method of an array to save it. Clearly no.
What's wrong? Apple can't affirm that in next OS release, it won't add an extra character. In some more complex cases, it's added <NSArray <0x address> or stuff similar like that.
Suggestion 1:
Modify your entity to have an ARRAY (or usually a Set) of String.
Learn about Core-Data relationship (but that's clearly a DataBase basic knownledge). A relationship one to many should be the thing to do.You could even keep in memory what were the choices, by adding for creating the entity Options, with a String property name (name of the option), another one boolean isChecked, etc.
Suggestion 2:
If you have a limited number of options (like says one to 5), add 5 options string to your entity, and iterate to set them
testQuestionModel.option1 = question["option"][0]
testQuestionModel.option2 = question["option"][1] (if it's of course not out of range for the array)
...
Suggestion 3:
Not really recommended (in my opinion it's missing the whole advantage of the database, especially fetch and predicates, on previous sample you could fetched easily which options were checked), but if you still want to save them as a String, save them as JSON (ie. stringified).
In pseudo code (I'm not sure about the exact syntax, there are no fail safe like try/catch, optional/wrapping):
let options = questions["options"] as [String]
let jsonData = JSONSerialization.data(withJSONObject: (question["options"], options:[])
let jsonString = String.init(data:jsonData encoding:.utf8)
To retrieve them:
let options = JSONSerialization.jsonObject(with data: myJSONString.data(encoding:.utf8), options:[]) as [String]
done using Library SwiftyJSON.
if let dataFromString = yourString?.data(using: String.Encoding.utf8, allowLossyConversion: false) {
do{
let json = try JSON(data: dataFromString)
print(json)
let arrayValue = json.rawValue as! NSArray
print(arrayValue)
} catch{
print(error.localizedDescription)
}
}
Source: https://github.com/SwiftyJSON/SwiftyJSON

how to save and read dictionary of touples to NSUserDefaults?

I have an dictionary = String: ([(String)], [(Int)], NSDate, Bool, [(String)]) and I attempted to deconstruct it into seperate arrays when then app calls applicationWillTerminate
var codes = [(String)]()
var messages = [[String]]()
var senders = [[Int]]()
var dates = [(NSDate)]()
var bools = [(Bool)]()
var pairs = [[String]]()
for code in self.dictionary.keys {
codes.append(code)
messages.append(self.dictionary[code]!.0)
senders.append(self.dictionary[code]!.1)
dates.append(self.dictionary[code]!.2)
bools.append(self.dictionary[code]!.3)
pairs.append(self.dictionary[code]!.4)
}
self.userDefaultsMessages.setObject(codes, forKey: "userMessagesArrays")
self.userDefaultsSenders.setObject(messages, forKey: "userSentArrays")
self.userDefaultsDates.setObject(senders, forKey: "userDatesArray")
self.userDefaultsDeletedBool.setObject(dates, forKey: "userDeletedArrays")
self.userDefaultsPairs.setObject(bools, forKey: "userPairsArrays")
self.userDefaultsCodeKeys.setObject(pairs, forKey: "userCodesArrays")
self.userDefaultsCodeKeys.synchronize()
self.userDefaultsMessages.synchronize()
self.userDefaultsSenders.synchronize()
self.userDefaultsDates.synchronize()
self.userDefaultsDeletedBool.synchronize()
self.userDefaultsPairs.synchronize()
and then I attempt to pull it all back together when the app calls applicationDidBecomeActive
//read
if let savedCodesArray : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userCodesArrays") {
self.userCodes = savedCodesArray! as! [String]
if let savedMessagesArray : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userMessagesArrays") {
self.Usermessages = savedMessagesArray! as! [[String]]
if let savedSendersArray : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userSentArrays") {
self.Usersenders = savedSendersArray! as! [[Int]]
if let savedDatesArray : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userDatesArray") {
self.Userdates = savedDatesArray! as! [NSDate]
if let savedBools : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userDeletedArrays") {
self.Userbools = savedBools! as! [Bool]
if let savedPairs : AnyObject? = self.userDefaultsCodeKeys.objectForKey("userPairsArrays") {
self.Userpairs = savedPairs! as! [[String]]
var indexPath: Int = 0
for code in self.userCodes {
self.dictionary[code]! = [self.Usermessages[indexPath], self.Usermessages[indexPath], self.Userdates[indexPath], self.Userbools[indexPath], self.Userpairs[indexPath]]
}
}
}
}
}
}
}
I am fairly new to iOS development and could use help, how would i save the single dictionary = String: ([(String)], [(Int)], NSDate, Bool, [(String)]) to NSUserDefaults and then later read it.. the documentation was not very helpful since it only worked with simple dictionaries
the code looks incredibly cumbersome so I know I can't be doing it right. It should be a simple solution since I only have one variable to save to NSUserDefaults.
Short answer: You can't.
NSUserDefaults will only record "property list objects" (dictionaries, arrays, strings, numbers (integer and float), dates, binary data, and Boolean values).
You can't save any other types of data into NSUserDefaults, or into a property list. The only solution is to convert other data types into those types.
Tuples are not one of those types, so they can't be saved into user defaults.
First, your interaction with NSUserDefaults is completely wrong. You only need to interact with the shared NSUserDefaults.sharedUserDefaults() instance, not create separate instances, which seems to be what you're doing (i.e. userDefaultsMessages, userDefaultsSenders, etc.).
Second, you don't need to call synchronize() at all. There are very few conditions under which you need to call it manually, and this isn't one of them.
Third, the easiest way to store a particular tuple in NSUserDefaults is to convert it into an Array (or NSArray) and store the result. Of course, this assumes that the tuple contains only types that can be serialized, which your example seems to contain. Unfortunately there's no general solution to this, but creating an array from a tuple is straight forward, as you can just map the tuple indices to array indices.
Finally, such large tuples are usually the result of poor design somewhere along the line. Perhaps refactoring would help resolve your storage issue?

NSMutableArray with nested NSMutableDictionary or nested Subclasses

I'm writing an iOS App in Swift and I'm parsing XML data from a server through a Rest API on the server side using the NSXMLParser delegate.
I have the following data structure:
<alarm>
<rootcause> some properties... </rootcause>
<symptoms>
<symptom> some properties... </symptom>
<symptom> some properties... </symptom>
</symptoms>
</alarm>
Right now I'm parsing the data into an NSmutableArray that contains an NSDictionary for each alarm, that contains a nested dictionary for each RootCause and a NSMutableDictionary with symptoms that contains many instances of NSDictionary for each symptom.
1. NSMutableArray: alarms
2. NSmutableDictionary: alarm
3.NSMutabbleDictionary: rootcause
3.NSMutableDictionary: symptoms
4.NSMutableDictionary: symptom1
4. NSMutableDictionary: symptom2
....
Of course this is a little bit complicated data model, so my question is wether I should create Subclasses of NSObject that contain other nested classes and build my data model or I should keep my data structure of nested NSDictionaries.
Or what would be the best approach to manage changes in the data model in the future and better debugging, etc.
The best way is to create your own data structure, something like this:
class symptom
{
let yourValue = ""
let someOtherValue = 0
}
class alarm
{
var rootcause = ""
var symptoms:[symptom] = []
//or if you have just a string
var symptoms:[String] = []
}
Then all you do is:
var alarms:[alarm] = []
for al in allAramsXML
{
let tmp = alarm()
for sym in al.symptoms
{
let tmpSym = symptom()
tmpSym.yourValue = ""
tmp.symptoms.append(tmpSym)
}
alarms.append(tmp)
}
Converting it gives you compiler validation and you don't have to mess around with string keys to get to your data. So you should make model classes, yes.
An example could look like this:
struct Symptom {
let id : Int
let description : String
}
struct Cause {
let id : Int
}
struct Alarm {
let rootCause : Cause
let symptoms : [Symptom]
}
let alarms : [Alarm] = [Alarm(rootCause: Cause(id: 1), symptoms: [Symptom(id: 2, description: "some description")])]

Resources