Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testflight build expiration #269

Merged
merged 5 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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}"