Slide 1

Slide 1 text

@monoqlo QRίʔυಡΈऔΓʁ ָউͰ͢Α AVFoundationΛ৴ͨ͡ ͓Ε͕όΧͩͬͨ

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

ḪΔ͜ͱ ಥવͰ͕͢

Slide 4

Slide 4 text

໿ 1೥൒

Slide 5

Slide 5 text

2017 ೥ 3 ݄ ๭೔

Slide 6

Slide 6 text

ָউͰ͢Α✌✌ QRίʔυಡΈऔΔ ΞϓϦͭ͘ΕΔʁ

Slide 7

Slide 7 text

͜ͷ࣌ߟ͍͑ͯͨ͜ͱ

Slide 8

Slide 8 text

AVFoundation Ͱ༨༟Ͱ͠ΐ • AVCaptureSession • AVCaptureMetadataOutput • AVMetadataMachineReadableCodeObject • etc…

Slide 9

Slide 9 text

QRίʔυ ౉͞Εͨ

Slide 10

Slide 10 text

https://www.55truck.com/shaken.html

Slide 11

Slide 11 text

https://www.55truck.com/shaken.html

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

"8/-/1/-/- /- /999999/02” "2/- //160114/1001/BKG-NKR85” "AN/0168/- /- /0381/13/09” "2/඼઒ɹɹ̔̌̌ͤ̒̐̎” “̒/1/NKR85-7011300/4JJ1/1"

Slide 14

Slide 14 text

http://mangalifewin.takeshobo.co.jp/rensai/popute3/

Slide 15

Slide 15 text

"8/-/1/-/- /- /999999/02” "2/- //160114/1001/BKG-NKR85” "AN/0168/- /- /0381/13/09” "2/඼઒ɹɹ̔̌̌ͤ̒̐̎” “̒/1/NKR85-7011300/4JJ1/1"

Slide 16

Slide 16 text

"2/- //160114/1001/BKG-NKR85AN/0168/- /- /0381/13/098/-/1/-/- /- /999999/02” "2/඼઒ɹɹ̔̌̌ͤ̒̐̎̒/1/NKR85-7011300/4JJ1/1”

Slide 17

Slide 17 text

φχϞϊ ͜Ε͸…

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

http://www.qrcode.com

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

΋ͬͱ QRίʔυ Λ஌Ζ͏

Slide 22

Slide 22 text

৘ใٕज़ʵࣗಈೝࣝٴͼσʔλऔಘٕज़ʵ ̧̦ίʔυόʔίʔυγϯϘϧମܥ࢓༷ JIS ن֨ X0510

Slide 23

Slide 23 text

http://www.jisc.go.jp/

Slide 24

Slide 24 text

શ 115 ϖʔδ…

Slide 25

Slide 25 text

QRίʔυͷ࢓૊Έ ௒μΠδΣετ൛

Slide 26

Slide 26 text

ࠇനϞδϡʔϧͷೝࣝ START ܕࣜ৘ใͷ෮߸ ܕ൪ͷܾఆ ϚεΫॲཧͷղআ σʔλ & ޡΓగਖ਼ίʔυޠͷ෮ݩ

Slide 27

Slide 27 text

ޡΓగਖ਼ ޡΓݕग़ σʔλΛ෮߸ σʔλೖख END ޡΓφγ ޡΓΞϦ

Slide 28

Slide 28 text

“Ϟʔυ” ղऍ ෮߸͢ΔͨΊʹॏཁͳͷ͕

Slide 29

Slide 29 text

“Ϟʔυ” ͱ͸

Slide 30

Slide 30 text

• ECI • ߏ଄త࿈઀ • FNC1 • (ऴ୺ύλʔϯ) • ਺ࣈ • ӳ਺ࣈ • 8ϏοτόΠτ • ׽ࣈ Ϟʔυ = σʔλΛ֨ೲ͢Δܗࣜ • ѹॖޮ཰޲্Λ໨తͱ͠ɺଟ਺ͷϞʔυ͕ఆٛ͞Ε͍ͯΔ • QRίʔυੜ੒࣌ɺσʔλʹ߹Θͤͯ࠷దͳ΋ͷΛબ୒͢Δ • ϔομʔͱͯ͠จࣈ਺ࢦࣔࢠͱͱ΋ʹσʔλʹ෇Ճ͢Δ • ૊Έ߹Θͤͯ࢖༻Մʢex. ӳ਺ࣈ + ׽ࣈʣ ߏ଄త࿈઀

Slide 31

Slide 31 text

• ਺ࣈ • ӳ਺ࣈ • 8ϏοτόΠτ • ׽ࣈ • ECI • ߏ଄త࿈઀ • FNC1 • (ऴ୺ύλʔϯ) Ϟʔυ = σʔλΛ֨ೲ͢Δܗࣜ • ѹॖޮ཰޲্Λ໨తͱ͠ɺଟ਺ͷϞʔυ͕ఆٛ͞Ε͍ͯΔ • QRίʔυੜ੒࣌ɺσʔλʹ߹Θͤͯ࠷దͳ΋ͷΛબ୒͢Δ • ϔομʔͱͯ͠จࣈ਺ࢦࣔࢠͱͱ΋ʹσʔλʹ෇Ճ͢Δ • ૊Έ߹Θͤͯ࢖༻Մʢex. ӳ਺ࣈ + ׽ࣈʣ ߏ଄త࿈઀

Slide 32

Slide 32 text

ߏ଄త࿈઀ Ϟʔυ

Slide 33

Slide 33 text

ϔομʔ࢓༷ 1. Ϟʔυࢦࣔࢠʢ0011ʣɿ4bit 2. γϯϘϧྻࢦࣔࢠɿ8bit 1. γϯϘϧҐஔɿ4bit 2. ࿈݁͞ΕΔγϯϘϧͷ߹ܭ਺ɿ4bit 3. ύϦςΟɿ8bit ෼ׂલͷσʔλΛόΠτ͝ͱʹXORԋࢉͨ͠஋

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

ϓϩύςΟ var corners: [CGPoint] A Swift array of corner points. var stringValue: String? Returns the error corrected data decoded into a human- readable string.

Slide 36

Slide 36 text

ͦΜͳ΋ͷͳ͍ AVMetadataMachineReadableCodeObject ʹ

Slide 37

Slide 37 text

ࠇനϞδϡʔϧͷೝࣝ START ܕࣜ৘ใͷ෮߸ ܕ൪ͷܾఆ ϚεΫॲཧͷղআ σʔλ & ޡΓగਖ਼ίʔυޠͷ෮ݩ

Slide 38

Slide 38 text

ޡΓగਖ਼ ޡΓݕग़ σʔλΛ෮߸ σʔλೖख END ޡΓφγ ޡΓΞϦ AVMetadataMachine ReadableCodeObject ͷ stringValue ͸ ෳ߹ޙͷίϨ

Slide 39

Slide 39 text

ϥΠϒϥϦΛ୳͢ ߏ଄త࿈઀Λ͏·͘ѻ͑Δ

Slide 40

Slide 40 text

None Swift ੡ͷϥΠϒϥϦ

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

ZXingObjC ٹੈओ TheLevelUp/ZXingObjC: An Objective-C Port of ZXing

Slide 43

Slide 43 text

γϯϘϧҐஔ/߹ܭ਺ͱύϦςΟ͕औΕΔ @objc func captureResult(_ capture: ZXCapture!, result: ZXResult!) { guard let _ = result else { return } // ෼ׂQRίʔυͷ৔߹ɺZXResult#resultMetadata ͷҎԼ2ͭΛ֬ೝͯ͠ // Ͳͷ෦෼ͷQRίʔυ͕ಡΈࠐ·Εͨͷ͔൑ఆ͢Δ͜ͱ͕Ͱ͖Δ // - kResultMetadataTypeStructuredAppendSequence // - kResultMetadataTypeStructuredAppendParity guard let metaData = result.resultMetadata, let sequenceValue = metaData[Int(kResultMetadataTypeStructuredAppendSequence.rawValue)] as? Int, let parity = metaData[Int(kResultMetadataTypeStructuredAppendParity.rawValue)] as? Int else { return } // sequence: ಡΈऔͬͨQRίʔυͷ൪߸ 4bit + τʔλϧͷQRίʔυ਺ 4bit let bit = String(sequenceValue, radix: 2) let pad = String(repeating: "0", count: (8 - bit.count)) let sequence = pad + bit let current = Int(sequence.prefix(4), radix: 2)! let total = Int(sequence.suffix(from: 4), radix: 2)! … } @objc func captureResult(_ capture: ZXCapture!, result: ZXResult!) { guard let _ = result else { return } // ෼ׂQRίʔυͷ৔߹ɺZXResult#resultMetadata ͷҎԼ2ͭΛ֬ೝͯ͠ // Ͳͷ෦෼ͷQRίʔυ͕ಡΈࠐ·Εͨͷ͔൑ఆ͢Δ͜ͱ͕Ͱ͖Δ // - kResultMetadataTypeStructuredAppendSequence // - kResultMetadataTypeStructuredAppendParity

Slide 44

Slide 44 text

γϯϘϧҐஔ/߹ܭ਺ͱύϦςΟ͕औΕΔ @objc func captureResult(_ capture: ZXCapture!, result: ZXResult!) { guard let _ = result else { return } // ෼ׂQRίʔυͷ৔߹ɺZXResult#resultMetadata ͷҎԼ2ͭΛ֬ೝͯ͠ // Ͳͷ෦෼ͷQRίʔυ͕ಡΈࠐ·Εͨͷ͔൑ఆ͢Δ͜ͱ͕Ͱ͖Δ // - kResultMetadataTypeStructuredAppendSequence // - kResultMetadataTypeStructuredAppendParity guard let metaData = result.resultMetadata, let sequenceValue = metaData[Int(kResultMetadataTypeStructuredAppendSequence.rawValue)] as? Int, let parity = metaData[Int(kResultMetadataTypeStructuredAppendParity.rawValue)] as? Int else { return } // sequence: ಡΈऔͬͨQRίʔυͷ൪߸ 4bit + τʔλϧͷQRίʔυ਺ 4bit let bit = String(sequenceValue, radix: 2) let pad = String(repeating: "0", count: (8 - bit.count)) let sequence = pad + bit let current = Int(sequence.prefix(4), radix: 2)! let total = Int(sequence.suffix(from: 4), radix: 2)! … } @objc func captureResult(_ capture: ZXCapture!, result: ZXResult!) { guard let _ = result else { return } // ෼ׂQRίʔυͷ৔߹ɺZXResult#resultMetadata ͷҎԼ2ͭΛ֬ೝͯ͠ // Ͳͷ෦෼ͷQRίʔυ͕ಡΈࠐ·Εͨͷ͔൑ఆ͢Δ͜ͱ͕Ͱ͖Δ // - kResultMetadataTypeStructuredAppendSequence // - kResultMetadataTypeStructuredAppendParity guard let metaData = result.resultMetadata, let sequenceValue = metaData[Int(kResultMetadataTypeStructuredAppendSequence.rawValue)] as? Int, let parity = metaData[Int(kResultMetadataTypeStructuredAppendParity.rawValue)] as? Int else { return }

Slide 45

Slide 45 text

γϯϘϧҐஔ/߹ܭ਺ͱύϦςΟ͕औΕΔ @objc func captureResult(_ capture: ZXCapture!, result: ZXResult!) { guard let _ = result else { return } // ෼ׂQRίʔυͷ৔߹ɺZXResult#resultMetadata ͷҎԼ2ͭΛ֬ೝͯ͠ // Ͳͷ෦෼ͷQRίʔυ͕ಡΈࠐ·Εͨͷ͔൑ఆ͢Δ͜ͱ͕Ͱ͖Δ // - kResultMetadataTypeStructuredAppendSequence // - kResultMetadataTypeStructuredAppendParity guard let metaData = result.resultMetadata, let sequenceValue = metaData[Int(kResultMetadataTypeStructuredAppendSequence.rawValue)] as? Int, let parity = metaData[Int(kResultMetadataTypeStructuredAppendParity.rawValue)] as? Int else { return } // sequence: ಡΈऔͬͨQRίʔυͷ൪߸ 4bit + τʔλϧͷQRίʔυ਺ 4bit let bit = String(sequenceValue, radix: 2) let pad = String(repeating: "0", count: (8 - bit.count)) let sequence = pad + bit let current = Int(sequence.prefix(4), radix: 2)! let total = Int(sequence.suffix(from: 4), radix: 2)! … } @objc func captureResult(_ capture: ZXCapture!, result: ZXResult!) { guard let _ = result else { return } // ෼ׂQRίʔυͷ৔߹ɺZXResult#resultMetadata ͷҎԼ2ͭΛ֬ೝͯ͠ // Ͳͷ෦෼ͷQRίʔυ͕ಡΈࠐ·Εͨͷ͔൑ఆ͢Δ͜ͱ͕Ͱ͖Δ // - kResultMetadataTypeStructuredAppendSequence // - kResultMetadataTypeStructuredAppendParity guard let metaData = result.resultMetadata, let sequenceValue = metaData[Int(kResultMetadataTypeStructuredAppendSequence.rawValue)] as? Int, let parity = metaData[Int(kResultMetadataTypeStructuredAppendParity.rawValue)] as? Int else { return } // sequence: ಡΈऔͬͨQRίʔυͷ൪߸ 4bit + τʔλϧͷQRίʔυ਺ 4bit let bit = String(sequenceValue, radix: 2) let pad = String(repeating: "0", count: (8 - bit.count)) let sequence = pad + bit let current = Int(sequence.prefix(4), radix: 2)! let total = Int(sequence.suffix(from: 4), radix: 2)! … }

Slide 46

Slide 46 text

γϯϘϧҐஔ/߹ܭ਺ͱύϦςΟ͕औΕΔ @objc func captureResult(_ capture: ZXCapture!, result: ZXResult!) { guard let _ = result else { return } // ෼ׂQRίʔυͷ৔߹ɺZXResult#resultMetadata ͷҎԼ2ͭΛ֬ೝͯ͠ // Ͳͷ෦෼ͷQRίʔυ͕ಡΈࠐ·Εͨͷ͔൑ఆ͢Δ͜ͱ͕Ͱ͖Δ // - kResultMetadataTypeStructuredAppendSequence // - kResultMetadataTypeStructuredAppendParity guard let metaData = result.resultMetadata, let sequenceValue = metaData[Int(kResultMetadataTypeStructuredAppendSequence.rawValue)] as? Int, let parity = metaData[Int(kResultMetadataTypeStructuredAppendParity.rawValue)] as? Int else { return } // sequence: ಡΈऔͬͨQRίʔυͷ൪߸ 4bit + τʔλϧͷQRίʔυ਺ 4bit let bit = String(sequenceValue, radix: 2) let pad = String(repeating: "0", count: (8 - bit.count)) let sequence = pad + bit let current = Int(sequence.prefix(4), radix: 2)! let total = Int(sequence.suffix(from: 4), radix: 2)! … } @objc func captureResult(_ capture: ZXCapture!, result: ZXResult!) { guard let _ = result else { return } // ෼ׂQRίʔυͷ৔߹ɺZXResult#resultMetadata ͷҎԼ2ͭΛ֬ೝͯ͠ // Ͳͷ෦෼ͷQRίʔυ͕ಡΈࠐ·Εͨͷ͔൑ఆ͢Δ͜ͱ͕Ͱ͖Δ // - kResultMetadataTypeStructuredAppendSequence // - kResultMetadataTypeStructuredAppendParity // sequence: ಡΈऔͬͨQRίʔυͷ൪߸ 4bit + τʔλϧͷQRίʔυ਺ 4bit let bit = String(sequenceValue, radix: 2) let pad = String(repeating: "0", count: (8 - bit.count)) let sequence = pad + bit let current = Int(sequence.prefix(4), radix: 2)! let total = Int(sequence.suffix(from: 4), radix: 2)! … } guard let metaData = result.resultMetadata, let sequenceValue = metaData[Int(kResultMetadataTypeStructuredAppendSequence.rawValue)] as? Int, let parity = metaData[Int(kResultMetadataTypeStructuredAppendParity.rawValue)] as? Int else { return }

Slide 47

Slide 47 text

ղܾ ΍ͬͨʔʂʂ

Slide 48

Slide 48 text

ຊ౰ʹʁ

Slide 49

Slide 49 text

ZXingObjC ͷऑ఺ • ಡΈऔΓਫ਼౓͕͋·Γߴ͘ͳ͍ • AVFoundation ΍σϯιʔެࣜΞϓϦʹൺ΂Δͱ݁ߏ ͕͔͔࣌ؒΔ͜ͱ͕͋Δ • ։ൃ͕࣮࣭ࢭ·͍ͬͯΔ • Swift ʹஔ͖׵͍͑ͨͱ͔ॻ͔ΕͯΔ͚Ͳ͍ͭʹͳΔ ͔·ͬͨ͘Θ͔Βͳ͍

Slide 50

Slide 50 text

ઈ๬ ະདྷ͕ͳ͍

Slide 51

Slide 51 text

ઈ๬ ະདྷ͕ͳ͍ ʁ

Slide 52

Slide 52 text

ઈ๬ ະདྷ͕ͳ͍ ʁ 2017 ೥ ൒͹ ·Ͱͷ͓࿩

Slide 53

Slide 53 text

࣌͸ྲྀΕ… 2018 ೥ 9 ݄ 1 ೔

Slide 54

Slide 54 text

iOS ͷόʔδϣϯγΣΞભҠ ೥݄ ݄ ݄ ݄ ݄ ೥݄ ݄ ݄ ݄ iOS 11 iOS 10 iOS 9 or Earlier ※ 2018೥9݄͸༧ଌ

Slide 55

Slide 55 text

iOS 10 ͷαϙʔτ ͦΖͦΖ੾Ε·͢ΑͶʁ ࠓ݄͸ iOS 12 ͕ϦϦʔε͞Ε·͢

Slide 56

Slide 56 text

CIQRCodeDescriptor ਅͷٹੈओ in AVMetadataMachineReadableCodeObject

Slide 57

Slide 57 text

ϓϩύςΟ var corners: [CGPoint] A Swift array of corner points. var stringValue: String? Returns the error corrected data decoded into a human- readable string. var descriptor: CIBarcodeDescriptor? A barcode description for use in Core Image.

Slide 58

Slide 58 text

CIQRCodeDescriptor ͱ͸

Slide 59

Slide 59 text

CIQRCodeDescriptor ͱ͸ • iOS 11 Ͱ௥Ճ͞ΕͨڧྗͳΫϥε • ந৅Ϋϥε CIBarcodeDescriptor ͷαϒΫϥε • QRίʔυΛੜ੒͢ΔͨΊʹඞཁे෼ͳσʔλΛ࣋ͭ • CIFilter Ͱ QRίʔυը૾Λੜ੒͢Δ͜ͱ΋Ͱ͖Δ

Slide 60

Slide 60 text

CIQRCodeDescriptor ͱ͸ • iOS 11 Ͱ௥Ճ͞ΕͨڧྗͳΫϥε • ந৅Ϋϥε CIBarcodeDescriptor ͷαϒΫϥε • CIFilter Ͱ QRίʔυը૾Λੜ੒͢Δ͜ͱ΋Ͱ͖Δ func generateQRCodeImage(from descriptor: CIQRCodeDescriptor) -> UIImage? { let inputParams: [String: Any] = ["inputBarcodeDescriptor": descriptor] let barcodeCreationFilter = CIFilter(name: "CIBarcodeGenerator", parameters: inputParams) guard let outputImage = barcodeCreationFilter?.outputImage, let cgImage = CIContext().createCGImage(outputImage, from: outputImage.extent) else { return nil } return UIImage(cgImage: cgImage) }

Slide 61

Slide 61 text

େࣄͳͷ͸͜ͷ 2 ͭ var errorCorrectedPayload: Data The error-corrected payload containing the data encoded in the QR code. var symbolVersion: Int The version of the QR code.

Slide 62

Slide 62 text

ࠇനϞδϡʔϧͷೝࣝ START ܕࣜ৘ใͷ෮߸ ܕ൪ͷܾఆ ϚεΫॲཧͷղআ σʔλ & ޡΓగਖ਼ίʔυޠͷ෮ݩ

Slide 63

Slide 63 text

ޡΓగਖ਼ ޡΓݕग़ σʔλΛ෮߸ σʔλೖख END ޡΓφγ ޡΓΞϦ AVMetadataMachine ReadableCodeObject ͷ stringValue ͸ ෳ߹ޙͷίϨ

Slide 64

Slide 64 text

ޡΓగਖ਼ ޡΓݕग़ σʔλΛ෮߸ σʔλೖख END ޡΓφγ ޡΓΞϦ errorCorrectedPayload ͸ ίίͷσʔλʂ

Slide 65

Slide 65 text

େࣄͳͷ͸͜ͷ 2 ͭ var errorCorrectedPayload: Data The error-corrected payload containing the data encoded in the QR code. var symbolVersion: Int The version of the QR code.

Slide 66

Slide 66 text

େࣄͳͷ͸͜ͷ 2 ͭ var errorCorrectedPayload: Data The error-corrected payload containing the data encoded in the QR code. var symbolVersion: Int The version of the QR code.

Slide 67

Slide 67 text

จࣈ਺ࢦࣔࢠͷϏοτ਺ ܕ൪ TZNCPM7FSTJPO ਺ࣈ Ϟʔυ ӳ਺ࣈ Ϟʔυ ϏοτόΠτ Ϟʔυ ׽ࣈ Ϟʔυ ʙ ʙ ʙ

Slide 68

Slide 68 text

symbolVersion + errorCorrectedPayload σίʔυͰ͖Δ

Slide 69

Slide 69 text

ͱ͍͏͜ͱ͸…ʁ

Slide 70

Slide 70 text

iOS 11 Ҏ߱ͷੈքͰ͸ 1. AVCaptureSession ͰಡΈऔΔ ߴਫ਼౓Ͱ଎͍

Slide 71

Slide 71 text

iOS 11 Ҏ߱ͷੈքͰ͸ AVMetadata Machine ReadableCode Object 2. CIQRCodeDescriptor #errorCorrectedPayload ΛऔΓग़͢ 1. AVCaptureSession ͰಡΈऔΔ

Slide 72

Slide 72 text

iOS 11 Ҏ߱ͷੈքͰ͸ AVMetadata Machine ReadableCode Object 2. CIQRCodeDescriptor #errorCorrectedPayload ΛऔΓग़͢ σίʔυ͢Δ 1. AVFoundation ͰಡΈऔΔ ͕Μ͹ͬͯ 3.

Slide 73

Slide 73 text

ղܾ ΍ͬͨʔʂʂࠓ౓ͦ͜

Slide 74

Slide 74 text

σίʔυ͢Δ ͕Μ͹ͬͯ 3. ͕Μ͹ͬͯ

Slide 75

Slide 75 text

͕Μ͹Δ = ࣗલͰσίʔυॲཧΛॻ͘

Slide 76

Slide 76 text

ࣗલσίʔυͷํ਑ • શϞʔυʹରԠ͠Α͏ͱࢥͬͨΒʢͨͿΜʣ݁ߏେม… • ͱ͍͏͔σϯιʔެࣜΞϓϦ΋ରԠͯ͠ͳ͍ • ࠷௿ݶಡΈऔΓ͍ͨQRίʔυͷσίʔυॲཧͰଥڠ • ಠࣗΤϯίʔυ͞ΕͨQRίʔυ΋σίʔυͰ͖Δ͠ɺ ෯͕޿͕Δʢͱ͔ॻ͍ͯΔ͏ͪʹσϯιʔ͕Ԍ্͠·ͨ͠ʣ => https://srad.jp/submission/78134/

Slide 77

Slide 77 text

σίʔυͷҰྫ https://github.com/ monoqlo/QRCodeReader

Slide 78

Slide 78 text

࣌ؒ͋Ε͹ গ͚ͩ͠ DEMO

Slide 79

Slide 79 text

·ͱΊ

Slide 80

Slide 80 text

0. iOS 10 ͷαϙʔτΛ੾ΔʢҰ൪େࣄʣ

Slide 81

Slide 81 text

·ͱΊʢiOS 11 Ҏ߱ͷੈքʣ 1. AVCaptureSession ͰಡΈऔΔ AVMetadata Machine ReadableCode Object 2. CIQRCodeDescriptor #errorCorrectedPayload ΛऔΓग़͢ ͕Μ͹ͬͯσίʔυ͢Δ 3.

Slide 82

Slide 82 text

Appendix • Speaker Info @monoqlo • This Slide URL https://speakerdeck.com/monoqlo/iosdc-2018