4
\$\begingroup\$

I made a class to manage saved file in drive with Swift, and I tried to do the best work.

Can someone get me a feedback about this class? What can I improve? Is this a good way to save a file?

import UIKit

class TCFileData:NSObject, NSCoding
{
    override init() {
    }

    required init(coder aDecoder: NSCoder) {
    }

    func encodeWithCoder(aCoder: NSCoder) {
        fatalError("encodeWithCoder(aCoder: NSCoder:) has not been implemented");
    }
}

struct TCFile {

    static func save<T:TCFileData>(#name:String, object:T)->Bool
    {
        let path = getPath(name);
        let saveData = NSKeyedArchiver.archivedDataWithRootObject(object);
        return saveData.writeToFile(path as String, atomically: true);
    }

    static func save<T:TCFileData>(object:T)->Bool
    {
        return save(name: NSStringFromClass(object.classForCoder), object: object);
    }

    static func hasFile(#name:String)->Bool
    {
        return NSFileManager.defaultManager().fileExistsAtPath(getPath(name) as String);
    }

    static func hasFile<T:TCFileData>(classType:T.Type)->Bool
    {
        return hasFile(name: getFileName(classType));
    }

    static func loadFile<T:TCFileData>(#name:String, classType:T.Type)->T!
    {
        var data:T?;

        if(hasFile(name: name))
        {
            if let rawData = NSData(contentsOfFile: getPath(name) as String) {
                var object: AnyObject? = NSKeyedUnarchiver.unarchiveObjectWithData(rawData);
                data = object as? T;
            }
        }

        return data;
    }

    static func loadFile<T:TCFileData>(classType:T.Type)->T!
    {
        return loadFile(name: getFileName(classType), classType:classType);
    }

    static func loadFile<T:TCFileData>()->T!
    {
        return loadFile(name: getFileName(T.self), classType:T.self);
    }

    static func remove(#name:String)->Bool
    {
        if(hasFile(name: name))
        {
            return NSFileManager.defaultManager().removeItemAtPath(getPath(name) as String, error: nil);
        }

        return false;
    }

    static func remove<T:TCFileData>(classType:T.Type)->Bool
    {
        return remove(name: getFileName(classType));
    }

    static private func getPath(file:String)->NSString
    {
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray;
        let documentsDirectory = paths.objectAtIndex(0) as! NSString;
        let path = documentsDirectory.stringByAppendingPathComponent(file);
        return path;
    }

    static private func getFileName<T:TCFileData>(classType:T.Type)->String
    {
        return NSStringFromClass(classType.self.classForCoder());
    }
}
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Your organization is a bit confusing.

TCFileData is a hacked attempt to force abstract classes into a language without abstract classes, and TCFile is a hacked attempted at creating a namespace for a language that doesn't have namespaces, and your use of generics here seems unnecessary.

I think what makes more sense here is to completely eliminate the class and make the struct more of a wrapper object. The problem with the class is that it unnecessarily eliminates using your file management code with a plethora of existing objects which already conform to NSCoding (and that's all your code actually requires).

We want a class that starts off looking something like this:

class TCFileManager<T: NSCoding> {
    var contents: T?
    var path: String

    init(path: String, contents: T? = nil) {
        self.path = path
        self.contents = contents
    }
}

And now, from here, we implement all of the methods your struct already implemented, but they mostly don't take arguments or return anything. Instead, they use the set values on their properties, and they set these values.

So, for example, we might want to load a dictionary I saved as a plist to my desktop:

let fileManager = TCFileManager<NSDictionary>(path: "/Users/nhgrif/Desktop/myPlist.plist")
fileManager.load()
let plist = fileManager.contents

Of course, we could also set load to return the loaded object in addition to setting the .contents property, but you get the gist.

Once we instantiate the file manager object with a path, a small set of methods can make a lot of things much easier to do.

Consider, we could also have a move method which takes a string parameter for the path to move the file to. The method would first do what you expect (move the file), but then the object's path property changes to the path passed in.

And obviously, you'd want to implement a save method as well, etc.

But I think converting your code into this sort of shape will make a lot of things make more sense and be easier to use.

\$\endgroup\$
1
  • \$\begingroup\$ It makes perfect sense, my class I develop to mobile and I assumed save in default user document in device. With your explanation I can do more and TCFileManager can be more usefull for many different projects. Thankyo very much for review \$\endgroup\$
    – ViTUu
    Apr 16, 2015 at 20:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.