Convert array items to string on condition - Swift - arrays

I am trying to create a file name from the URL. I have to forcefully remove the first item in the array every time. Like the C# implementation where they check for empty string.
Is it possible to remove the "/" in the array? Is there a better way to implement this?
let url = "https://stackoverflow.com/questions/ask"
var filePathComponents:[String] = []
filePathComponents = assetURL.pathComponents as! [String]
filePathComponents.removeAtIndex(0)
let fileName = "-".join(filePathComponents)

I would recommend
filePathComponents.filter { return $0 != "/" }

You don't have to use mutability if you use range indexing (with ..<).
And to make it convenient to use, let's put it in an extension as a computed property.
Example for String:
extension String {
var pathNameWithoutPrefix: String {
get {
return "-".join(self.pathComponents[2 ..< self.pathComponents.count])
}
}
}
let filePath = "http://stackoverflow.com/questions/ask".pathNameWithoutPrefix
println(filePath) // "questions-ask"
Example for NSURL:
extension NSURL {
var pathURLWithoutPrefix: NSURL? {
get {
if let filePathComponents = self.pathComponents as? [String] {
return NSURL(string: "-".join(filePathComponents[1 ..< filePathComponents.count]))
}
return nil
}
}
}
if let url = NSURL(string: "http://stackoverflow.com/questions/ask"),
let fileURL = url.pathURLWithoutPrefix {
println(fileURL) // "questions-ask"
}
Here's the same extensions for Swift 2:
extension String {
var pathNameWithoutPrefix: String {
get {
let str = self as NSString
return "-".join(str.pathComponents[2 ..< str.pathComponents.count])
}
}
}
extension NSURL {
var pathURLWithoutPrefix: NSURL? {
get {
if let filePathComponents = self.pathComponents {
return NSURL(string: "-".join(filePathComponents[1 ..< filePathComponents.count]))
}
return nil
}
}
}

let linkURL = NSURL(string: "http://stackoverflow.com/questions/ask")!
if let comps = linkURL.pathComponents as? [String] {
let fileName = "-".join(dropFirst(comps)) // "questions-ask"
}
Swift 2.0
let linkURL = NSURL(string: "http://stackoverflow.com/questions/ask")!
if let comps = linkURL.pathComponents?.dropFirst() {
let fileName = "-".join(comps) // "questions-ask"
}

Different approach using the host and path properties of NSURL
if let assetURL = NSURL(string: "http://stackoverflow.com/questions/ask") {
let fileNameWithHost = assetURL.host!.stringByAppendingPathComponent(assetURL.path!) // "stackoverflow.com/questions/ask"
let fileNameWithoutHost = assetURL.path!.substringFromIndex(assetURL.path!.startIndex.successor()) // "questions/ask"
}

Related

Load two json files of Arrays of Dictionaries. Add missing dictionaries from the file A to file B

I need to sync two json files to add new content from File A (located in the app bundle) to File B after an app update.
Both json files are arrays of dictionaries. I need to iterate the dictionaries form File A, and based on the "id" value, if a dictionary is not present in File B I need to append those missing dictionaries and save File B back to the file system.
I have a solution below that does this, and seems to work. But it's SO ugly! Granted I put this together in about 15 minutes cringing the whole way but I'm sure there has to be a better way of handling this. Also, I don't want to further muddy the waters by converting these dictionaries to structs or models for the comparison only to convert them back to dictionaries -> json.
Any advise here would be great! I prefer clean code and this is a mess.
typealias JSON = [[String: Any]]
static private func uglySync() {
let fileName: String = "someFileName"
guard let sourceUrl = Bundle.main.url(forResource: fileName, withExtension: "json") else { return }
guard let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let destinationUrl = destinationDirectory.appendingPathComponent("Data/" + fileName + ".json")
do {
let sourceData = try Data(contentsOf: sourceUrl)
do {
if let sourceArray = try JSONSerialization.jsonObject(with: sourceData, options: .mutableContainers) as? JSON {
do {
let destinationData = try Data(contentsOf: destinationUrl)
do {
if let destinationArray = try JSONSerialization.jsonObject(with: destinationData, options: .mutableContainers) as? JSON {
var mutableArray = destinationArray
sourceArray.forEach({ (item) in
if let itemId = item["id"] as? String {
let foundItem = destinationArray.filter { $0["id"] as! String == itemId }.first
if foundItem == nil {
mutableArray.append(item)
}
}
})
do {
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationUrl)
} catch let error as NSError {
print("Couldn't write to file: \(error.localizedDescription)")
}
} else {
print("Cound not process json")
}
} catch {
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
} else {
print("Cound not process json")
}
} catch {
print(error.localizedDescription)
}
} catch {
print(error.localizedDescription)
}
// oh wow the try catches :/
}
I've grouped converting the files to jsonArray to simplify the do...catch. Alternatively, if you don't need to print the error message, you could opt to have Optional try? as well to remove the do...catch block.
typealias JSONArray = [[String: Any]]
private func jsonArray(from fileURL: URL) -> JSONArray? {
do {
let fileData: Data = try Data(contentsOf: fileURL)
guard let jsonArray = (try JSONSerialization.jsonObject(with: fileData, options: .mutableContainers)) as? JSONArray else {
debugPrint("Failed to find JSON Array table")
return nil
}
return jsonArray
} catch {
print(error.localizedDescription)
return nil
}
}
func sync() {
let fileName: String = "someFileName"
guard
let fileURL: URL = Bundle.main.url(forResource: fileName, withExtension: "json"),
let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first,
let destinationURL: URL = destinationDirectory.appendingPathComponent("Data/" + fileName + ".json"),
let sourceArray = jsonArray(from: fileURL),
let destinationArray = jsonArray(from: destinationURL)
else {
return
}
var mutableArray = destinationArray
let destinationIDArray = destinationArray.compactMap { $0["id"] as? String }
mutableArray.forEach { (item) in
if let itemId = item["id"] as? String, !(destinationIDArray.contains { $0 == itemId }) {
mutableArray.append(item)
}
}
// Update File
do {
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationURL)
} catch {
print("Couldn't write to file: \(error.localizedDescription)")
}
}
I think you can put the different trys in the same do block.
do {
try function1()
try function2()
} catch {
print(error.localizedDescription)
}
So afterwards your function may look like
typealias JSON = [[String: Any]]
static private func moderatelyOkSync() {
let fileName: String = "someFileName"
guard let sourceUrl = Bundle.main.url(forResource: fileName, withExtension: "json") else { return }
guard let destinationDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let destinationUrl = destinationDirectory.appendingPathComponent("Data/" + fileName + ".json")
do {
let sourceData = try Data(contentsOf: sourceUrl)
if let sourceArray = try JSONSerialization.jsonObject(with: sourceData, options: .mutableContainers) as? JSON {
let destinationData = try Data(contentsOf: destinationUrl)
}
var mutableArray = destinationArray
sourceArray.forEach({ (item) in
if let itemId = item["id"] as? String {
let foundItem = destinationArray.filter { $0["id"] as! String == itemId }.first
if foundItem == nil {
mutableArray.append(item)
}
}
})
let jsonData = try JSONSerialization.data(withJSONObject: mutableArray, options: .prettyPrinted)
try jsonData.write(to: destinationUrl)
} catch {
print(error.localizedDescription)
}
}
The way I would do it is to Decode the json files with struct and then encode(serialization) it to the other files. Because the code to do that would be a 2 liner, but you would first have to layout all the variables in the struct. Probably still not optimal

convert nsmanganedobject to array to find sum of array

My code below is trying to take core data from a NSManagedObject append it to an array. The core data element is saved as a string. My code is not compelling. Ideally the code should be able to append code into the array then the array is filled, find the sum of the numbers added together and print them into the viewDidLoad() func.
var itemName : [NSManagedObject] = []
func performAction() {
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Data")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
var retrievedData = [Double]()
for data in result as! [NSManagedObject] {
if let value = data.value(forKey: "ee") as? Double {
retrievedData.append(value)
}
}
let arraySum = retrievedData.reduce(0, +)
print(arraySum)
} catch {
print("Failed")
}
}
I reviewed your code when you will need to change small thing over there. Replace performAction function as per my updated answer.
func performAction() {
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Data")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
var retrievedData = [Double]()
for data in result as! [NSManagedObject] {
if let value = data.value(forKey: "ee") as? String {
retrievedData.append(Double(value) ?? 0)
}
}
let arraySum = retrievedData.reduce(0, +)
print(arraySum)
} catch {
print("Failed")
}
}

Issue with storing data within an Array - Swift

I currently have my set-up as followed:
I am running a query in Firebase to extract all of the genres within an array of genres, like so:
var genresLabelIndex : [String] = ["Horror", "Fiction", "Romance"]
Then I am creating a blank arrays for each of the genres to be able to store the information of the genres within each of the areas like so:
var horrorData = [InformationForFeed]()
var fictionData = [InformationForFeed]()
var romanceData = [InformationForFeed]()
InformationForFeed looks like so:
class InformationForFeed {
fileprivate var _uploadKey:String!
fileprivate var _userKey:String!
fileprivate var _imageURL:String!
fileprivate var _socialMedia:[String]
var uploadKey:String!{
return _uploadKey
}
var userKey:String!{
return _userKey
}
var imageURL:String!{
return _imageURL
}
init(dictionary:Dictionary<String,AnyObject>, socials: [String]) {
_socialMedia = socials
if let uploadKey = dictionary["upload_key"] as? String {
self._uploadKey = uploadKey
}
if let userKey = dictionary["user_key"] as? String {
self._userKey = userKey
}
if let imageURL = dictionary["imageUrl"] as? String {
self._imageURL = imageURL
}
}
}
I am then creating an Array of the list of genres arrays like so:
1) First I am creating an empty array of arrays like this:
var genreArrayIndex : [[InformationForFeed]] = []
2) Then within my init() of the UIView I am setting what will be in the array like this:
genreArrayIndex = [self.horrorData, self.fictionData, self.romanceData]
I then will run a function called getData() that will run my query and start storing the information.
I store my information of each genre in a tempArray, and then I set the genreArrayIndex[index] to equal the tempArray and then clear the tempArray as seen in getData below.
func getData() {
for genre in genresLabelIndex {
let dbReference = Database.database().reference().child("genres").child(genre)
let query = dbReference.queryLimited(toLast: 6)
query.observeSingleEvent(of: .value, with: { (snapshot : DataSnapshot) in
if snapshot.childrenCount > 0 {
for s in snapshot.children.allObjects as! [DataSnapshot] {
let item = s.value as! Dictionary<String,AnyObject?>
let facebook = (s.childSnapshot(forPath: "social_links").value as? NSDictionary)?["facebook_link"]
let audible = (s.childSnapshot(forPath: "social_links").value as? NSDictionary)?["audible_link"]
let amazon = (s.childSnapshot(forPath: "social_links").value as? NSDictionary)?["amazon_link"]
var socialsArray = [String]()
if facebook != nil {
socialsArray.append(facebook! as! String)
} else {
socialsArray.append("nil")
}
if audible != nil {
socialsArray.append(audible! as! String)
} else {
socialsArray.append("nil")
}
if amazon != nil {
socialsArray.append(amazon! as! String)
} else {
socialsArray.append("nil")
}
let data = InformationForFeed(dictionary: item as Dictionary<String,AnyObject>, socials: socialsArray)
self.newArray.append(data)
}
}
self.genreArrayIndex[self.genreArrayIndexCount] = self.newArray
self.genreArrayIndexCount = self.genreArrayIndexCount + 1
self.newArray.removeAll()
self.internalIndex = self.internalIndex + 1
if self.internalIndex == self.genresLabelIndex.count {
self.tableView.reloadData()
}
})
}
}
My tempArray looks like this:
var newArray = [InformationForFeed]()
The index looks like this:
var genreArrayIndexCount : Int = 0
Now comes the issue....
All of the information is properly being stored in the genreArrayIndex .....but... it is not actually storing the information in the arrays that being stored in genreArrayIndex.
So in other words if you were to print(self.genreArrayIndex) it would be fully populated. But if you were to print(self.fictionData) it would be blank.
How can I resolve this?
Array is a value type. That means its contents are copies. Initializing genreArrayIndex with empty horrorData, (and others) and then filling it with getData() does not also copy the data back into horrorData. I would recommend eliminating
genreArrayIndex = [self.horrorData, self.fictionData, self.romanceData]
and replacing horrorData, fictionData, ... with computed properties. Perhaps like this.
var horrorData: [InformationFeed] {
return genreArrayIndex[0]
}

Swift - read plist file to an array?

I have created a mini translation from English words to Spanish words. I would like to use the englishArray.plist instead of my englishArray = ["the cat"] How can I create this?
I have also used a localizable.strings to retrieve the value "the cat" for "el gato" but I would like to retrieve this from englishArray.plist
I started off with this but not sure if I'm on the right path
let path = NSBundle.mainBundle().pathForResource("englishArray", ofType: "plist")
let plistEnglishArray = NSArray(contentsOfFile: path!)
Here is the rest of my code:
var englishArray: [String] = ["rainbow", "boots", "heart", "leaf", "umbrella", "tired", "angry", "cry", "the cat" ]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.translateTextField.delegate = self
picker.delegate = self
picker.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func translateButtonTapped(sender: UIButton) {
let emptyString = self.translateTextField.text
if (emptyString!.isEmpty) {
print("please enter a word")
}
for transIndex in englishArray.indices {
if englishArray[transIndex] == emptyString!.lowercaseString {
//englishArray
//translateLabel.text = "\(spanishArray[transIndex])"
translateLabel.text = NSLocalizedString(emptyString!.lowercaseString, comment:"")
print(emptyString)
return
}
}
}
Swift 4
The absolute simplest way to do this is
let url = Bundle.main.url(forResource: "Sounds", withExtension: "plist")!
let soundsData = try! Data(contentsOf: url)
let myPlist = try! PropertyListSerialization.propertyList(from: soundsData, options: [], format: nil)
The object myPlist is an Array or Dictionary, whichever you used as the base of your plist.
Change your root object to Array, then
var myEnglishArray: [String] = []
if let URL = NSBundle.mainBundle().URLForResource("englishArray", withExtension: "plist") {
if let englishFromPlist = NSArray(contentsOfURL: URL) as? [String] {
myEnglishArray = englishFromPlist
}
}
Swift 4
You can use Codable which is pure swift type.
Firstly load Plist file from bundle then use PropertyListDecoder
Complete code -
func setData() {
// location of plist file
if let settingsURL = Bundle.main.path(forResource: "JsonPlist", ofType: "plist") {
do {
var settings: MySettings?
let data = try Data(contentsOf: URL(fileURLWithPath: settingsURL))
let decoder = PropertyListDecoder()
settings = try decoder.decode(MySettings.self, from: data)
print("array is \(settings?.englishArray ?? [""])")//prints array is ["Good morning", "Good afternoon"]
} catch {
print(error)
}
}
}
}
struct MySettings: Codable {
var englishArray: [String]?
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
englishArray = try values.decodeIfPresent([String].self, forKey: .englishArray)
}
}
This will read a resource in your bundle with the name "englishArray.plist" and store it in the immutable variable english. It will be an Optional that you should test before using.
It uses a closure to read the file and return the array, this lets you use a immutable value rather than a mutable variable that can be changed. It's a good idea to use immutables wherever you can - they promote stability.
import Foundation
let english:[String]? = {
guard let URL = NSBundle
.mainBundle()
.URLForResource("englishArray", withExtension: "plist") else {
return nil
}
return NSArray(contentsOfURL: URL) as? [String]
}()
Here is the solution for swift 3. For this solution you do not need to change types in your plist structure (keep Dictionary, Array, as is). Also note that since your array's name in plist is also englishArray so the (value for key) argument in the second if statement is also englishArray.
var myEnglishArray: [String] = []
if let URL = Bundle.main.url(forResource: "englishArray", withExtension: "plist") {
guard let englishFromPlist = NSDictionary(contentsOf: URL) else { return [] }
if let englishArray = englishFromPlist.value(forKey: "englishArray") as? [String] {
for myEnglish in englishArray {
myEnglishArray.append(myEnglish)
}
}
}

How to compare a variable String() to a string in swift?

I have a class in which I need to check a URL for json data compile that in an array and see if the latest content is an article/project/survey. My code compiles but it compare the strings to see if its an article/project/survey. Not sure what im doing wrong?
my code is
class LocalNotificationsManager {
var articleSurveyOrProject = String()
func checkForNewContent() {
let url = "https://cdn.contentful.com/spaces/maz0qqmvcx21/entries?access_token=ae8163cb8390af28cd3d7e28aba405bac8284f9fe4375a605782170aef2b0b48";
var jsonData:NSData?
let url = "https://cdn.contentful.com/spaces/maz0qqmvcx21/entries?access_token=ae8163cb8390af28cd3d7e28aba405bac8284f9fe4375a605782170aef2b0b48";
var jsonData:NSData?
var latestContentDates = [String]()
do{
jsonData = try NSData(contentsOfURL: NSURL(string: url)!, options: NSDataReadingOptions.DataReadingUncached)
let jsonObject:AnyObject? = try NSJSONSerialization.JSONObjectWithData(jsonData!, options: NSJSONReadingOptions.AllowFragments)
if let itemArray = jsonObject?.objectForKey("items") as? NSArray{
for item in itemArray{
if let sysItem = item.objectForKey("sys"){
//this is createdAt
if let createdAt = sysItem.objectForKey("createdAt") as? String{
print("createdAt:\(createdAt)")
latestContentDates.append(createdAt)
}
if let contentTypeItem = sysItem.objectForKey("contentType")!.objectForKey("sys"){
//this is id
if let id = contentTypeItem.objectForKey("id") as? String{
content.append(id)
}
}
}
}
}
}catch let err as NSError{
print("err:\(err)")
}
let articleSurveyOrProject = content[0]
print("articleSurveyOrProject:\(articleSurveyOrProject)")
sendLocalNotification()
}
func sendLocalNotification() {
if (articleSurveyOrProject == "article") {
print(Article)
} else if (articleSurveyOrProject == "survey") {
print("Survey")
} else if (articleSurveyOrProject == "project")
{
print("Project")
} else {
print("Oops! something went wrong it didnt get any values")
}
}
}
Note: Im working in swift2
The problem is this line:
let articleSurveyOrProject = content[0]
Delete let.

Resources