Skip to content

Commit

Permalink
Merge branch 'deploy/1.4.0' into productive
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeehut committed Dec 17, 2016
2 parents 68b70dd + de3f77c commit fb6ee1c
Show file tree
Hide file tree
Showing 14 changed files with 299 additions and 127 deletions.
2 changes: 2 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ opt_in_rules:

disabled_rules:
- vertical_whitespace # Turn off until configurable to 2 newlines
- cyclomatic_complexity

included:
- Sources
- Tests

excluded:
- Carthage
Expand Down
3 changes: 1 addition & 2 deletions CSVImporter.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "CSVImporter"
s.version = "1.3.1"
s.version = "1.4.0"
s.summary = "Import CSV files line by line with ease."

s.description = <<-DESC
Expand All @@ -25,6 +25,5 @@ Pod::Spec.new do |s|
s.source_files = "Sources", "Sources/**/*.swift"
s.framework = "Foundation"
s.dependency "HandySwift", "~> 1.3"
s.dependency "FileKit", "~> 4.0"

end
89 changes: 49 additions & 40 deletions CSVImporter.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions Cartfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
# Simple and expressive file management in Swift
github "nvzqz/FileKit" "29e8c684fdb3018b3fe8ca6b84fe4fb06ef891bf"

# Handy Swift features that didn't make it into the Swift standard library.
github "Flinesoft/HandySwift" ~> 1.3
4 changes: 2 additions & 2 deletions Cartfile.private
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# The Swift (and Objective-C) testing framework.
github "Quick/Quick" "master"
github "Quick/Quick"

# A Matcher Framework for Swift and Objective-C
github "Quick/Nimble" "master"
github "Quick/Nimble"
7 changes: 3 additions & 4 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
github "nvzqz/FileKit" "29e8c684fdb3018b3fe8ca6b84fe4fb06ef891bf"
github "Flinesoft/HandySwift" "1.3.0"
github "Quick/Nimble" "76c4cb3567f3492c00e84a1000ba8622355c8323"
github "Quick/Quick" "8e794df56011c6282f1074e8fd5c113d5f013e63"
github "Flinesoft/HandySwift" "1.3.2"
github "Quick/Nimble" "v5.1.1"
github "Quick/Quick" "v1.0.0"
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
alt="codebeat badge">
</a>
<a href="https://github.com/Flinesoft/CSVImporter/releases">
<img src="https://img.shields.io/badge/Version-1.3.1-blue.svg"
alt="Version: 1.3.1">
<img src="https://img.shields.io/badge/Version-1.4.0-blue.svg"
alt="Version: 1.4.0">
</a>
<img src="https://img.shields.io/badge/Swift-3-FFAC45.svg"
alt="Swift: 3">
Expand Down Expand Up @@ -56,7 +56,7 @@ You can of course also just include this framework manually into your project by
Simply add this line to your Cartfile:

```
github "Flinesoft/CSVImporter" ~> 1.3
github "Flinesoft/CSVImporter" ~> 1.4
```

And run `carthage update`. Then drag & drop the HandySwift.framework in the Carthage/build folder to your project. Also do the same with the dependent frameworks `Filekit` and `HandySwift`. Now you can `import CSVImporter` in each class you want to use its features. Refer to the [Carthage README](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) for detailed / updated instructions.
Expand All @@ -71,7 +71,7 @@ platform :ios, '8.0'
use_frameworks!

target 'MyAppTarget' do
pod 'CSVImporter', '~> 1.3'
pod 'CSVImporter', '~> 1.4'
end
```

Expand Down
77 changes: 35 additions & 42 deletions Sources/Code/CSVImporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,26 @@
//

import Foundation
import FileKit
import HandySwift

/// An enum to represent the possible line endings of CSV files.
public enum LineEnding: String {
case NL = "\n"
case CR = "\r"
case CRLF = "\r\n"
case Unknown = ""
case nl = "\n"
case cr = "\r"
case crlf = "\r\n"
case unknown = ""
}

private let chunkSize = 4096

/// Importer for CSV files that maps your lines to a specified data structure.
open class CSVImporter<T> {

public class CSVImporter<T> {
// MARK: - Stored Instance Properties

let csvFile: TextFile
let delimiter: String
var lineEnding: LineEnding
let encoding: String.Encoding

var lastProgressReport: Date?

Expand All @@ -39,10 +38,7 @@ open class CSVImporter<T> {
// MARK: - Computed Instance Properties

var shouldReportProgress: Bool {
get {
return self.progressClosure != nil &&
(self.lastProgressReport == nil || Date().timeIntervalSince(self.lastProgressReport!) > 0.1)
}
return self.progressClosure != nil && (self.lastProgressReport == nil || Date().timeIntervalSince(self.lastProgressReport!) > 0.1)
}


Expand All @@ -54,10 +50,11 @@ open class CSVImporter<T> {
/// - path: The path to the CSV file to import.
/// - delimiter: The delimiter used within the CSV file for separating fields. Defaults to ",".
/// - lineEnding: The lineEnding of the file. If not specified will be determined automatically.
public init(path: String, delimiter: String = ",", lineEnding: LineEnding = .Unknown) {
self.csvFile = TextFile(path: Path(path))
public init(path: String, delimiter: String = ",", lineEnding: LineEnding = .unknown, encoding: String.Encoding = .utf8) {
self.csvFile = TextFile(path: path, encoding: encoding)
self.delimiter = delimiter
self.lineEnding = lineEnding
self.encoding = encoding

delimiterQuoteDelimiter = "\(delimiter)\"\"\(delimiter)"
delimiterDelimiter = delimiter+delimiter
Expand All @@ -70,9 +67,9 @@ open class CSVImporter<T> {
/// - Parameters:
/// - url: File URL for the CSV file to import.
/// - delimiter: The delimiter used within the CSV file for separating fields. Defaults to ",".
public convenience init?(url: URL, delimiter: String = ",", lineEnding: LineEnding = .Unknown) {
public convenience init?(url: URL, delimiter: String = ",", lineEnding: LineEnding = .unknown, encoding: String.Encoding = .utf8) {
guard url.isFileURL else { return nil }
self.init(path: url.path, delimiter: delimiter, lineEnding: lineEnding)
self.init(path: url.path, delimiter: delimiter, lineEnding: lineEnding, encoding: encoding)
}

// MARK: - Instance Methods
Expand All @@ -82,7 +79,7 @@ open class CSVImporter<T> {
/// - Parameters:
/// - mapper: A closure to map the data received in a line to your data structure.
/// - Returns: `self` to enable consecutive method calls (e.g. `importer.startImportingRecords {...}.onProgress {...}`).
open func startImportingRecords(mapper closure: @escaping (_ recordValues: [String]) -> T) -> Self {
public func startImportingRecords(mapper closure: @escaping (_ recordValues: [String]) -> T) -> Self {
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
var importedRecords: [T] = []

Expand All @@ -109,7 +106,8 @@ open class CSVImporter<T> {
/// - structure: A closure for doing something with the found structure within the first line of the CSV file.
/// - recordMapper: A closure to map the dictionary data interpreted from a line to your data structure.
/// - Returns: `self` to enable consecutive method calls (e.g. `importer.startImportingRecords {...}.onProgress {...}`).
open func startImportingRecords(structure structureClosure: @escaping (_ headerValues: [String]) -> Void, recordMapper closure: @escaping (_ recordValues: [String: String]) -> T) -> Self {
public func startImportingRecords(structure structureClosure: @escaping (_ headerValues: [String]) -> Void,
recordMapper closure: @escaping (_ recordValues: [String: String]) -> T) -> Self {
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
var recordStructure: [String]?
var importedRecords: [T] = []
Expand Down Expand Up @@ -147,37 +145,35 @@ open class CSVImporter<T> {
/// - valuesInLine: The values found within a line.
/// - Returns: `true` on finish or `false` if can't read file.
func importLines(_ closure: (_ valuesInLine: [String]) -> Void) -> Bool {
if lineEnding == .Unknown {
if lineEnding == .unknown {
lineEnding = lineEndingForFile()
}
if let csvStreamReader = self.csvFile.streamReader(lineEnding.rawValue) {
for line in csvStreamReader {
autoreleasepool {
let valuesInLine = readValuesInLine(line)
closure(valuesInLine)
}
}
guard let csvStreamReader = self.csvFile.streamReader(lineEnding: lineEnding, chunkSize: chunkSize) else { return false }

return true
} else {
return false
for line in csvStreamReader {
autoreleasepool {
let valuesInLine = readValuesInLine(line)
closure(valuesInLine)
}
}

return true
}

/// Determines the line ending for the CSV file
///
/// - Returns: the lineEnding for the CSV file or default of NL.
fileprivate func lineEndingForFile() -> LineEnding {
var lineEnding: LineEnding = .NL
var lineEnding: LineEnding = .nl
if let fileHandle = self.csvFile.handleForReading {
if let data = (fileHandle.readData(ofLength: chunkSize) as NSData).mutableCopy() as? NSMutableData {
if let contents = NSString(bytesNoCopy: data.mutableBytes, length: data.length, encoding: String.Encoding.utf8.rawValue, freeWhenDone: false) {
if contents.contains(LineEnding.CRLF.rawValue) {
lineEnding = .CRLF
} else if contents.contains(LineEnding.NL.rawValue) {
lineEnding = .NL
} else if contents.contains(LineEnding.CR.rawValue) {
lineEnding = .CR
if let contents = NSString(bytesNoCopy: data.mutableBytes, length: data.length, encoding: encoding.rawValue, freeWhenDone: false) {
if contents.contains(LineEnding.crlf.rawValue) {
lineEnding = .crlf
} else if contents.contains(LineEnding.nl.rawValue) {
lineEnding = .nl
} else if contents.contains(LineEnding.cr.rawValue) {
lineEnding = .cr
}
}
}
Expand Down Expand Up @@ -248,7 +244,7 @@ open class CSVImporter<T> {
/// - Parameters:
/// - closure: The closure to be called on failure.
/// - Returns: `self` to enable consecutive method calls (e.g. `importer.startImportingRecords {...}.onProgress {...}`).
open func onFail(_ closure: @escaping () -> Void) -> Self {
public func onFail(_ closure: @escaping () -> Void) -> Self {
self.failClosure = closure
return self
}
Expand All @@ -259,7 +255,7 @@ open class CSVImporter<T> {
/// - Parameters:
/// - closure: The closure to be called on progress. Takes the current count of imported lines as argument.
/// - Returns: `self` to enable consecutive method calls (e.g. `importer.startImportingRecords {...}.onProgress {...}`).
open func onProgress(_ closure: @escaping (_ importedDataLinesCount: Int) -> Void) -> Self {
public func onProgress(_ closure: @escaping (_ importedDataLinesCount: Int) -> Void) -> Self {
self.progressClosure = closure
return self
}
Expand All @@ -268,7 +264,7 @@ open class CSVImporter<T> {
///
/// - Parameters:
/// - closure: The closure to be called on finish. Takes the array of all imported records mapped to as its argument.
open func onFinish(_ closure: @escaping (_ importedRecords: [T]) -> Void) {
public func onFinish(_ closure: @escaping (_ importedRecords: [T]) -> Void) {
self.finishClosure = closure
}

Expand All @@ -293,7 +289,6 @@ open class CSVImporter<T> {
}
}
}

}

func reportFinish(_ importedRecords: [T]) {
Expand All @@ -303,8 +298,6 @@ open class CSVImporter<T> {
}
}
}


}


Expand Down
Loading

0 comments on commit fb6ee1c

Please sign in to comment.