Slide 1

Slide 1 text

͢΂ͯͷϔϧεέΞσʔλΛඥ ղ͘ iOSDC 2024/08/24 13:55ʙ TRACK A ೔޲ڧ

Slide 2

Slide 2 text

͢΂ͯͷϔϧεέΞσʔλ

Slide 3

Slide 3 text

͢΂ͯͷϔϧεέΞσʔλ iOS 17.6.1 •ӈ্ͷΞΠίϯΛΫϦοΫ

Slide 4

Slide 4 text

͢΂ͯͷϔϧεέΞσʔλ iOS 17.6.1 •࠷Լ෦ͷʮ͢΂ͯͷϔϧεέΞσʔλΛॻ͖ग़͢ʯΛԡԼ

Slide 5

Slide 5 text

ઈରʹʮ͢΂ͯͷϔϧεέΞσʔλΛॻ͖ग़͢ʯ͸ 
 ࠓԡ͞ͳ͍Ͱ͍ͩ͘͞

Slide 6

Slide 6 text

※ݸਓ͕ࠩ͋Γ·͢ ͢΂ͯͷϔϧεέΞσʔλ iOS 17.6.1 •਺े෼ޙ※ ɺڞ༗γʔτ͕ग़ݱ͠ɺ͢΂ͯͷϔϧεέΞσʔλΛऔΓग़ ͤΔ o MacʹAir Drop͢ΔͱZIP͕औΓग़ͤΔ

Slide 7

Slide 7 text

·ͱΊ

Slide 8

Slide 8 text

·ͱΊ • ʮ͢΂ͯͷϔϧεέΞσʔλΛॻ͖ग़͢ʯΛબ୒͢Δ͜ͱʹΑͬͯɺϔϧεέΞσʔλ ΛΤΫεϙʔτͰ͖Δ o ֤छΞϓϦ΍αʔϏεͰΠϯϙʔτͯ͠࢖͑Δʂ

Slide 9

Slide 9 text

໰୊఺

Slide 10

Slide 10 text

ΤΫεϙʔτ͞Ε͍ͯΔ಺༰͕ෆ໌

Slide 11

Slide 11 text

ΤΫεϙʔτ͞Ε͍ͯΔ಺༰͕ෆ໌ https://support.apple.com/ja-jp/guide/iphone/iph5ede58c3d/ ios

Slide 12

Slide 12 text

μ΢ϯϩʔυͨ͠σʔλΛΈͯΈΔ

Slide 13

Slide 13 text

ϔϧεέΞσʔλͷத਎ APPLE_HEALTH_EXPOR T ᵓ── ELECTROCARDIOGRAM S ᵓ── EXPORT.XM L ᵓ── EXPORT_CDA.XM L └── WORKOUT-ROUTES

Slide 14

Slide 14 text

WORKOUT-ROUTES

Slide 15

Slide 15 text

•GPX֦ுࢠϑΝΠϧ͕٧·͍ͬͯ Δ WORKOUT- ROUTESϑΥϧμ

Slide 16

Slide 16 text

GPXϑΝΠϧ(GPS eXchange Format) • GPSσόΠε΍ιϑτ΢ΣΞؒͰҐஔ৘ใΛަ׵͢ΔͨΊͷσʔλϑΥʔϚοτ • σʔλϑΥʔϚοτ͸XMLϕʔε • WAYPOINT,TRACKS,ROUTESͷ৘ใͷछྨ͕͋Δ o ͜ͷ͏ͪɺϔϧεέΞσʔλʹ͸TRACKS͕ऩ࿥͞Ε͍ͯΔ ▪ ϫʔΫΞ΢τͷϧʔτ͕ऩΊΒΕ͍ͯΔ

Slide 17

Slide 17 text

GPXͷར༻ྫ GOOGLE MAPʹGPXϑΝΠϧΠϯϙʔτ͢Δͱ஍ਤʹي੻͕ ඳը͞ΕΔ

Slide 18

Slide 18 text

iOSͱͷൺֱ iOSͷϫʔΫΞ΢τ Google MAP

Slide 19

Slide 19 text

GPXͷத਎ route 2022-03-09 7:53pm 28.7103500.00000092.615112 course>15.5980627.818013

Slide 20

Slide 20 text

trkptλά಺ίϯςϯπ ໊લ ஋ ஋ͷྫ උߟ lat Ң౓ 35.685067 trksegଐੑ lon ܦ౓ 139.729145 trksegଐੑ ele ඪߴ 35.618244 time ه࿥͞Εͨ೔࣌ 2018-09-19T03:08 :01Z speed Ҡಈ଎౓(m/s) 1.623222 Apple extensions course Ҡಈํ޲(°) 186.328125 Apple extensions hAcc ਫฏํ޲ͷਫ਼౓ 2.350130 Apple extensions vAcc ਨ௚ํ޲ͷਫ਼౓ 1.664492 Apple extensions

Slide 21

Slide 21 text

MacOS XMLDocumentʹΑΔGPXͷղੳྫ func loadGPXData() { guard let gpxPath = Bundle.main.url(forResource: "route_2021-09-24_6.30pm", withExtension: "gpx"), let xmlDoc = try? XMLDocument(contentsOf: gpxPath, options: .documentTidyXML) else { return } let xpath = "//gpx/trk/trkseg/trkpt" let trkptNodes = try! xmlDoc.nodes(forXPath: xpath) var coordinates: [CLLocationCoordinate2D] = [] trkptNodes.forEach { node in if let element = node as? XMLElement, let latStr = element.attribute(forName: "lat")?.stringValue, let lonStr = element.attribute(forName: "lon")?.stringValue, let lat = CLLocationDegrees(latStr), let lon = CLLocationDegrees(lonStr) { let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon) coordinates.append(coordinate) } } }

Slide 22

Slide 22 text

ELECTROCARDIOGRAMS

Slide 23

Slide 23 text

•CSVϑΝΠϧ͕٧·͍ͬͯΔ •৺ిਤ(ECG)ͷ݁Ռ͕ه࿥͞Εͯ ͍Δ electrocardiogra msϑΥϧμ

Slide 24

Slide 24 text

CSVϑΝΠϧʹ͍ͭͯ

Slide 25

Slide 25 text

ҰൠతͳCSV ϑΝΠϧ •̍ߦ໨ʹϔομʔ෦ʢ೚ҙʣ •֤छ̍ߦʹσʔλ͕֨ೲ͞Ε ͍ͯΔ

Slide 26

Slide 26 text

ECGͷCSVϑΝΠϧ

Slide 27

Slide 27 text

ECGͷCSVϑΝΠϧ •લ൒͸̍ߦʹKEY-VALUEͷܗͰϝλσʔλ͕֨ೲ͞Ε͍ͯΔ

Slide 28

Slide 28 text

ECGͷCSVϑΝΠϧ •લ൒͸̍ߦʹKEY-VALUEͷܗͰϝλσʔλ͕֨ೲ͞Ε͍ͯΔ •αϯϓϧϨʔτ͸511.3441ϔϧπ(1ඵؒʹ511ճҎ্ ) • ޙ൒ʹ৺ిਤͷిѹ͕̍ߦͣͭ

Slide 29

Slide 29 text

EGCͷCSVϑΝΠϧ(೔ຊޠ) •IOSͷݴޠઃఆʹΑͬͯग़ྗ͢ΔCSVͷϝλσʔλͷ಺༰͕มΘΔ

Slide 30

Slide 30 text

ECG csv ͷswift data struct ECGData { var name: String var dateOfBirth: String var recordedDate: String var classification: String var symptoms: String? var softwareVersion: String var device: String var sampleRate: Double var lead: String var unit: String var voltage: [Double] }

Slide 31

Slide 31 text

ECG csv ͷswiftॲ ཧྫ // CSVϑΝΠϧͷ಺༰Λύʔεͯ͠Structʹม׵͢Δؔ਺ func parseECGCSV(csvString: String) -> ECGData { let rows = csvString.components(separatedBy: "\n") let name = splitCSVLine(rows[0])[1] let dateOfBirth = splitCSVLine(rows[1])[1] let recordedDate = splitCSVLine(rows[2])[1] let classification = splitCSVLine(rows[3])[1] let symptoms = splitCSVLine(rows[4]).count > 1 ? splitCSVLine(rows[4])[1] : nil let softwareVersion = splitCSVLine(rows[5])[1] let device = splitCSVLine(rows[6])[1] let sampleRate = Double(splitCSVLine(rows[7])[1].components(separatedBy: " ")[0])! let lead = splitCSVLine(rows[10])[1] let unit = splitCSVLine(rows[11])[1] // ిѹͷऔಘ var signals = [Double]() for i in 13..

Slide 32

Slide 32 text

CSVҰྻ ͷॲཧྫ func splitCSVLine(_ line: String) -> [String] { var result = [String]() var currentField = "" var inQuotes = false for character in line { if character == "\"" { inQuotes.toggle() // Ҿ༻ූ͕ݱΕͨΒinQuotesϑϥάΛ੾Γସ͑ } else if character == "," && !inQuotes { result.append(currentField.trimmingCharacters(in: .whitespaces)) currentField = "" } else { currentField.append(character) } } result.append(currentField.trimmingCharacters(in: .whitespaces)) return result }

Slide 33

Slide 33 text

EXPORT.XML

Slide 34

Slide 34 text

ϔϧεέΞσʔλͷத਎ APPLE_HEALTH_EXPOR T ᵓ── ELECTROCARDIOGRAM S ᵓ── EXPORT.XM L ᵓ── EXPORT_CDA.XM L └── WORKOUT-ROUTES

Slide 35

Slide 35 text

ϔϧεέΞσʔλͷத਎ APPLE_HEALTH_EXPOR T ᵓ── ELECTROCARDIOGRAM S ᵓ── EXPORT.XML(2.7GB ) ᵓ── EXPORT_CDA.XML(1.1GB ) └── WORKOUT-ROUTES

Slide 36

Slide 36 text

ڊେͳXMLΛ։͘ • ͦͷ··μϒϧΫϦοΫ o XcodeͰ։͘ ▪ ϑϦʔζ • ςΩετΤσΟοτͰ։͘ o ͱΓ͋͑ͣಈ͘ ▪ ͕ͩҰ౓ʹεΫϩʔϧ͢ΔͱϑϦʔζ͢Δ৔߹͕͋Δ

Slide 37

Slide 37 text

EXPORT.XMLΛݟΔ

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

XML DTD(DOCUMENT TYPE DEFINITION) • XMLจॻʹ͸จॻߏ଄Λද͢DTD͕෇༩͞Ε͍ͯΔ৔߹͕͋Δ • DTDʹ͸XMLʹؚ·Ε͍ͯΔཁૉ΍ଐੑ͕ఆٛ͞Ε͍ͯΔ o DTD෦෼ΛಡΈऔͬͯXMLͷจॻ࢓༷Λ೺Ѳ͢Δ͜ͱ͕Մೳ

Slide 42

Slide 42 text

EXPORT.XML ͷDTDʹఆٛ ࡁΈͷཁૉ ཁૉ ಺༰ HealthData root ExportDate ΤΫεϙʔτ೔෇ Me ݸਓ৘ใ Record ه࿥͞Ε݈ͨ߁Ϩίʔυ Correlation ه࿥͞Εͨσʔλͷάϧʔ ϓ Workout ϫʔΫΞ΢τ ActivitySummary ΞΫςΟϏςΟৄࡉ MetadataEntry ϝλσʔλ HeartRateVariabilityMetadataList ৺ഥมಈϦετ InstantaneousBeatsPerMinute ৺ഥ਺ ClinicalRecord ྟচه࿥ Audiogram ௌྗݕࠪ SensitivityPoint ௌྗײ౓ VisionPrescription ࢹྗ RightEye ӈ໨ʹؔ͢Δσʔλ LeftEye ࠨ໨ʹؔ͢Δσʔλ Attachment ఴ෇ϑΝΠϧ

Slide 43

Slide 43 text

EXPORT.XML ͷDTDʹఆٛ ࡁΈͷཁૉ ཁૉ ಺༰ HealthData root ExportDate ΤΫεϙʔτ೔෇ Me ݸਓ৘ใ Record ه࿥͞Ε݈ͨ߁Ϩίʔυ Correlation ه࿥͞Εͨσʔλͷάϧʔ ϓ Workout ϫʔΫΞ΢τ ActivitySummary ΞΫςΟϏςΟৄࡉ MetadataEntry ϝλσʔλ HeartRateVariabilityMetadataList ৺ഥมಈϦετ InstantaneousBeatsPerMinute ৺ഥ਺ ClinicalRecord ྟচه࿥ Audiogram ௌྗݕࠪ SensitivityPoint ௌྗײ౓ VisionPrescription ࢹྗ RightEye ӈ໨ʹؔ͢Δσʔλ LeftEye ࠨ໨ʹؔ͢Δσʔλ Attachment ఴ෇ϑΝΠϧ

Slide 44

Slide 44 text

RECORD

Slide 45

Slide 45 text

RECORD typeλά͔ΒσʔλΛಛఆ͢Δ

Slide 46

Slide 46 text

HKQuantityTypeIdentifierBodyMass

Slide 47

Slide 47 text

RECORD RECORDλά͔ΒσʔλΛऔಘ

Slide 48

Slide 48 text

correlation

Slide 49

Slide 49 text

correlation 
 ৯΂෺ͱӫཆૉ ͷσʔλ

Slide 50

Slide 50 text

Ͳ͏΍ͬͯڊେͳXML͔ΒσʔλΛऔಘ͢Δ͔

Slide 51

Slide 51 text

•DOM͸XMLશମΛϝϞϦ্ʹ࣋ͭ   o ϝϞϦΛେྔফඅ͢Δ o ॲཧ࣌ؒ΋௕͘ͳΔ DOM
 (XMLDocument)

Slide 52

Slide 52 text

SAXΛ࢖ͬͯڊେͳxmlΛॲཧ • XMLϑΝΠϧΛஞ࣍ಡΈࠐΈΛߦ͏ͨΊɺϝϞϦফඅ͕গͳ͍ • ඞཁͳΠϕϯτ΍λά͚ͩΛॲཧͰ͖Δ • iOSͰ͸XMLParser

Slide 53

Slide 53 text

XMLParserʹΑΔ SAXॲཧྫ class HealthDataParser: NSObject, XMLParserDelegate { private var bodyMassRecords: [[String: String]] = [] private var isParsingRecord = false func parseHealthData(contents: URL) -> [[String: String]] { let parser = XMLParser(contentsOf: contents) parser?.delegate = self parser?.parse() return bodyMassRecords } func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { if elementName == "Record" && attributeDict["type"] == "HKQuantityTypeIdentifierBodyMass" { isParsingRecord = true if attributeDict.contains(where: { key, value in key == "value" && Double(value)! < 70 }) { bodyMassRecords.append(attributeDict) if bodyMassRecords.count > 20 { parser.abortParsing() } } } } func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if elementName == "Record" { isParsingRecord = false } } } • XMLParserΛϩʔΧϧϑΝΠϧURLύ ε(contentsOf:)Ͱੜ੒ • ඞཁͳऔಘର৅ͷσʔλ(record)ͷॲཧ Λߦ͏ • औಘ͠ऴΘͬͨΒ parser.abortParsing()Λݺͼग़ͯ͠ XMLͷಡΈࠐΈΛΩϟϯηϧ͢Δ

Slide 54

Slide 54 text

EXPORT_CDA.XMLΛݟΔ

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

CDA 
 ʢCLINICAL DOCUMENT ARCHITECTUREʣ

Slide 57

Slide 57 text

CDA 
 ʢCLINICAL DOCUMENT ARCHITECTUREʣ • ҩྍ৘ใͷඪ४ن֨ o HL7(HEALTH LEVEL 7)͕։ൃ • ҩྍ৘ใΛඪ४Խ͞Εͨํ๏Ͱిࢠతʹަ׵͢Δ͜ͱ • จॻ͸XMLܗࣜͰදݱ͞Ε͍ͯΔ o ϔομʔ o ྟচ৘ใ(COMPONENT)

Slide 58

Slide 58 text

CDAͷຊ จ(ױऀ৘ ใ) ೔޲ڧ

Slide 59

Slide 59 text

CDAͷຊ จ(ྟচ৘ ใ) OMRON CONNECT 006.006.00000.001 69.3 HKQUANTITYTYPEIDENTIFIERBODYMASS KG SEQUENCENUMBER 0

Slide 60

Slide 60 text

CDAͷຊ จ(ྟচ৘ ใ) OMRON CONNECT 006.006.00000.001 69.3 HKQUANTITYTYPEIDENTIFIERBODYMASS KG SEQUENCENUMBER 0

Slide 61

Slide 61 text

CODEλά

Slide 62

Slide 62 text

CODEλά

Slide 63

Slide 63 text

LOINC 
 ʢLOGICAL OBSERVATION IDENTIFIERS NAMES AND CODESʣ • ҩྍݕࠪ݁Ռ΍ྟচ؍࡯Λࣝผ͢ΔͨΊͷσʔλϕʔε͓Αͼੈքڞ௨ͷඪ४ن֨ • ֤ݕࠪ΍؍࡯݁Ռʹݻ༗ͷ7ܻͷLONICίʔυׂ͕Γ౰ͯΒΕΔ o ίʔυʹΑͬͯྟচݕࠪ΍਍ྍه࿥ͷ಺༰Λॲཧ͢Δ • CodeSystem 2.16.840.1.113883.6.1

Slide 64

Slide 64 text

COMPONENT ͔Β஋Λऔಘ omron connect 006.006.00000.001 69.3 HKQuantityTypeIdentifierBodyMass kg SequenceNumber 0 • codeλά 3141-9ͰମॏΛ͍ࣔͯ͠Δ͜ ͱ͕Θ͔Δ • Valueλά(xsi:type)ʹvalue(஋)ͱ unit(୯Ґ) o iOS͕ੜ੒͢Δσʔλʹvalueλά͸ෳ ਺͋Δ৔߹΋͋Δ

Slide 65

Slide 65 text

https://loinc.org/3141-9

Slide 66

Slide 66 text

EXPORT XMLͷҧ͍ • export.xml o HealthKitΛج४ͱͨ͠σʔλ͕ग़ྗ͞Ε Δ o ղੳʹ͸HealthKitͷ஌ࣝ΍ॲཧ͕ඞཁʹ ͳΔ • export_cda.xml o ඪ४ن֨ʹԊͬͨσʔλ͕ग़ྗ͞ΕΔ o ӫཆͳͲඪ४ن֨ʹແ͍σʔλ͸ग़ྗ͞ Εͳ͍

Slide 67

Slide 67 text

·ͱΊ • iPhone͔ΒϔϧεέΞσʔλΛऔΓग़͢͜ͱ͕Ͱ͖Δ • workout-routesͱͯ͠ϫʔΫΞ΢τϧʔτΛࣔ͢gpxϑΝΠϧ͕औಘͰ͖Δ • electrocardiogramsͱͯ͠৺ిਤ͕csvϑΝΠϧͰऔಘͰ͖Δ o ҰͭͷCSVϑΝΠϧʹϝλσʔλ෦෼ͱిѹ෦෼ʹ෼͔Ε͍ͯΔ o σʔλ͸୺຤ͷݴޠઃఆʹӨڹ͞ΕΔ • xmlϑΝΠϧ͔ΒϔϧεέΞσʔλΛऔΓग़͢͜ͱ͕Ͱ͖Δ o export.xml͔Β͸HealthKitʹ४ڌͨ͠σʔλΛऔΓग़͢͜ͱ͕Ͱ͖Δ o export_cda.xml͔Β͸ඪ४ن֨ͷ݈߁σʔλΛऔΓग़͢͜ͱ͕Ͱ͖Δ ▪ xml͸ڊେʹͳ͍ͬͯΔͷͰSAXͰύʔε