Compare commits

...

4 Commits

Author SHA1 Message Date
Christoph Hagen
9b07f498dd Update dependencies, fix warnings 2025-01-31 14:19:35 +01:00
Christoph Hagen
3bfb256026 Create .gitignore 2025-01-31 11:59:07 +01:00
Christoph Hagen
1a0550523f Update logo, add image to readme 2024-04-15 23:40:32 +02:00
Christoph Hagen
29d38f3ad6 Add short readme 2024-04-15 16:23:53 +02:00
13 changed files with 89 additions and 43 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.DS_Store

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 KiB

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

View File

@@ -11,7 +11,6 @@
8850025D2B5C273C00E7D4DB /* WorkoutTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8850025C2B5C273C00E7D4DB /* WorkoutTab.swift */; };
8850025F2B5C273E00E7D4DB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8850025E2B5C273E00E7D4DB /* Assets.xcassets */; };
885002622B5C273E00E7D4DB /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 885002612B5C273E00E7D4DB /* Preview Assets.xcassets */; };
885002772B5C2FC400E7D4DB /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = 885002762B5C2FC400E7D4DB /* SQLite */; };
885002792B5C320400E7D4DB /* Optional+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885002782B5C320400E7D4DB /* Optional+Extensions.swift */; };
8850028D2B5D0B5000E7D4DB /* WorkoutDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8850028C2B5D0B5000E7D4DB /* WorkoutDetailView.swift */; };
8850028F2B5D0EAF00E7D4DB /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8850028E2B5D0EAF00E7D4DB /* Date+Extensions.swift */; };
@@ -33,6 +32,8 @@
E27BC6942B5FD587003A8873 /* Workout+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27BC6932B5FD587003A8873 /* Workout+Mock.swift */; };
E27BC6962B5FD61D003A8873 /* WorkoutEvent+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27BC6952B5FD61D003A8873 /* WorkoutEvent+Mock.swift */; };
E27BC6982B5FD76F003A8873 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27BC6972B5FD76F003A8873 /* Data+Extensions.swift */; };
E29E17BF2D4D04A200E0EE54 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = E29E17BE2D4D04A200E0EE54 /* SQLite */; };
E29E17C22D4D04B900E0EE54 /* HealthDB in Frameworks */ = {isa = PBXBuildFile; productRef = E29E17C12D4D04B900E0EE54 /* HealthDB */; };
E2A38EA32B9A024500BAD02E /* Workout+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A38EA22B9A024500BAD02E /* Workout+Extensions.swift */; };
E2A38EA52B9C6EA900BAD02E /* SearchHealthStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2A38EA42B9C6EA900BAD02E /* SearchHealthStoreView.swift */; };
E2A38EA82B9C6EE800BAD02E /* SFSafeSymbols in Frameworks */ = {isa = PBXBuildFile; productRef = E2A38EA72B9C6EE800BAD02E /* SFSafeSymbols */; };
@@ -60,7 +61,6 @@
E2E552902BA236A000BF5E9B /* DatabaseList.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E5528F2BA236A000BF5E9B /* DatabaseList.swift */; };
E2E552922BA236D000BF5E9B /* DatabaseFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E552912BA236D000BF5E9B /* DatabaseFile.swift */; };
E2E5529B2BA3935600BF5E9B /* HKWorkout+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E5529A2BA3935600BF5E9B /* HKWorkout+Extensions.swift */; };
E2E5529E2BA47BA600BF5E9B /* HealthDB in Frameworks */ = {isa = PBXBuildFile; productRef = E2E5529D2BA47BA600BF5E9B /* HealthDB */; };
E2E552A12BA4B14600BF5E9B /* HeartRateSample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E552A02BA4B14600BF5E9B /* HeartRateSample.swift */; };
E2E552A32BA4B58F00BF5E9B /* HeartRateGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E552A22BA4B58F00BF5E9B /* HeartRateGraph.swift */; };
E2E552A72BA7531C00BF5E9B /* Event+Identifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2E552A62BA7531C00BF5E9B /* Event+Identifiable.swift */; };
@@ -122,6 +122,7 @@
E2D82B4C2BCD5F780075EAF0 /* SymptomsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymptomsList.swift; sourceTree = "<group>"; };
E2D82B4E2BCD61590075EAF0 /* VitalsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VitalsList.swift; sourceTree = "<group>"; };
E2D82B502BCD626D0075EAF0 /* OtherSamplesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherSamplesList.swift; sourceTree = "<group>"; };
E2D82B522BCD6CC10075EAF0 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
E2E552882BA2194400BF5E9B /* DatabasesTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabasesTab.swift; sourceTree = "<group>"; };
E2E5528B2BA21C0700BF5E9B /* Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = "<group>"; };
E2E5528D2BA21C5900BF5E9B /* FileManager+Directory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Directory.swift"; sourceTree = "<group>"; };
@@ -153,10 +154,10 @@
files = (
885002A62B5D296700E7D4DB /* Collections in Frameworks */,
E20881D32B76912000D41D95 /* HealthKitExtensions in Frameworks */,
885002772B5C2FC400E7D4DB /* SQLite in Frameworks */,
885002AA2B5D296700E7D4DB /* OrderedCollections in Frameworks */,
E2E5529E2BA47BA600BF5E9B /* HealthDB in Frameworks */,
885002A82B5D296700E7D4DB /* DequeModule in Frameworks */,
E29E17C22D4D04B900E0EE54 /* HealthDB in Frameworks */,
E29E17BF2D4D04A200E0EE54 /* SQLite in Frameworks */,
E2A38EA82B9C6EE800BAD02E /* SFSafeSymbols in Frameworks */,
E2FDFF202B6BE34C0080A7B3 /* SwiftProtobuf in Frameworks */,
);
@@ -168,6 +169,7 @@
8850024E2B5C273C00E7D4DB = {
isa = PBXGroup;
children = (
E2D82B522BCD6CC10075EAF0 /* README.md */,
885002592B5C273C00E7D4DB /* HealthImport */,
885002582B5C273C00E7D4DB /* Products */,
E2E5529C2BA47BA600BF5E9B /* Frameworks */,
@@ -333,14 +335,14 @@
);
name = HealthImport;
packageProductDependencies = (
885002762B5C2FC400E7D4DB /* SQLite */,
885002A52B5D296700E7D4DB /* Collections */,
885002A72B5D296700E7D4DB /* DequeModule */,
885002A92B5D296700E7D4DB /* OrderedCollections */,
E2FDFF1F2B6BE34C0080A7B3 /* SwiftProtobuf */,
E20881D22B76912000D41D95 /* HealthKitExtensions */,
E2A38EA72B9C6EE800BAD02E /* SFSafeSymbols */,
E2E5529D2BA47BA600BF5E9B /* HealthDB */,
E29E17BE2D4D04A200E0EE54 /* SQLite */,
E29E17C12D4D04B900E0EE54 /* HealthDB */,
);
productName = HealthImport;
productReference = 885002572B5C273C00E7D4DB /* HealthImport.app */;
@@ -371,12 +373,12 @@
);
mainGroup = 8850024E2B5C273C00E7D4DB;
packageReferences = (
885002752B5C2FC400E7D4DB /* XCRemoteSwiftPackageReference "SQLite" */,
885002A42B5D296700E7D4DB /* XCRemoteSwiftPackageReference "swift-collections" */,
E2FDFF1E2B6BE34C0080A7B3 /* XCRemoteSwiftPackageReference "swift-protobuf" */,
E20881D12B76912000D41D95 /* XCRemoteSwiftPackageReference "HealthKitExtensions" */,
E2A38EA62B9C6EE800BAD02E /* XCRemoteSwiftPackageReference "SFSafeSymbols" */,
E2E552972BA3748500BF5E9B /* XCRemoteSwiftPackageReference "HealthDB" */,
E29E17BD2D4D04A200E0EE54 /* XCRemoteSwiftPackageReference "SQLite" */,
E29E17C02D4D04B900E0EE54 /* XCRemoteSwiftPackageReference "HealthDB" */,
);
productRefGroup = 885002582B5C273C00E7D4DB /* Products */;
projectDirPath = "";
@@ -686,14 +688,6 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
885002752B5C2FC400E7D4DB /* XCRemoteSwiftPackageReference "SQLite" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/stephencelis/SQLite.swift";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.14.1;
};
};
885002A42B5D296700E7D4DB /* XCRemoteSwiftPackageReference "swift-collections" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-collections.git";
@@ -710,6 +704,22 @@
minimumVersion = 0.4.0;
};
};
E29E17BD2D4D04A200E0EE54 /* XCRemoteSwiftPackageReference "SQLite" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/stephencelis/SQLite.swift";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.15.3;
};
};
E29E17C02D4D04B900E0EE54 /* XCRemoteSwiftPackageReference "HealthDB" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/christophhagen/HealthDB";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.4.4;
};
};
E2A38EA62B9C6EE800BAD02E /* XCRemoteSwiftPackageReference "SFSafeSymbols" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols";
@@ -718,14 +728,6 @@
minimumVersion = 5.2.0;
};
};
E2E552972BA3748500BF5E9B /* XCRemoteSwiftPackageReference "HealthDB" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/christophhagen/HealthDB";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.4.0;
};
};
E2FDFF1E2B6BE34C0080A7B3 /* XCRemoteSwiftPackageReference "swift-protobuf" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-protobuf.git";
@@ -737,11 +739,6 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
885002762B5C2FC400E7D4DB /* SQLite */ = {
isa = XCSwiftPackageProductDependency;
package = 885002752B5C2FC400E7D4DB /* XCRemoteSwiftPackageReference "SQLite" */;
productName = SQLite;
};
885002A52B5D296700E7D4DB /* Collections */ = {
isa = XCSwiftPackageProductDependency;
package = 885002A42B5D296700E7D4DB /* XCRemoteSwiftPackageReference "swift-collections" */;
@@ -762,16 +759,21 @@
package = E20881D12B76912000D41D95 /* XCRemoteSwiftPackageReference "HealthKitExtensions" */;
productName = HealthKitExtensions;
};
E29E17BE2D4D04A200E0EE54 /* SQLite */ = {
isa = XCSwiftPackageProductDependency;
package = E29E17BD2D4D04A200E0EE54 /* XCRemoteSwiftPackageReference "SQLite" */;
productName = SQLite;
};
E29E17C12D4D04B900E0EE54 /* HealthDB */ = {
isa = XCSwiftPackageProductDependency;
package = E29E17C02D4D04B900E0EE54 /* XCRemoteSwiftPackageReference "HealthDB" */;
productName = HealthDB;
};
E2A38EA72B9C6EE800BAD02E /* SFSafeSymbols */ = {
isa = XCSwiftPackageProductDependency;
package = E2A38EA62B9C6EE800BAD02E /* XCRemoteSwiftPackageReference "SFSafeSymbols" */;
productName = SFSafeSymbols;
};
E2E5529D2BA47BA600BF5E9B /* HealthDB */ = {
isa = XCSwiftPackageProductDependency;
package = E2E552972BA3748500BF5E9B /* XCRemoteSwiftPackageReference "HealthDB" */;
productName = HealthDB;
};
E2FDFF1F2B6BE34C0080A7B3 /* SwiftProtobuf */ = {
isa = XCSwiftPackageProductDependency;
package = E2FDFF1E2B6BE34C0080A7B3 /* XCRemoteSwiftPackageReference "swift-protobuf" */;

View File

@@ -1,13 +1,13 @@
{
"originHash" : "5b8e27ff27b74293d3ae2085172fcc80a2317825fae6f3e7879caab9728af319",
"originHash" : "853d27802fcd1b74fc9030c3fd718aa74e5a159d9bd4e67bca057ac38d56b364",
"pins" : [
{
"identity" : "healthdb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/christophhagen/HealthDB",
"state" : {
"revision" : "cd077d034d88d31cf283bcf5c0663cb027496a66",
"version" : "0.4.1"
"revision" : "b02391d12d52623fa15fa79a097c7fefd16ddf32",
"version" : "0.4.4"
}
},
{
@@ -33,8 +33,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/stephencelis/SQLite.swift",
"state" : {
"revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb",
"version" : "0.14.1"
"revision" : "a95fc6df17d108bd99210db5e8a9bac90fe984b8",
"version" : "0.15.3"
}
},
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 461 KiB

After

Width:  |  Height:  |  Size: 432 KiB

View File

@@ -1,7 +1,7 @@
import Foundation
import HealthKit
extension HKWorkoutEvent: Identifiable {
extension HKWorkoutEvent: @retroactive Identifiable {
public var id: Int {
Int(dateInterval.start.timeIntervalSinceReferenceDate) << 16 +

View File

@@ -10,7 +10,7 @@ private let df: DateFormatter = {
return df
}()
extension Workout: Identifiable {
extension Workout: @retroactive Identifiable {
public var id: Int {
dataId

View File

@@ -83,7 +83,7 @@ struct SearchHealthStoreView: View {
SearchHealthStoreView()
}
extension HKQuantityTypeIdentifier: Identifiable {
extension HKQuantityTypeIdentifier: @retroactive Identifiable {
public var id: String {
rawValue

View File

@@ -449,7 +449,7 @@ struct WorkoutDetailView: View {
}
}
extension String: Identifiable {
extension String: @retroactive Identifiable {
public var id: Self { self }
}

43
README.md Normal file
View File

@@ -0,0 +1,43 @@
![](screens.png)
# HealthImport
This iOS app is a prototype to open the raw databases containing Apple Health data. These `healthdb_secure.sqlite` files can be extracted from (encrypted ) iOS Backups.
The structure of the database has been reverse-engineered so that most health data can be viewed and extracted.
This app is a front-end for [HealthDB](https://github.com/christophhagen/healthdb), which provides the interface to access the database files.
## Features
HealthImport offers a view basic features to view and extract health data, mostly centered around workouts.
### Managing databases
Multiple databases can be imported into the app.
The databases can be renamed and a default database can be selected.
One database can be opened at a time, so that the contents can be viewed.
### Workouts
The existing workouts contained in each database can be viewed in the "Workouts" tab.
The interface has been designed to be similar to the Apple Fitness app.
A filter allows selecting only those workouts of a specific activity type, and the details of a workout can be viewed.
It shows the workout route, heart rate, and statistics for each workout.
If the app has the required permissions then it also checks if a similar workout already exists on the phone, and can add missing workouts from the database to `Health`.
This feature is useful to restore workouts that have been deleted from an existing backup.
### Samples
The tab "Health" is structured similar to the Apple Health app, and allows to view different samples stored in the database.
Each supported type can be searched by start and end date, and shows a simple list of the samples found.
## Roadmap
In the future, additional features are envisioned:
- View most recent samples, similar to the Apple Health app
- Support viewing special sample types, like ECGs
- Show details for samples, and allow adding them to Apple Health
- Support more filtering options to find samples

BIN
screens.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB