Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c60d292b27 | |||
| 4452349d49 | |||
| d726caa79d | |||
| 0ea2eb8900 | |||
| f8d69b3aed | |||
| 98f4ccbce8 | |||
| d57c87e69d | |||
| c3eef703ab | |||
| 538302d69c | |||
| 9c87bd3f71 | |||
| 3d7fe8e045 | |||
| f2fbd4227a | |||
| 86ee3389bb | |||
| 29f22e4c7d |
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15400" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15400"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@@ -620,7 +620,7 @@
|
||||
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleSourceList:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
|
||||
<action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||
@@ -707,21 +707,21 @@
|
||||
<objects>
|
||||
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModule="AppReceiptValidator_Demo_macOS" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" wantsLayer="YES" id="m2S-Jp-Qdl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="631" height="270"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="698" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ynK-dw-fFc">
|
||||
<rect key="frame" x="0.0" y="22" width="316" height="248"/>
|
||||
<rect key="frame" x="0.0" y="71" width="349" height="199"/>
|
||||
<clipView key="contentView" drawsBackground="NO" id="yEL-yO-YvB">
|
||||
<rect key="frame" x="1" y="1" width="314" height="246"/>
|
||||
<rect key="frame" x="1" y="1" width="347" height="197"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView toolTip="Paste Base64 here" importsGraphics="NO" richText="NO" verticallyResizable="YES" usesFontPanel="YES" findStyle="panel" allowsCharacterPickerTouchBarItem="NO" allowsUndo="YES" usesRuler="YES" allowsNonContiguousLayout="YES" textCompletion="NO" id="0xW-mT-lME" customClass="DropAcceptingTextView" customModule="AppReceiptValidator_Demo_macOS" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="314" height="246"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="347" height="197"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<size key="minSize" width="314" height="246"/>
|
||||
<size key="minSize" width="347" height="197"/>
|
||||
<size key="maxSize" width="629" height="10000000"/>
|
||||
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
</textView>
|
||||
@@ -732,22 +732,22 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="hkR-e4-WtN">
|
||||
<rect key="frame" x="299" y="1" width="16" height="246"/>
|
||||
<rect key="frame" x="332" y="1" width="16" height="197"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<scrollView toolTip="Parsed Receipt will be shown here" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6mt-ay-mAL">
|
||||
<rect key="frame" x="316" y="0.0" width="315" height="270"/>
|
||||
<rect key="frame" x="349" y="0.0" width="349" height="270"/>
|
||||
<clipView key="contentView" drawsBackground="NO" id="YwZ-F9-Cvh">
|
||||
<rect key="frame" x="1" y="1" width="313" height="268"/>
|
||||
<rect key="frame" x="1" y="1" width="347" height="268"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" usesFontPanel="YES" findStyle="panel" allowsCharacterPickerTouchBarItem="NO" allowsUndo="YES" usesRuler="YES" allowsNonContiguousLayout="YES" textCompletion="NO" id="GHT-gS-G1g" customClass="TextView" customModule="AppReceiptValidator_Demo_macOS" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="313" height="268"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="347" height="268"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<size key="minSize" width="313" height="268"/>
|
||||
<size key="minSize" width="347" height="268"/>
|
||||
<size key="maxSize" width="463" height="10000000"/>
|
||||
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
</textView>
|
||||
@@ -758,46 +758,112 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="uk9-Sg-RUp">
|
||||
<rect key="frame" x="298" y="1" width="16" height="268"/>
|
||||
<rect key="frame" x="332" y="1" width="16" height="268"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yvX-By-nNw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="316" height="22"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Optional DeviceIdentifier for Validation (MAC Address, UUID, Base64)" drawsBackground="YES" id="Z2r-sB-vIS">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<action selector="identifierDidChange:" target="XfG-lQ-9wD" id="ccM-mH-NPQ"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="0.0" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X6E-h2-FTp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349" height="71"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yvX-By-nNw">
|
||||
<rect key="frame" x="0.0" y="50" width="316" height="21"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Optional DeviceIdentifier for Validation (MAC Address, UUID, Base64)" drawsBackground="YES" id="Z2r-sB-vIS">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<action selector="identifierDidChange:" target="XfG-lQ-9wD" id="ccM-mH-NPQ"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<customView verticalHuggingPriority="251" verticalCompressionResistancePriority="749" translatesAutoresizingMaskIntoConstraints="NO" id="MIY-2Q-BBh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="276" height="50"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="top" spacing="14" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Fk-0l-MkU">
|
||||
<rect key="frame" x="5" y="2" width="264" height="48"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="252" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3HO-XF-hNE">
|
||||
<rect key="frame" x="-2" y="32" width="122" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Local MAC Address" id="cII-wV-fEg">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MYU-Qf-vgG">
|
||||
<rect key="frame" x="130" y="32" width="37" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" title="Label" id="toH-92-xds">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7M8-eM-sps">
|
||||
<rect key="frame" x="173" y="20" width="97" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Reload" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nhJ-Nu-6Mt">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="determineDeviceIdentifier:" target="XfG-lQ-9wD" id="0Iu-Ou-tt2"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="3Fk-0l-MkU" firstAttribute="top" secondItem="MIY-2Q-BBh" secondAttribute="top" id="G9h-eg-R7c"/>
|
||||
<constraint firstItem="3Fk-0l-MkU" firstAttribute="leading" secondItem="MIY-2Q-BBh" secondAttribute="leading" constant="5" id="XhE-X8-Yll"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3Fk-0l-MkU" secondAttribute="bottom" constant="2" id="eS8-I6-F8I"/>
|
||||
<constraint firstAttribute="trailing" secondItem="3Fk-0l-MkU" secondAttribute="trailing" constant="7" id="u5d-kZ-Hiq"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="6mt-ay-mAL" firstAttribute="leading" secondItem="yvX-By-nNw" secondAttribute="trailing" id="231-cE-39Q"/>
|
||||
<constraint firstItem="6mt-ay-mAL" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" id="CdY-lv-wee"/>
|
||||
<constraint firstItem="X6E-h2-FTp" firstAttribute="top" secondItem="ynK-dw-fFc" secondAttribute="bottom" id="0st-m4-dJZ"/>
|
||||
<constraint firstItem="ynK-dw-fFc" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" id="1Pf-3A-rBH"/>
|
||||
<constraint firstAttribute="bottom" secondItem="X6E-h2-FTp" secondAttribute="bottom" id="1kz-OO-S1i"/>
|
||||
<constraint firstItem="6mt-ay-mAL" firstAttribute="leading" secondItem="X6E-h2-FTp" secondAttribute="trailing" id="9Vb-5X-3ef"/>
|
||||
<constraint firstItem="ynK-dw-fFc" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" id="Cvz-FD-lzg"/>
|
||||
<constraint firstItem="yvX-By-nNw" firstAttribute="top" secondItem="ynK-dw-fFc" secondAttribute="bottom" id="KSy-yc-TCD"/>
|
||||
<constraint firstItem="ynK-dw-fFc" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" id="Rno-M6-Pjq"/>
|
||||
<constraint firstItem="6mt-ay-mAL" firstAttribute="width" secondItem="m2S-Jp-Qdl" secondAttribute="width" multiplier="1:2" id="SWu-Yn-pVc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="yvX-By-nNw" secondAttribute="bottom" id="WOQ-2e-jWj"/>
|
||||
<constraint firstAttribute="trailing" secondItem="6mt-ay-mAL" secondAttribute="trailing" id="Yun-Rd-wsm"/>
|
||||
<constraint firstItem="6mt-ay-mAL" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" id="clC-xc-JDh"/>
|
||||
<constraint firstAttribute="bottom" secondItem="6mt-ay-mAL" secondAttribute="bottom" id="lKc-RV-ybx"/>
|
||||
<constraint firstItem="yvX-By-nNw" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" id="s2r-QB-ZQb"/>
|
||||
<constraint firstItem="ynK-dw-fFc" firstAttribute="width" secondItem="m2S-Jp-Qdl" secondAttribute="width" multiplier="1:2" id="wsI-iB-DvJ"/>
|
||||
<constraint firstItem="X6E-h2-FTp" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" id="ysr-U6-n9c"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="dropReceivingView" destination="0xW-mT-lME" id="cuR-H1-BKN"/>
|
||||
<outlet property="identifierTextField" destination="yvX-By-nNw" id="pW5-lJ-8Cy"/>
|
||||
<outlet property="inputTextView" destination="0xW-mT-lME" id="8Y7-yb-63r"/>
|
||||
<outlet property="localDeviceIdentifierLabel" destination="MYU-Qf-vgG" id="cqz-uf-16l"/>
|
||||
<outlet property="outputTextView" destination="GHT-gS-G1g" id="cVN-4m-knJ"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75.5" y="726"/>
|
||||
<point key="canvasLocation" x="75" y="726"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
||||
@@ -18,6 +18,7 @@ class ViewController: NSViewController, NSTextViewDelegate, NSTextFieldDelegate
|
||||
@IBOutlet private var identifierTextField: NSTextField!
|
||||
@IBOutlet private var outputTextView: NSTextView!
|
||||
@IBOutlet private var dropReceivingView: DropAcceptingTextView!
|
||||
@IBOutlet private var localDeviceIdentifierLabel: NSTextField!
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
@@ -34,6 +35,7 @@ class ViewController: NSViewController, NSTextViewDelegate, NSTextFieldDelegate
|
||||
|
||||
self.identifierDidChange(self.identifierTextField)
|
||||
}
|
||||
self.renderLocalDeviceIdentifierText()
|
||||
}
|
||||
|
||||
// MARK: - NSTextViewDelegate
|
||||
@@ -55,6 +57,10 @@ class ViewController: NSViewController, NSTextViewDelegate, NSTextFieldDelegate
|
||||
func paste(_ sender: Any) {
|
||||
self.inputTextView.paste(sender)
|
||||
}
|
||||
|
||||
@IBAction func determineDeviceIdentifier(_ sender: Any) {
|
||||
self.renderLocalDeviceIdentifierText()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -113,9 +119,21 @@ private extension ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func localDeviceIdentifierString() -> String {
|
||||
guard let device = AppReceiptValidator.Parameters.DeviceIdentifier.getPrimaryNetworkMACAddress() else { return "DeviceIdentifier could not be determined" }
|
||||
|
||||
return "\(device.addressString) (HEX), \(device.data.base64EncodedString()) (B64)"
|
||||
}
|
||||
|
||||
func render(string: String) {
|
||||
self.outputTextView.string = string
|
||||
}
|
||||
|
||||
func renderLocalDeviceIdentifierText() {
|
||||
NSLog("Local MAC Address: " + localDeviceIdentifierString())
|
||||
self.localDeviceIdentifierLabel.attributedStringValue =
|
||||
NSAttributedString(string: localDeviceIdentifierString())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TextView
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// ReceiptDateFormatterTests.swift
|
||||
// AppReceiptValidator
|
||||
//
|
||||
// Created by Hannes Oud on 09.10.20.
|
||||
// Copyright © 2020 IdeasOnCanvas GmbH. All rights reserved.
|
||||
//
|
||||
|
||||
import AppReceiptValidator
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
|
||||
final class ReceiptDateFormatterTests: XCTestCase {
|
||||
|
||||
func testDateFormatting() throws {
|
||||
let dateStrings = [
|
||||
"2020-01-01T12:00:00Z",
|
||||
"2020-01-01T12:00:00.123Z",
|
||||
"2020-01-01T12:00:00.999Z",
|
||||
"2020-01-01T12:00:01Z"
|
||||
]
|
||||
for dateString in dateStrings {
|
||||
let parsed = try XCTUnwrap(AppReceiptValidator.ReceiptDateFormatter.date(from: dateString))
|
||||
XCTAssertEqual(AppReceiptValidator.ReceiptDateFormatter.string(from: parsed), dateString)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
D114544621A6BDE6001BEC61 /* DeviceIdentifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D114544521A6BDE6001BEC61 /* DeviceIdentifierTests.swift */; };
|
||||
D114544721A6BDE6001BEC61 /* DeviceIdentifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D114544521A6BDE6001BEC61 /* DeviceIdentifierTests.swift */; };
|
||||
D11B81CF2530687D00E19863 /* ReceiptDateFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11B81CE2530687D00E19863 /* ReceiptDateFormatterTests.swift */; };
|
||||
D11B81D02530687D00E19863 /* ReceiptDateFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11B81CE2530687D00E19863 /* ReceiptDateFormatterTests.swift */; };
|
||||
D1239FFF1F6A7B5000D0421E /* AppleIncRootCertificate.cer in Resources */ = {isa = PBXBuildFile; fileRef = D19095C41F601DEA0095729B /* AppleIncRootCertificate.cer */; };
|
||||
D123A0001F6A7CCF00D0421E /* AppleIncRootCertificate.cer in Resources */ = {isa = PBXBuildFile; fileRef = D19095C41F601DEA0095729B /* AppleIncRootCertificate.cer */; };
|
||||
D13E5B7D20331B9B001880F0 /* DropAcceptingTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D13E5B7C20331B9B001880F0 /* DropAcceptingTextView.swift */; };
|
||||
@@ -278,6 +280,7 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
D114544521A6BDE6001BEC61 /* DeviceIdentifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceIdentifierTests.swift; sourceTree = "<group>"; };
|
||||
D11B81CE2530687D00E19863 /* ReceiptDateFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptDateFormatterTests.swift; sourceTree = "<group>"; };
|
||||
D13E5B7C20331B9B001880F0 /* DropAcceptingTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAcceptingTextView.swift; sourceTree = "<group>"; };
|
||||
D14FA72E1F6143C400545540 /* Date+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Convenience.swift"; sourceTree = "<group>"; };
|
||||
D14FA7311F61472400545540 /* mac_mindnode_rebought_receipt */ = {isa = PBXFileReference; lastKnownFileType = file; path = mac_mindnode_rebought_receipt; sourceTree = "<group>"; };
|
||||
@@ -606,6 +609,7 @@
|
||||
D1D6F5411F5D8A3800E86FE1 /* AppReceiptValidationTests.swift */,
|
||||
D1AA845A1F6ABB31007F2558 /* AppReceiptPropertyValidationTests.swift */,
|
||||
D150A0ED1F669A880026ED04 /* AppReceiptValidationInAppPurchaseTests.swift */,
|
||||
D11B81CE2530687D00E19863 /* ReceiptDateFormatterTests.swift */,
|
||||
D114544521A6BDE6001BEC61 /* DeviceIdentifierTests.swift */,
|
||||
D1D6F5481F5D9B1100E86FE1 /* Tools */,
|
||||
D1D6F5431F5D8DBC00E86FE1 /* Test Assets */,
|
||||
@@ -1208,7 +1212,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0900;
|
||||
LastUpgradeCheck = 0930;
|
||||
LastUpgradeCheck = 1130;
|
||||
ORGANIZATIONNAME = "IdeasOnCanvas GmbH";
|
||||
TargetAttributes = {
|
||||
D19095801F6000A40095729B = {
|
||||
@@ -1403,6 +1407,7 @@
|
||||
D19095CD1F601E960095729B /* AppReceiptValidationTests.swift in Sources */,
|
||||
D1AA845D1F6ABB59007F2558 /* AppReceiptPropertyValidationTests.swift in Sources */,
|
||||
D150A0EF1F669A880026ED04 /* AppReceiptValidationInAppPurchaseTests.swift in Sources */,
|
||||
D11B81D02530687D00E19863 /* ReceiptDateFormatterTests.swift in Sources */,
|
||||
D114544721A6BDE6001BEC61 /* DeviceIdentifierTests.swift in Sources */,
|
||||
D150A0F01F67E0990026ED04 /* Date+Convenience.swift in Sources */,
|
||||
);
|
||||
@@ -1416,6 +1421,7 @@
|
||||
D19095CE1F601E980095729B /* AppReceiptValidationTests.swift in Sources */,
|
||||
D1AA845C1F6ABB59007F2558 /* AppReceiptPropertyValidationTests.swift in Sources */,
|
||||
D150A0EE1F669A880026ED04 /* AppReceiptValidationInAppPurchaseTests.swift in Sources */,
|
||||
D11B81CF2530687D00E19863 /* ReceiptDateFormatterTests.swift in Sources */,
|
||||
D114544621A6BDE6001BEC61 /* DeviceIdentifierTests.swift in Sources */,
|
||||
D150A0F11F67E0990026ED04 /* Date+Convenience.swift in Sources */,
|
||||
);
|
||||
@@ -1519,7 +1525,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
@@ -1537,7 +1543,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
||||
+10
-14
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
LastUpgradeVersion = "1130"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -41,6 +41,15 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D1D6F4E31F5D691400E86FE1"
|
||||
BuildableName = "AppReceiptValidator Demo iOS.app"
|
||||
BlueprintName = "AppReceiptValidator Demo iOS"
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
@@ -55,17 +64,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D1D6F4E31F5D691400E86FE1"
|
||||
BuildableName = "AppReceiptValidator Demo iOS.app"
|
||||
BlueprintName = "AppReceiptValidator Demo iOS"
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -87,8 +85,6 @@
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
+10
-14
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
LastUpgradeVersion = "1130"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -41,6 +41,15 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D19095801F6000A40095729B"
|
||||
BuildableName = "AppReceiptValidator Demo macOS.app"
|
||||
BlueprintName = "AppReceiptValidator Demo macOS"
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
@@ -55,17 +64,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D19095801F6000A40095729B"
|
||||
BuildableName = "AppReceiptValidator Demo macOS.app"
|
||||
BlueprintName = "AppReceiptValidator Demo macOS"
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -87,8 +85,6 @@
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
+10
-14
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
LastUpgradeVersion = "1130"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,6 +27,15 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D1D6F4B41F5D684C00E86FE1"
|
||||
BuildableName = "AppReceiptValidator.framework"
|
||||
BlueprintName = "AppReceiptValidator iOS"
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
@@ -41,17 +50,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D1D6F4B41F5D684C00E86FE1"
|
||||
BuildableName = "AppReceiptValidator.framework"
|
||||
BlueprintName = "AppReceiptValidator iOS"
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -72,8 +70,6 @@
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
+10
-14
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
LastUpgradeVersion = "1130"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -27,6 +27,15 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D1D6F4C11F5D687400E86FE1"
|
||||
BuildableName = "AppReceiptValidator.framework"
|
||||
BlueprintName = "AppReceiptValidator macOS"
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
@@ -41,17 +50,6 @@
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D1D6F4C11F5D687400E86FE1"
|
||||
BuildableName = "AppReceiptValidator.framework"
|
||||
BlueprintName = "AppReceiptValidator macOS"
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -72,8 +70,6 @@
|
||||
ReferencedContainer = "container:AppReceiptValidator.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
||||
@@ -88,16 +88,6 @@ public struct AppReceiptValidator {
|
||||
let receiptContainer = try self.extractPKCS7Container(data: receiptData)
|
||||
return try parseReceipt(pkcs7: receiptContainer, parseUnofficialParts: true)
|
||||
}
|
||||
|
||||
/// Uses receipt-conform representation of dates like "2017-01-01T12:00:00Z"
|
||||
public static let asn1DateFormatter: DateFormatter = {
|
||||
// Date formatter code from https://www.objc.io/issues/17-security/receipt-validation/#parsing-the-receipt
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
|
||||
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
return dateFormatter
|
||||
}()
|
||||
}
|
||||
|
||||
// MARK: - Full Validation
|
||||
@@ -117,16 +107,13 @@ private extension AppReceiptValidator {
|
||||
var sha1Context = SHA_CTX()
|
||||
|
||||
SHA1_Init(&sha1Context)
|
||||
deviceIdentifierData.withUnsafeBytes { poi -> Void in
|
||||
print(poi)
|
||||
}
|
||||
_ = deviceIdentifierData.withUnsafeBytes { pointer -> Void in
|
||||
deviceIdentifierData.withUnsafeBytes { pointer -> Void in
|
||||
SHA1_Update(&sha1Context, pointer.baseAddress, deviceIdentifierData.count)
|
||||
}
|
||||
_ = receiptOpaqueValueData.withUnsafeBytes { pointer -> Void in
|
||||
receiptOpaqueValueData.withUnsafeBytes { pointer -> Void in
|
||||
SHA1_Update(&sha1Context, pointer.baseAddress, receiptOpaqueValueData.count)
|
||||
}
|
||||
_ = receiptBundleIdData.withUnsafeBytes { pointer -> Void in
|
||||
receiptBundleIdData.withUnsafeBytes { pointer -> Void in
|
||||
SHA1_Update(&sha1Context, pointer.baseAddress, receiptBundleIdData.count)
|
||||
}
|
||||
SHA1_Final(&computedHash, &sha1Context)
|
||||
@@ -354,6 +341,59 @@ private extension AppReceiptValidator {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ReceiptDateFormatter
|
||||
|
||||
extension AppReceiptValidator {
|
||||
|
||||
/// Static formatting methods to use for string encoded date values in receipts
|
||||
public enum ReceiptDateFormatter {
|
||||
|
||||
/// Uses receipt-conform representation of dates like "2017-01-01T12:00:00Z",
|
||||
/// as a fallback, dates like "2017-01-01T12:00:00.123Z" are also parsed.
|
||||
public static func date(from string: String) -> Date? {
|
||||
return self.asn1DateFormatter.date(from: string) // expected
|
||||
?? self.fallbackDateFormatterWithMS.date(from: string) // try again with milliseconds
|
||||
}
|
||||
|
||||
/// Returns receipt-conform string representation of dates like "2017-01-01T12:00:00Z",
|
||||
/// but if the date has sub-second fractions a millisecond representation like "2017-01-01T12:00:00.123Z" is returned.
|
||||
public static func string(from date: Date) -> String {
|
||||
if floor(date.timeIntervalSince1970) == date.timeIntervalSince1970 {
|
||||
// Integer seconds granularity is what we expect
|
||||
return self.asn1DateFormatter.string(from: date)
|
||||
} else {
|
||||
// millis seconds granularity is what we expect
|
||||
return self.fallbackDateFormatterWithMS.string(from: date)
|
||||
}
|
||||
}
|
||||
|
||||
/// Uses receipt-conform representation of dates like "2017-01-01T12:00:00Z"
|
||||
static let asn1DateFormatter: DateFormatter = {
|
||||
// Date formatter code from https://www.objc.io/issues/17-security/receipt-validation/#parsing-the-receipt
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
|
||||
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
/// Uses receipt-conform representation of dates like "2017-01-01T12:00:00.123Z"
|
||||
///
|
||||
/// This is not the officially intended format, but added after hearing reports about new format adding ms https://twitter.com/depth42/status/1314179654811607041
|
||||
private static let fallbackDateFormatterWithMS: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'"
|
||||
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
return dateFormatter
|
||||
}()
|
||||
}
|
||||
|
||||
/// Uses receipt-conform representation of dates like "2017-01-01T12:00:00Z"
|
||||
@available(*, deprecated, message: "Use AppReceiptValidator.ReceiptDateFormatter.string(from:) or AppReceiptValidator.ReceiptDateFormatter.date(from:) instead, to cover unexpected date formats")
|
||||
public static let asn1DateFormatter: DateFormatter = ReceiptDateFormatter.asn1DateFormatter
|
||||
}
|
||||
|
||||
// MARK: - Result
|
||||
|
||||
extension AppReceiptValidator {
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ extension AppReceiptValidator.Parameters.DeviceIdentifier {
|
||||
/// Original implementation https://gist.github.com/mminer/82975d3781e2f42fc644d7fbfbf4f905
|
||||
///
|
||||
/// - Returns: The MAC Address as Data and String representation
|
||||
static func getPrimaryNetworkMACAddress() -> (data: Data, addressString: String)? {
|
||||
public static func getPrimaryNetworkMACAddress() -> (data: Data, addressString: String)? {
|
||||
let matching = IOServiceMatching("IOEthernetInterface") as NSMutableDictionary
|
||||
matching[kIOPropertyMatchKey] = ["IOPrimaryInterface": true]
|
||||
var servicesIterator: io_iterator_t = 0
|
||||
|
||||
@@ -153,7 +153,7 @@ extension ASN1Object {
|
||||
var dateValue: Date? {
|
||||
guard let string = self.stringValue else { return nil }
|
||||
|
||||
return AppReceiptValidator.asn1DateFormatter.date(from: string)
|
||||
return AppReceiptValidator.ReceiptDateFormatter.date(from: string)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ private struct StringFormatter {
|
||||
func format(_ date: Date?) -> String {
|
||||
guard let date = date else { return fallback }
|
||||
|
||||
return quoted(AppReceiptValidator.asn1DateFormatter.string(from: date))
|
||||
return quoted(AppReceiptValidator.ReceiptDateFormatter.string(from: date))
|
||||
}
|
||||
|
||||
func format(_ string: String?) -> String {
|
||||
@@ -318,7 +318,7 @@ private func parseBase64(string: String) -> Data? {
|
||||
|
||||
// Parses a string of type "2017-01-01T12:00:00Z"
|
||||
private func parseDate(string: String) -> Date? {
|
||||
guard let date = AppReceiptValidator.asn1DateFormatter.date(from: string) else {
|
||||
guard let date = AppReceiptValidator.ReceiptDateFormatter.date(from: string) else {
|
||||
assertionFailure("Date could not be parsed from string '\(string)', make sure it has a correct format, example `2017-01-01T12:00:00Z`")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public enum KnownUnofficialReceiptAttribute: Int32 {
|
||||
var parsingType: ParsingType {
|
||||
switch self {
|
||||
case .date1, .date2, .date3:
|
||||
return .string
|
||||
return .date
|
||||
case .provisioningType, .ageRating, .clientName:
|
||||
return .string
|
||||
}
|
||||
@@ -115,7 +115,7 @@ extension UnofficialReceipt.Entry.Value: CustomStringConvertible {
|
||||
case .string(let value):
|
||||
return "\"\(value)\""
|
||||
case .date(let date):
|
||||
return AppReceiptValidator.asn1DateFormatter.string(from: date)
|
||||
return AppReceiptValidator.ReceiptDateFormatter.string(from: date)
|
||||
case .bytes(let bytes):
|
||||
if bytes.count == 2 && bytes.first == 12 && bytes.dropFirst().first == 0 {
|
||||
return "2 bytes (12, 0)"
|
||||
|
||||
Reference in New Issue
Block a user