Xcode项目组结构的最佳实践?

时间:2016-10-09 16:14:12

标签: ios xcode file directory project-structure

在提供代码示例的教程和示例中,有时我看到Xcode的Project导航器中的项目文件按照MVC模式按组排列(" Views"," Controllers& #34;,"模型"),以及其他时间按功能组织成组("登录","清单",例如)。

关于iOS,是否有任何Apple的会议/推荐? 哪个应该是最好的做法?

3 个答案:

答案 0 :(得分:16)

开发人员以多种方式组织他们的组,代码和文件。但我使用如下内容:

  • CoreData :包含DataModel和实体类。

  • 扩展程序:包含一个类(默认的apple类扩展+项目类扩展。)

  • Helper :包含第三方类/框架(例如SWRevealController)+桥接类(例如基于Swift的项目中的Obj C类)

  • 模型:创建一个单例类(例如,AppModel - NSArray,NSDictionary,String等)来保存数据。解决和存储数据的Web服务响应也在这里完成。

  • 服务:包含Web服务流程(例如,登录验证,HTTP请求/响应)

  • 查看:包含Storyboard,LaunchScreen.XIB和View Classes。创建子文件夹单元格 - 包含UITableViewCell,UICollectionViewCell等。

  • 控制器:包含与UIElements相关的逻辑或代码(例如,UIButton的参考+点击动作)

此结构来自another Stack Overflow post

这些也可能对您有所帮助:

  1. http://akosma.com/2009/07/28/code-organization-in-xcode-projects/

  2. https://github.com/futurice/ios-good-practices/issues/28

  3. http://www.slideshare.net/MassimoOliviero/architecting-ios-project

答案 1 :(得分:6)

我实际上创建了一个项目来演示我认为我的小型或中型代码库的Xcode项目结构。你可以找到它here

以下是它的概述:

  • 来源 - 所有源代码
    • 帐户 - 与帐户相关的类(与会话相关的类,帐户逻辑等)
    • 应用程序 - 与应用程序相关的类。应用程序委托,配置类等
    • Core Additions - 来自苹果课程的扩展和子类
      • 实用程序 - 常规实用程序类。有用的扩展,格式化实用程序,便利类等
      • 基于元素的文件夹 - UIView,UITableViewCell等文件夹
    • 本地持久性 - 本地持久层。与本地数据库的所有交互(领域,核心数据)
      • 存储库 - 所有与模型相关的本地持久性逻辑
    • 常量 - 所有常量。网址,字体,颜色,错误等
    • 模型 - 所有模型(服务器端实体的表示)。我们也会抛出任何对象映射逻辑
    • 模块 - 在这里,我们可以找到按功能划分的每个应用程序的部分
      • 基于模块的文件夹 - 每个文件夹包含所有特定于模块的视图控制器,视图,代理和相关类
    • 网络 - 应用程序的网络层(例如负责与Web服务交互的类)
      • 服务 - 所有与模型相关的网络逻辑
  • 故事板 - 包含所有故事板文件
  • 资源 - 任何其他资源,如媒体,文档,本地化文件等

答案 2 :(得分:0)

这些天:制作更大的文件

我不愿意再使用“模型”文件夹或“控制器”文件夹在MVC级别分离逻辑。

从Objective-C到Swift过渡期间,对我来说最大的转变就是可以使用命名空间。而且,Swift中的访问控制要好得多。

一旦您的代码成为“内部”或“公开”代码,它就会保留更集中的文件

因此,我一直试图将具有共同目的的逻辑分组在一起。事实证明这很棘手,但是下面是一个示例,说明如何使用访问控制使项目的其余部分不知道“ ReportTableViewCell”,该子类仅在我的应用程序的特定场景中使用。

以下内容位于单个文件中,唯一不是私有的是视图控制器。

final class ReportsViewController: UIViewController {
    
    private let tableView = ReportTableView()
    
    private let controller = try! ReportFetchController<ReportTableView>(context: CoreDataManager.sharedInstance.viewContext)
    
    private lazy var tableViewHandler = ReportsTableViewHandler(controller)
    
    ... etc ...
}

private final class ReportTableViewCell: UITableViewCell {
    
    ...etc...
}

private final class ReportTableView: UITableView {
        
    func dequeue(at indexPath: IndexPath, for record: Report) -> ReportTableViewCell {
        
        ...etc...
    }
    
    override init(frame: CGRect, style: UITableView.Style) {
        // register cell here
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

private final class ReportsTableViewHandler: NSObject, UITableViewDataSource, UITableViewDelegate {
    
    private let controller: ReportFetchController<ReportTableView>
    
    init(_ controller: ReportFetchController<ReportTableView>) {
        self.controller = controller
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return controller.fetchedResultsController.sections?.count ?? 0
    }
    
    ...etc...
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let tableView = tableView as! ReportTableView
        let entity = controller.fetchedResultsController.object(at: indexPath) as Report
        return tableView.dequeue(at: indexPath, for: entity)
    }
    
    ...etc...
}

// This is a subclassed NSFetchedResultsController
private final class ReportFetchController<D: FetchedResultsControllerDelegate>: FetchedResultsController<D> where D.Record: Report {
    
    ...etc...
    
    override func predicate() -> NSCompoundPredicate {
        var predicates = [NSPredicate]()
        if let property = property {
            predicates.append(NSPredicate(format: "property == %@", property))
        }
        return NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
    }
    
    private let sectionKeyPath = "created"
    
    override func sectionNameKeyPath() -> String {
        return sectionKeyPath
    }
    
    override func sortDescriptors() -> [NSSortDescriptor] {
        return [NSSortDescriptor(key: sectionKeyPath, ascending: false)]
    }
}

我也避免使用名为“ helpers”或“ extensions”的文件夹,因为该文件夹非常开放。这是我们作为程序员所做的。我们编写“帮助”代码,并出于目的扩展代码。

例如,在决定将哪个视图控制器压入堆栈时,需要以下内容。因此,我在需要它的文件中私下扩展了它。

private extension Boundary {

    var requiresDirection: Bool {
        switch self {
        case .ceiling: return false
        case .wall: return true
        case .fence: return true
        }
    }
}

'requiresDirection'属性对我项目中的其他任何内容(包括'Boundary'枚举)都没有用。因此,让我们将其本地化到程序中需要它的部分,隐藏在文件的深处。