twix
1
Is this possible without SwiftyJSON or other frameworks? Im trying to understand if keyDecodingStrategy is the solution, or if there is another.
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
do {
let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil)
for url in fileURLs {
print(url) // <- that is the place for processing the urls
let data = try Data(contentsOf: url)
print(data)
}
}
} catch {
print("Error while enumerating files \(documentsURL.path): \(error.localizedDescription)")
}
kmahar
(Kaitlin Mahar)
2
If I'm understanding your question correctly, It should certainly be possible to use JSONDecoder to do this.
You can write a struct that only includes the key you actually care about extracting. For example, if I had a bunch of JSON files, each one formatted like this:
{"someStringField": "x", "someIntField": 1}
And I only wanted to collect the "someStringField" values, I could write a Decodable struct that looks like this:
struct MyJSONData: Decodable {
let someStringField: String
}
I can get a specific instance and the desired field with
let s = try JSONDecoder().decode(MyJSONData.self, from: data)
let str = s.someStringField
The decoder only cares that all the properties of MyJSONData are present in the data. It does not care if all of the key/value pairs in the data are present in MyJSONData.
1 Like
twix
3
Thanks for your answer, but in my case it is not working well.
**Error loading data from disk keyNotFound(CodingKeys(stringValue: "type", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"type\", intValue: nil) (\"type\").", underlyingError: nil))**
The Json structure is something like this:
[{"name”:”PLAYLISTNAME”,
”objects":[{ "group":"LIVE:",
"title”:”Channel 1”,
”type":"LiveChannel",
"url”:”URL”,
"logo”:”LOGO” }] …
There are multiple playlists and want I want to archive is to get the type of the objects from all playlists.
Right now I'm able to get the type of objects from a selected playlist at a time by using this code:
class Playlist: Equatable, Codable {
init(name: String, objects: [Object] = []) {
self.name = name
self.objects = objects
}
// MARK: Properties
let name: String
var objects: [Object]
}
func fileURL() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
let filename = "playlists.json"
let fullURL = documentsDirectory.appendingPathComponent(filename)
return fullURL
}
func loadFromPersistentStore() -> [Playlist] {
do {
let data = try Data(contentsOf: fileURL())
let jd = JSONDecoder()
let playlists = try jd.decode([Playlist].self, from: data)
return playlists
} catch let error {
print("Error loading data from disk \(error)")
}
return []
}
GalCohen
(Gal Cohen)
4
what does your implementation of Object look like? Should be something like this:
class Object: Decodable {
let type: String // or an enum? see below - let type: ObjectType
// ... and other properties
}
enum ObjectType: String, Decodable {
case liveChannel = "LiveChannel"
// and so on...
}
twix
5
class Object: Equatable, Codable {
enum Group: String, Codable {
case LiveChannel
case TelevisionShow
case Movie
case unsupported
static let allGroups: [Group] = [.LiveChannel, .TelevisionShow, .Movie, .unsupported]
}
init(type: Group.RawValue, title: String, group: String, url: String, logo: String) {
self.type = type
self.title = title
self.group = group
self.url = url
self.logo = logo
}
// MARK: Properties
let type: String
let title: String
let group: String
let url: String
let logo: String
}
The problem is that I can just get info from one playlist at a time:
static func objectsByType(playList: Playlist, type: Group) -> [Object] {
return playList.objects.filter{ $0.type == type.rawValue }
}
How can I do something like => allplaylists.objects.filter{ $0.type == type.rawValue }
GalCohen
(Gal Cohen)
6
This will return an array of all the objects in the playlist matching a type with that rawValue. You can then iterate over that array and use these Objects however you like. I'm sorry, I think I'm having trouble understanding what you're trying to accomplish.
twix
7
Yes, it returns all objects from one selected playlist, but the Json file contains few playlists. The goal is to return all objects from all playlists.
GalCohen
(Gal Cohen)
8
Sorry, I'm on the road so I may have a few typos. But what you have is almost there. You need a function that takes an array of playlists and collects all objects that contain that particular type.
func objectsByType(playLists: [Playlist], type: Object.Group) -> [Object] {
var found: [Object] = []
for list in playLists {
let objectsMatchingType = list.objects.filter { $0.type == type.rawValue }
found.append(contentsOf: objectsMatchingType)
}
return found
}
1 Like
Or you could flatMap:
func objectsByType(playLists: [Playlist], type: Object.Group) -> [Object] {
return playLists
.flatMap { $0.objects } // gives me an array of all objects from all playlists
.filter { $0.type == type.rawValue } // filter it to get the type you look for
}
1 Like
twix
10
It worked great until I decided to convert the way I store to CoreData, now I get multiple errors.
func objectsByType(playList: Playlist, type: Object.ObjectType) -> [Object] {
return playList.objects.filter{ $0.type == type.rawValue }
}
Value of type 'Any' has no member 'type'
Cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members
GalCohen
(Gal Cohen)
11
I think you'll need to post more of the code give us some context here
twix
12
I found the solution 
// Request Items with type = Movie
let request: NSFetchRequest<Item> = Item.fetchRequest()
request.predicate = NSPredicate(format: "type = %@", Item.ObjectType.Movie.rawValue)
allMovies = try! CoreDataStack.context.fetch(request)