Skip to content

Commit

Permalink
Merge pull request #269 from loopandlearn/testflight-build-expiration
Browse files Browse the repository at this point in the history
Testflight build expiration
  • Loading branch information
marionbarker committed Mar 25, 2024
2 parents 3ed9f74 + ea35959 commit 9e12b8f
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 5 deletions.
5 changes: 5 additions & 0 deletions BuildDetails.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
37 changes: 37 additions & 0 deletions LoopFollow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
DD7E198A2ACDA62600DBD158 /* SensorStart.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19892ACDA62600DBD158 /* SensorStart.swift */; };
DD7FFAFD2A72953000C3A304 /* EKEventStore+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */; };
DD98F54424BCEFEE0007425A /* ShareClientExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */; };
DDB0AF522BB1A8BE00AFA48B /* BuildDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB0AF512BB1A8BE00AFA48B /* BuildDetails.swift */; };
DDB0AF552BB1B24A00AFA48B /* BuildDetails.plist in Resources */ = {isa = PBXBuildFile; fileRef = DDB0AF542BB1B24A00AFA48B /* BuildDetails.plist */; };
DDCF979424C0D380002C9752 /* UIViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979324C0D380002C9752 /* UIViewExtension.swift */; };
DDCF979624C1443C002C9752 /* GeneralSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */; };
DDCF979824C1489C002C9752 /* GraphSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979724C1489C002C9752 /* GraphSettingsViewController.swift */; };
Expand Down Expand Up @@ -212,6 +214,9 @@
DD7E19892ACDA62600DBD158 /* SensorStart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorStart.swift; sourceTree = "<group>"; };
DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EKEventStore+Extensions.swift"; sourceTree = "<group>"; };
DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareClientExtension.swift; sourceTree = "<group>"; };
DDB0AF502BB1A84500AFA48B /* capture-build-details.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "capture-build-details.sh"; sourceTree = "<group>"; };
DDB0AF512BB1A8BE00AFA48B /* BuildDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildDetails.swift; sourceTree = "<group>"; };
DDB0AF542BB1B24A00AFA48B /* BuildDetails.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = BuildDetails.plist; sourceTree = "<group>"; };
DDCF979324C0D380002C9752 /* UIViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtension.swift; sourceTree = "<group>"; };
DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsViewController.swift; sourceTree = "<group>"; };
DDCF979724C1489C002C9752 /* GraphSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphSettingsViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -454,6 +459,14 @@
path = Extensions;
sourceTree = "<group>";
};
DDB0AF4F2BB1A81F00AFA48B /* Scripts */ = {
isa = PBXGroup;
children = (
DDB0AF502BB1A84500AFA48B /* capture-build-details.sh */,
);
path = Scripts;
sourceTree = "<group>";
};
FC16A97624995FEE003D6245 /* Application */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -632,6 +645,8 @@
FC97880B2485969B00A7906C = {
isa = PBXGroup;
children = (
DDB0AF542BB1B24A00AFA48B /* BuildDetails.plist */,
DDB0AF4F2BB1A81F00AFA48B /* Scripts */,
DDCFCAF12B17273200BE5751 /* LoopFollowDisplayNameConfig.xcconfig */,
FC3AE7B3249E8E0E00AAE1E0 /* LoopFollow.xcdatamodeld */,
FC5A5C3C2497B229009C550E /* Config.xcconfig */,
Expand Down Expand Up @@ -676,6 +691,7 @@
FCD2A27C24C9D044009F7B7B /* Globals.swift */,
FC8589BE252B54F500C8FC73 /* Mobileprovision.swift */,
DD07B5C829E2F9C400C6A635 /* NightscoutUtils.swift */,
DDB0AF512BB1A8BE00AFA48B /* BuildDetails.swift */,
);
path = helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -710,6 +726,7 @@
FC9788112485969B00A7906C /* Frameworks */,
FC9788122485969B00A7906C /* Resources */,
04DA71CCA0280FA5FA2DF7A6 /* [CP] Embed Pods Frameworks */,
DDB0AF532BB1AA0900AFA48B /* Capture Build Details */,
);
buildRules = (
);
Expand Down Expand Up @@ -786,6 +803,7 @@
FC7CE53F248ABE37001F83B8 /* Siri_Alert_Glucose_Dropping_Fast.caf in Resources */,
FC7CE561248ABE37001F83B8 /* Wrong_Answer.caf in Resources */,
FCC6885C2489559400A0279D /* blank.wav in Resources */,
DDB0AF552BB1B24A00AFA48B /* BuildDetails.plist in Resources */,
FC7CE528248ABE37001F83B8 /* Cartoon_Bounce_To_Ceiling.caf in Resources */,
FC7CE57B248ABE37001F83B8 /* Alert_Tone_Busy.caf in Resources */,
FC7CE51B248ABE37001F83B8 /* Thunder_Sound_FX.caf in Resources */,
Expand Down Expand Up @@ -924,6 +942,24 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
DDB0AF532BB1AA0900AFA48B /* Capture Build Details */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Capture Build Details";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Scripts/capture-build-details.sh\"\n";
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
Expand All @@ -947,6 +983,7 @@
DD7E19862ACDA59700DBD158 /* BGCheck.swift in Sources */,
FC16A97D24996747003D6245 /* Alarms.swift in Sources */,
FC16A97B249966A3003D6245 /* AlarmSound.swift in Sources */,
DDB0AF522BB1A8BE00AFA48B /* BuildDetails.swift in Sources */,
DD493AE72ACF23CF009A6922 /* DeviceStatus.swift in Sources */,
FCFEECA2248857A600402A7F /* SettingsViewController.swift in Sources */,
FCFEECA02488157B00402A7F /* Chart.swift in Sources */,
Expand Down
55 changes: 50 additions & 5 deletions LoopFollow/ViewControllers/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,50 @@ class SettingsViewController: FormViewController {

}

// Determine if the build is from TestFlight
func isTestFlightBuild() -> Bool {
#if targetEnvironment(simulator)
return false
#else
if Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision") != nil {
return false
}
guard let receiptName = Bundle.main.appStoreReceiptURL?.lastPathComponent else {
return false
}
return "sandboxReceipt".caseInsensitiveCompare(receiptName) == .orderedSame
#endif
}

// Get the build date from the build details
func buildDate() -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE MMM d HH:mm:ss 'UTC' yyyy"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(identifier: "UTC")

guard let dateString = BuildDetails.default.buildDateString,
let date = dateFormatter.date(from: dateString) else {
return nil
}
return date
}

// Calculate the expiration date based on the build type
func calculateExpirationDate() -> Date {
if isTestFlightBuild(), let buildDate = buildDate() {
// For TestFlight, add 90 days to the build date
return Calendar.current.date(byAdding: .day, value: 90, to: buildDate)!
} else {
// For Xcode builds, use the provisioning profile's expiration date
if let provision = MobileProvision.read() {
return provision.expirationDate
} else {
return Date() // Fallback to current date if unable to read provisioning profile
}
}
}

override func viewDidLoad() {
super.viewDidLoad()
if UserDefaultsRepository.forceDarkMode.value {
Expand All @@ -49,10 +93,11 @@ class SettingsViewController: FormViewController {
UserDefaultsRepository.showNS.value = false
UserDefaultsRepository.showDex.value = false

var expiration: Date = Date()
if let provision = MobileProvision.read() {
expiration = provision.expirationDate
}
let expiration = calculateExpirationDate()
var expirationHeaderString = "App Expiration"
if isTestFlightBuild() {
expirationHeaderString = "Beta (TestFlight) Expiration"
}

form
+++ Section(header: "Data Settings", footer: "")
Expand Down Expand Up @@ -254,7 +299,7 @@ class SettingsViewController: FormViewController {

+++ Section(header: getAppVersion(), footer: "")

+++ Section(header: "App Expiration", footer: String(expiration.description))
+++ Section(header: expirationHeaderString, footer: String(expiration.description))

showHideNSDetails()
checkNightscoutStatus()
Expand Down
29 changes: 29 additions & 0 deletions LoopFollow/helpers/BuildDetails.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// BuildDetails.swift
// LoopFollow
//
// Created by Jonas Björkert on 2024-03-25.
// Copyright © 2024 Jon Fawcett. All rights reserved.
//

import Foundation

class BuildDetails {
static var `default` = BuildDetails()

let dict: [String: Any]

init() {
guard let url = Bundle.main.url(forResource: "BuildDetails", withExtension: "plist"),
let data = try? Data(contentsOf: url),
let parsed = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any] else {
dict = [:]
return
}
dict = parsed
}

var buildDateString: String? {
return dict["com-LoopFollow-build-date"] as? String
}
}
20 changes: 20 additions & 0 deletions Scripts/capture-build-details.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/sh -e

# capture-build-details.sh
# LoopFollow
#
# Created by Jonas Björkert on 2024-03-25.
# Copyright © 2024 Jon Fawcett. All rights reserved.

info_plist_path="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/BuildDetails.plist"

# Ensure the path to BuildDetails.plist is valid.
if [ "${info_plist_path}" == "/" -o ! -e "${info_plist_path}" ]; then
echo "ERROR: BuildDetails.plist file does not exist at path: ${info_plist_path}" >&2
exit 1
fi

echo "Gathering build date..."

# Capture the current date and write it to BuildDetails.plist
plutil -replace com-LoopFollow-build-date -string "$(date)" "${info_plist_path}"

0 comments on commit 9e12b8f

Please sign in to comment.