feat: 支持原生模块健康数据
This commit is contained in:
70
ios/OutLive/AppDelegate.swift
Normal file
70
ios/OutLive/AppDelegate.swift
Normal file
@@ -0,0 +1,70 @@
|
||||
import Expo
|
||||
import React
|
||||
import ReactAppDependencyProvider
|
||||
|
||||
@UIApplicationMain
|
||||
public class AppDelegate: ExpoAppDelegate {
|
||||
var window: UIWindow?
|
||||
|
||||
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
|
||||
var reactNativeFactory: RCTReactNativeFactory?
|
||||
|
||||
public override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||||
) -> Bool {
|
||||
let delegate = ReactNativeDelegate()
|
||||
let factory = ExpoReactNativeFactory(delegate: delegate)
|
||||
delegate.dependencyProvider = RCTAppDependencyProvider()
|
||||
|
||||
reactNativeDelegate = delegate
|
||||
reactNativeFactory = factory
|
||||
bindReactNativeFactory(factory)
|
||||
|
||||
#if os(iOS) || os(tvOS)
|
||||
window = UIWindow(frame: UIScreen.main.bounds)
|
||||
factory.startReactNative(
|
||||
withModuleName: "main",
|
||||
in: window,
|
||||
launchOptions: launchOptions)
|
||||
#endif
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
// Linking API
|
||||
public override func application(
|
||||
_ app: UIApplication,
|
||||
open url: URL,
|
||||
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
||||
) -> Bool {
|
||||
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
|
||||
}
|
||||
|
||||
// Universal Links
|
||||
public override func application(
|
||||
_ application: UIApplication,
|
||||
continue userActivity: NSUserActivity,
|
||||
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
|
||||
) -> Bool {
|
||||
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
||||
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
|
||||
}
|
||||
}
|
||||
|
||||
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
|
||||
// Extension point for config-plugins
|
||||
|
||||
override func sourceURL(for bridge: RCTBridge) -> URL? {
|
||||
// needed to return the correct URL for expo-dev-client.
|
||||
bridge.bundleURL ?? bundleURL()
|
||||
}
|
||||
|
||||
override func bundleURL() -> URL? {
|
||||
#if DEBUG
|
||||
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
|
||||
#else
|
||||
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
16
ios/OutLive/HealthKitManager.m
Normal file
16
ios/OutLive/HealthKitManager.m
Normal file
@@ -0,0 +1,16 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
|
||||
@interface RCT_EXTERN_MODULE(HealthKitManager, NSObject)
|
||||
|
||||
RCT_EXTERN_METHOD(requestAuthorization:(RCTPromiseResolveBlock)resolver
|
||||
rejecter:(RCTPromiseRejectBlock)rejecter)
|
||||
|
||||
RCT_EXTERN_METHOD(getSleepData:(NSDictionary *)options
|
||||
resolver:(RCTPromiseResolveBlock)resolver
|
||||
rejecter:(RCTPromiseRejectBlock)rejecter)
|
||||
|
||||
|
||||
RCT_EXTERN_METHOD(getAuthorizationStatus:(RCTPromiseResolveBlock)resolver
|
||||
rejecter:(RCTPromiseRejectBlock)rejecter)
|
||||
|
||||
@end
|
||||
260
ios/OutLive/HealthKitManager.swift
Normal file
260
ios/OutLive/HealthKitManager.swift
Normal file
@@ -0,0 +1,260 @@
|
||||
//
|
||||
// HealthKitManager.swift
|
||||
// digitalpilates
|
||||
//
|
||||
// Native module for HealthKit authorization and sleep data access
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import React
|
||||
import HealthKit
|
||||
|
||||
@objc(HealthKitManager)
|
||||
class HealthKitManager: NSObject, RCTBridgeModule {
|
||||
|
||||
// HealthKit store instance
|
||||
private let healthStore = HKHealthStore()
|
||||
|
||||
static func moduleName() -> String! {
|
||||
return "HealthKitManager"
|
||||
}
|
||||
|
||||
static func requiresMainQueueSetup() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: - Authorization
|
||||
|
||||
@objc
|
||||
func requestAuthorization(
|
||||
_ resolver: @escaping RCTPromiseResolveBlock,
|
||||
rejecter: @escaping RCTPromiseRejectBlock
|
||||
) {
|
||||
|
||||
// Check if HealthKit is available on the device
|
||||
guard HKHealthStore.isHealthDataAvailable() else {
|
||||
rejecter("HEALTHKIT_NOT_AVAILABLE", "HealthKit is not available on this device", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Define the data types we want to read
|
||||
let readTypes: Set<HKObjectType> = [
|
||||
HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!,
|
||||
HKObjectType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKObjectType.quantityType(forIdentifier: .heartRate)!,
|
||||
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
|
||||
HKObjectType.quantityType(forIdentifier: .heartRateVariabilitySDNN)!,
|
||||
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!
|
||||
]
|
||||
|
||||
// Define the data types we want to write (if any)
|
||||
let writeTypes: Set<HKSampleType> = [
|
||||
HKObjectType.quantityType(forIdentifier: .bodyMass)!
|
||||
]
|
||||
|
||||
// Request authorization
|
||||
healthStore.requestAuthorization(toShare: writeTypes, read: readTypes) { [weak self] (success, error) in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
rejecter("AUTHORIZATION_ERROR", "Failed to authorize HealthKit: \(error.localizedDescription)", error)
|
||||
return
|
||||
}
|
||||
|
||||
if success {
|
||||
// Add a small delay to ensure HealthKit has updated permission status
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
// Check individual permissions
|
||||
var permissions: [String: Any] = [:]
|
||||
|
||||
for type in readTypes {
|
||||
let status = self?.healthStore.authorizationStatus(for: type)
|
||||
let statusString = self?.authorizationStatusToString(status)
|
||||
permissions[type.identifier] = statusString
|
||||
}
|
||||
|
||||
let result: [String: Any] = [
|
||||
"success": true,
|
||||
"permissions": permissions
|
||||
]
|
||||
|
||||
resolver(result)
|
||||
}
|
||||
} else {
|
||||
rejecter("AUTHORIZATION_DENIED", "User denied HealthKit authorization", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Permission Status
|
||||
|
||||
@objc
|
||||
func getAuthorizationStatus(
|
||||
_ resolver: @escaping RCTPromiseResolveBlock,
|
||||
rejecter: @escaping RCTPromiseRejectBlock
|
||||
) {
|
||||
|
||||
// Check if HealthKit is available on the device
|
||||
guard HKHealthStore.isHealthDataAvailable() else {
|
||||
rejecter("HEALTHKIT_NOT_AVAILABLE", "HealthKit is not available on this device", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Define the data types we want to check
|
||||
let readTypes: Set<HKObjectType> = [
|
||||
HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!,
|
||||
HKObjectType.quantityType(forIdentifier: .stepCount)!,
|
||||
HKObjectType.quantityType(forIdentifier: .heartRate)!,
|
||||
HKObjectType.quantityType(forIdentifier: .restingHeartRate)!,
|
||||
HKObjectType.quantityType(forIdentifier: .heartRateVariabilitySDNN)!,
|
||||
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!
|
||||
]
|
||||
|
||||
// Check individual permissions
|
||||
var permissions: [String: Any] = [:]
|
||||
|
||||
for type in readTypes {
|
||||
let status = healthStore.authorizationStatus(for: type)
|
||||
let statusString = authorizationStatusToString(status)
|
||||
permissions[type.identifier] = statusString
|
||||
}
|
||||
|
||||
let result: [String: Any] = [
|
||||
"success": true,
|
||||
"permissions": permissions
|
||||
]
|
||||
|
||||
resolver(result)
|
||||
}
|
||||
|
||||
// MARK: - Sleep Data
|
||||
|
||||
@objc
|
||||
func getSleepData(
|
||||
_ options: NSDictionary,
|
||||
resolver: @escaping RCTPromiseResolveBlock,
|
||||
rejecter: @escaping RCTPromiseRejectBlock
|
||||
) {
|
||||
|
||||
guard HKHealthStore.isHealthDataAvailable() else {
|
||||
rejecter("HEALTHKIT_NOT_AVAILABLE", "HealthKit is not available on this device", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Check authorization status for sleep analysis
|
||||
let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!
|
||||
let authStatus = healthStore.authorizationStatus(for: sleepType)
|
||||
|
||||
guard authStatus == .sharingAuthorized else {
|
||||
rejecter("NOT_AUTHORIZED", "Not authorized to read sleep data", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse options
|
||||
let startDate = parseDate(from: options["startDate"] as? String) ?? Calendar.current.date(byAdding: .day, value: -7, to: Date())!
|
||||
let endDate = parseDate(from: options["endDate"] as? String) ?? Date()
|
||||
let limit = options["limit"] as? Int ?? 100
|
||||
|
||||
// Create predicate for date range
|
||||
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
|
||||
|
||||
// Create sort descriptor to get latest data first
|
||||
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
|
||||
|
||||
// Create query
|
||||
let query = HKSampleQuery(
|
||||
sampleType: sleepType,
|
||||
predicate: predicate,
|
||||
limit: limit,
|
||||
sortDescriptors: [sortDescriptor]
|
||||
) { [weak self] (query, samples, error) in
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
rejecter("QUERY_ERROR", "Failed to query sleep data: \(error.localizedDescription)", error)
|
||||
return
|
||||
}
|
||||
|
||||
guard let sleepSamples = samples as? [HKCategorySample] else {
|
||||
resolver([])
|
||||
return
|
||||
}
|
||||
|
||||
let sleepData = sleepSamples.map { sample in
|
||||
return [
|
||||
"id": sample.uuid.uuidString,
|
||||
"startDate": self?.dateToISOString(sample.startDate) ?? "",
|
||||
"endDate": self?.dateToISOString(sample.endDate) ?? "",
|
||||
"value": sample.value,
|
||||
"categoryType": self?.sleepValueToString(sample.value) ?? "unknown",
|
||||
"duration": sample.endDate.timeIntervalSince(sample.startDate),
|
||||
"source": [
|
||||
"name": sample.sourceRevision.source.name,
|
||||
"bundleIdentifier": sample.sourceRevision.source.bundleIdentifier
|
||||
],
|
||||
"metadata": sample.metadata ?? [:]
|
||||
]
|
||||
}
|
||||
|
||||
let result: [String: Any] = [
|
||||
"data": sleepData,
|
||||
"count": sleepData.count,
|
||||
"startDate": self?.dateToISOString(startDate) ?? "",
|
||||
"endDate": self?.dateToISOString(endDate) ?? ""
|
||||
]
|
||||
|
||||
resolver(result)
|
||||
}
|
||||
}
|
||||
|
||||
healthStore.execute(query)
|
||||
}
|
||||
|
||||
// MARK: - Helper Methods
|
||||
|
||||
private func authorizationStatusToString(_ status: HKAuthorizationStatus?) -> String {
|
||||
guard let status = status else { return "notDetermined" }
|
||||
|
||||
switch status {
|
||||
case .notDetermined:
|
||||
return "notDetermined"
|
||||
case .sharingDenied:
|
||||
return "denied"
|
||||
case .sharingAuthorized:
|
||||
return "authorized"
|
||||
@unknown default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
private func sleepValueToString(_ value: Int) -> String {
|
||||
switch value {
|
||||
case HKCategoryValueSleepAnalysis.inBed.rawValue:
|
||||
return "inBed"
|
||||
case HKCategoryValueSleepAnalysis.asleepUnspecified.rawValue:
|
||||
return "asleep"
|
||||
case HKCategoryValueSleepAnalysis.awake.rawValue:
|
||||
return "awake"
|
||||
case HKCategoryValueSleepAnalysis.asleepCore.rawValue:
|
||||
return "core"
|
||||
case HKCategoryValueSleepAnalysis.asleepDeep.rawValue:
|
||||
return "deep"
|
||||
case HKCategoryValueSleepAnalysis.asleepREM.rawValue:
|
||||
return "rem"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
private func parseDate(from string: String?) -> Date? {
|
||||
guard let string = string else { return nil }
|
||||
|
||||
let formatter = ISO8601DateFormatter()
|
||||
return formatter.date(from: string)
|
||||
}
|
||||
|
||||
private func dateToISOString(_ date: Date) -> String {
|
||||
let formatter = ISO8601DateFormatter()
|
||||
return formatter.string(from: date)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 248 KiB |
14
ios/OutLive/Images.xcassets/AppIcon.appiconset/Contents.json
Normal file
14
ios/OutLive/Images.xcassets/AppIcon.appiconset/Contents.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "App-Icon-1024x1024@1x.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
||||
6
ios/OutLive/Images.xcassets/Contents.json
Normal file
6
ios/OutLive/Images.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "expo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors": [
|
||||
{
|
||||
"color": {
|
||||
"components": {
|
||||
"alpha": "1.000",
|
||||
"blue": "1.00000000000000",
|
||||
"green": "1.00000000000000",
|
||||
"red": "1.00000000000000"
|
||||
},
|
||||
"color-space": "srgb"
|
||||
},
|
||||
"idiom": "universal"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
||||
23
ios/OutLive/Images.xcassets/SplashScreenLogo.imageset/Contents.json
vendored
Normal file
23
ios/OutLive/Images.xcassets/SplashScreenLogo.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"idiom": "universal",
|
||||
"filename": "image.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"filename": "image@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"filename": "image@3x.png",
|
||||
"scale": "3x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
||||
BIN
ios/OutLive/Images.xcassets/SplashScreenLogo.imageset/image.png
vendored
Normal file
BIN
ios/OutLive/Images.xcassets/SplashScreenLogo.imageset/image.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
ios/OutLive/Images.xcassets/SplashScreenLogo.imageset/image@2x.png
vendored
Normal file
BIN
ios/OutLive/Images.xcassets/SplashScreenLogo.imageset/image@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
BIN
ios/OutLive/Images.xcassets/SplashScreenLogo.imageset/image@3x.png
vendored
Normal file
BIN
ios/OutLive/Images.xcassets/SplashScreenLogo.imageset/image@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
BIN
ios/OutLive/Images.xcassets/drink_water.imageset/1x.png
vendored
Normal file
BIN
ios/OutLive/Images.xcassets/drink_water.imageset/1x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
23
ios/OutLive/Images.xcassets/drink_water.imageset/Contents.json
vendored
Normal file
23
ios/OutLive/Images.xcassets/drink_water.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "1x",
|
||||
"filename": "1x.png"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "2x",
|
||||
"filename": "1x.png"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "3x",
|
||||
"filename": "1x.png"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
||||
104
ios/OutLive/Info.plist
Normal file
104
ios/OutLive/Info.plist
Normal file
@@ -0,0 +1,104 @@
|
||||
<?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>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>com.expo.modules.backgroundtask.processing</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Out Live</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.12</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>digitalpilates</string>
|
||||
<string>com.anonymous.digitalpilates</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>应用需要使用相机以拍摄您的体态照片用于AI测评。</string>
|
||||
<key>NSHealthShareUsageDescription</key>
|
||||
<string>应用需要访问您的健康数据(步数、能量消耗、心率变异性等)以展示运动统计和压力分析。</string>
|
||||
<key>NSHealthUpdateUsageDescription</key>
|
||||
<string>应用需要更新您的健康数据(体重信息)以记录您的健身进度。</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>应用需要使用麦克风进行语音识别,将您的语音转换为文字记录饮食信息。</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>应用需要写入相册以保存拍摄的体态照片(可选)。</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>应用需要访问相册以选择您的体态照片用于AI测评。</string>
|
||||
<key>NSSpeechRecognitionUsageDescription</key>
|
||||
<string>应用需要使用语音识别功能来转换您的语音为文字,帮助您快速记录饮食信息。</string>
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>
|
||||
</array>
|
||||
<key>NSUserNotificationsUsageDescription</key>
|
||||
<string>应用需要发送通知以提醒您喝水和站立活动。</string>
|
||||
<key>RCTNewArchEnabled</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>processing</string>
|
||||
<string>fetch</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleDefault</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Light</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
5
ios/OutLive/OutLive-Bridging-Header.h
Normal file
5
ios/OutLive/OutLive-Bridging-Header.h
Normal file
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTViewManager.h>
|
||||
16
ios/OutLive/OutLive.entitlements
Normal file
16
ios/OutLive/OutLive.entitlements
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.developer.applesignin</key>
|
||||
<array>
|
||||
<string>Default</string>
|
||||
</array>
|
||||
<key>com.apple.developer.healthkit</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.healthkit.access</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
48
ios/OutLive/PrivacyInfo.xcprivacy
Normal file
48
ios/OutLive/PrivacyInfo.xcprivacy
Normal file
@@ -0,0 +1,48 @@
|
||||
<?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>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
<string>0A2A.1</string>
|
||||
<string>3B52.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>E174.1</string>
|
||||
<string>85F4.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
46
ios/OutLive/SplashScreen.storyboard
Normal file
46
ios/OutLive/SplashScreen.storyboard
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24053.1"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<scene sceneID="EXPO-SCENE-1">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="SplashScreenViewController" id="EXPO-VIEWCONTROLLER-1" sceneMemberID="viewController">
|
||||
<view key="view" userInteractionEnabled="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="EXPO-ContainerView" userLabel="ContainerView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView id="EXPO-SplashScreen" userLabel="SplashScreenLogo" image="SplashScreenLogo" contentMode="scaleAspectFit" clipsSubviews="true" userInteractionEnabled="false" translatesAutoresizingMaskIntoConstraints="false">
|
||||
<rect key="frame" x="176.5" y="406" width="40" height="40"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="Rmq-lb-GrQ"/>
|
||||
<constraints>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="centerX" secondItem="EXPO-ContainerView" secondAttribute="centerX" id="cad2ab56f97c5429bf29decf850647a4216861d4"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="centerY" secondItem="EXPO-ContainerView" secondAttribute="centerY" id="1a145271b085b6ce89b1405a310f5b1bb7656595"/>
|
||||
</constraints>
|
||||
<color key="backgroundColor" name="SplashScreenBackground"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="EXPO-PLACEHOLDER-1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="0.0" y="0.0"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="SplashScreenLogo" width="40" height="40"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<namedColor name="SplashScreenBackground">
|
||||
<color alpha="1.000" blue="1.00000000000000" green="1.00000000000000" red="1.00000000000000" customColorSpace="sRGB" colorSpace="custom"/>
|
||||
</namedColor>
|
||||
</resources>
|
||||
</document>
|
||||
12
ios/OutLive/Supporting/Expo.plist
Normal file
12
ios/OutLive/Supporting/Expo.plist
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
<key>EXUpdatesCheckOnLaunch</key>
|
||||
<string>ALWAYS</string>
|
||||
<key>EXUpdatesEnabled</key>
|
||||
<false/>
|
||||
<key>EXUpdatesLaunchWaitMs</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user