mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 09:41:00 +00:00
Modularize base16 encoding
This commit is contained in:
parent
4bcfde6bfb
commit
a689463907
15 changed files with 215 additions and 33 deletions
5
Base16/.gitignore
vendored
Normal file
5
Base16/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
22
Base16/Package.swift
Normal file
22
Base16/Package.swift
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// swift-tools-version:5.3
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "Base16",
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "Base16",
|
||||||
|
targets: ["Base16"])
|
||||||
|
],
|
||||||
|
dependencies: [],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "Base16",
|
||||||
|
dependencies: []),
|
||||||
|
.testTarget(
|
||||||
|
name: "Base16Tests",
|
||||||
|
dependencies: ["Base16"])
|
||||||
|
]
|
||||||
|
)
|
68
Base16/Sources/Base16/Data+Base16.swift
Normal file
68
Base16/Sources/Base16/Data+Base16.swift
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum Base16EncodingError: Error {
|
||||||
|
case invalidLength
|
||||||
|
case invalidByteString(String)
|
||||||
|
case invalidStringEncoding
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Data {
|
||||||
|
enum Base16EncodingOptions {
|
||||||
|
case uppercase
|
||||||
|
}
|
||||||
|
|
||||||
|
func base16EncodedString(options: [Base16EncodingOptions] = []) -> String {
|
||||||
|
map { String(format: Self.format(options: options), $0) }.joined()
|
||||||
|
}
|
||||||
|
|
||||||
|
func base16EncodedData(options: [Base16EncodingOptions] = []) -> Data {
|
||||||
|
Data(base16EncodedString(options: options).utf8)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(base16Encoded string: String) throws {
|
||||||
|
let stringLength = string.count
|
||||||
|
|
||||||
|
guard stringLength % 2 == 0 else {
|
||||||
|
throw Base16EncodingError.invalidLength
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = [UInt8]()
|
||||||
|
|
||||||
|
data.reserveCapacity(stringLength / 2)
|
||||||
|
|
||||||
|
var i = string.startIndex
|
||||||
|
|
||||||
|
while i != string.endIndex {
|
||||||
|
let j = string.index(i, offsetBy: 2)
|
||||||
|
let byteString = string[i..<j]
|
||||||
|
|
||||||
|
guard let byte = UInt8(byteString, radix: 16) else {
|
||||||
|
throw Base16EncodingError.invalidByteString(String(byteString))
|
||||||
|
}
|
||||||
|
|
||||||
|
data.append(byte)
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
|
||||||
|
self = Data(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(base16Encoded data: Data) throws {
|
||||||
|
guard let string = String(data: data, encoding: .utf8) else {
|
||||||
|
throw Base16EncodingError.invalidStringEncoding
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.init(base16Encoded: string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension Data {
|
||||||
|
static let lowercaseBase16Format = "%02.2hhx"
|
||||||
|
static let uppercaseBase16Format = "%02.2hhX"
|
||||||
|
|
||||||
|
static func format(options: [Base16EncodingOptions]) -> String {
|
||||||
|
options.contains(.uppercase) ? uppercaseBase16Format : lowercaseBase16Format
|
||||||
|
}
|
||||||
|
}
|
101
Base16/Tests/Base16Tests/Base16Tests.swift
Normal file
101
Base16/Tests/Base16Tests/Base16Tests.swift
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
@testable import Base16
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class Base16Tests: XCTestCase {
|
||||||
|
let testData = Data([182, 239, 215, 173, 251, 168, 76, 252,
|
||||||
|
140, 7, 39, 163, 56, 255, 171, 35,
|
||||||
|
121, 205, 26, 252, 53, 166, 159, 67,
|
||||||
|
100, 70, 140, 79, 47, 26, 138, 209])
|
||||||
|
let testDataLowercaseString = "b6efd7adfba84cfc8c0727a338ffab2379cd1afc35a69f4364468c4f2f1a8ad1"
|
||||||
|
let testDataLowercaseStringData = Data([98, 54, 101, 102, 100, 55, 97, 100,
|
||||||
|
102, 98, 97, 56, 52, 99, 102, 99,
|
||||||
|
56, 99, 48, 55, 50, 55, 97, 51, 51,
|
||||||
|
56, 102, 102, 97, 98, 50, 51, 55,
|
||||||
|
57, 99, 100, 49, 97, 102, 99, 51,
|
||||||
|
53, 97, 54, 57, 102, 52, 51, 54,
|
||||||
|
52, 52, 54, 56, 99, 52, 102, 50,
|
||||||
|
102, 49, 97, 56, 97, 100, 49])
|
||||||
|
let testDataUppercaseString = "B6EFD7ADFBA84CFC8C0727A338FFAB2379CD1AFC35A69F4364468C4F2F1A8AD1"
|
||||||
|
let testDataUppercaseStringData = Data([66, 54, 69, 70, 68, 55, 65, 68,
|
||||||
|
70, 66, 65, 56, 52, 67, 70, 67,
|
||||||
|
56, 67, 48, 55, 50, 55, 65, 51,
|
||||||
|
51, 56, 70, 70, 65, 66, 50, 51,
|
||||||
|
55, 57, 67, 68, 49, 65, 70, 67,
|
||||||
|
51, 53, 65, 54, 57, 70, 52, 51,
|
||||||
|
54, 52, 52, 54, 56, 67, 52, 70,
|
||||||
|
50, 70, 49, 65, 56, 65, 68, 49])
|
||||||
|
|
||||||
|
func testLowercaseString() {
|
||||||
|
XCTAssertEqual(testData.base16EncodedString(), testDataLowercaseString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUppercaseString() {
|
||||||
|
XCTAssertEqual(testData.base16EncodedString(options: [.uppercase]),
|
||||||
|
testDataUppercaseString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLowercaseData() {
|
||||||
|
XCTAssertEqual(testData.base16EncodedData(), testDataLowercaseStringData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUppercaseData() {
|
||||||
|
XCTAssertEqual(testData.base16EncodedData(options: [.uppercase]),
|
||||||
|
testDataUppercaseStringData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInitializationFromLowercaseString() throws {
|
||||||
|
XCTAssertEqual(try Data(base16Encoded: testDataLowercaseString), testData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInitializationFromUppercaseString() throws {
|
||||||
|
XCTAssertEqual(try Data(base16Encoded: testDataUppercaseString), testData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInitializationFromLowercaseData() throws {
|
||||||
|
XCTAssertEqual(try Data(base16Encoded: testDataLowercaseStringData), testData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInitializationFromUppercaseData() throws {
|
||||||
|
XCTAssertEqual(try Data(base16Encoded: testDataUppercaseStringData), testData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInvalidLength() throws {
|
||||||
|
let invalidLength = String(testDataLowercaseString.prefix(testDataLowercaseString.count - 1))
|
||||||
|
|
||||||
|
XCTAssertThrowsError(try Data(base16Encoded: invalidLength)) {
|
||||||
|
guard case Base16EncodingError.invalidLength = $0 else {
|
||||||
|
XCTFail("Expected invalid length error")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInvalidByteString() {
|
||||||
|
let invalidString = testDataLowercaseString.replacingOccurrences(of: "a", with: "z")
|
||||||
|
|
||||||
|
XCTAssertThrowsError(try Data(base16Encoded: invalidString)) {
|
||||||
|
guard case let Base16EncodingError.invalidByteString(invalidByteString) = $0 else {
|
||||||
|
XCTFail("Expected invalid byte string error")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(invalidByteString, "zd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInvalidStringEncoding() {
|
||||||
|
let invalidData = testDataLowercaseString.data(using: .utf16)!
|
||||||
|
|
||||||
|
XCTAssertThrowsError(try Data(base16Encoded: invalidData)) {
|
||||||
|
guard case Base16EncodingError.invalidStringEncoding = $0 else {
|
||||||
|
XCTFail("Expected string encoding error")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ public struct Identity: Codable, Hashable, Identifiable {
|
||||||
public let preferences: Identity.Preferences
|
public let preferences: Identity.Preferences
|
||||||
public let instance: Identity.Instance?
|
public let instance: Identity.Instance?
|
||||||
public let account: Identity.Account?
|
public let account: Identity.Account?
|
||||||
public let lastRegisteredDeviceToken: String?
|
public let lastRegisteredDeviceToken: Data?
|
||||||
public let pushSubscriptionAlerts: PushSubscription.Alerts
|
public let pushSubscriptionAlerts: PushSubscription.Alerts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ public extension IdentityDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePushSubscription(alerts: PushSubscription.Alerts,
|
func updatePushSubscription(alerts: PushSubscription.Alerts,
|
||||||
deviceToken: String? = nil,
|
deviceToken: Data? = nil,
|
||||||
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||||
databaseQueue.writePublisher {
|
databaseQueue.writePublisher {
|
||||||
let data = try IdentityRecord.databaseJSONEncoder(for: "pushSubscriptionAlerts").encode(alerts)
|
let data = try IdentityRecord.databaseJSONEncoder(for: "pushSubscriptionAlerts").encode(alerts)
|
||||||
|
@ -180,7 +180,7 @@ public extension IdentityDatabase {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func identitiesWithOutdatedDeviceTokens(deviceToken: String) -> AnyPublisher<[Identity], Error> {
|
func identitiesWithOutdatedDeviceTokens(deviceToken: Data) -> AnyPublisher<[Identity], Error> {
|
||||||
databaseQueue.readPublisher(
|
databaseQueue.readPublisher(
|
||||||
value: Self.identitiesRequest()
|
value: Self.identitiesRequest()
|
||||||
.filter(Column("lastRegisteredDeviceToken") != deviceToken)
|
.filter(Column("lastRegisteredDeviceToken") != deviceToken)
|
||||||
|
@ -221,7 +221,7 @@ private extension IdentityDatabase {
|
||||||
.references("instance", column: "uri")
|
.references("instance", column: "uri")
|
||||||
t.column("preferences", .blob).notNull()
|
t.column("preferences", .blob).notNull()
|
||||||
t.column("pushSubscriptionAlerts", .blob).notNull()
|
t.column("pushSubscriptionAlerts", .blob).notNull()
|
||||||
t.column("lastRegisteredDeviceToken", .text)
|
t.column("lastRegisteredDeviceToken", .blob)
|
||||||
}
|
}
|
||||||
|
|
||||||
try db.create(table: "account", ifNotExists: true) { t in
|
try db.create(table: "account", ifNotExists: true) { t in
|
||||||
|
|
|
@ -10,7 +10,7 @@ struct IdentityRecord: Codable, Hashable, FetchableRecord, PersistableRecord {
|
||||||
let lastUsedAt: Date
|
let lastUsedAt: Date
|
||||||
let preferences: Identity.Preferences
|
let preferences: Identity.Preferences
|
||||||
let instanceURI: String?
|
let instanceURI: String?
|
||||||
let lastRegisteredDeviceToken: String?
|
let lastRegisteredDeviceToken: Data?
|
||||||
let pushSubscriptionAlerts: PushSubscription.Alerts
|
let pushSubscriptionAlerts: PushSubscription.Alerts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension Data {
|
|
||||||
func hexEncodedString() -> String {
|
|
||||||
map { String(format: "%02hhx", $0) }.joined()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -41,7 +41,6 @@
|
||||||
D0C7D4D724F7616A001EBDBB /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */; };
|
D0C7D4D724F7616A001EBDBB /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */; };
|
||||||
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */; };
|
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */; };
|
||||||
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46F24F76169001EBDBB /* View+Extensions.swift */; };
|
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46F24F76169001EBDBB /* View+Extensions.swift */; };
|
||||||
D0C7D4DC24F7616A001EBDBB /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D47124F76169001EBDBB /* Data+Extensions.swift */; };
|
|
||||||
D0E2C1D124FD97F000854680 /* ViewModels in Frameworks */ = {isa = PBXBuildFile; productRef = D0E2C1D024FD97F000854680 /* ViewModels */; };
|
D0E2C1D124FD97F000854680 /* ViewModels in Frameworks */ = {isa = PBXBuildFile; productRef = D0E2C1D024FD97F000854680 /* ViewModels */; };
|
||||||
D0E5361C24E3EB4D00FB1CE1 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5361B24E3EB4D00FB1CE1 /* NotificationService.swift */; };
|
D0E5361C24E3EB4D00FB1CE1 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5361B24E3EB4D00FB1CE1 /* NotificationService.swift */; };
|
||||||
D0E5362024E3EB4D00FB1CE1 /* Notification Service Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
D0E5362024E3EB4D00FB1CE1 /* Notification Service Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = D0E5361924E3EB4D00FB1CE1 /* Notification Service Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
@ -87,6 +86,7 @@
|
||||||
D0666A2124C677B400F3F04B /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
D0666A2124C677B400F3F04B /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D0666A2524C677B400F3F04B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
D0666A2524C677B400F3F04B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
D085C3BB25008DEC008A6C5E /* DB */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DB; sourceTree = "<group>"; };
|
D085C3BB25008DEC008A6C5E /* DB */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DB; sourceTree = "<group>"; };
|
||||||
|
D0AD03552505814D0085A466 /* Base16 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Base16; sourceTree = "<group>"; };
|
||||||
D0BDF66524FD7A6400C7FA1C /* ServiceLayer */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ServiceLayer; sourceTree = "<group>"; };
|
D0BDF66524FD7A6400C7FA1C /* ServiceLayer */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ServiceLayer; sourceTree = "<group>"; };
|
||||||
D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = "<group>"; };
|
D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = "<group>"; };
|
||||||
D0BEB1F624F9A84B001B0F04 /* LoadingTableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingTableFooterView.swift; sourceTree = "<group>"; };
|
D0BEB1F624F9A84B001B0F04 /* LoadingTableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingTableFooterView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -120,7 +120,6 @@
|
||||||
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
|
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KingfisherOptionsInfo+Extensions.swift"; sourceTree = "<group>"; };
|
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KingfisherOptionsInfo+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = "<group>"; };
|
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D0C7D47124F76169001EBDBB /* Data+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = "<group>"; };
|
|
||||||
D0D7C013250440610039AD6F /* CodableBloomFilter */ = {isa = PBXFileReference; lastKnownFileType = folder; path = CodableBloomFilter; sourceTree = "<group>"; };
|
D0D7C013250440610039AD6F /* CodableBloomFilter */ = {isa = PBXFileReference; lastKnownFileType = folder; path = CodableBloomFilter; sourceTree = "<group>"; };
|
||||||
D0E0F1E424FC49FC002C04BF /* Mastodon */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Mastodon; sourceTree = "<group>"; };
|
D0E0F1E424FC49FC002C04BF /* Mastodon */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Mastodon; sourceTree = "<group>"; };
|
||||||
D0E2C1CF24FD8BA400854680 /* ViewModels */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ViewModels; sourceTree = "<group>"; };
|
D0E2C1CF24FD8BA400854680 /* ViewModels */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ViewModels; sourceTree = "<group>"; };
|
||||||
|
@ -183,6 +182,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D0C7D45224F76169001EBDBB /* Assets.xcassets */,
|
D0C7D45224F76169001EBDBB /* Assets.xcassets */,
|
||||||
|
D0AD03552505814D0085A466 /* Base16 */,
|
||||||
D0D7C013250440610039AD6F /* CodableBloomFilter */,
|
D0D7C013250440610039AD6F /* CodableBloomFilter */,
|
||||||
D085C3BB25008DEC008A6C5E /* DB */,
|
D085C3BB25008DEC008A6C5E /* DB */,
|
||||||
D0C7D46824F76169001EBDBB /* Extensions */,
|
D0C7D46824F76169001EBDBB /* Extensions */,
|
||||||
|
@ -291,7 +291,6 @@
|
||||||
D0C7D46824F76169001EBDBB /* Extensions */ = {
|
D0C7D46824F76169001EBDBB /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D0C7D47124F76169001EBDBB /* Data+Extensions.swift */,
|
|
||||||
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */,
|
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */,
|
||||||
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
D0C7D46B24F76169001EBDBB /* NSMutableAttributedString+Extensions.swift */,
|
||||||
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
|
D0C7D46A24F76169001EBDBB /* String+Extensions.swift */,
|
||||||
|
@ -493,7 +492,6 @@
|
||||||
D0C7D4A224F7616A001EBDBB /* NotificationTypesPreferencesView.swift in Sources */,
|
D0C7D4A224F7616A001EBDBB /* NotificationTypesPreferencesView.swift in Sources */,
|
||||||
D0BEB1F724F9A84B001B0F04 /* LoadingTableFooterView.swift in Sources */,
|
D0BEB1F724F9A84B001B0F04 /* LoadingTableFooterView.swift in Sources */,
|
||||||
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */,
|
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */,
|
||||||
D0C7D4DC24F7616A001EBDBB /* Data+Extensions.swift in Sources */,
|
|
||||||
D0BEB1FF24F9E5BB001B0F04 /* ListsView.swift in Sources */,
|
D0BEB1FF24F9E5BB001B0F04 /* ListsView.swift in Sources */,
|
||||||
D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */,
|
D0C7D49724F7616A001EBDBB /* IdentitiesView.swift in Sources */,
|
||||||
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
||||||
|
|
|
@ -14,12 +14,13 @@ let package = Package(
|
||||||
targets: ["Secrets"])
|
targets: ["Secrets"])
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
.package(path: "Base16"),
|
||||||
.package(path: "Keychain")
|
.package(path: "Keychain")
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "Secrets",
|
name: "Secrets",
|
||||||
dependencies: ["Keychain"]),
|
dependencies: ["Base16", "Keychain"]),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "SecretsTests",
|
name: "SecretsTests",
|
||||||
dependencies: ["Secrets"])
|
dependencies: ["Secrets"])
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Base16
|
||||||
import Foundation
|
import Foundation
|
||||||
import Keychain
|
import Keychain
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ public extension Secrets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "x'\(passphraseData.uppercaseHexEncodedString())'"
|
return "x'\(passphraseData.base16EncodedString(options: [.uppercase]))'"
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteAllItems() throws {
|
func deleteAllItems() throws {
|
||||||
|
@ -225,9 +226,3 @@ private struct PushKey {
|
||||||
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
|
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
|
||||||
kSecAttrKeySizeInBits as String: sizeInBits]
|
kSecAttrKeySizeInBits as String: sizeInBits]
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Data {
|
|
||||||
func uppercaseHexEncodedString() -> String {
|
|
||||||
map { String(format: "%02hhX", $0) }.joined()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ public extension AllIdentitiesService {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePushSubscriptions(deviceToken: String) -> AnyPublisher<Never, Error> {
|
func updatePushSubscriptions(deviceToken: Data) -> AnyPublisher<Never, Error> {
|
||||||
identityDatabase.identitiesWithOutdatedDeviceTokens(deviceToken: deviceToken)
|
identityDatabase.identitiesWithOutdatedDeviceTokens(deviceToken: deviceToken)
|
||||||
.tryMap { identities -> [AnyPublisher<Never, Never>] in
|
.tryMap { identities -> [AnyPublisher<Never, Never>] in
|
||||||
try identities.map {
|
try identities.map {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright © 2020 Metabolist. All rights reserved.
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Base16
|
||||||
import Combine
|
import Combine
|
||||||
import DB
|
import DB
|
||||||
import Foundation
|
import Foundation
|
||||||
|
@ -167,7 +168,7 @@ public extension IdentityService {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPushSubscription(deviceToken: String, alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
|
func createPushSubscription(deviceToken: Data, alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
|
||||||
let publicKey: String
|
let publicKey: String
|
||||||
let auth: String
|
let auth: String
|
||||||
|
|
||||||
|
@ -180,7 +181,7 @@ public extension IdentityService {
|
||||||
|
|
||||||
let identityID = identity.id
|
let identityID = identity.id
|
||||||
let endpoint = Self.pushSubscriptionEndpointURL
|
let endpoint = Self.pushSubscriptionEndpointURL
|
||||||
.appendingPathComponent(deviceToken)
|
.appendingPathComponent(deviceToken.base16EncodedString())
|
||||||
.appendingPathComponent(identityID.uuidString)
|
.appendingPathComponent(identityID.uuidString)
|
||||||
|
|
||||||
return mastodonAPIClient.request(
|
return mastodonAPIClient.request(
|
||||||
|
|
|
@ -9,14 +9,14 @@ class AppDelegate: NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
func registerForRemoteNotifications() -> AnyPublisher<String, Error> {
|
func registerForRemoteNotifications() -> AnyPublisher<Data, Error> {
|
||||||
$application
|
$application
|
||||||
.compactMap { $0 }
|
.compactMap { $0 }
|
||||||
.handleEvents(receiveOutput: { $0.registerForRemoteNotifications() })
|
.handleEvents(receiveOutput: { $0.registerForRemoteNotifications() })
|
||||||
.setFailureType(to: Error.self)
|
.setFailureType(to: Error.self)
|
||||||
.zip(remoteNotificationDeviceTokens)
|
.zip(remoteNotificationDeviceTokens)
|
||||||
.first()
|
.first()
|
||||||
.map { $1.hexEncodedString() }
|
.map { $1 }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,11 @@ public final class RootViewModel: ObservableObject {
|
||||||
@Published private var mostRecentlyUsedIdentityID: UUID?
|
@Published private var mostRecentlyUsedIdentityID: UUID?
|
||||||
private let allIdentitiesService: AllIdentitiesService
|
private let allIdentitiesService: AllIdentitiesService
|
||||||
private let userNotificationService: UserNotificationService
|
private let userNotificationService: UserNotificationService
|
||||||
private let registerForRemoteNotifications: () -> AnyPublisher<String, Error>
|
private let registerForRemoteNotifications: () -> AnyPublisher<Data, Error>
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
public init(environment: AppEnvironment,
|
public init(environment: AppEnvironment,
|
||||||
registerForRemoteNotifications: @escaping () -> AnyPublisher<String, Error>) throws {
|
registerForRemoteNotifications: @escaping () -> AnyPublisher<Data, Error>) throws {
|
||||||
allIdentitiesService = try AllIdentitiesService(environment: environment)
|
allIdentitiesService = try AllIdentitiesService(environment: environment)
|
||||||
userNotificationService = UserNotificationService(environment: environment)
|
userNotificationService = UserNotificationService(environment: environment)
|
||||||
self.registerForRemoteNotifications = registerForRemoteNotifications
|
self.registerForRemoteNotifications = registerForRemoteNotifications
|
||||||
|
|
Loading…
Reference in a new issue