Skip to content

Commit

Permalink
Merge pull request #3 from xremix/develop
Browse files Browse the repository at this point in the history
v0.3.0 More Application Identifier, logic refactor
  • Loading branch information
xremix authored Jun 27, 2017
2 parents b0cae18 + f382eaa commit bf2a411
Show file tree
Hide file tree
Showing 12 changed files with 316 additions and 89 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,24 @@ print(barcode.lotNumber) // S123456
- `serialNumber`
- `amount`

Currently implementing:
- `productionDate`
- `dueDate`
- `packagingDate`
- `bestBeforeDate`
- `productVariant`
- `secondaryDataFields`
- `numberOfUnitsContained`
- `serialShippingContainerCode`
- `gtinOfContainedTradeItems`


Other properties can be extended pretty easily. **You** can contribute yourself, or open an [issue](https://github.com/xremix/SwiftGS1Barcode/issues/new).


#### Speed
Parsing 500 barcodes takes 0.258ms right now.

## Installation
### CocoaPods
You can install the library to you project using [CocoaPods](https://cocoapods.org). Add the following code to your `Podfile`:
Expand Down Expand Up @@ -67,4 +82,4 @@ https://www.gs1.at/fileadmin/user_upload/Liste_GS1_Austria_Application_Identifie
https://www.appcoda.com/cocoapods-making-guide/


![Analytics](https://ga-beacon.appspot.com/UA-40522413-9/SwiftGS1Barcode/readme?pixel)
![Analytics](https://ga-beacon.appspot.com/UA-40522413-9/SwiftGS1Barcode/readme?pixel)
2 changes: 1 addition & 1 deletion SwiftGS1Barcode.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'SwiftGS1Barcode'
s.version = '0.2.0'
s.version = '0.3.0'
s.summary = 'A GS1 Barcode Library and Parser for Swift'

s.description = <<-DESC
Expand Down
20 changes: 12 additions & 8 deletions SwiftGS1Barcode.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
64754D181F011B9700B22B62 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 64754D171F011B9700B22B62 /* README.md */; };
64AE611D1F01081800F3B9C0 /* SwiftGS1Barcode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 64AE61131F01081800F3B9C0 /* SwiftGS1Barcode.framework */; };
64AE61241F01081800F3B9C0 /* SwiftGS1Barcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 64AE61161F01081800F3B9C0 /* SwiftGS1Barcode.h */; settings = {ATTRIBUTES = (Public, ); }; };
64DD17361F014E1400F10103 /* GS1Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64DD17351F014E1400F10103 /* GS1Node.swift */; };
64DD17381F014F8700F10103 /* GS1NodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64DD17371F014F8700F10103 /* GS1NodeTests.swift */; };
64DD17361F014E1400F10103 /* GS1ApplicationIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64DD17351F014E1400F10103 /* GS1ApplicationIdentifier.swift */; };
64DD17381F014F8700F10103 /* GS1ApplicationIdentifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64DD17371F014F8700F10103 /* GS1ApplicationIdentifierTests.swift */; };
E28E99891F01ABC50093C02B /* GS1BarcodeNodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28E99881F01ABC50093C02B /* GS1BarcodeNodeTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -55,8 +56,9 @@
64AE61171F01081800F3B9C0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
64AE611C1F01081800F3B9C0 /* SwiftGS1BarcodeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftGS1BarcodeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
64AE61231F01081800F3B9C0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
64DD17351F014E1400F10103 /* GS1Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GS1Node.swift; sourceTree = "<group>"; };
64DD17371F014F8700F10103 /* GS1NodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GS1NodeTests.swift; sourceTree = "<group>"; };
64DD17351F014E1400F10103 /* GS1ApplicationIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GS1ApplicationIdentifier.swift; sourceTree = "<group>"; };
64DD17371F014F8700F10103 /* GS1ApplicationIdentifierTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GS1ApplicationIdentifierTests.swift; sourceTree = "<group>"; };
E28E99881F01ABC50093C02B /* GS1BarcodeNodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GS1BarcodeNodeTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -106,8 +108,8 @@
64754D011F010E9100B22B62 /* Barcode.swift */,
64754CFF1F010E8400B22B62 /* BarcodeParser.swift */,
64754D071F010ECA00B22B62 /* DateExtension.swift */,
64DD17351F014E1400F10103 /* GS1ApplicationIdentifier.swift */,
64754CFD1F010A1000B22B62 /* GS1Barcode.swift */,
64DD17351F014E1400F10103 /* GS1Node.swift */,
64754D031F010EA000B22B62 /* SimpleBarcode.swift */,
64754D051F010EBB00B22B62 /* StringExtension.swift */,
);
Expand All @@ -121,9 +123,10 @@
64754D0B1F01103C00B22B62 /* BarcodeParserTests.swift */,
64754D151F01107B00B22B62 /* DateTests.swift */,
64754D111F01106D00B22B62 /* GS1BarcodeTests.swift */,
64DD17371F014F8700F10103 /* GS1NodeTests.swift */,
64DD17371F014F8700F10103 /* GS1ApplicationIdentifierTests.swift */,
64754D0F1F01106500B22B62 /* SimpleBarcodeTests.swift */,
64754D131F01107500B22B62 /* StringTests.swift */,
E28E99881F01ABC50093C02B /* GS1BarcodeNodeTests.swift */,
);
path = SwiftGS1BarcodeTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -243,7 +246,7 @@
buildActionMask = 2147483647;
files = (
64754D041F010EA000B22B62 /* SimpleBarcode.swift in Sources */,
64DD17361F014E1400F10103 /* GS1Node.swift in Sources */,
64DD17361F014E1400F10103 /* GS1ApplicationIdentifier.swift in Sources */,
64754D081F010ECA00B22B62 /* DateExtension.swift in Sources */,
64754CFE1F010A1000B22B62 /* GS1Barcode.swift in Sources */,
64754D181F011B9700B22B62 /* README.md in Sources */,
Expand All @@ -260,7 +263,8 @@
64754D161F01107B00B22B62 /* DateTests.swift in Sources */,
64754D141F01107500B22B62 /* StringTests.swift in Sources */,
64754D0C1F01103C00B22B62 /* BarcodeParserTests.swift in Sources */,
64DD17381F014F8700F10103 /* GS1NodeTests.swift in Sources */,
E28E99891F01ABC50093C02B /* GS1BarcodeNodeTests.swift in Sources */,
64DD17381F014F8700F10103 /* GS1ApplicationIdentifierTests.swift in Sources */,
64754D121F01106D00B22B62 /* GS1BarcodeTests.swift in Sources */,
64754D101F01106500B22B62 /* SimpleBarcodeTests.swift in Sources */,
);
Expand Down
9 changes: 7 additions & 2 deletions SwiftGS1Barcode/BarcodeParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import UIKit

public class GS1BarcodeParser: NSObject {
static func reduce(data: String?, by node: GS1Node)->String?{
static func reduce(data: String?, by node: GS1ApplicationIdentifier)->String?{
if data == nil{
return data
}
Expand All @@ -20,9 +20,14 @@ public class GS1BarcodeParser: NSObject {
}
return data!.substring(from: length)
}
static func parseGS1Node(node: GS1Node, data: String)->GS1Node{
static func parseGS1ApplicationIdentifier(node: GS1ApplicationIdentifier, data: String)->GS1ApplicationIdentifier{
print("Parsing node with identifier \(node.identifier) of type \(String(describing: node.type?.description))")

if !data.startsWith(node.identifier){
print("Passed invalid Node with wrong node identifier")
return node
}

// Get Pure Data by removing the identifier
var nodeData = data
nodeData = nodeData.substring(from: node.identifier.length)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// GS1Node.swift
// GS1ApplicationIdentifier.swift
// SwiftGS1Barcode
//
// Created by Toni Hoffmann on 26.06.17.
Expand All @@ -17,7 +17,7 @@ enum GS1Type: String{
}
}

class GS1Node: NSObject{
class GS1ApplicationIdentifier: NSObject{
var identifier: String
var maxLength: Int
var dynamicLength: Bool = false
Expand Down
107 changes: 71 additions & 36 deletions SwiftGS1Barcode/GS1Barcode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,50 @@
import UIKit

// Struct used in the GS1 Barcode Class
struct GS1Nodes{
var gtinNode = GS1Node("01", length: 14, type: .String)
var gtinIndicatorDigitNode = GS1Node("01", length: 1, type: .Int)
var lotNumberNode = GS1Node("10", length: 20, type: .String, dynamicLength: true)
var expirationDateNode = GS1Node(dateIdentifier: "17")
var serialNumberNode = GS1Node("21", length: 20, type: .String, dynamicLength: true)
var amountNode = GS1Node("30", length: 8, type: .Int, dynamicLength: true)
}

public class GS1Barcode: NSObject, Barcode {
public var raw: String?
private var parseSuccessFull: Bool = false
var nodes = GS1Nodes()
private var lastParseSuccessfull: Bool = false

// All
var applicationIdentifiers = [
"gtinIndicatorDigit": GS1ApplicationIdentifier("01", length: 1, type: .Int),
"gtin": GS1ApplicationIdentifier("01", length: 14, type: .String),
"lotNumber": GS1ApplicationIdentifier("10", length: 20, type: .String, dynamicLength: true),
"expirationDate": GS1ApplicationIdentifier(dateIdentifier: "17"),
"serialNumber": GS1ApplicationIdentifier("21", length: 20, type: .String, dynamicLength: true),
"amount": GS1ApplicationIdentifier("30", length: 8, type: .Int, dynamicLength: true),
// Experimental Support
"serialShippingContainerCode": GS1ApplicationIdentifier("00", length: 18, type: .String),
"gtinOfContainedTradeItems": GS1ApplicationIdentifier("02", length: 14, type: .String),
"productionDate": GS1ApplicationIdentifier(dateIdentifier: "11"),
"dueDate": GS1ApplicationIdentifier(dateIdentifier: "12"),
"packagingDate": GS1ApplicationIdentifier(dateIdentifier: "13"),
"bestBeforeDate": GS1ApplicationIdentifier(dateIdentifier: "15"),
"productVariant": GS1ApplicationIdentifier("20", length: 2, type: .String),
"secondaryDataFields": GS1ApplicationIdentifier("22", length:29, type: .String, dynamicLength:true),
"numberOfUnitsContained": GS1ApplicationIdentifier("37", length:8, type: .String, dynamicLength:true),

]
// Mapping for User Friendly Usage
public var gtin: String?{ get {return nodes.gtinNode.stringValue} }
public var lotNumber: String?{ get {return nodes.lotNumberNode.stringValue} }
public var expirationDate: NSDate?{ get {return nodes.expirationDateNode.dateValue} }
public var serialNumber: String?{ get {return nodes.serialNumberNode.stringValue} }
public var amount: Int?{ get {return nodes.amountNode.intValue} }
public var gtinIndicatorDigit: Int? {get {return nodes.gtinIndicatorDigitNode.intValue}}
public var gtin: String?{ get {return applicationIdentifiers["gtin"]!.stringValue} }
public var lotNumber: String?{ get {return applicationIdentifiers["lotNumber"]!.stringValue} }
public var expirationDate: NSDate?{ get {return applicationIdentifiers["expirationDate"]!.dateValue} }
public var serialNumber: String?{ get {return applicationIdentifiers["serialNumber"]!.stringValue} }
public var amount: Int?{ get {return applicationIdentifiers["amount"]!.intValue} }
public var gtinIndicatorDigit: Int? {get {return applicationIdentifiers["gtinIndicatorDigit"]!.intValue}}

// Experimental Support
public var serialShippingContainerCode: String? {get{return applicationIdentifiers["serialShippingContainerCode"]!.stringValue}}
public var gtinOfContainedTradeItems: String? {get{return applicationIdentifiers["gtinOfContainedTradeItems"]!.stringValue}}
public var productionDate: NSDate? {get{return applicationIdentifiers["productionDate"]!.dateValue}}
public var dueDate: NSDate? {get{return applicationIdentifiers["dueDate"]!.dateValue}}
public var packagingDate: NSDate? {get{return applicationIdentifiers["packagingDate"]!.dateValue}}
public var bestBeforeDate: NSDate? {get{return applicationIdentifiers["bestBeforeDate"]!.dateValue}}
public var productVariant: String? {get{return applicationIdentifiers["productVariant"]!.stringValue}}
public var secondaryDataFields: String? {get{return applicationIdentifiers["secondaryDataFields"]!.stringValue}}
public var numberOfUnitsContained: String? {get{return applicationIdentifiers["numberOfUnitsContained"]!.stringValue}}


required override public init() {
super.init()
Expand All @@ -42,42 +65,54 @@ public class GS1Barcode: NSObject, Barcode {

// Validating if the barcode got parsed correctly
func validate() -> Bool {
return parseSuccessFull && raw != "" && raw != nil
return lastParseSuccessfull && raw != "" && raw != nil
}

func parseNode(node: inout GS1ApplicationIdentifier, data: inout String)->Bool{
if(data.startsWith(node.identifier)){
node = GS1BarcodeParser.parseGS1ApplicationIdentifier(node: node, data: data)
// Fixes issue where two nodes have the same identifier
if node.identifier == "01"{
applicationIdentifiers["gtinIndicatorDigit"] = GS1BarcodeParser.parseGS1ApplicationIdentifier(node: applicationIdentifiers["gtinIndicatorDigit"]!, data: data)
}
data = GS1BarcodeParser.reduce(data: data, by: node)!

return true
}
return false
}

func parse() ->Bool{
self.parseSuccessFull = false
self.lastParseSuccessfull = false
var data = raw

if data != nil{
while data!.characters.count > 0 {
// Removing Group Seperator from the beginning of the string
if(data!.startsWith("\u{1D}")){
data = data!.substring(from: 1)
}

if(data!.startsWith(nodes.gtinNode.identifier)){
nodes.gtinNode = GS1BarcodeParser.parseGS1Node(node: nodes.gtinNode, data: data!)
nodes.gtinIndicatorDigitNode = GS1BarcodeParser.parseGS1Node(node: nodes.gtinIndicatorDigitNode, data: data!)
data = GS1BarcodeParser.reduce(data: data, by: nodes.gtinNode)
}else if(data!.startsWith(nodes.lotNumberNode.identifier)){
nodes.lotNumberNode = GS1BarcodeParser.parseGS1Node(node: nodes.lotNumberNode, data: data!)
data = GS1BarcodeParser.reduce(data: data, by: nodes.lotNumberNode)
}else if(data!.startsWith(nodes.expirationDateNode.identifier)){
nodes.expirationDateNode = GS1BarcodeParser.parseGS1Node(node: nodes.expirationDateNode, data: data!)
data = GS1BarcodeParser.reduce(data: data, by: nodes.expirationDateNode)
}else if(data!.startsWith(nodes.serialNumberNode.identifier)){
nodes.serialNumberNode = GS1BarcodeParser.parseGS1Node(node: nodes.serialNumberNode, data: data!)
data = GS1BarcodeParser.reduce(data: data, by: nodes.serialNumberNode)
}else if(data!.startsWith(nodes.amountNode.identifier)){
nodes.amountNode = GS1BarcodeParser.parseGS1Node(node: nodes.amountNode, data: data!)
data = GS1BarcodeParser.reduce(data: data, by: nodes.amountNode)
}else{
// Checking the nodes by it's identifier and passing it to the Barcode Parser to get the value and cut the data
var foundOne = false
for nodeKey in applicationIdentifiers.keys{
// Exclude the gtinIndicatorDigit, because it get's added later for the gtin identifier
if nodeKey != "gtinIndicatorDigit"{
// If could parse node, continue and do the loop once again
if(parseNode(node: &applicationIdentifiers[nodeKey]!, data: &data!)){
foundOne = true
continue
}
}
}
// If no node was found return false and keep the lastParseSuccessfull to false
if !foundOne{
print("Do not know identifier. Canceling Parsing")
return false
}
}
}
self.parseSuccessFull = true
self.lastParseSuccessfull = true
return true
}
}
2 changes: 1 addition & 1 deletion SwiftGS1Barcode/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.2.0</string>
<string>0.3.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
1 change: 0 additions & 1 deletion SwiftGS1Barcode/StringExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ extension String{
let end = self.index(start, offsetBy: length)
let range = start..<end


return self.substring(with: range) // play
}
func substring(_ from: Int, to: Int)->String{
Expand Down
26 changes: 12 additions & 14 deletions SwiftGS1BarcodeTests/BarcodeParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,38 @@ class BarcodeParserTests: XCTestCase {
}

func testGtinPraser(){
var node = GS1Node("01", length:14, type: .String)
// var node = GS1Node(identifier: "01", type: .FixedLengthBased, fixedValue: 14)
node = GS1BarcodeParser.parseGS1Node(node: node, data: "010012349993333001")
var node = GS1ApplicationIdentifier("01", length:14, type: .String)
// var node = GS1ApplicationIdentifier(identifier: "01", type: .FixedLengthBased, fixedValue: 14)
node = GS1BarcodeParser.parseGS1ApplicationIdentifier(node: node, data: "010012349993333001")
XCTAssertEqual(node.stringValue, "00123499933330")
}
func testDatePraser(){
var node = GS1Node("17", length:6, type: .Date)
// var node = GS1Node(identifier: "01", type: .Date)
node = GS1BarcodeParser.parseGS1Node(node: node, data: "17210228")
var node = GS1ApplicationIdentifier("17", length:6, type: .Date)
// var node = GS1ApplicationIdentifier(identifier: "01", type: .Date)
node = GS1BarcodeParser.parseGS1ApplicationIdentifier(node: node, data: "17210228")
XCTAssertEqual(node.dateValue, NSDate.from(year: 2021, month: 2, day: 28)) // 17
}
func testGroupSeperatorBasedInt(){
var node = GS1Node("30", length: 99, type: .Int, dynamicLength: true)
node = GS1BarcodeParser.parseGS1Node(node: node, data: "3001\u{1D}12341234")
var node = GS1ApplicationIdentifier("30", length: 99, type: .Int, dynamicLength: true)
node = GS1BarcodeParser.parseGS1ApplicationIdentifier(node: node, data: "3001\u{1D}12341234")
XCTAssertEqual(node.originalValue, "01")
XCTAssertEqual(node.intValue, 1)
XCTAssertEqual(node.dateValue, nil)
}
func testGroupSeperatorBased(){
// TODO What? why is this 03 and not 30?
var node = GS1Node("03", length: 8, type: .String, dynamicLength: true)
node = GS1BarcodeParser.parseGS1Node(node: node, data: "3001\u{1D}12341234")
var node = GS1ApplicationIdentifier("30", length: 8, type: .String, dynamicLength: true)
node = GS1BarcodeParser.parseGS1ApplicationIdentifier(node: node, data: "3001\u{1D}12341234")
XCTAssertEqual(node.originalValue, "01")
XCTAssertEqual(node.stringValue, "01")
XCTAssertEqual(node.intValue, nil)
XCTAssertEqual(node.dateValue, nil)
}
func testGroupSeperatorBasedEndOfString(){
var node = GS1Node("03", length: 8, type: .String, dynamicLength: true)
node = GS1BarcodeParser.parseGS1Node(node: node, data: "3001")
var node = GS1ApplicationIdentifier("30", length: 8, type: .String, dynamicLength: true)
node = GS1BarcodeParser.parseGS1ApplicationIdentifier(node: node, data: "3001")
XCTAssertEqual(node.originalValue, "01")
XCTAssertEqual(node.stringValue, "01")
XCTAssertEqual(node.intValue, nil)
XCTAssertEqual(node.dateValue, nil)
}

}
Loading

0 comments on commit bf2a411

Please sign in to comment.