diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..61f9ecb --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# OS X +.DS_Store + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +profile +*.moved-aside +DerivedData +*.hmap +*.ipa +project.xcworkspace + +# Bundler +.bundle + +Carthage +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control +# +# Note: if you ignore the Pods directory, make sure to uncomment +# `pod install` in .travis.yml +# + +Pods/ +Carthage/ +.gitmodules diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6467470 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: objective-c +osx_image: xcode10 + + +script: +# Build Travis project and run tests +- xcodebuild -project ShareClient.xcodeproj -scheme ShareClient build -destination name="iPhone SE" test diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000..5abe0d1 --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "LoopKit/LoopKit" ~> 2.0 diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000..ea5e0dd --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1 @@ +github "LoopKit/LoopKit" "v2.2.1" diff --git a/Common/LocalizedString.swift b/Common/LocalizedString.swift new file mode 100644 index 0000000..b0e5401 --- /dev/null +++ b/Common/LocalizedString.swift @@ -0,0 +1,21 @@ +// +// LocalizedString.swift +// LoopKit +// +// Created by Retina15 on 8/6/18. +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import Foundation + +internal class FrameworkBundle { + static let main = Bundle(for: FrameworkBundle.self) +} + +func LocalizedString(_ key: String, tableName: String? = nil, value: String? = nil, comment: String) -> String { + if let value = value { + return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, value: value, comment: comment) + } else { + return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, comment: comment) + } +} diff --git a/ShareClient.xcodeproj/project.pbxproj b/ShareClient.xcodeproj/project.pbxproj index b1e0661..1d48918 100644 --- a/ShareClient.xcodeproj/project.pbxproj +++ b/ShareClient.xcodeproj/project.pbxproj @@ -6,14 +6,65 @@ objectVersion = 46; objects = { +/* Begin PBXAggregateTarget section */ + 43A8EC75210E653B00A81379 /* Cartfile */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 43A8EC76210E653C00A81379 /* Build configuration list for PBXAggregateTarget "Cartfile" */; + buildPhases = ( + 43A8EC79210E654300A81379 /* Build Carthage Dependencies */, + ); + dependencies = ( + ); + name = Cartfile; + productName = Cartfile; + }; +/* End PBXAggregateTarget section */ + /* Begin PBXBuildFile section */ + 4325E9CF210E6A0A00969CE5 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4325E9CE210E6A0A00969CE5 /* HKUnit.swift */; }; + 4325E9D1210E6A3D00969CE5 /* TimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4325E9D0210E6A3D00969CE5 /* TimeInterval.swift */; }; + 4325E9D3210E6ADA00969CE5 /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4325E9D2210E6ADA00969CE5 /* IdentifiableClass.swift */; }; + 4325E9D5210E6B4400969CE5 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4325E9D4210E6B4300969CE5 /* UIColor.swift */; }; 432B0E8C1CDFC3C50045347B /* ShareClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 432B0E8B1CDFC3C50045347B /* ShareClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; 432B0E931CDFC3C50045347B /* ShareClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 432B0E881CDFC3C50045347B /* ShareClient.framework */; }; 432B0E981CDFC3C50045347B /* ShareClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 432B0E971CDFC3C50045347B /* ShareClientTests.swift */; }; + 43A8EC7C210E661400A81379 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43A8EC7B210E661400A81379 /* LoopKit.framework */; }; + 43A8EC86210E664300A81379 /* ShareClientUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 43A8EC84210E664300A81379 /* ShareClientUI.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 43A8EC8A210E664C00A81379 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43A8EC7B210E661400A81379 /* LoopKit.framework */; }; + 43A8EC8C210E665700A81379 /* LoopKitUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43A8EC8B210E665600A81379 /* LoopKitUI.framework */; }; + 43A8EC90210E676500A81379 /* ShareClientSetupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A8EC8D210E676500A81379 /* ShareClientSetupViewController.swift */; }; + 43A8EC91210E676500A81379 /* ShareClientManager+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A8EC8E210E676500A81379 /* ShareClientManager+UI.swift */; }; + 43A8EC92210E676500A81379 /* ShareClientSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A8EC8F210E676500A81379 /* ShareClientSettingsViewController.swift */; }; + 43A8EC93210E679B00A81379 /* ShareClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 432B0E881CDFC3C50045347B /* ShareClient.framework */; }; + 43A8EC95210E67B000A81379 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43A8EC94210E67B000A81379 /* HealthKit.framework */; }; + 43A8EC97210E680100A81379 /* ShareService+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A8EC96210E680100A81379 /* ShareService+UI.swift */; }; + 43A8EC99210E682A00A81379 /* ShareService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A8EC98210E682A00A81379 /* ShareService.swift */; }; + 43A8EC9B210E68BA00A81379 /* ShareGlucose+GlucoseKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A8EC9A210E68BA00A81379 /* ShareGlucose+GlucoseKit.swift */; }; + 43A8EC9D210E68CE00A81379 /* ShareClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A8EC9C210E68CE00A81379 /* ShareClientManager.swift */; }; + 43AB511B21330D1400B3D58D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43AB511921330D1400B3D58D /* Localizable.strings */; }; + 43AB5127213315D300B3D58D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43AB5125213315D300B3D58D /* Localizable.strings */; }; + 43AB51362133177800B3D58D /* LocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AB51352133177800B3D58D /* LocalizedString.swift */; }; + 43AB51372133177800B3D58D /* LocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AB51352133177800B3D58D /* LocalizedString.swift */; }; 43C418AF1CE0488900405B6A /* ShareClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C418AE1CE0488900405B6A /* ShareClient.swift */; }; + C17D3CC52184CBCD003458C8 /* LoopKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 43A8EC7B210E661400A81379 /* LoopKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C17D3CC62184CC26003458C8 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43A8EC7B210E661400A81379 /* LoopKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 4325E9D6210E714B00969CE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 432B0E7F1CDFC3C50045347B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 43A8EC75210E653B00A81379; + remoteInfo = Cartfile; + }; + 4325E9D8210E715200969CE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 432B0E7F1CDFC3C50045347B /* Project object */; + proxyType = 1; + remoteGlobalIDString = 43A8EC75210E653B00A81379; + remoteInfo = Cartfile; + }; 432B0E941CDFC3C50045347B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 432B0E7F1CDFC3C50045347B /* Project object */; @@ -23,13 +74,64 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + C17D3CC42184CBB7003458C8 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + C17D3CC52184CBCD003458C8 /* LoopKit.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ + 4325E9CE210E6A0A00969CE5 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = ""; }; + 4325E9D0210E6A3D00969CE5 /* TimeInterval.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeInterval.swift; sourceTree = ""; }; + 4325E9D2210E6ADA00969CE5 /* IdentifiableClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentifiableClass.swift; sourceTree = ""; }; + 4325E9D4210E6B4300969CE5 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; 432B0E881CDFC3C50045347B /* ShareClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ShareClient.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 432B0E8B1CDFC3C50045347B /* ShareClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShareClient.h; sourceTree = ""; }; 432B0E8D1CDFC3C50045347B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 432B0E921CDFC3C50045347B /* ShareClientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ShareClientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 432B0E971CDFC3C50045347B /* ShareClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareClientTests.swift; sourceTree = ""; }; 432B0E991CDFC3C50045347B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 43A8EC7B210E661400A81379 /* LoopKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoopKit.framework; path = Carthage/Build/iOS/LoopKit.framework; sourceTree = ""; }; + 43A8EC82210E664300A81379 /* ShareClientUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ShareClientUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 43A8EC84210E664300A81379 /* ShareClientUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShareClientUI.h; sourceTree = ""; }; + 43A8EC85210E664300A81379 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 43A8EC8B210E665600A81379 /* LoopKitUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoopKitUI.framework; path = Carthage/Build/iOS/LoopKitUI.framework; sourceTree = ""; }; + 43A8EC8D210E676500A81379 /* ShareClientSetupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareClientSetupViewController.swift; sourceTree = ""; }; + 43A8EC8E210E676500A81379 /* ShareClientManager+UI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ShareClientManager+UI.swift"; sourceTree = ""; }; + 43A8EC8F210E676500A81379 /* ShareClientSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareClientSettingsViewController.swift; sourceTree = ""; }; + 43A8EC94210E67B000A81379 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; }; + 43A8EC96210E680100A81379 /* ShareService+UI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ShareService+UI.swift"; sourceTree = ""; }; + 43A8EC98210E682A00A81379 /* ShareService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareService.swift; sourceTree = ""; }; + 43A8EC9A210E68BA00A81379 /* ShareGlucose+GlucoseKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ShareGlucose+GlucoseKit.swift"; sourceTree = ""; }; + 43A8EC9C210E68CE00A81379 /* ShareClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareClientManager.swift; sourceTree = ""; }; + 43AB511A21330D1400B3D58D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; + 43AB511C21330D6800B3D58D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + 43AB511D21330D6B00B3D58D /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + 43AB511E21330D7000B3D58D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + 43AB511F21330D7800B3D58D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512021330D7E00B3D58D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512121330D8600B3D58D /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512221330D8A00B3D58D /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512321330D8E00B3D58D /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512421330D9400B3D58D /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + 43AB5126213315D300B3D58D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; + 43AB51282133161100B3D58D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + 43AB51292133161200B3D58D /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512A2133161300B3D58D /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + 43AB512B2133161600B3D58D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512C2133161700B3D58D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512D2133161800B3D58D /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512E2133161900B3D58D /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; + 43AB512F2133161A00B3D58D /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + 43AB51302133161A00B3D58D /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + 43AB51352133177800B3D58D /* LocalizedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizedString.swift; sourceTree = ""; }; 43C418AE1CE0488900405B6A /* ShareClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareClient.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -38,6 +140,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 43A8EC95210E67B000A81379 /* HealthKit.framework in Frameworks */, + 43A8EC7C210E661400A81379 /* LoopKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -46,6 +150,17 @@ buildActionMask = 2147483647; files = ( 432B0E931CDFC3C50045347B /* ShareClient.framework in Frameworks */, + C17D3CC62184CC26003458C8 /* LoopKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 43A8EC7E210E664300A81379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 43A8EC93210E679B00A81379 /* ShareClient.framework in Frameworks */, + 43A8EC8C210E665700A81379 /* LoopKitUI.framework in Frameworks */, + 43A8EC8A210E664C00A81379 /* LoopKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -55,9 +170,12 @@ 432B0E7E1CDFC3C50045347B = { isa = PBXGroup; children = ( + 43AB51342133176B00B3D58D /* Common */, 432B0E8A1CDFC3C50045347B /* ShareClient */, 432B0E961CDFC3C50045347B /* ShareClientTests */, + 43A8EC83210E664300A81379 /* ShareClientUI */, 432B0E891CDFC3C50045347B /* Products */, + 43A8EC7A210E661300A81379 /* Frameworks */, ); sourceTree = ""; }; @@ -66,6 +184,7 @@ children = ( 432B0E881CDFC3C50045347B /* ShareClient.framework */, 432B0E921CDFC3C50045347B /* ShareClientTests.xctest */, + 43A8EC82210E664300A81379 /* ShareClientUI.framework */, ); name = Products; sourceTree = ""; @@ -73,7 +192,13 @@ 432B0E8A1CDFC3C50045347B /* ShareClient */ = { isa = PBXGroup; children = ( + 43AB5125213315D300B3D58D /* Localizable.strings */, + 4325E9CE210E6A0A00969CE5 /* HKUnit.swift */, 43C418AE1CE0488900405B6A /* ShareClient.swift */, + 43A8EC9C210E68CE00A81379 /* ShareClientManager.swift */, + 43A8EC9A210E68BA00A81379 /* ShareGlucose+GlucoseKit.swift */, + 43A8EC98210E682A00A81379 /* ShareService.swift */, + 4325E9D0210E6A3D00969CE5 /* TimeInterval.swift */, 432B0E8B1CDFC3C50045347B /* ShareClient.h */, 432B0E8D1CDFC3C50045347B /* Info.plist */, ); @@ -89,6 +214,40 @@ path = ShareClientTests; sourceTree = ""; }; + 43A8EC7A210E661300A81379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 43A8EC94210E67B000A81379 /* HealthKit.framework */, + 43A8EC8B210E665600A81379 /* LoopKitUI.framework */, + 43A8EC7B210E661400A81379 /* LoopKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 43A8EC83210E664300A81379 /* ShareClientUI */ = { + isa = PBXGroup; + children = ( + 43AB511921330D1400B3D58D /* Localizable.strings */, + 4325E9D2210E6ADA00969CE5 /* IdentifiableClass.swift */, + 43A8EC84210E664300A81379 /* ShareClientUI.h */, + 43A8EC8E210E676500A81379 /* ShareClientManager+UI.swift */, + 43A8EC8F210E676500A81379 /* ShareClientSettingsViewController.swift */, + 43A8EC8D210E676500A81379 /* ShareClientSetupViewController.swift */, + 43A8EC96210E680100A81379 /* ShareService+UI.swift */, + 4325E9D4210E6B4300969CE5 /* UIColor.swift */, + 43A8EC85210E664300A81379 /* Info.plist */, + ); + path = ShareClientUI; + sourceTree = ""; + }; + 43AB51342133176B00B3D58D /* Common */ = { + isa = PBXGroup; + children = ( + 43AB51352133177800B3D58D /* LocalizedString.swift */, + ); + path = Common; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -100,6 +259,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 43A8EC7F210E664300A81379 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 43A8EC86210E664300A81379 /* ShareClientUI.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -115,6 +282,7 @@ buildRules = ( ); dependencies = ( + 4325E9D7210E714B00969CE5 /* PBXTargetDependency */, ); name = ShareClient; productName = ShareClient; @@ -128,6 +296,7 @@ 432B0E8E1CDFC3C50045347B /* Sources */, 432B0E8F1CDFC3C50045347B /* Frameworks */, 432B0E901CDFC3C50045347B /* Resources */, + C17D3CC42184CBB7003458C8 /* CopyFiles */, ); buildRules = ( ); @@ -139,6 +308,25 @@ productReference = 432B0E921CDFC3C50045347B /* ShareClientTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 43A8EC81210E664300A81379 /* ShareClientUI */ = { + isa = PBXNativeTarget; + buildConfigurationList = 43A8EC87210E664300A81379 /* Build configuration list for PBXNativeTarget "ShareClientUI" */; + buildPhases = ( + 43A8EC7D210E664300A81379 /* Sources */, + 43A8EC7E210E664300A81379 /* Frameworks */, + 43A8EC7F210E664300A81379 /* Headers */, + 43A8EC80210E664300A81379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 4325E9D9210E715200969CE5 /* PBXTargetDependency */, + ); + name = ShareClientUI; + productName = ShareClientUI; + productReference = 43A8EC82210E664300A81379 /* ShareClientUI.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -146,17 +334,26 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = "Mark Wilson"; TargetAttributes = { 432B0E871CDFC3C50045347B = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0900; + LastSwiftMigration = 1000; }; 432B0E911CDFC3C50045347B = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 0900; }; + 43A8EC75210E653B00A81379 = { + CreatedOnToolsVersion = 9.4.1; + ProvisioningStyle = Automatic; + }; + 43A8EC81210E664300A81379 = { + CreatedOnToolsVersion = 9.4.1; + LastSwiftMigration = 1000; + ProvisioningStyle = Automatic; + }; }; }; buildConfigurationList = 432B0E821CDFC3C50045347B /* Build configuration list for PBXProject "ShareClient" */; @@ -165,6 +362,16 @@ hasScannedForEncodings = 0; knownRegions = ( en, + Base, + fr, + de, + "zh-Hans", + es, + it, + nl, + nb, + ru, + pl, ); mainGroup = 432B0E7E1CDFC3C50045347B; productRefGroup = 432B0E891CDFC3C50045347B /* Products */; @@ -173,6 +380,8 @@ targets = ( 432B0E871CDFC3C50045347B /* ShareClient */, 432B0E911CDFC3C50045347B /* ShareClientTests */, + 43A8EC81210E664300A81379 /* ShareClientUI */, + 43A8EC75210E653B00A81379 /* Cartfile */, ); }; /* End PBXProject section */ @@ -182,6 +391,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 43AB5127213315D300B3D58D /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -192,14 +402,45 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 43A8EC80210E664300A81379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 43AB511B21330D1400B3D58D /* Localizable.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 43A8EC79210E654300A81379 /* Build Carthage Dependencies */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Build Carthage Dependencies"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"$CARTHAGE\" = \"YES\" ]; then\n echo \"Skipping carthage build because we're already in one\"\nelif [ -d $PROJECT_DIR/../../../Loop.xcworkspace ]; then\n echo \"Skipping carthage build because we're in a workspace\"\nelif [ -f $PROJECT_DIR/.gitmodules ]; then\n echo \"Skipping checkout due to presence of .gitmodules file\"\n if [ $ACTION = \"install\" ]; then\n echo \"You're installing: Make sure to keep all submodules up-to-date and run carthage build after changes.\"\n fi\nelse\n unset LLVM_TARGET_TRIPLE_SUFFIX\n echo \"Bootstrapping carthage dependencies\"\n /usr/local/bin/carthage bootstrap --project-directory \"$SRCROOT\" --cache-builds\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 432B0E831CDFC3C50045347B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4325E9CF210E6A0A00969CE5 /* HKUnit.swift in Sources */, + 4325E9D1210E6A3D00969CE5 /* TimeInterval.swift in Sources */, + 43A8EC9D210E68CE00A81379 /* ShareClientManager.swift in Sources */, + 43AB51362133177800B3D58D /* LocalizedString.swift in Sources */, + 43A8EC9B210E68BA00A81379 /* ShareGlucose+GlucoseKit.swift in Sources */, 43C418AF1CE0488900405B6A /* ShareClient.swift in Sources */, + 43A8EC99210E682A00A81379 /* ShareService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -211,9 +452,33 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 43A8EC7D210E664300A81379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 43A8EC92210E676500A81379 /* ShareClientSettingsViewController.swift in Sources */, + 4325E9D5210E6B4400969CE5 /* UIColor.swift in Sources */, + 43A8EC97210E680100A81379 /* ShareService+UI.swift in Sources */, + 43AB51372133177800B3D58D /* LocalizedString.swift in Sources */, + 4325E9D3210E6ADA00969CE5 /* IdentifiableClass.swift in Sources */, + 43A8EC91210E676500A81379 /* ShareClientManager+UI.swift in Sources */, + 43A8EC90210E676500A81379 /* ShareClientSetupViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 4325E9D7210E714B00969CE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 43A8EC75210E653B00A81379 /* Cartfile */; + targetProxy = 4325E9D6210E714B00969CE5 /* PBXContainerItemProxy */; + }; + 4325E9D9210E715200969CE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 43A8EC75210E653B00A81379 /* Cartfile */; + targetProxy = 4325E9D8210E715200969CE5 /* PBXContainerItemProxy */; + }; 432B0E951CDFC3C50045347B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 432B0E871CDFC3C50045347B /* ShareClient */; @@ -221,11 +486,53 @@ }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + 43AB511921330D1400B3D58D /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 43AB511A21330D1400B3D58D /* Base */, + 43AB511C21330D6800B3D58D /* fr */, + 43AB511D21330D6B00B3D58D /* de */, + 43AB511E21330D7000B3D58D /* zh-Hans */, + 43AB511F21330D7800B3D58D /* es */, + 43AB512021330D7E00B3D58D /* it */, + 43AB512121330D8600B3D58D /* nl */, + 43AB512221330D8A00B3D58D /* nb */, + 43AB512321330D8E00B3D58D /* ru */, + 43AB512421330D9400B3D58D /* pl */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 43AB5125213315D300B3D58D /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 43AB5126213315D300B3D58D /* Base */, + 43AB51282133161100B3D58D /* fr */, + 43AB51292133161200B3D58D /* de */, + 43AB512A2133161300B3D58D /* zh-Hans */, + 43AB512B2133161600B3D58D /* es */, + 43AB512C2133161700B3D58D /* it */, + 43AB512D2133161800B3D58D /* nl */, + 43AB512E2133161900B3D58D /* nb */, + 43AB512F2133161A00B3D58D /* ru */, + 43AB51302133161A00B3D58D /* pl */, + ); + name = Localizable.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ 432B0E9A1CDFC3C50045347B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CARTHAGE_PLATFORM_PATH_iphoneos = iOS; + CARTHAGE_PLATFORM_PATH_iphonesimulator = iOS; + CARTHAGE_PLATFORM_PATH_watchos = watchOS; + CARTHAGE_PLATFORM_PATH_watchsimulator = watchOS; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -235,12 +542,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -268,7 +577,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.1; + LOCALIZED_STRING_MACRO_NAMES = ( + NSLocalizedString, + CFLocalizedString, + LocalizedString, + ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -276,6 +590,7 @@ TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Debug; }; @@ -283,6 +598,11 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CARTHAGE_PLATFORM_PATH_iphoneos = iOS; + CARTHAGE_PLATFORM_PATH_iphonesimulator = iOS; + CARTHAGE_PLATFORM_PATH_watchos = watchOS; + CARTHAGE_PLATFORM_PATH_watchsimulator = watchOS; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -292,12 +612,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -319,7 +641,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.1; + LOCALIZED_STRING_MACRO_NAMES = ( + NSLocalizedString, + CFLocalizedString, + LocalizedString, + ); MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -327,51 +654,70 @@ VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Release; }; 432B0E9D1CDFC3C50045347B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/$(CARTHAGE_PLATFORM_PATH_$(PLATFORM_NAME))", + ); INFOPLIST_FILE = ShareClient/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mddub.ShareClient; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos watchos watchsimulator"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2,4"; }; name = Debug; }; 432B0E9E1CDFC3C50045347B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/$(CARTHAGE_PLATFORM_PATH_$(PLATFORM_NAME))", + ); INFOPLIST_FILE = ShareClient/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mddub.ShareClient; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.0; + SUPPORTED_PLATFORMS = "iphonesimulator iphoneos watchos watchsimulator"; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2,4"; }; name = Release; }; 432B0EA01CDFC3C50045347B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited),", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = ShareClientTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mddub.ShareClientTests; @@ -383,6 +729,10 @@ 432B0EA11CDFC3C50045347B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited),", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = ShareClientTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.mddub.ShareClientTests; @@ -391,6 +741,89 @@ }; name = Release; }; + 43A8EC77210E653C00A81379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 43A8EC78210E653C00A81379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 43A8EC88210E664300A81379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 2; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = ShareClientUI/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.loopkit.ShareClientUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 43A8EC89210E664300A81379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 2; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = ShareClientUI/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.loopkit.ShareClientUI; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -421,6 +854,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 43A8EC76210E653C00A81379 /* Build configuration list for PBXAggregateTarget "Cartfile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 43A8EC77210E653C00A81379 /* Debug */, + 43A8EC78210E653C00A81379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 43A8EC87210E664300A81379 /* Build configuration list for PBXNativeTarget "ShareClientUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 43A8EC88210E664300A81379 /* Debug */, + 43A8EC89210E664300A81379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 432B0E7F1CDFC3C50045347B /* Project object */; diff --git a/ShareClient.xcodeproj/xcshareddata/xcschemes/ShareClient.xcscheme b/ShareClient.xcodeproj/xcshareddata/xcschemes/ShareClient.xcscheme index 0d2c14e..13e3884 100644 --- a/ShareClient.xcodeproj/xcshareddata/xcschemes/ShareClient.xcscheme +++ b/ShareClient.xcodeproj/xcshareddata/xcschemes/ShareClient.xcscheme @@ -1,6 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ShareClient/Base.lproj/Localizable.strings b/ShareClient/Base.lproj/Localizable.strings new file mode 100644 index 0000000..0c5ccd9 Binary files /dev/null and b/ShareClient/Base.lproj/Localizable.strings differ diff --git a/ShareClient/HKUnit.swift b/ShareClient/HKUnit.swift new file mode 100644 index 0000000..e3f0dde --- /dev/null +++ b/ShareClient/HKUnit.swift @@ -0,0 +1,16 @@ +// +// HKUnit.swift +// xDripG5 +// +// Created by Nate Racklyeft on 8/6/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import HealthKit + + +extension HKUnit { + static let milligramsPerDeciliter: HKUnit = { + return HKUnit.gramUnit(with: .milli).unitDivided(by: HKUnit.literUnit(with: .deci)) + }() +} diff --git a/ShareClient/Info.plist b/ShareClient/Info.plist index 2bb1ea9..a6f720e 100644 --- a/ShareClient/Info.plist +++ b/ShareClient/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.4.1 + 1.1 CFBundleSignature ???? CFBundleVersion diff --git a/ShareClient/ShareClient.swift b/ShareClient/ShareClient.swift index 5f23358..70d869c 100644 --- a/ShareClient/ShareClient.swift +++ b/ShareClient/ShareClient.swift @@ -207,7 +207,7 @@ public class ShareClient { private func parseDate(_ wt: String) throws -> Date { // wt looks like "/Date(1462404576000)/" let re = try NSRegularExpression(pattern: "\\((.*)\\)") - if let match = re.firstMatch(in: wt, range: NSMakeRange(0, wt.characters.count)) { + if let match = re.firstMatch(in: wt, range: NSMakeRange(0, wt.count)) { #if swift(>=4) let matchRange = match.range(at: 1) #else diff --git a/ShareClient/ShareClientManager.swift b/ShareClient/ShareClientManager.swift new file mode 100644 index 0000000..d62b30f --- /dev/null +++ b/ShareClient/ShareClientManager.swift @@ -0,0 +1,101 @@ +// +// ShareClientManager.swift +// Loop +// +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import LoopKit +import HealthKit + + +public class ShareClientManager: CGMManager { + public static var managerIdentifier = "DexShareClient" + + public init() { + shareService = ShareService(keychainManager: keychain) + } + + required convenience public init?(rawState: CGMManager.RawStateValue) { + self.init() + } + + public var rawState: CGMManager.RawStateValue { + return [:] + } + + private let keychain = KeychainManager() + + public var shareService: ShareService { + didSet { + try! keychain.setDexcomShareUsername(shareService.username, password: shareService.password, url: shareService.url) + } + } + + public static let localizedTitle = LocalizedString("Dexcom Share", comment: "Title for the CGMManager option") + + public let appURL: URL? = nil + + weak public var cgmManagerDelegate: CGMManagerDelegate? + + public let providesBLEHeartbeat = false + + public let shouldSyncToRemoteService = false + + public var sensorState: SensorDisplayable? { + return latestBackfill + } + + public let managedDataInterval: TimeInterval? = nil + + public private(set) var latestBackfill: ShareGlucose? + + public func fetchNewDataIfNeeded(_ completion: @escaping (CGMResult) -> Void) { + guard let shareClient = shareService.client else { + completion(.noData) + return + } + + // If our last glucose was less than 4.5 minutes ago, don't fetch. + if let latestGlucose = latestBackfill, latestGlucose.startDate.timeIntervalSinceNow > -TimeInterval(minutes: 4.5) { + completion(.noData) + return + } + + shareClient.fetchLast(6) { (error, glucose) in + if let error = error { + completion(.error(error)) + return + } + guard let glucose = glucose else { + completion(.noData) + return + } + + // Ignore glucose values that are up to a minute newer than our previous value, to account for possible time shifting in Share data + let startDate = self.cgmManagerDelegate?.startDateToFilterNewData(for: self)?.addingTimeInterval(TimeInterval(minutes: 1)) + let newGlucose = glucose.filterDateRange(startDate, nil) + let newSamples = newGlucose.filter({ $0.isStateValid }).map { + return NewGlucoseSample(date: $0.startDate, quantity: $0.quantity, isDisplayOnly: false, syncIdentifier: "\(Int($0.startDate.timeIntervalSince1970))", device: self.device) + } + + self.latestBackfill = newGlucose.first + + if newSamples.count > 0 { + completion(.newData(newSamples)) + } else { + completion(.noData) + } + } + } + + public var device: HKDevice? = nil + + public var debugDescription: String { + return [ + "## ShareClientManager", + "latestBackfill: \(String(describing: latestBackfill))", + "" + ].joined(separator: "\n") + } +} diff --git a/ShareClient/ShareGlucose+GlucoseKit.swift b/ShareClient/ShareGlucose+GlucoseKit.swift new file mode 100644 index 0000000..b40c86f --- /dev/null +++ b/ShareClient/ShareGlucose+GlucoseKit.swift @@ -0,0 +1,47 @@ +// +// ShareGlucose+GlucoseKit.swift +// Naterade +// +// Created by Nathan Racklyeft on 5/8/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import Foundation +import HealthKit +import LoopKit + + +extension ShareGlucose: GlucoseValue { + public var startDate: Date { + return timestamp + } + + public var quantity: HKQuantity { + return HKQuantity(unit: .milligramsPerDeciliter, doubleValue: Double(glucose)) + } +} + + +extension ShareGlucose: SensorDisplayable { + public var isStateValid: Bool { + return glucose >= 39 + } + + public var trendType: GlucoseTrend? { + return GlucoseTrend(rawValue: Int(trend)) + } + + public var isLocal: Bool { + return false + } +} + +extension SensorDisplayable { + public var stateDescription: String { + if isStateValid { + return LocalizedString("OK", comment: "Sensor state description for the valid state") + } else { + return LocalizedString("Needs Attention", comment: "Sensor state description for the non-valid state") + } + } +} diff --git a/ShareClient/ShareService.swift b/ShareClient/ShareService.swift new file mode 100644 index 0000000..9cb7a2c --- /dev/null +++ b/ShareClient/ShareService.swift @@ -0,0 +1,139 @@ +// +// ShareService.swift +// Loop +// +// Created by Nate Racklyeft on 7/2/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import Foundation +import LoopKit + + +// Encapsulates the Dexcom Share client service and its authentication +public class ShareService: ServiceAuthentication { + public var credentialValues: [String?] + + public let title: String = LocalizedString("Dexcom Share", comment: "The title of the Dexcom Share service") + + public init(username: String?, password: String?, url: URL?) { + credentialValues = [ + username, + password, + url?.absoluteString + ] + + /* + To enable Loop to use a custom share server, change the value of customServer + and remove the comment markers on line 55 and 62. + + You can find installation instructions for one such custom share server at + https://github.com/dabear/NightscoutShareServer + */ + + /* + let customServer = "https://REPLACEME" + let customServerTitle = "Custom" + + credentials[2].options?.append( + (title: LocalizedString(customServerTitle, comment: "Custom share server option title"), + value: customServer)) + */ + + if let username = username, let password = password, let url = url { + isAuthorized = true + client = ShareClient(username: username, password: password, shareServer: url.absoluteString) + } + } + + // The share client, if credentials are present + private(set) var client: ShareClient? + + public var username: String? { + return credentialValues[0] + } + + var password: String? { + return credentialValues[1] + } + + var url: URL? { + guard let urlString = credentialValues[2] else { + return nil + } + + return URL(string: urlString) + } + + public var isAuthorized: Bool = false + + public func verify(_ completion: @escaping (_ success: Bool, _ error: Error?) -> Void) { + guard let username = username, let password = password, let url = url else { + completion(false, nil) + return + } + + let client = ShareClient(username: username, password: password, shareServer: url.absoluteString) + client.fetchLast(1) { (error, _) in + completion(error == nil, error) + } + self.client = client + } + + public func reset() { + isAuthorized = false + client = nil + } +} + + +private let DexcomShareURL = URL(string: KnownShareServers.US.rawValue)! +private let DexcomShareServiceLabel = "DexcomShare1" + + +extension KeychainManager { + func setDexcomShareUsername(_ username: String?, password: String?, url: URL?) throws { + let credentials: InternetCredentials? + + if let username = username, let password = password, let url = url { + credentials = InternetCredentials(username: username, password: password, url: url) + } else { + credentials = nil + } + + // Replace the legacy URL-keyed credentials + try replaceInternetCredentials(nil, forURL: DexcomShareURL) + + try replaceInternetCredentials(credentials, forLabel: DexcomShareServiceLabel) + } + + func getDexcomShareCredentials() -> (username: String, password: String, url: URL)? { + do { // Silence all errors and return nil + do { + let credentials = try getInternetCredentials(label: DexcomShareServiceLabel) + + return (username: credentials.username, password: credentials.password, url: credentials.url) + } catch KeychainManagerError.copy { + // Fetch and replace the legacy URL-keyed credentials + let credentials = try getInternetCredentials(url: DexcomShareURL) + + try setDexcomShareUsername(credentials.username, password: credentials.password, url: credentials.url) + + return (username: credentials.username, password: credentials.password, url: credentials.url) + } + } catch { + return nil + } + } +} + + +extension ShareService { + public convenience init(keychainManager: KeychainManager = KeychainManager()) { + if let (username, password, url) = keychainManager.getDexcomShareCredentials() { + self.init(username: username, password: password, url: url) + } else { + self.init(username: nil, password: nil, url: nil) + } + } +} diff --git a/ShareClient/TimeInterval.swift b/ShareClient/TimeInterval.swift new file mode 100644 index 0000000..5a8046a --- /dev/null +++ b/ShareClient/TimeInterval.swift @@ -0,0 +1,61 @@ +// +// NSTimeInterval.swift +// Naterade +// +// Created by Nathan Racklyeft on 1/9/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import Foundation + + +extension TimeInterval { + static func hours(_ hours: Double) -> TimeInterval { + return self.init(hours: hours) + } + + static func minutes(_ minutes: Int) -> TimeInterval { + return self.init(minutes: Double(minutes)) + } + + static func minutes(_ minutes: Double) -> TimeInterval { + return self.init(minutes: minutes) + } + + static func seconds(_ seconds: Double) -> TimeInterval { + return self.init(seconds) + } + + static func milliseconds(_ milliseconds: Double) -> TimeInterval { + return self.init(milliseconds / 1000) + } + + init(minutes: Double) { + self.init(minutes * 60) + } + + init(hours: Double) { + self.init(minutes: hours * 60) + } + + init(seconds: Double) { + self.init(seconds) + } + + init(milliseconds: Double) { + self.init(milliseconds / 1000) + } + + var milliseconds: Double { + return self * 1000 + } + + var minutes: Double { + return self / 60.0 + } + + var hours: Double { + return minutes / 60.0 + } + +} diff --git a/ShareClient/de.lproj/Localizable.strings b/ShareClient/de.lproj/Localizable.strings new file mode 100644 index 0000000..861ce5c Binary files /dev/null and b/ShareClient/de.lproj/Localizable.strings differ diff --git a/ShareClient/es.lproj/Localizable.strings b/ShareClient/es.lproj/Localizable.strings new file mode 100644 index 0000000..a2dbc8a Binary files /dev/null and b/ShareClient/es.lproj/Localizable.strings differ diff --git a/ShareClient/fr.lproj/Localizable.strings b/ShareClient/fr.lproj/Localizable.strings new file mode 100644 index 0000000..37a06aa Binary files /dev/null and b/ShareClient/fr.lproj/Localizable.strings differ diff --git a/ShareClient/it.lproj/Localizable.strings b/ShareClient/it.lproj/Localizable.strings new file mode 100644 index 0000000..43e410b Binary files /dev/null and b/ShareClient/it.lproj/Localizable.strings differ diff --git a/ShareClient/nb.lproj/Localizable.strings b/ShareClient/nb.lproj/Localizable.strings new file mode 100644 index 0000000..31d3169 Binary files /dev/null and b/ShareClient/nb.lproj/Localizable.strings differ diff --git a/ShareClient/nl.lproj/Localizable.strings b/ShareClient/nl.lproj/Localizable.strings new file mode 100644 index 0000000..16befb6 Binary files /dev/null and b/ShareClient/nl.lproj/Localizable.strings differ diff --git a/ShareClient/pl.lproj/Localizable.strings b/ShareClient/pl.lproj/Localizable.strings new file mode 100644 index 0000000..aacca8f Binary files /dev/null and b/ShareClient/pl.lproj/Localizable.strings differ diff --git a/ShareClient/ru.lproj/Localizable.strings b/ShareClient/ru.lproj/Localizable.strings new file mode 100644 index 0000000..74085ab Binary files /dev/null and b/ShareClient/ru.lproj/Localizable.strings differ diff --git a/ShareClient/zh-Hans.lproj/Localizable.strings b/ShareClient/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000..871bf95 Binary files /dev/null and b/ShareClient/zh-Hans.lproj/Localizable.strings differ diff --git a/ShareClientTests/Info.plist b/ShareClientTests/Info.plist index e7ba0f3..8a2d354 100644 --- a/ShareClientTests/Info.plist +++ b/ShareClientTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.4.1 + 1.1 CFBundleSignature ???? CFBundleVersion diff --git a/ShareClientTests/ShareClientTests.swift b/ShareClientTests/ShareClientTests.swift index dabd268..f3c6475 100644 --- a/ShareClientTests/ShareClientTests.swift +++ b/ShareClientTests/ShareClientTests.swift @@ -7,7 +7,7 @@ // import XCTest -@testable import ShareClient + class ShareClientTests: XCTestCase { diff --git a/ShareClientUI/Base.lproj/Localizable.strings b/ShareClientUI/Base.lproj/Localizable.strings new file mode 100644 index 0000000..da3f31f Binary files /dev/null and b/ShareClientUI/Base.lproj/Localizable.strings differ diff --git a/ShareClientUI/IdentifiableClass.swift b/ShareClientUI/IdentifiableClass.swift new file mode 100644 index 0000000..29095b5 --- /dev/null +++ b/ShareClientUI/IdentifiableClass.swift @@ -0,0 +1,24 @@ +// +// IdentifiableClass.swift +// Naterade +// +// Created by Nathan Racklyeft on 5/22/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import Foundation + + +protocol IdentifiableClass: class { + static var className: String { get } +} + + +extension IdentifiableClass { + static var className: String { + return NSStringFromClass(self).components(separatedBy: ".").last! + } +} + + +extension UITableViewCell: IdentifiableClass { } diff --git a/ShareClientUI/Info.plist b/ShareClientUI/Info.plist new file mode 100644 index 0000000..29dea2b --- /dev/null +++ b/ShareClientUI/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.1 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/ShareClientUI/ShareClientManager+UI.swift b/ShareClientUI/ShareClientManager+UI.swift new file mode 100644 index 0000000..e16b503 --- /dev/null +++ b/ShareClientUI/ShareClientManager+UI.swift @@ -0,0 +1,25 @@ +// +// ShareClientManager+UI.swift +// Loop +// +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import LoopKitUI +import HealthKit +import ShareClient + + +extension ShareClientManager: CGMManagerUI { + public static func setupViewController() -> (UIViewController & CGMManagerSetupViewController)? { + return ShareClientSetupViewController() + } + + public func settingsViewController(for glucoseUnit: HKUnit) -> UIViewController { + return ShareClientSettingsViewController(cgmManager: self, glucoseUnit: glucoseUnit, allowsDeletion: true) + } + + public var smallImage: UIImage? { + return nil + } +} diff --git a/ShareClientUI/ShareClientSettingsViewController.swift b/ShareClientUI/ShareClientSettingsViewController.swift new file mode 100644 index 0000000..9181c0b --- /dev/null +++ b/ShareClientUI/ShareClientSettingsViewController.swift @@ -0,0 +1,201 @@ +// +// ShareClientSettingsViewController.swift +// Loop +// +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import UIKit +import HealthKit +import LoopKit +import LoopKitUI +import ShareClient + + +public class ShareClientSettingsViewController: UITableViewController { + + public let cgmManager: ShareClientManager + + public let glucoseUnit: HKUnit + + public let allowsDeletion: Bool + + public init(cgmManager: ShareClientManager, glucoseUnit: HKUnit, allowsDeletion: Bool) { + self.cgmManager = cgmManager + self.glucoseUnit = glucoseUnit + self.allowsDeletion = allowsDeletion + + super.init(style: .grouped) + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func viewDidLoad() { + super.viewDidLoad() + + title = cgmManager.localizedTitle + + tableView.rowHeight = UITableView.automaticDimension + tableView.estimatedRowHeight = 44 + + tableView.sectionHeaderHeight = UITableView.automaticDimension + tableView.estimatedSectionHeaderHeight = 55 + + tableView.register(SettingsTableViewCell.self, forCellReuseIdentifier: SettingsTableViewCell.className) + tableView.register(TextButtonTableViewCell.self, forCellReuseIdentifier: TextButtonTableViewCell.className) + } + + // MARK: - UITableViewDataSource + + private enum Section: Int, CaseIterable { + case authentication + case latestReading + case delete + } + + override public func numberOfSections(in tableView: UITableView) -> Int { + return allowsDeletion ? Section.allCases.count : Section.allCases.count - 1 + } + + private enum LatestReadingRow: Int, CaseIterable { + case glucose + case date + case trend + } + + override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch Section(rawValue: section)! { + case .authentication: + return 1 + case .latestReading: + return LatestReadingRow.allCases.count + case .delete: + return 1 + } + } + + private lazy var glucoseFormatter: QuantityFormatter = { + let formatter = QuantityFormatter() + formatter.setPreferredNumberFormatter(for: glucoseUnit) + return formatter + }() + + private lazy var dateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateStyle = .long + formatter.timeStyle = .long + formatter.doesRelativeDateFormatting = true + return formatter + }() + + public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + switch Section(rawValue: indexPath.section)! { + case .authentication: + let cell = tableView.dequeueReusableCell(withIdentifier: SettingsTableViewCell.className, for: indexPath) as! SettingsTableViewCell + + let service = cgmManager.shareService + + cell.textLabel?.text = LocalizedString("Credentials", comment: "Title of cell to set credentials") + cell.detailTextLabel?.text = service.username ?? SettingsTableViewCell.TapToSetString + cell.accessoryType = .disclosureIndicator + + return cell + case .latestReading: + let cell = tableView.dequeueReusableCell(withIdentifier: SettingsTableViewCell.className, for: indexPath) as! SettingsTableViewCell + let glucose = cgmManager.latestBackfill + + switch LatestReadingRow(rawValue: indexPath.row)! { + case .glucose: + cell.textLabel?.text = LocalizedString("Glucose", comment: "Title describing glucose value") + + if let quantity = glucose?.quantity, let formatted = glucoseFormatter.string(from: quantity, for: glucoseUnit) { + cell.detailTextLabel?.text = formatted + } else { + cell.detailTextLabel?.text = SettingsTableViewCell.NoValueString + } + case .date: + cell.textLabel?.text = LocalizedString("Date", comment: "Title describing glucose date") + + if let date = glucose?.timestamp { + cell.detailTextLabel?.text = dateFormatter.string(from: date) + } else { + cell.detailTextLabel?.text = SettingsTableViewCell.NoValueString + } + case .trend: + cell.textLabel?.text = LocalizedString("Trend", comment: "Title describing glucose trend") + + cell.detailTextLabel?.text = glucose?.trendType?.localizedDescription ?? SettingsTableViewCell.NoValueString + } + + return cell + case .delete: + let cell = tableView.dequeueReusableCell(withIdentifier: TextButtonTableViewCell.className, for: indexPath) as! TextButtonTableViewCell + + cell.textLabel?.text = LocalizedString("Delete CGM", comment: "Title text for the button to remove a CGM from Loop") + cell.textLabel?.textAlignment = .center + cell.tintColor = .delete + cell.isEnabled = true + return cell + } + } + + public override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + switch Section(rawValue: section)! { + case .authentication: + return nil + case .latestReading: + return LocalizedString("Latest Reading", comment: "Section title for latest glucose reading") + case .delete: + return nil + } + } + + public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + switch Section(rawValue: indexPath.section)! { + case .authentication: + let vc = AuthenticationViewController(authentication: cgmManager.shareService) + vc.authenticationObserver = { [weak self] (service) in + self?.cgmManager.shareService = service + + self?.tableView.reloadRows(at: [indexPath], with: .none) + } + + show(vc, sender: nil) + case .latestReading: + tableView.deselectRow(at: indexPath, animated: true) + case .delete: + let confirmVC = UIAlertController(cgmDeletionHandler: { + self.cgmManager.cgmManagerDelegate?.cgmManagerWantsDeletion(self.cgmManager) + self.navigationController?.popViewController(animated: true) + }) + + present(confirmVC, animated: true) { + tableView.deselectRow(at: indexPath, animated: true) + } + } + } +} + + +private extension UIAlertController { + convenience init(cgmDeletionHandler handler: @escaping () -> Void) { + self.init( + title: nil, + message: LocalizedString("Are you sure you want to delete this CGM?", comment: "Confirmation message for deleting a CGM"), + preferredStyle: .actionSheet + ) + + addAction(UIAlertAction( + title: LocalizedString("Delete CGM", comment: "Button title to delete CGM"), + style: .destructive, + handler: { (_) in + handler() + } + )) + + let cancel = LocalizedString("Cancel", comment: "The title of the cancel action in an action sheet") + addAction(UIAlertAction(title: cancel, style: .cancel, handler: nil)) + } +} diff --git a/ShareClientUI/ShareClientSetupViewController.swift b/ShareClientUI/ShareClientSetupViewController.swift new file mode 100644 index 0000000..79ee2fa --- /dev/null +++ b/ShareClientUI/ShareClientSetupViewController.swift @@ -0,0 +1,47 @@ +// +// ShareClientSetupViewController.swift +// Loop +// +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import UIKit +import LoopKit +import LoopKitUI +import ShareClient + + +class ShareClientSetupViewController: UINavigationController, CGMManagerSetupViewController { + var setupDelegate: CGMManagerSetupViewControllerDelegate? + + let cgmManager = ShareClientManager() + + init() { + let authVC = AuthenticationViewController(authentication: cgmManager.shareService) + + super.init(rootViewController: authVC) + + authVC.authenticationObserver = { [weak self] (service) in + self?.cgmManager.shareService = service + } + authVC.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancel)) + authVC.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(save)) + } + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func cancel() { + setupDelegate?.cgmManagerSetupViewControllerDidCancel(self) + } + + @objc private func save() { + setupDelegate?.cgmManagerSetupViewController(self, didSetUpCGMManager: cgmManager) + } + +} diff --git a/ShareClientUI/ShareClientUI.h b/ShareClientUI/ShareClientUI.h new file mode 100644 index 0000000..12a7a32 --- /dev/null +++ b/ShareClientUI/ShareClientUI.h @@ -0,0 +1,19 @@ +// +// ShareClientUI.h +// ShareClientUI +// +// Created by Nathan Racklyeft on 7/29/18. +// Copyright © 2018 Mark Wilson. All rights reserved. +// + +#import + +//! Project version number for ShareClientUI. +FOUNDATION_EXPORT double ShareClientUIVersionNumber; + +//! Project version string for ShareClientUI. +FOUNDATION_EXPORT const unsigned char ShareClientUIVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/ShareClientUI/ShareService+UI.swift b/ShareClientUI/ShareService+UI.swift new file mode 100644 index 0000000..1fc86e2 --- /dev/null +++ b/ShareClientUI/ShareService+UI.swift @@ -0,0 +1,38 @@ +// +// ShareService+UI.swift +// Loop +// +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import LoopKitUI +import ShareClient + + +extension ShareService: ServiceAuthenticationUI { + public var credentialFormFields: [ServiceCredential] { + return [ + ServiceCredential( + title: LocalizedString("Username", comment: "The title of the Dexcom share username credential"), + isSecret: false, + keyboardType: .asciiCapable + ), + ServiceCredential( + title: LocalizedString("Password", comment: "The title of the Dexcom share password credential"), + isSecret: true, + keyboardType: .asciiCapable + ), + ServiceCredential( + title: LocalizedString("Server", comment: "The title of the Dexcom share server URL credential"), + isSecret: false, + options: [ + (title: LocalizedString("US", comment: "U.S. share server option title"), + value: KnownShareServers.US.rawValue), + (title: LocalizedString("Outside US", comment: "Outside US share server option title"), + value: KnownShareServers.NON_US.rawValue) + + ] + ) + ] + } +} diff --git a/ShareClientUI/UIColor.swift b/ShareClientUI/UIColor.swift new file mode 100644 index 0000000..0095c6b --- /dev/null +++ b/ShareClientUI/UIColor.swift @@ -0,0 +1,22 @@ +// +// UIColor.swift +// LoopKitUI +// +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import UIKit + + +extension UIColor { + static let delete = UIColor.higRed() +} + + +// MARK: - HIG colors +// See: https://developer.apple.com/ios/human-interface-guidelines/visual-design/color/ +extension UIColor { + private static func higRed() -> UIColor { + return UIColor(red: 1, green: 59 / 255, blue: 48 / 255, alpha: 1) + } +} diff --git a/ShareClientUI/de.lproj/Localizable.strings b/ShareClientUI/de.lproj/Localizable.strings new file mode 100644 index 0000000..b674fae Binary files /dev/null and b/ShareClientUI/de.lproj/Localizable.strings differ diff --git a/ShareClientUI/es.lproj/Localizable.strings b/ShareClientUI/es.lproj/Localizable.strings new file mode 100644 index 0000000..2692614 Binary files /dev/null and b/ShareClientUI/es.lproj/Localizable.strings differ diff --git a/ShareClientUI/fr.lproj/Localizable.strings b/ShareClientUI/fr.lproj/Localizable.strings new file mode 100644 index 0000000..22f3090 Binary files /dev/null and b/ShareClientUI/fr.lproj/Localizable.strings differ diff --git a/ShareClientUI/it.lproj/Localizable.strings b/ShareClientUI/it.lproj/Localizable.strings new file mode 100644 index 0000000..0d0428a Binary files /dev/null and b/ShareClientUI/it.lproj/Localizable.strings differ diff --git a/ShareClientUI/nb.lproj/Localizable.strings b/ShareClientUI/nb.lproj/Localizable.strings new file mode 100644 index 0000000..3b6f0f3 Binary files /dev/null and b/ShareClientUI/nb.lproj/Localizable.strings differ diff --git a/ShareClientUI/nl.lproj/Localizable.strings b/ShareClientUI/nl.lproj/Localizable.strings new file mode 100644 index 0000000..19cf30d Binary files /dev/null and b/ShareClientUI/nl.lproj/Localizable.strings differ diff --git a/ShareClientUI/pl.lproj/Localizable.strings b/ShareClientUI/pl.lproj/Localizable.strings new file mode 100644 index 0000000..99edf0d Binary files /dev/null and b/ShareClientUI/pl.lproj/Localizable.strings differ diff --git a/ShareClientUI/ru.lproj/Localizable.strings b/ShareClientUI/ru.lproj/Localizable.strings new file mode 100644 index 0000000..a1c724d Binary files /dev/null and b/ShareClientUI/ru.lproj/Localizable.strings differ diff --git a/ShareClientUI/zh-Hans.lproj/Localizable.strings b/ShareClientUI/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000..e444031 Binary files /dev/null and b/ShareClientUI/zh-Hans.lproj/Localizable.strings differ