├── architecture.jpg
├── MVP-Sample
├── Assets.xcassets
│ ├── Contents.json
│ ├── Shoes
│ │ ├── Contents.json
│ │ ├── 12732.imageset
│ │ │ ├── 12732@3x.jpg
│ │ │ └── Contents.json
│ │ ├── 12746.imageset
│ │ │ ├── 12746@3x.jpg
│ │ │ └── Contents.json
│ │ ├── 12837.imageset
│ │ │ ├── 12837@3x.jpg
│ │ │ └── Contents.json
│ │ ├── 12841.imageset
│ │ │ ├── 12841@3x.jpg
│ │ │ └── Contents.json
│ │ ├── 9Osfhjm.imageset
│ │ │ ├── 9Osfhjm.jpg
│ │ │ └── Contents.json
│ │ ├── JwKauWO.imageset
│ │ │ ├── JwKauWO.jpg
│ │ │ └── Contents.json
│ │ ├── N6OObvC.imageset
│ │ │ ├── N6OObvC.jpg
│ │ │ └── Contents.json
│ │ ├── QRdx9Il.imageset
│ │ │ ├── QRdx9Il.jpg
│ │ │ └── Contents.json
│ │ ├── TNOsGqn.imageset
│ │ │ ├── TNOsGqn.jpg
│ │ │ └── Contents.json
│ │ ├── cQnmyKQ.imageset
│ │ │ ├── cQnmyKQ.jpg
│ │ │ └── Contents.json
│ │ ├── mm6pknp.imageset
│ │ │ ├── mm6pknp.jpg
│ │ │ └── Contents.json
│ │ ├── o0l47Ca.imageset
│ │ │ ├── o0l47Ca.jpg
│ │ │ └── Contents.json
│ │ ├── p4Ud82Q.imageset
│ │ │ ├── p4Ud82Q.jpg
│ │ │ └── Contents.json
│ │ ├── M29vcEQ0Ry5qcGc=.imageset
│ │ │ ├── M29vcEQ0Ry5qcGc=.jpg
│ │ │ └── Contents.json
│ │ ├── MTE1MDZAM3guanBn.imageset
│ │ │ ├── MTE1MDZAM3guanBn.jpg
│ │ │ └── Contents.json
│ │ ├── MTIxNjhAM3guanBn.imageset
│ │ │ ├── MTIxNjhAM3guanBn.jpg
│ │ │ └── Contents.json
│ │ ├── RDdGVGV6eS5qcGc=.imageset
│ │ │ ├── RDdGVGV6eS5qcGc=.jpg
│ │ │ └── Contents.json
│ │ ├── wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-001-Release-Date-1.imageset
│ │ │ ├── wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-001-Release-Date-1.jpg
│ │ │ └── Contents.json
│ │ ├── wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-400-Release-Date-1.imageset
│ │ │ ├── wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-400-Release-Date-1.jpg
│ │ │ └── Contents.json
│ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MDdqb3JkYW4tbGVnYWN5LTMxMi1BUTQxNjAtMzAxLTEuanBn.imageset
│ │ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MDdqb3JkYW4tbGVnYWN5LTMxMi1BUTQxNjAtMzAxLTEuanBn.jpg
│ │ │ └── Contents.json
│ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUt5cmllLTUtVGFjby1BTzI5MTgtOTAyLmpwZw==.imageset
│ │ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUt5cmllLTUtVGFjby1BTzI5MTgtOTAyLmpwZw==.jpg
│ │ │ └── Contents.json
│ │ ├── wp-contentuploads201902nike-air-fear-of-god-180-black-AT8087-002-release-date-1.imageset
│ │ │ ├── wp-contentuploads201902nike-air-fear-of-god-180-black-AT8087-002-release-date-1.jpg
│ │ │ └── Contents.json
│ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTIuanBn.imageset
│ │ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTIuanBn.jpg
│ │ │ └── Contents.json
│ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTMuanBn.imageset
│ │ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTMuanBn.jpg
│ │ │ └── Contents.json
│ │ ├── wp-contentuploads201903Air-Jordan-4-GS-Monsoon-Blue-BQ9043-400-Release-Date-Price.imageset
│ │ │ ├── wp-contentuploads201903Air-Jordan-4-GS-Monsoon-Blue-BQ9043-400-Release-Date-Price.jpg
│ │ │ └── Contents.json
│ │ ├── wp-contentuploads201904Nike-Kyrie-5-Mamba-Mentality-AO2918-102-Release-Date-Price.imageset
│ │ │ ├── wp-contentuploads201904Nike-Kyrie-5-Mamba-Mentality-AO2918-102-Release-Date-Price.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autocys1ywaekpuvespws0x4lebron-16-remix-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autocys1ywaekpuvespws0x4lebron-16-remix-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MDRhaXItam9yZGFuLTEwLW9ybGFuZG8tcmVsZWFzZS1pbmZvLmpwZw==.imageset
│ │ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MDRhaXItam9yZGFuLTEwLW9ybGFuZG8tcmVsZWFzZS1pbmZvLmpwZw==.jpg
│ │ │ └── Contents.json
│ │ ├── wp-contentuploads201903nike-air-fear-of-god-moccasin-black-at8086-002-release-date-1.imageset
│ │ │ ├── wp-contentuploads201903nike-air-fear-of-god-moccasin-black-at8086-002-release-date-1.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autoxrj4afk5rpddzxbbx6wclebron-16-martin-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autoxrj4afk5rpddzxbbx6wclebron-16-martin-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autoegkplcxbgvbraxiwrkv4air-max2-light-atmos-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autoegkplcxbgvbraxiwrkv4air-max2-light-atmos-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autoq8vrexcyxpmj80u4esgvlebron-16-air-trainer-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autoq8vrexcyxpmj80u4esgvlebron-16-air-trainer-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autovziuczzeyzitgvjtar79air-max-1-on-air-tokyo-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autovziuczzeyzitgvjtar79air-max-1-on-air-tokyo-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autovztgxht8el3fnj7oe6uzair-max-98-on-air-nyc-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autovztgxht8el3fnj7oe6uzair-max-98-on-air-nyc-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── wp-contentuploads201903Air-Jordan-11-Low-Navy-Blue-Snakeskin-CD6846-102-Release-Date-Price.imageset
│ │ │ ├── wp-contentuploads201903Air-Jordan-11-Low-Navy-Blue-Snakeskin-CD6846-102-Release-Date-Price.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autokqoxst75honzfjrnqqyjair-max-97-on-air-seoul-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autokqoxst75honzfjrnqqyjair-max-97-on-air-seoul-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── wp-contentuploads201901nike-air-fear-of-god-moccasin-pure-platinum-AT8086-001-release-date-1.imageset
│ │ │ ├── wp-contentuploads201901nike-air-fear-of-god-moccasin-pure-platinum-AT8086-001-release-date-1.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_PDP_864_v1f_auto,b_rgb:f5f5f5ecliaenqoa7rgkgvp2xyair-jordan-1-low-mens-shoe-z3Tl2VeJ.imageset
│ │ │ ├── aimagest_PDP_864_v1f_auto,b_rgb:f5f5f5ecliaenqoa7rgkgvp2xyair-jordan-1-low-mens-shoe-z3Tl2VeJ.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autopselea04ks9hnkcbsahdair-max-97-on-air-london-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autopselea04ks9hnkcbsahdair-max-97-on-air-london-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autovxm7ancrgvsdat5i7o0unike-pg-3-mamba-mentality-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autovxm7ancrgvsdat5i7o0unike-pg-3-mamba-mentality-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autobyweh2fwj8cxlvkcjyjbadapt-bb-future-of-the-game-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autobyweh2fwj8cxlvkcjyjbadapt-bb-future-of-the-game-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autotunyzqjx91k3yk55mggeair-max2-light-purple-berry-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autotunyzqjx91k3yk55mggeair-max2-light-purple-berry-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autourcu79fr3do9u0a4soicair-max-97-on-air-shanghai-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autourcu79fr3do9u0a4soicair-max-97-on-air-shanghai-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autoepba4fhpfb9ajdmpmea2air-foamposite-hyper-crimson-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autoepba4fhpfb9ajdmpmea2air-foamposite-hyper-crimson-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── solelinksstoragereleaseImages3666AQ1090-200_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-200-1.imageset
│ │ │ ├── solelinksstoragereleaseImages3666AQ1090-200_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-200-1.jpg
│ │ │ └── Contents.json
│ │ ├── solelinksstoragereleaseImages3667AQ1090-300_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-300-1.imageset
│ │ │ ├── solelinksstoragereleaseImages3667AQ1090-300_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-300-1.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_lsw_1920,c_limit,f_autodil46qlauylplvdx6dudair-foamposite-hyper-crimson-release-date.imageset
│ │ │ ├── aimagest_prod_lsw_1920,c_limit,f_autodil46qlauylplvdx6dudair-foamposite-hyper-crimson-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_lsw_1920,c_limit,f_autovkzcokvv4fh2jjk71djdair-foamposite-hyper-crimson-release-date.imageset
│ │ │ ├── aimagest_prod_lsw_1920,c_limit,f_autovkzcokvv4fh2jjk71djdair-foamposite-hyper-crimson-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_lsw_1920,c_limit,f_autoy0udif4vqu87bklmxs9uair-foamposite-hyper-crimson-release-date.imageset
│ │ │ ├── aimagest_prod_lsw_1920,c_limit,f_autoy0udif4vqu87bklmxs9uair-foamposite-hyper-crimson-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autoxcsral03nckwhqi3vshbair-vapormax-plus-on-air-paris-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autoxcsral03nckwhqi3vshbair-vapormax-plus-on-air-paris-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autocr2smyshlm9vamsquxamnike-blazer-mid-77-black-canvas-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autocr2smyshlm9vamsquxamnike-blazer-mid-77-black-canvas-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autot8cz8v6lx119os3pn94onike-air-trainer-3-medicine-ball-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autot8cz8v6lx119os3pn94onike-air-trainer-3-medicine-ball-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autoyqhwscvw99o0a1w6pmwiwomens-air-jordan-12-midnight-black-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autoyqhwscvw99o0a1w6pmwiwomens-air-jordan-12-midnight-black-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MDZOaWtlLUt5cmllLTQtVHJpcGxlLUJsYWNrLTk0MzgwNy0wMDgtUmVsZWFzZS1EYXRlLmpwZw==.imageset
│ │ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MDZOaWtlLUt5cmllLTQtVHJpcGxlLUJsYWNrLTk0MzgwNy0wMDgtUmVsZWFzZS1EYXRlLmpwZw==.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autojejlqawpimm6qoanfwzcnike-blazer-mid-77-vintage-pink-foam-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autojejlqawpimm6qoanfwzcnike-blazer-mid-77-vintage-pink-foam-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autovp6fcofbqm8skzhjuldmnike-blazer-mid-77-vintage-sail-white-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autovp6fcofbqm8skzhjuldmnike-blazer-mid-77-vintage-sail-white-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1NzlfMS5wbmc=.imageset
│ │ │ ├── bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1NzlfMS5wbmc=.png
│ │ │ └── Contents.json
│ │ ├── bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1ODNfMS5wbmc=.imageset
│ │ │ ├── bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1ODNfMS5wbmc=.png
│ │ │ └── Contents.json
│ │ ├── aimagest_prod_ssw_960,c_limit,f_autoeyeg1kej2njudfya2fhqnike-womens-air-max-95-plant-color-collection-release-date.imageset
│ │ │ ├── aimagest_prod_ssw_960,c_limit,f_autoeyeg1kej2njudfya2fhqnike-womens-air-max-95-plant-color-collection-release-date.jpg
│ │ │ └── Contents.json
│ │ ├── bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1ZWVlZTc1ODFfMV8xLnBuZw==.imageset
│ │ │ ├── bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1ZWVlZTc1ODFfMV8xLnBuZw==.png
│ │ │ └── Contents.json
│ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MDZKb3JkYW4tTGVnYWN5LTMxMi1TdG9ybS1CbHVlLVJvYXlsLUFRNDE2MC0xMDQtUmVsZWFzZS1EYXRlLmpwZw==.imageset
│ │ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MDZKb3JkYW4tTGVnYWN5LTMxMi1TdG9ybS1CbHVlLVJvYXlsLUFRNDE2MC0xMDQtUmVsZWFzZS1EYXRlLmpwZw==.jpg
│ │ │ └── Contents.json
│ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUFpci1Gb3JjZS0yNzAtVXRpbGl0eS1CbGFjay1Wb2x0LUFRMDU3Mi0wMDEtUmVsZWFzZS1EYXRlLmpwZw==.imageset
│ │ │ ├── d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUFpci1Gb3JjZS0yNzAtVXRpbGl0eS1CbGFjay1Wb2x0LUFRMDU3Mi0wMDEtUmVsZWFzZS1EYXRlLmpwZw==.jpg
│ │ │ └── Contents.json
│ │ ├── YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBuenlibnRkZDNhY3B3YW1xYXFycWNvcnRlei1iYXNpYy1sZWF0aGVyLW1lbnMtc2hvZS1SUFBDRzUuanBn.imageset
│ │ │ ├── YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBuenlibnRkZDNhY3B3YW1xYXFycWNvcnRlei1iYXNpYy1sZWF0aGVyLW1lbnMtc2hvZS1SUFBDRzUuanBn.jpg
│ │ │ └── Contents.json
│ │ ├── d3d3LnNuZWFrZXJlbGl0ZS5jb213cC1jb250ZW50dXBsb2FkczIwMTgwNmFkaWRhcy1ZZWV6eS1Cb29zdC0zNTAtVjItQnV0dGVyLVJlbGVhc2UtRGF0ZS1GMzY5ODAuanBn.imageset
│ │ │ ├── d3d3LnNuZWFrZXJlbGl0ZS5jb213cC1jb250ZW50dXBsb2FkczIwMTgwNmFkaWRhcy1ZZWV6eS1Cb29zdC0zNTAtVjItQnV0dGVyLVJlbGVhc2UtRGF0ZS1GMzY5ODAuanBn.jpg
│ │ │ └── Contents.json
│ │ ├── YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBmc29sM2I3aGUxcnJkYmpsbTV4MGFpci1mb290c2NhcGUtbWlkLXV0aWxpdHktZG0tbWVucy1zaG9lLXM3WGdTNC5qcGc=.imageset
│ │ │ ├── YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBmc29sM2I3aGUxcnJkYmpsbTV4MGFpci1mb290c2NhcGUtbWlkLXV0aWxpdHktZG0tbWVucy1zaG9lLXM3WGdTNC5qcGc=.jpg
│ │ │ └── Contents.json
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvazF1OHJnMmZuemZwMjJqZTdwbnVuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tbjctcmVsZWFzZS1kYXRlLmpwZw==.imageset
│ │ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvazF1OHJnMmZuemZwMjJqZTdwbnVuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tbjctcmVsZWFzZS1kYXRlLmpwZw==.jpg
│ │ │ └── Contents.json
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbnlieW95ampmYzhrbzVnZ3dianJ3b21lbnMtYWlyLWpvcmRhbi0zLWJvcmRlYXV4LXJlbGVhc2UtZGF0ZS5qcGc=.imageset
│ │ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbnlieW95ampmYzhrbzVnZ3dianJ3b21lbnMtYWlyLWpvcmRhbi0zLWJvcmRlYXV4LXJlbGVhc2UtZGF0ZS5qcGc=.jpg
│ │ │ └── Contents.json
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvZjViM2E1amNxaWFycm5qZzR0aWJuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1yZWxlYXNlLWRhdGUuanBn.imageset
│ │ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvZjViM2E1amNxaWFycm5qZzR0aWJuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1yZWxlYXNlLWRhdGUuanBn.jpg
│ │ │ └── Contents.json
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcDl0cnV4eHB2M3Bkb2U3M3hobG9haXItam9yZGFuLTYtcmV0cm8tdGlua2VyLWluZnJhcmVkLXJlbGVhc2UtZGF0ZS5qcGc=.imageset
│ │ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcDl0cnV4eHB2M3Bkb2U3M3hobG9haXItam9yZGFuLTYtcmV0cm8tdGlua2VyLWluZnJhcmVkLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
│ │ │ └── Contents.json
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveTU0ejh3bnc0ZWVyMXR4MWFrdmluaWtlLWFpci1tYXgtOTctYmxhY2stbWV0YWxsaWMtZ29sZC1yZWxlYXNlLWRhdGUuanBn.imageset
│ │ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveTU0ejh3bnc0ZWVyMXR4MWFrdmluaWtlLWFpci1tYXgtOTctYmxhY2stbWV0YWxsaWMtZ29sZC1yZWxlYXNlLWRhdGUuanBn.jpg
│ │ │ └── Contents.json
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvYXd4YXh5N2l0cDA0eWx5ZGlwY2FuaWtlLWFpci1tYXgtOTUtbnJnLWJsYWNrLWFudGhyYWNpdGUtcmVsZWFzZS1kYXRlLmpwZw==.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvYXd4YXh5N2l0cDA0eWx5ZGlwY2FuaWtlLWFpci1tYXgtOTUtbnJnLWJsYWNrLWFudGhyYWNpdGUtcmVsZWFzZS1kYXRlLmpwZw==.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd29rOG0zcHgwMTF2ZWptNzJzeWRuaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd29rOG0zcHgwMTF2ZWptNzJzeWRuaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd2oxYXJ3Z21xazB4MHJqbm54aDduaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd2oxYXJ3Z21xazB4MHJqbm54aDduaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvanF3eW1laGJ5c21ueG1hd3JyNmJ3b21lbnMtYWlyLWZvcmNlLTEtc2FnZS1oaWdoLXdoaXRlLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvanF3eW1laGJ5c21ueG1hd3JyNmJ3b21lbnMtYWlyLWZvcmNlLTEtc2FnZS1oaWdoLXdoaXRlLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbWJpaDc5Z3htbG1veTFhbmx5cHZ3b21lbnMtYWYxLWplc3Rlci1oaS14eC1ibGFjay1kdXN0eS1wZWFjaC1yZWxlYXNlLWRhdGUuanBn.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbWJpaDc5Z3htbG1veTFhbmx5cHZ3b21lbnMtYWYxLWplc3Rlci1oaS14eC1ibGFjay1kdXN0eS1wZWFjaC1yZWxlYXNlLWRhdGUuanBn.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvc2Jsc3F6aWthcHFnemk3OXl5cXNuaWtlLWFpci1tYXgtOTctbnJnLXRlYW0tcmVkLW1pZG5pZ2h0LXNwcnVjZS1yZWxlYXNlLWRhdGUuanBn.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvc2Jsc3F6aWthcHFnemk3OXl5cXNuaWtlLWFpci1tYXgtOTctbnJnLXRlYW0tcmVkLW1pZG5pZ2h0LXNwcnVjZS1yZWxlYXNlLWRhdGUuanBn.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcXh1MDBpaHh5dDVyZjBzZ2ZyYnVuaWtlLWFpci1tYXgtOTUtcHJlbWl1bS12b2x0LXZvbHQtZ2xvdy1ibGFjay1yZWxlYXNlLWRhdGUuanBn.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcXh1MDBpaHh5dDVyZjBzZ2ZyYnVuaWtlLWFpci1tYXgtOTUtcHJlbWl1bS12b2x0LXZvbHQtZ2xvdy1ibGFjay1yZWxlYXNlLWRhdGUuanBn.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdTVkOGNscms4dGN3anR1aXlsYnZuaWtlLXdvbWVucy1haXItZm9yY2UtMS1yZWJlbC14eC1wcmFsaW5lLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdTVkOGNscms4dGN3anR1aXlsYnZuaWtlLXdvbWVucy1haXItZm9yY2UtMS1yZWJlbC14eC1wcmFsaW5lLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdWNycWRwMXdiYnh2cWp5MDdmcmV3b21lbnMtYWYxLWplc3Rlci1oaS14eC12aW9sZXQtYXNoLWJsdWUtZm9yY2UtcmVsZWFzZS1kYXRlLmpwZw==.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdWNycWRwMXdiYnh2cWp5MDdmcmV3b21lbnMtYWYxLWplc3Rlci1oaS14eC12aW9sZXQtYXNoLWJsdWUtZm9yY2UtcmVsZWFzZS1kYXRlLmpwZw==.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveXFhY3F2cWNqaHFwYmduZ3VheWVhaXItam9yZGFuLTEyLWludGVybmF0aW9uYWwtZmxpZ2h0LWNvbGxlZ2UtbmF2eS1yZWxlYXNlLWRhdGUuanBn.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveXFhY3F2cWNqaHFwYmduZ3VheWVhaXItam9yZGFuLTEyLWludGVybmF0aW9uYWwtZmxpZ2h0LWNvbGxlZ2UtbmF2eS1yZWxlYXNlLWRhdGUuanBn.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcThpMXJvZGd4OWNjaXI1Ymdzc2VuaWtlLWFpci1mb3JjZS0xLWx2OC11dGlsaXR5LXZvbHQtd29sZi1ncmV5LXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcThpMXJvZGd4OWNjaXI1Ymdzc2VuaWtlLWFpci1mb3JjZS0xLWx2OC11dGlsaXR5LXZvbHQtd29sZi1ncmV5LXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdmd4NXR6cmd2YTBpcmxzYnJjcGluaWtlLXNiLWxvdy1wcm8tZGlhbW9uZC1ibGFjay10cm9waWNhbC10d2lzdC1jaHJvbWUtcmVsZWFzZS1kYXRlLmpwZw==.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdmd4NXR6cmd2YTBpcmxzYnJjcGluaWtlLXNiLWxvdy1wcm8tZGlhbW9uZC1ibGFjay10cm9waWNhbC10d2lzdC1jaHJvbWUtcmVsZWFzZS1kYXRlLmpwZw==.jpg
│ │ ├── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd3drY29xcHY5dnh5aDRwZ3dkZ25uaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1jb2xsZWN0aW9uLXRvdGFsLW9yYW5nZS1yZWxlYXNlLWRhdGUuanBn.imageset
│ │ │ ├── Contents.json
│ │ │ └── YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd3drY29xcHY5dnh5aDRwZ3dkZ25uaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1jb2xsZWN0aW9uLXRvdGFsLW9yYW5nZS1yZWxlYXNlLWRhdGUuanBn.jpg
│ │ └── compleximagesfl_lossy,q_autoc_crop,h_502,w_856,x_0,y_183c_scale,w_690,dpr_2.0v1ctxstklbxucwkotuhh72air-jordan-1-shattered-backboard-3-0-black-pale-vanilla-starfish-555088-028-rendering.imageset
│ │ │ ├── Contents.json
│ │ │ └── compleximagesfl_lossy,q_autoc_crop,h_502,w_856,x_0,y_183c_scale,w_690,dpr_2.0v1ctxstklbxucwkotuhh72air-jordan-1-shattered-backboard-3-0-black-pale-vanilla-starfish-555088-028-rendering.jpg
│ ├── banner
│ │ ├── Contents.json
│ │ ├── banner1.imageset
│ │ │ ├── banner1.jpg
│ │ │ └── Contents.json
│ │ ├── banner2.imageset
│ │ │ ├── banner2.jpg
│ │ │ └── Contents.json
│ │ ├── banner3.imageset
│ │ │ ├── banner3.jpg
│ │ │ └── Contents.json
│ │ ├── banner4.imageset
│ │ │ ├── banner4.jpg
│ │ │ └── Contents.json
│ │ ├── banner5.imageset
│ │ │ ├── banner5.jpg
│ │ │ └── Contents.json
│ │ └── banner6.imageset
│ │ │ ├── banner6.jpg
│ │ │ └── Contents.json
│ ├── loading
│ │ ├── Contents.json
│ │ ├── frame-0.imageset
│ │ │ ├── frame-0.png
│ │ │ └── Contents.json
│ │ ├── frame-1.imageset
│ │ │ ├── frame-1.png
│ │ │ └── Contents.json
│ │ ├── frame-2.imageset
│ │ │ ├── frame-2.png
│ │ │ └── Contents.json
│ │ ├── frame-3.imageset
│ │ │ ├── frame-3.png
│ │ │ └── Contents.json
│ │ ├── frame-4.imageset
│ │ │ ├── frame-4.png
│ │ │ └── Contents.json
│ │ ├── frame-5.imageset
│ │ │ ├── frame-5.png
│ │ │ └── Contents.json
│ │ ├── frame-6.imageset
│ │ │ ├── frame-6.png
│ │ │ └── Contents.json
│ │ ├── frame-7.imageset
│ │ │ ├── frame-7.png
│ │ │ └── Contents.json
│ │ ├── frame-8.imageset
│ │ │ ├── frame-8.png
│ │ │ └── Contents.json
│ │ ├── frame-9.imageset
│ │ │ ├── frame-9.png
│ │ │ └── Contents.json
│ │ ├── frame-10.imageset
│ │ │ ├── frame-10.png
│ │ │ └── Contents.json
│ │ ├── frame-11.imageset
│ │ │ ├── frame-11.png
│ │ │ └── Contents.json
│ │ ├── frame-12.imageset
│ │ │ ├── frame-12.png
│ │ │ └── Contents.json
│ │ ├── frame-13.imageset
│ │ │ ├── frame-13.png
│ │ │ └── Contents.json
│ │ ├── frame-14.imageset
│ │ │ ├── frame-14.png
│ │ │ └── Contents.json
│ │ ├── frame-15.imageset
│ │ │ ├── frame-15.png
│ │ │ └── Contents.json
│ │ ├── frame-16.imageset
│ │ │ ├── frame-16.png
│ │ │ └── Contents.json
│ │ ├── frame-17.imageset
│ │ │ ├── frame-17.png
│ │ │ └── Contents.json
│ │ ├── frame-18.imageset
│ │ │ ├── frame-18.png
│ │ │ └── Contents.json
│ │ ├── frame-19.imageset
│ │ │ ├── frame-19.png
│ │ │ └── Contents.json
│ │ ├── frame-20.imageset
│ │ │ ├── frame-20.png
│ │ │ └── Contents.json
│ │ ├── frame-21.imageset
│ │ │ ├── frame-21.png
│ │ │ └── Contents.json
│ │ ├── frame-22.imageset
│ │ │ ├── frame-22.png
│ │ │ └── Contents.json
│ │ ├── frame-23.imageset
│ │ │ ├── frame-23.png
│ │ │ └── Contents.json
│ │ ├── frame-24.imageset
│ │ │ ├── frame-24.png
│ │ │ └── Contents.json
│ │ ├── frame-25.imageset
│ │ │ ├── frame-25.png
│ │ │ └── Contents.json
│ │ ├── frame-26.imageset
│ │ │ ├── frame-26.png
│ │ │ └── Contents.json
│ │ ├── frame-27.imageset
│ │ │ ├── frame-27.png
│ │ │ └── Contents.json
│ │ ├── frame-28.imageset
│ │ │ ├── frame-28.png
│ │ │ └── Contents.json
│ │ └── frame-29.imageset
│ │ │ ├── frame-29.png
│ │ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ ├── Icon-20.png
│ │ ├── Icon-29.png
│ │ ├── Icon-40.png
│ │ ├── Icon-76.png
│ │ ├── Icon-20@2x.png
│ │ ├── Icon-20@3x.png
│ │ ├── Icon-29@2x.png
│ │ ├── Icon-29@3x.png
│ │ ├── Icon-40@2x.png
│ │ ├── Icon-40@3x.png
│ │ ├── Icon-512@2x.png
│ │ ├── Icon-60@2x.png
│ │ ├── Icon-60@3x.png
│ │ ├── Icon-76@2x.png
│ │ ├── Icon-20@2x-1.png
│ │ ├── Icon-29@2x-1.png
│ │ ├── Icon-40@2x-1.png
│ │ └── Icon-83.5@2x.png
├── Other
│ ├── Util
│ │ ├── NameProvidable.swift
│ │ ├── ProjectNameHelper.swift
│ │ ├── ScrollViewCalculator.swift
│ │ └── NotificationUtility.swift
│ ├── Extension
│ │ ├── String+extensions.swift
│ │ ├── UIColor+extensions.swift
│ │ ├── UIStoryboard+Extensions.swift
│ │ ├── UICollectionView+Extensions.swift
│ │ └── UITableView+Extensions.swift
│ └── Constants
│ │ └── ThemeColor.swift
├── DomainLayer
│ ├── DataModel
│ │ ├── BannerModel.swift
│ │ └── ShoesModel.swift
│ ├── ShoesRepositorySpec.swift
│ ├── BannerRepositorySpec.swift
│ └── UseCases
│ │ ├── FetchDataUseCaseSpec.swift
│ │ ├── FetchShoesUseCase.swift
│ │ └── FetchBannerUseCase.swift
├── DataLayer
│ ├── DataModel
│ │ ├── RemoteBannerModel.swift
│ │ └── RemoteBaseModel.swift
│ └── Repositories
│ │ ├── Remote
│ │ ├── NetworkFetchable.swift
│ │ ├── RemoteBannerRepository.swift
│ │ ├── RemoteShoesRepository.swift
│ │ ├── BannerFetcher.swift
│ │ └── ShoesListFetcher.swift
│ │ ├── Cache
│ │ └── CacheShoesRepository.swift
│ │ └── Local
│ │ └── LocalShoesRepository.swift
├── ViewLayer
│ ├── Scenes
│ │ ├── SetReuseViewModelable.swift
│ │ ├── ShoesList
│ │ │ ├── Banner
│ │ │ │ ├── ShoesListBannerCollectionViewCell.swift
│ │ │ │ ├── ShoesListBannerRouter.swift
│ │ │ │ ├── ShoesListBannerBuilder.swift
│ │ │ │ └── ShoesListBannerPresenter.swift
│ │ │ ├── ShoesListRouter.swift
│ │ │ ├── ShoesListTableViewCell.swift
│ │ │ └── ShoesListViewBuilder.swift
│ │ ├── ViewBuilderSpec.swift
│ │ └── ShoesDetail
│ │ │ ├── ShoesDetailViewBuilder.swift
│ │ │ ├── ShoesDetailDescriptionTableViewCell.swift
│ │ │ ├── ShoesDetailInfoTableViewCell.swift
│ │ │ └── ShoesDetailPresenter.swift
│ ├── BaseClass
│ │ ├── BaseViewController.swift
│ │ └── BaseNavigationController.swift
│ └── CustomView
│ │ ├── Loading
│ │ ├── LoadingImageView.swift
│ │ └── LoadingViewManager.swift
│ │ └── FailHeaderView.swift
├── AppDelegate.swift
├── SupportFiles
│ └── Dummy
│ │ └── banner_remote_source.json
└── Info.plist
├── MVP-Sample.xcodeproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── MVP-SampleTests
├── Helper
│ ├── Model
│ │ ├── RemoteBannerModel.swift
│ │ ├── BannerModel.swift
│ │ └── ShoesModel.swift
│ └── Repository
│ │ ├── MockFetchShoesRepository.swift
│ │ └── MockRemoteBannerRepository.swift
├── Info.plist
├── MVP_SampleTests.swift
└── DomainLayer
│ ├── FetchShoesUseCaseTests.swift
│ └── FetchBannerUseCaseTests.swift
├── LICENSE
├── .gitignore
└── README.md
/architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/architecture.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-20.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-29.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-40.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-512@2x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/12732.imageset/12732@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/12732.imageset/12732@3x.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/12746.imageset/12746@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/12746.imageset/12746@3x.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/12837.imageset/12837@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/12837.imageset/12837@3x.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/12841.imageset/12841@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/12841.imageset/12841@3x.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/9Osfhjm.imageset/9Osfhjm.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/9Osfhjm.imageset/9Osfhjm.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/JwKauWO.imageset/JwKauWO.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/JwKauWO.imageset/JwKauWO.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/N6OObvC.imageset/N6OObvC.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/N6OObvC.imageset/N6OObvC.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/QRdx9Il.imageset/QRdx9Il.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/QRdx9Il.imageset/QRdx9Il.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/TNOsGqn.imageset/TNOsGqn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/TNOsGqn.imageset/TNOsGqn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/cQnmyKQ.imageset/cQnmyKQ.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/cQnmyKQ.imageset/cQnmyKQ.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/mm6pknp.imageset/mm6pknp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/mm6pknp.imageset/mm6pknp.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/o0l47Ca.imageset/o0l47Ca.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/o0l47Ca.imageset/o0l47Ca.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/p4Ud82Q.imageset/p4Ud82Q.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/p4Ud82Q.imageset/p4Ud82Q.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-20@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-20@2x-1.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-29@2x-1.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner1.imageset/banner1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/banner/banner1.imageset/banner1.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner2.imageset/banner2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/banner/banner2.imageset/banner2.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner3.imageset/banner3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/banner/banner3.imageset/banner3.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner4.imageset/banner4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/banner/banner4.imageset/banner4.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner5.imageset/banner5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/banner/banner5.imageset/banner5.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner6.imageset/banner6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/banner/banner6.imageset/banner6.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-0.imageset/frame-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-0.imageset/frame-0.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-1.imageset/frame-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-1.imageset/frame-1.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-2.imageset/frame-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-2.imageset/frame-2.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-3.imageset/frame-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-3.imageset/frame-3.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-4.imageset/frame-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-4.imageset/frame-4.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-5.imageset/frame-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-5.imageset/frame-5.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-6.imageset/frame-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-6.imageset/frame-6.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-7.imageset/frame-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-7.imageset/frame-7.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-8.imageset/frame-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-8.imageset/frame-8.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-9.imageset/frame-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-9.imageset/frame-9.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-10.imageset/frame-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-10.imageset/frame-10.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-11.imageset/frame-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-11.imageset/frame-11.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-12.imageset/frame-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-12.imageset/frame-12.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-13.imageset/frame-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-13.imageset/frame-13.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-14.imageset/frame-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-14.imageset/frame-14.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-15.imageset/frame-15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-15.imageset/frame-15.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-16.imageset/frame-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-16.imageset/frame-16.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-17.imageset/frame-17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-17.imageset/frame-17.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-18.imageset/frame-18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-18.imageset/frame-18.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-19.imageset/frame-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-19.imageset/frame-19.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-20.imageset/frame-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-20.imageset/frame-20.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-21.imageset/frame-21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-21.imageset/frame-21.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-22.imageset/frame-22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-22.imageset/frame-22.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-23.imageset/frame-23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-23.imageset/frame-23.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-24.imageset/frame-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-24.imageset/frame-24.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-25.imageset/frame-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-25.imageset/frame-25.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-26.imageset/frame-26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-26.imageset/frame-26.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-27.imageset/frame-27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-27.imageset/frame-27.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-28.imageset/frame-28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-28.imageset/frame-28.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-29.imageset/frame-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/loading/frame-29.imageset/frame-29.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/M29vcEQ0Ry5qcGc=.imageset/M29vcEQ0Ry5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/M29vcEQ0Ry5qcGc=.imageset/M29vcEQ0Ry5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/MTE1MDZAM3guanBn.imageset/MTE1MDZAM3guanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/MTE1MDZAM3guanBn.imageset/MTE1MDZAM3guanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/MTIxNjhAM3guanBn.imageset/MTIxNjhAM3guanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/MTIxNjhAM3guanBn.imageset/MTIxNjhAM3guanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/RDdGVGV6eS5qcGc=.imageset/RDdGVGV6eS5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/RDdGVGV6eS5qcGc=.imageset/RDdGVGV6eS5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Util/NameProvidable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NameProvidable.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/19.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol NameProvidable {
12 | var name: String { get }
13 | }
14 |
--------------------------------------------------------------------------------
/MVP-Sample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MVP-Sample/DomainLayer/DataModel/BannerModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BannerModel.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct BannerModel: Codable {
12 | let imageName: String
13 | let url: URL?
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/DataModel/RemoteBannerModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RemoteBannerModel.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct RemoteBannerModel: Codable {
12 | let imageName: String
13 | let url: String
14 | }
15 |
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/DataModel/RemoteBaseModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RemoteBaseModel.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct RemoteBaseModel: Codable {
12 | let code: Int
13 | let msg: String
14 | let body: T
15 | }
16 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/SetReuseViewModelable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SetReuseViewModelable.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/13.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol SetReuseViewModelable {
12 |
13 | associatedtype ViewModel
14 | func setReuseViewMode(_ viewModel: ViewModel)
15 | }
16 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Extension/String+extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+extensions.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/11.
6 | // Copyright © 2018年 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 |
13 | func toBase64() -> String? {
14 | return data(using: .utf8)?.base64EncodedString()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-001-Release-Date-1.imageset/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-001-Release-Date-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-001-Release-Date-1.imageset/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-001-Release-Date-1.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-400-Release-Date-1.imageset/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-400-Release-Date-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-400-Release-Date-1.imageset/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-400-Release-Date-1.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDdqb3JkYW4tbGVnYWN5LTMxMi1BUTQxNjAtMzAxLTEuanBn.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MDdqb3JkYW4tbGVnYWN5LTMxMi1BUTQxNjAtMzAxLTEuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDdqb3JkYW4tbGVnYWN5LTMxMi1BUTQxNjAtMzAxLTEuanBn.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MDdqb3JkYW4tbGVnYWN5LTMxMi1BUTQxNjAtMzAxLTEuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUt5cmllLTUtVGFjby1BTzI5MTgtOTAyLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUt5cmllLTUtVGFjby1BTzI5MTgtOTAyLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUt5cmllLTUtVGFjby1BTzI5MTgtOTAyLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUt5cmllLTUtVGFjby1BTzI5MTgtOTAyLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/12732.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "12732@3x.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/12746.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "12746@3x.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/12837.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "12837@3x.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/12841.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "12841@3x.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/9Osfhjm.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "9Osfhjm.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/JwKauWO.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "JwKauWO.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/N6OObvC.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "N6OObvC.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/QRdx9Il.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "QRdx9Il.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/TNOsGqn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "TNOsGqn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/cQnmyKQ.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "cQnmyKQ.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/mm6pknp.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "mm6pknp.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/o0l47Ca.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "o0l47Ca.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/p4Ud82Q.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "p4Ud82Q.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-SampleTests/Helper/Model/RemoteBannerModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RemoteBannerModel.swift
3 | // MVP-SampleTests
4 | //
5 | // Created by NixonShih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | @testable import MVP_Sample
10 |
11 | extension RemoteBannerModel {
12 |
13 | static var newBanner: RemoteBannerModel {
14 | return RemoteBannerModel(imageName: "123", url: "345")
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "banner1.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "banner2.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "banner3.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "banner4.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "banner5.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/banner/banner6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "banner6.jpg",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-0.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-0.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-1.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-10.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-10.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-11.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-11.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-12.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-12.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-13.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-13.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-14.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-14.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-15.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-15.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-16.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-16.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-17.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-17.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-18.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-18.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-19.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-19.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-20.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-20.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-21.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-21.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-22.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-22.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-23.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-23.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-24.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-24.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-25.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-25.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-26.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-26.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-27.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-27.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-28.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-28.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-29.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-29.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-3.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-4.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-5.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-6.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-7.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-7.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-8.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-8.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/loading/frame-9.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "frame-9.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201902nike-air-fear-of-god-180-black-AT8087-002-release-date-1.imageset/wp-contentuploads201902nike-air-fear-of-god-180-black-AT8087-002-release-date-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201902nike-air-fear-of-god-180-black-AT8087-002-release-date-1.imageset/wp-contentuploads201902nike-air-fear-of-god-180-black-AT8087-002-release-date-1.jpg
--------------------------------------------------------------------------------
/MVP-Sample/DomainLayer/DataModel/ShoesModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesModel.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/10.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct ShoesModel: Codable {
12 |
13 | let date: String
14 | let description: String?
15 | var imageName: String
16 | let nickname: String?
17 | let price: Int
18 | let title: String
19 | }
20 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTIuanBn.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTIuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTIuanBn.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTIuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTMuanBn.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTMuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTMuanBn.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTMuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Air-Jordan-4-GS-Monsoon-Blue-BQ9043-400-Release-Date-Price.imageset/wp-contentuploads201903Air-Jordan-4-GS-Monsoon-Blue-BQ9043-400-Release-Date-Price.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Air-Jordan-4-GS-Monsoon-Blue-BQ9043-400-Release-Date-Price.imageset/wp-contentuploads201903Air-Jordan-4-GS-Monsoon-Blue-BQ9043-400-Release-Date-Price.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201904Nike-Kyrie-5-Mamba-Mentality-AO2918-102-Release-Date-Price.imageset/wp-contentuploads201904Nike-Kyrie-5-Mamba-Mentality-AO2918-102-Release-Date-Price.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201904Nike-Kyrie-5-Mamba-Mentality-AO2918-102-Release-Date-Price.imageset/wp-contentuploads201904Nike-Kyrie-5-Mamba-Mentality-AO2918-102-Release-Date-Price.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/M29vcEQ0Ry5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "M29vcEQ0Ry5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/MTE1MDZAM3guanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "MTE1MDZAM3guanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/MTIxNjhAM3guanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "MTIxNjhAM3guanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/RDdGVGV6eS5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "RDdGVGV6eS5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/DomainLayer/ShoesRepositorySpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesRepositorySpec.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/11.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol ShoesRepositorySpec {
12 |
13 | typealias FetchShoesCompletionHandler = (Result<[ShoesModel], Error>) -> ()
14 | func fetchShoes(_ completionHandler: @escaping FetchShoesCompletionHandler)
15 | }
16 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autocys1ywaekpuvespws0x4lebron-16-remix-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autocys1ywaekpuvespws0x4lebron-16-remix-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autocys1ywaekpuvespws0x4lebron-16-remix-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autocys1ywaekpuvespws0x4lebron-16-remix-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDRhaXItam9yZGFuLTEwLW9ybGFuZG8tcmVsZWFzZS1pbmZvLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MDRhaXItam9yZGFuLTEwLW9ybGFuZG8tcmVsZWFzZS1pbmZvLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDRhaXItam9yZGFuLTEwLW9ybGFuZG8tcmVsZWFzZS1pbmZvLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MDRhaXItam9yZGFuLTEwLW9ybGFuZG8tcmVsZWFzZS1pbmZvLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903nike-air-fear-of-god-moccasin-black-at8086-002-release-date-1.imageset/wp-contentuploads201903nike-air-fear-of-god-moccasin-black-at8086-002-release-date-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903nike-air-fear-of-god-moccasin-black-at8086-002-release-date-1.imageset/wp-contentuploads201903nike-air-fear-of-god-moccasin-black-at8086-002-release-date-1.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoxrj4afk5rpddzxbbx6wclebron-16-martin-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoxrj4afk5rpddzxbbx6wclebron-16-martin-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoxrj4afk5rpddzxbbx6wclebron-16-martin-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoxrj4afk5rpddzxbbx6wclebron-16-martin-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/DomainLayer/BannerRepositorySpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BannerRepositorySpec.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol BannerRepositorySpec {
12 |
13 | typealias FetchBannerCompletionHandler = (Result<[RemoteBannerModel], Error>) -> ()
14 | func fetchBanners(_ completionHandler: @escaping FetchBannerCompletionHandler)
15 | }
16 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoegkplcxbgvbraxiwrkv4air-max2-light-atmos-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoegkplcxbgvbraxiwrkv4air-max2-light-atmos-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoegkplcxbgvbraxiwrkv4air-max2-light-atmos-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoegkplcxbgvbraxiwrkv4air-max2-light-atmos-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoq8vrexcyxpmj80u4esgvlebron-16-air-trainer-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoq8vrexcyxpmj80u4esgvlebron-16-air-trainer-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoq8vrexcyxpmj80u4esgvlebron-16-air-trainer-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoq8vrexcyxpmj80u4esgvlebron-16-air-trainer-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovziuczzeyzitgvjtar79air-max-1-on-air-tokyo-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autovziuczzeyzitgvjtar79air-max-1-on-air-tokyo-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovziuczzeyzitgvjtar79air-max-1-on-air-tokyo-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autovziuczzeyzitgvjtar79air-max-1-on-air-tokyo-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovztgxht8el3fnj7oe6uzair-max-98-on-air-nyc-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autovztgxht8el3fnj7oe6uzair-max-98-on-air-nyc-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovztgxht8el3fnj7oe6uzair-max-98-on-air-nyc-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autovztgxht8el3fnj7oe6uzair-max-98-on-air-nyc-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Air-Jordan-11-Low-Navy-Blue-Snakeskin-CD6846-102-Release-Date-Price.imageset/wp-contentuploads201903Air-Jordan-11-Low-Navy-Blue-Snakeskin-CD6846-102-Release-Date-Price.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Air-Jordan-11-Low-Navy-Blue-Snakeskin-CD6846-102-Release-Date-Price.imageset/wp-contentuploads201903Air-Jordan-11-Low-Navy-Blue-Snakeskin-CD6846-102-Release-Date-Price.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autokqoxst75honzfjrnqqyjair-max-97-on-air-seoul-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autokqoxst75honzfjrnqqyjair-max-97-on-air-seoul-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autokqoxst75honzfjrnqqyjair-max-97-on-air-seoul-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autokqoxst75honzfjrnqqyjair-max-97-on-air-seoul-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201901nike-air-fear-of-god-moccasin-pure-platinum-AT8086-001-release-date-1.imageset/wp-contentuploads201901nike-air-fear-of-god-moccasin-pure-platinum-AT8086-001-release-date-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201901nike-air-fear-of-god-moccasin-pure-platinum-AT8086-001-release-date-1.imageset/wp-contentuploads201901nike-air-fear-of-god-moccasin-pure-platinum-AT8086-001-release-date-1.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_PDP_864_v1f_auto,b_rgb:f5f5f5ecliaenqoa7rgkgvp2xyair-jordan-1-low-mens-shoe-z3Tl2VeJ.imageset/aimagest_PDP_864_v1f_auto,b_rgb:f5f5f5ecliaenqoa7rgkgvp2xyair-jordan-1-low-mens-shoe-z3Tl2VeJ.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_PDP_864_v1f_auto,b_rgb:f5f5f5ecliaenqoa7rgkgvp2xyair-jordan-1-low-mens-shoe-z3Tl2VeJ.imageset/aimagest_PDP_864_v1f_auto,b_rgb:f5f5f5ecliaenqoa7rgkgvp2xyair-jordan-1-low-mens-shoe-z3Tl2VeJ.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autopselea04ks9hnkcbsahdair-max-97-on-air-london-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autopselea04ks9hnkcbsahdair-max-97-on-air-london-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autopselea04ks9hnkcbsahdair-max-97-on-air-london-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autopselea04ks9hnkcbsahdair-max-97-on-air-london-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovxm7ancrgvsdat5i7o0unike-pg-3-mamba-mentality-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autovxm7ancrgvsdat5i7o0unike-pg-3-mamba-mentality-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovxm7ancrgvsdat5i7o0unike-pg-3-mamba-mentality-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autovxm7ancrgvsdat5i7o0unike-pg-3-mamba-mentality-release-date.jpg
--------------------------------------------------------------------------------
/MVP-SampleTests/Helper/Model/BannerModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BannerModel.swift
3 | // MVP-SampleTests
4 | //
5 | // Created by NixonShih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | @testable import MVP_Sample
10 |
11 | extension BannerModel: Equatable {
12 |
13 | public static func == (lhs: BannerModel, rhs: BannerModel) -> Bool {
14 | return lhs.imageName + (lhs.url?.absoluteString ?? "") == rhs.imageName + (rhs.url?.absoluteString ?? "")
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autobyweh2fwj8cxlvkcjyjbadapt-bb-future-of-the-game-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autobyweh2fwj8cxlvkcjyjbadapt-bb-future-of-the-game-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autobyweh2fwj8cxlvkcjyjbadapt-bb-future-of-the-game-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autobyweh2fwj8cxlvkcjyjbadapt-bb-future-of-the-game-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autotunyzqjx91k3yk55mggeair-max2-light-purple-berry-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autotunyzqjx91k3yk55mggeair-max2-light-purple-berry-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autotunyzqjx91k3yk55mggeair-max2-light-purple-berry-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autotunyzqjx91k3yk55mggeair-max2-light-purple-berry-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autourcu79fr3do9u0a4soicair-max-97-on-air-shanghai-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autourcu79fr3do9u0a4soicair-max-97-on-air-shanghai-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autourcu79fr3do9u0a4soicair-max-97-on-air-shanghai-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autourcu79fr3do9u0a4soicair-max-97-on-air-shanghai-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoepba4fhpfb9ajdmpmea2air-foamposite-hyper-crimson-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoepba4fhpfb9ajdmpmea2air-foamposite-hyper-crimson-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoepba4fhpfb9ajdmpmea2air-foamposite-hyper-crimson-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoepba4fhpfb9ajdmpmea2air-foamposite-hyper-crimson-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/solelinksstoragereleaseImages3666AQ1090-200_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-200-1.imageset/solelinksstoragereleaseImages3666AQ1090-200_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-200-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/solelinksstoragereleaseImages3666AQ1090-200_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-200-1.imageset/solelinksstoragereleaseImages3666AQ1090-200_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-200-1.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/solelinksstoragereleaseImages3667AQ1090-300_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-300-1.imageset/solelinksstoragereleaseImages3667AQ1090-300_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-300-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/solelinksstoragereleaseImages3667AQ1090-300_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-300-1.imageset/solelinksstoragereleaseImages3667AQ1090-300_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-300-1.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autodil46qlauylplvdx6dudair-foamposite-hyper-crimson-release-date.imageset/aimagest_prod_lsw_1920,c_limit,f_autodil46qlauylplvdx6dudair-foamposite-hyper-crimson-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autodil46qlauylplvdx6dudair-foamposite-hyper-crimson-release-date.imageset/aimagest_prod_lsw_1920,c_limit,f_autodil46qlauylplvdx6dudair-foamposite-hyper-crimson-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autovkzcokvv4fh2jjk71djdair-foamposite-hyper-crimson-release-date.imageset/aimagest_prod_lsw_1920,c_limit,f_autovkzcokvv4fh2jjk71djdair-foamposite-hyper-crimson-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autovkzcokvv4fh2jjk71djdair-foamposite-hyper-crimson-release-date.imageset/aimagest_prod_lsw_1920,c_limit,f_autovkzcokvv4fh2jjk71djdair-foamposite-hyper-crimson-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autoy0udif4vqu87bklmxs9uair-foamposite-hyper-crimson-release-date.imageset/aimagest_prod_lsw_1920,c_limit,f_autoy0udif4vqu87bklmxs9uair-foamposite-hyper-crimson-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autoy0udif4vqu87bklmxs9uair-foamposite-hyper-crimson-release-date.imageset/aimagest_prod_lsw_1920,c_limit,f_autoy0udif4vqu87bklmxs9uair-foamposite-hyper-crimson-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoxcsral03nckwhqi3vshbair-vapormax-plus-on-air-paris-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoxcsral03nckwhqi3vshbair-vapormax-plus-on-air-paris-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoxcsral03nckwhqi3vshbair-vapormax-plus-on-air-paris-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoxcsral03nckwhqi3vshbair-vapormax-plus-on-air-paris-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autocr2smyshlm9vamsquxamnike-blazer-mid-77-black-canvas-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autocr2smyshlm9vamsquxamnike-blazer-mid-77-black-canvas-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autocr2smyshlm9vamsquxamnike-blazer-mid-77-black-canvas-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autocr2smyshlm9vamsquxamnike-blazer-mid-77-black-canvas-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autot8cz8v6lx119os3pn94onike-air-trainer-3-medicine-ball-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autot8cz8v6lx119os3pn94onike-air-trainer-3-medicine-ball-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autot8cz8v6lx119os3pn94onike-air-trainer-3-medicine-ball-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autot8cz8v6lx119os3pn94onike-air-trainer-3-medicine-ball-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoyqhwscvw99o0a1w6pmwiwomens-air-jordan-12-midnight-black-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoyqhwscvw99o0a1w6pmwiwomens-air-jordan-12-midnight-black-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoyqhwscvw99o0a1w6pmwiwomens-air-jordan-12-midnight-black-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoyqhwscvw99o0a1w6pmwiwomens-air-jordan-12-midnight-black-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDZOaWtlLUt5cmllLTQtVHJpcGxlLUJsYWNrLTk0MzgwNy0wMDgtUmVsZWFzZS1EYXRlLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MDZOaWtlLUt5cmllLTQtVHJpcGxlLUJsYWNrLTk0MzgwNy0wMDgtUmVsZWFzZS1EYXRlLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDZOaWtlLUt5cmllLTQtVHJpcGxlLUJsYWNrLTk0MzgwNy0wMDgtUmVsZWFzZS1EYXRlLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MDZOaWtlLUt5cmllLTQtVHJpcGxlLUJsYWNrLTk0MzgwNy0wMDgtUmVsZWFzZS1EYXRlLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-Sample/DomainLayer/UseCases/FetchDataUseCaseSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FetchDataUseCaseSpec.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol FetchDataUseCaseSpec {
12 |
13 | associatedtype DataModel
14 |
15 | typealias FetchDataModelUseCaseCompletionHandler = (_ books: Result) -> ()
16 | func fetchDataModel(_ completionHandler: @escaping FetchDataModelUseCaseCompletionHandler)
17 | }
18 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autojejlqawpimm6qoanfwzcnike-blazer-mid-77-vintage-pink-foam-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autojejlqawpimm6qoanfwzcnike-blazer-mid-77-vintage-pink-foam-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autojejlqawpimm6qoanfwzcnike-blazer-mid-77-vintage-pink-foam-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autojejlqawpimm6qoanfwzcnike-blazer-mid-77-vintage-pink-foam-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovp6fcofbqm8skzhjuldmnike-blazer-mid-77-vintage-sail-white-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autovp6fcofbqm8skzhjuldmnike-blazer-mid-77-vintage-sail-white-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovp6fcofbqm8skzhjuldmnike-blazer-mid-77-vintage-sail-white-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autovp6fcofbqm8skzhjuldmnike-blazer-mid-77-vintage-sail-white-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Other/Constants/ThemeColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThemeColor.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2018/11/9.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | enum ThemeColor: String {
12 |
13 | case pureBlack = "000000"
14 | case pureWhite = "ffffff"
15 | case darkBlack = "252525"
16 | case darkRed = "b41b1b"
17 |
18 | var color: UIColor {
19 | let hex = Int(self.rawValue, radix: 16)
20 | return UIColor(rgb: hex ?? 0x000000)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Util/ProjectNameHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProjectNameHelper.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/19.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct ProjectNameHelper: NameProvidable {
12 |
13 | var name: String {
14 | guard
15 | let info = Bundle.main.infoDictionary,
16 | let bundleName = info["CFBundleName"] as? String else {
17 | fatalError()
18 | }
19 | return bundleName
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-001-Release-Date-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-001-Release-Date-1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-400-Release-Date-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wp-contentuploads201903Nike-Cortez-Los-Angeles-CI9873-400-Release-Date-1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1NzlfMS5wbmc=.imageset/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1NzlfMS5wbmc=.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1NzlfMS5wbmc=.imageset/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1NzlfMS5wbmc=.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1ODNfMS5wbmc=.imageset/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1ODNfMS5wbmc=.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1ODNfMS5wbmc=.imageset/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1ODNfMS5wbmc=.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDdqb3JkYW4tbGVnYWN5LTMxMi1BUTQxNjAtMzAxLTEuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3AtY29udGVudHVwbG9hZHMyMDE4MDdqb3JkYW4tbGVnYWN5LTMxMi1BUTQxNjAtMzAxLTEuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUt5cmllLTUtVGFjby1BTzI5MTgtOTAyLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUt5cmllLTUtVGFjby1BTzI5MTgtOTAyLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201902nike-air-fear-of-god-180-black-AT8087-002-release-date-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wp-contentuploads201902nike-air-fear-of-god-180-black-AT8087-002-release-date-1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoeyeg1kej2njudfya2fhqnike-womens-air-max-95-plant-color-collection-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoeyeg1kej2njudfya2fhqnike-womens-air-max-95-plant-color-collection-release-date.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoeyeg1kej2njudfya2fhqnike-womens-air-max-95-plant-color-collection-release-date.imageset/aimagest_prod_ssw_960,c_limit,f_autoeyeg1kej2njudfya2fhqnike-womens-air-max-95-plant-color-collection-release-date.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTIuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTIuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTMuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3AtY29udGVudHVwbG9hZHMyMDE4MTFhaXItam9yZGFuLTQtZG8tdGhlLXJpZ2h0LXRoaW5nLTMuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Air-Jordan-4-GS-Monsoon-Blue-BQ9043-400-Release-Date-Price.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wp-contentuploads201903Air-Jordan-4-GS-Monsoon-Blue-BQ9043-400-Release-Date-Price.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201904Nike-Kyrie-5-Mamba-Mentality-AO2918-102-Release-Date-Price.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wp-contentuploads201904Nike-Kyrie-5-Mamba-Mentality-AO2918-102-Release-Date-Price.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autocys1ywaekpuvespws0x4lebron-16-remix-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autocys1ywaekpuvespws0x4lebron-16-remix-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1ZWVlZTc1ODFfMV8xLnBuZw==.imageset/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1ZWVlZTc1ODFfMV8xLnBuZw==.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1ZWVlZTc1ODFfMV8xLnBuZw==.imageset/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1ZWVlZTc1ODFfMV8xLnBuZw==.png
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDRhaXItam9yZGFuLTEwLW9ybGFuZG8tcmVsZWFzZS1pbmZvLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3AtY29udGVudHVwbG9hZHMyMDE4MDRhaXItam9yZGFuLTEwLW9ybGFuZG8tcmVsZWFzZS1pbmZvLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDZKb3JkYW4tTGVnYWN5LTMxMi1TdG9ybS1CbHVlLVJvYXlsLUFRNDE2MC0xMDQtUmVsZWFzZS1EYXRlLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MDZKb3JkYW4tTGVnYWN5LTMxMi1TdG9ybS1CbHVlLVJvYXlsLUFRNDE2MC0xMDQtUmVsZWFzZS1EYXRlLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDZKb3JkYW4tTGVnYWN5LTMxMi1TdG9ybS1CbHVlLVJvYXlsLUFRNDE2MC0xMDQtUmVsZWFzZS1EYXRlLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MDZKb3JkYW4tTGVnYWN5LTMxMi1TdG9ybS1CbHVlLVJvYXlsLUFRNDE2MC0xMDQtUmVsZWFzZS1EYXRlLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903nike-air-fear-of-god-moccasin-black-at8086-002-release-date-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wp-contentuploads201903nike-air-fear-of-god-moccasin-black-at8086-002-release-date-1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoxrj4afk5rpddzxbbx6wclebron-16-martin-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autoxrj4afk5rpddzxbbx6wclebron-16-martin-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoegkplcxbgvbraxiwrkv4air-max2-light-atmos-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autoegkplcxbgvbraxiwrkv4air-max2-light-atmos-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autokqoxst75honzfjrnqqyjair-max-97-on-air-seoul-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autokqoxst75honzfjrnqqyjair-max-97-on-air-seoul-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoq8vrexcyxpmj80u4esgvlebron-16-air-trainer-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autoq8vrexcyxpmj80u4esgvlebron-16-air-trainer-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovziuczzeyzitgvjtar79air-max-1-on-air-tokyo-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autovziuczzeyzitgvjtar79air-max-1-on-air-tokyo-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovztgxht8el3fnj7oe6uzair-max-98-on-air-nyc-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autovztgxht8el3fnj7oe6uzair-max-98-on-air-nyc-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201901nike-air-fear-of-god-moccasin-pure-platinum-AT8086-001-release-date-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wp-contentuploads201901nike-air-fear-of-god-moccasin-pure-platinum-AT8086-001-release-date-1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/wp-contentuploads201903Air-Jordan-11-Low-Navy-Blue-Snakeskin-CD6846-102-Release-Date-Price.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "wp-contentuploads201903Air-Jordan-11-Low-Navy-Blue-Snakeskin-CD6846-102-Release-Date-Price.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_PDP_864_v1f_auto,b_rgb:f5f5f5ecliaenqoa7rgkgvp2xyair-jordan-1-low-mens-shoe-z3Tl2VeJ.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_PDP_864_v1f_auto,b_rgb:f5f5f5ecliaenqoa7rgkgvp2xyair-jordan-1-low-mens-shoe-z3Tl2VeJ.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autopselea04ks9hnkcbsahdair-max-97-on-air-london-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autopselea04ks9hnkcbsahdair-max-97-on-air-london-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovxm7ancrgvsdat5i7o0unike-pg-3-mamba-mentality-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autovxm7ancrgvsdat5i7o0unike-pg-3-mamba-mentality-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUFpci1Gb3JjZS0yNzAtVXRpbGl0eS1CbGFjay1Wb2x0LUFRMDU3Mi0wMDEtUmVsZWFzZS1EYXRlLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUFpci1Gb3JjZS0yNzAtVXRpbGl0eS1CbGFjay1Wb2x0LUFRMDU3Mi0wMDEtUmVsZWFzZS1EYXRlLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUFpci1Gb3JjZS0yNzAtVXRpbGl0eS1CbGFjay1Wb2x0LUFRMDU3Mi0wMDEtUmVsZWFzZS1EYXRlLmpwZw==.imageset/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUFpci1Gb3JjZS0yNzAtVXRpbGl0eS1CbGFjay1Wb2x0LUFRMDU3Mi0wMDEtUmVsZWFzZS1EYXRlLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autobyweh2fwj8cxlvkcjyjbadapt-bb-future-of-the-game-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autobyweh2fwj8cxlvkcjyjbadapt-bb-future-of-the-game-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoepba4fhpfb9ajdmpmea2air-foamposite-hyper-crimson-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autoepba4fhpfb9ajdmpmea2air-foamposite-hyper-crimson-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autotunyzqjx91k3yk55mggeair-max2-light-purple-berry-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autotunyzqjx91k3yk55mggeair-max2-light-purple-berry-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autourcu79fr3do9u0a4soicair-max-97-on-air-shanghai-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autourcu79fr3do9u0a4soicair-max-97-on-air-shanghai-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/solelinksstoragereleaseImages3666AQ1090-200_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-200-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "solelinksstoragereleaseImages3666AQ1090-200_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-200-1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/solelinksstoragereleaseImages3667AQ1090-300_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-300-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "solelinksstoragereleaseImages3667AQ1090-300_sivasdescalzo-nike-NIKE-REACT-ELEMENT-87-AQ1090-300-1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/Repositories/Remote/NetworkFetchable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkFetchable.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/19.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum NetworkError: Error {
12 | case url
13 | case encode
14 | case connection
15 | case decode
16 | case emptyContent
17 | case serviceError
18 | }
19 |
20 | protocol NetworkFetchable {
21 |
22 | associatedtype DataModel: Codable
23 | func fire(_ completionHandler: @escaping (Result) -> ())
24 | }
25 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autodil46qlauylplvdx6dudair-foamposite-hyper-crimson-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_lsw_1920,c_limit,f_autodil46qlauylplvdx6dudair-foamposite-hyper-crimson-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autovkzcokvv4fh2jjk71djdair-foamposite-hyper-crimson-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_lsw_1920,c_limit,f_autovkzcokvv4fh2jjk71djdair-foamposite-hyper-crimson-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_lsw_1920,c_limit,f_autoy0udif4vqu87bklmxs9uair-foamposite-hyper-crimson-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_lsw_1920,c_limit,f_autoy0udif4vqu87bklmxs9uair-foamposite-hyper-crimson-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoxcsral03nckwhqi3vshbair-vapormax-plus-on-air-paris-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autoxcsral03nckwhqi3vshbair-vapormax-plus-on-air-paris-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBuenlibnRkZDNhY3B3YW1xYXFycWNvcnRlei1iYXNpYy1sZWF0aGVyLW1lbnMtc2hvZS1SUFBDRzUuanBn.imageset/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBuenlibnRkZDNhY3B3YW1xYXFycWNvcnRlei1iYXNpYy1sZWF0aGVyLW1lbnMtc2hvZS1SUFBDRzUuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBuenlibnRkZDNhY3B3YW1xYXFycWNvcnRlei1iYXNpYy1sZWF0aGVyLW1lbnMtc2hvZS1SUFBDRzUuanBn.imageset/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBuenlibnRkZDNhY3B3YW1xYXFycWNvcnRlei1iYXNpYy1sZWF0aGVyLW1lbnMtc2hvZS1SUFBDRzUuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autocr2smyshlm9vamsquxamnike-blazer-mid-77-black-canvas-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autocr2smyshlm9vamsquxamnike-blazer-mid-77-black-canvas-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autot8cz8v6lx119os3pn94onike-air-trainer-3-medicine-ball-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autot8cz8v6lx119os3pn94onike-air-trainer-3-medicine-ball-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoyqhwscvw99o0a1w6pmwiwomens-air-jordan-12-midnight-black-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autoyqhwscvw99o0a1w6pmwiwomens-air-jordan-12-midnight-black-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDZOaWtlLUt5cmllLTQtVHJpcGxlLUJsYWNrLTk0MzgwNy0wMDgtUmVsZWFzZS1EYXRlLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3AtY29udGVudHVwbG9hZHMyMDE4MDZOaWtlLUt5cmllLTQtVHJpcGxlLUJsYWNrLTk0MzgwNy0wMDgtUmVsZWFzZS1EYXRlLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-SampleTests/Helper/Repository/MockFetchShoesRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FetchShoesRepository.swift
3 | // MVP-SampleTests
4 | //
5 | // Created by NixonShih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | @testable import MVP_Sample
10 |
11 | enum MockFetchShoesRepositoryError: Error {
12 | case fail
13 | }
14 |
15 | class MockFetchShoesRepository: ShoesRepositorySpec {
16 |
17 | var expectedResult: Result<[ShoesModel], Error>!
18 |
19 | func fetchShoes(_ completionHandler: @escaping FetchShoesCompletionHandler) {
20 | completionHandler(expectedResult)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autojejlqawpimm6qoanfwzcnike-blazer-mid-77-vintage-pink-foam-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autojejlqawpimm6qoanfwzcnike-blazer-mid-77-vintage-pink-foam-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autovp6fcofbqm8skzhjuldmnike-blazer-mid-77-vintage-sail-white-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autovp6fcofbqm8skzhjuldmnike-blazer-mid-77-vintage-sail-white-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1NzlfMS5wbmc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1NzlfMS5wbmc=.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1ODNfMS5wbmc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1RUVFRTc1ODNfMS5wbmc=.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesList/Banner/ShoesListBannerCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesListBannerCollectionViewCell.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ShoesListBannerCollectionViewCell: UICollectionViewCell, SetReuseViewModelable {
12 |
13 | @IBOutlet weak var bannerImageView: UIImageView!
14 |
15 | func setReuseViewMode(_ viewModel: String) {
16 | bannerImageView.image = UIImage(named: viewModel)
17 | bannerImageView.contentMode = .scaleAspectFit
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/aimagest_prod_ssw_960,c_limit,f_autoeyeg1kej2njudfya2fhqnike-womens-air-max-95-plant-color-collection-release-date.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "aimagest_prod_ssw_960,c_limit,f_autoeyeg1kej2njudfya2fhqnike-womens-air-max-95-plant-color-collection-release-date.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1ZWVlZTc1ODFfMV8xLnBuZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "bWVkaWFjYXRhbG9ncHJvZHVjdGNhY2hlMWJhc2UxMDAweDYwMDlkZjc4ZWFiMzM1MjVkMDhkNmU1ZmI4ZDI3MTM2ZTk1ZWVlZTc1ODFfMV8xLnBuZw==.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MDZKb3JkYW4tTGVnYWN5LTMxMi1TdG9ybS1CbHVlLVJvYXlsLUFRNDE2MC0xMDQtUmVsZWFzZS1EYXRlLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3AtY29udGVudHVwbG9hZHMyMDE4MDZKb3JkYW4tTGVnYWN5LTMxMi1TdG9ybS1CbHVlLVJvYXlsLUFRNDE2MC0xMDQtUmVsZWFzZS1EYXRlLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3d3LnNuZWFrZXJlbGl0ZS5jb213cC1jb250ZW50dXBsb2FkczIwMTgwNmFkaWRhcy1ZZWV6eS1Cb29zdC0zNTAtVjItQnV0dGVyLVJlbGVhc2UtRGF0ZS1GMzY5ODAuanBn.imageset/d3d3LnNuZWFrZXJlbGl0ZS5jb213cC1jb250ZW50dXBsb2FkczIwMTgwNmFkaWRhcy1ZZWV6eS1Cb29zdC0zNTAtVjItQnV0dGVyLVJlbGVhc2UtRGF0ZS1GMzY5ODAuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/d3d3LnNuZWFrZXJlbGl0ZS5jb213cC1jb250ZW50dXBsb2FkczIwMTgwNmFkaWRhcy1ZZWV6eS1Cb29zdC0zNTAtVjItQnV0dGVyLVJlbGVhc2UtRGF0ZS1GMzY5ODAuanBn.imageset/d3d3LnNuZWFrZXJlbGl0ZS5jb213cC1jb250ZW50dXBsb2FkczIwMTgwNmFkaWRhcy1ZZWV6eS1Cb29zdC0zNTAtVjItQnV0dGVyLVJlbGVhc2UtRGF0ZS1GMzY5ODAuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ViewBuilderSpec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewBuilderSpec.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/12.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol ViewBuilderSpec {
12 |
13 | associatedtype ViewType: UIViewController
14 |
15 | func build() -> ViewType
16 | func buildWithNavigationController() -> BaseNavigationController
17 | }
18 |
19 | extension ViewBuilderSpec {
20 |
21 | func buildWithNavigationController() -> BaseNavigationController {
22 | return BaseNavigationController(rootViewController: build())
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/MVP-SampleTests/Helper/Repository/MockRemoteBannerRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MockRemoteBannerRepository.swift
3 | // MVP-SampleTests
4 | //
5 | // Created by NixonShih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | @testable import MVP_Sample
10 |
11 | enum MockRemoteBannerRepositoryError: Error {
12 | case fail
13 | }
14 |
15 | class MockRemoteBannerRepository: BannerRepositorySpec {
16 |
17 | func fetchBanners(_ completionHandler: @escaping FetchBannerCompletionHandler) {
18 | completionHandler(expectedResult)
19 | }
20 |
21 | var expectedResult: Result<[RemoteBannerModel], Error>!
22 | }
23 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUFpci1Gb3JjZS0yNzAtVXRpbGl0eS1CbGFjay1Wb2x0LUFRMDU3Mi0wMDEtUmVsZWFzZS1EYXRlLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3AtY29udGVudHVwbG9hZHMyMDE4MTFOaWtlLUFpci1Gb3JjZS0yNzAtVXRpbGl0eS1CbGFjay1Wb2x0LUFRMDU3Mi0wMDEtUmVsZWFzZS1EYXRlLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBmc29sM2I3aGUxcnJkYmpsbTV4MGFpci1mb290c2NhcGUtbWlkLXV0aWxpdHktZG0tbWVucy1zaG9lLXM3WGdTNC5qcGc=.imageset/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBmc29sM2I3aGUxcnJkYmpsbTV4MGFpci1mb290c2NhcGUtbWlkLXV0aWxpdHktZG0tbWVucy1zaG9lLXM3WGdTNC5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBmc29sM2I3aGUxcnJkYmpsbTV4MGFpci1mb290c2NhcGUtbWlkLXV0aWxpdHktZG0tbWVucy1zaG9lLXM3WGdTNC5qcGc=.imageset/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBmc29sM2I3aGUxcnJkYmpsbTV4MGFpci1mb290c2NhcGUtbWlkLXV0aWxpdHktZG0tbWVucy1zaG9lLXM3WGdTNC5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBuenlibnRkZDNhY3B3YW1xYXFycWNvcnRlei1iYXNpYy1sZWF0aGVyLW1lbnMtc2hvZS1SUFBDRzUuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBuenlibnRkZDNhY3B3YW1xYXFycWNvcnRlei1iYXNpYy1sZWF0aGVyLW1lbnMtc2hvZS1SUFBDRzUuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvazF1OHJnMmZuemZwMjJqZTdwbnVuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tbjctcmVsZWFzZS1kYXRlLmpwZw==.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvazF1OHJnMmZuemZwMjJqZTdwbnVuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tbjctcmVsZWFzZS1kYXRlLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvazF1OHJnMmZuemZwMjJqZTdwbnVuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tbjctcmVsZWFzZS1kYXRlLmpwZw==.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvazF1OHJnMmZuemZwMjJqZTdwbnVuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tbjctcmVsZWFzZS1kYXRlLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbnlieW95ampmYzhrbzVnZ3dianJ3b21lbnMtYWlyLWpvcmRhbi0zLWJvcmRlYXV4LXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbnlieW95ampmYzhrbzVnZ3dianJ3b21lbnMtYWlyLWpvcmRhbi0zLWJvcmRlYXV4LXJlbGVhc2UtZGF0ZS5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbnlieW95ampmYzhrbzVnZ3dianJ3b21lbnMtYWlyLWpvcmRhbi0zLWJvcmRlYXV4LXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbnlieW95ampmYzhrbzVnZ3dianJ3b21lbnMtYWlyLWpvcmRhbi0zLWJvcmRlYXV4LXJlbGVhc2UtZGF0ZS5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/d3d3LnNuZWFrZXJlbGl0ZS5jb213cC1jb250ZW50dXBsb2FkczIwMTgwNmFkaWRhcy1ZZWV6eS1Cb29zdC0zNTAtVjItQnV0dGVyLVJlbGVhc2UtRGF0ZS1GMzY5ODAuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "d3d3LnNuZWFrZXJlbGl0ZS5jb213cC1jb250ZW50dXBsb2FkczIwMTgwNmFkaWRhcy1ZZWV6eS1Cb29zdC0zNTAtVjItQnV0dGVyLVJlbGVhc2UtRGF0ZS1GMzY5ODAuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBmc29sM2I3aGUxcnJkYmpsbTV4MGFpci1mb290c2NhcGUtbWlkLXV0aWxpdHktZG0tbWVucy1zaG9lLXM3WGdTNC5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc2ZfYXV0byxiX3JnYjpmNWY1ZjUsd180NDBmc29sM2I3aGUxcnJkYmpsbTV4MGFpci1mb290c2NhcGUtbWlkLXV0aWxpdHktZG0tbWVucy1zaG9lLXM3WGdTNC5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvazF1OHJnMmZuemZwMjJqZTdwbnVuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tbjctcmVsZWFzZS1kYXRlLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvazF1OHJnMmZuemZwMjJqZTdwbnVuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tbjctcmVsZWFzZS1kYXRlLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbnlieW95ampmYzhrbzVnZ3dianJ3b21lbnMtYWlyLWpvcmRhbi0zLWJvcmRlYXV4LXJlbGVhc2UtZGF0ZS5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbnlieW95ampmYzhrbzVnZ3dianJ3b21lbnMtYWlyLWpvcmRhbi0zLWJvcmRlYXV4LXJlbGVhc2UtZGF0ZS5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvZjViM2E1amNxaWFycm5qZzR0aWJuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvZjViM2E1amNxaWFycm5qZzR0aWJuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1yZWxlYXNlLWRhdGUuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvZjViM2E1amNxaWFycm5qZzR0aWJuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvZjViM2E1amNxaWFycm5qZzR0aWJuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1yZWxlYXNlLWRhdGUuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcDl0cnV4eHB2M3Bkb2U3M3hobG9haXItam9yZGFuLTYtcmV0cm8tdGlua2VyLWluZnJhcmVkLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcDl0cnV4eHB2M3Bkb2U3M3hobG9haXItam9yZGFuLTYtcmV0cm8tdGlua2VyLWluZnJhcmVkLXJlbGVhc2UtZGF0ZS5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcDl0cnV4eHB2M3Bkb2U3M3hobG9haXItam9yZGFuLTYtcmV0cm8tdGlua2VyLWluZnJhcmVkLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcDl0cnV4eHB2M3Bkb2U3M3hobG9haXItam9yZGFuLTYtcmV0cm8tdGlua2VyLWluZnJhcmVkLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveTU0ejh3bnc0ZWVyMXR4MWFrdmluaWtlLWFpci1tYXgtOTctYmxhY2stbWV0YWxsaWMtZ29sZC1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveTU0ejh3bnc0ZWVyMXR4MWFrdmluaWtlLWFpci1tYXgtOTctYmxhY2stbWV0YWxsaWMtZ29sZC1yZWxlYXNlLWRhdGUuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveTU0ejh3bnc0ZWVyMXR4MWFrdmluaWtlLWFpci1tYXgtOTctYmxhY2stbWV0YWxsaWMtZ29sZC1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveTU0ejh3bnc0ZWVyMXR4MWFrdmluaWtlLWFpci1tYXgtOTctYmxhY2stbWV0YWxsaWMtZ29sZC1yZWxlYXNlLWRhdGUuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvZjViM2E1amNxaWFycm5qZzR0aWJuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1yZWxlYXNlLWRhdGUuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvZjViM2E1amNxaWFycm5qZzR0aWJuaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1yZWxlYXNlLWRhdGUuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcDl0cnV4eHB2M3Bkb2U3M3hobG9haXItam9yZGFuLTYtcmV0cm8tdGlua2VyLWluZnJhcmVkLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcDl0cnV4eHB2M3Bkb2U3M3hobG9haXItam9yZGFuLTYtcmV0cm8tdGlua2VyLWluZnJhcmVkLXJlbGVhc2UtZGF0ZS5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveTU0ejh3bnc0ZWVyMXR4MWFrdmluaWtlLWFpci1tYXgtOTctYmxhY2stbWV0YWxsaWMtZ29sZC1yZWxlYXNlLWRhdGUuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveTU0ejh3bnc0ZWVyMXR4MWFrdmluaWtlLWFpci1tYXgtOTctYmxhY2stbWV0YWxsaWMtZ29sZC1yZWxlYXNlLWRhdGUuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvYXd4YXh5N2l0cDA0eWx5ZGlwY2FuaWtlLWFpci1tYXgtOTUtbnJnLWJsYWNrLWFudGhyYWNpdGUtcmVsZWFzZS1kYXRlLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvYXd4YXh5N2l0cDA0eWx5ZGlwY2FuaWtlLWFpci1tYXgtOTUtbnJnLWJsYWNrLWFudGhyYWNpdGUtcmVsZWFzZS1kYXRlLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvYXd4YXh5N2l0cDA0eWx5ZGlwY2FuaWtlLWFpci1tYXgtOTUtbnJnLWJsYWNrLWFudGhyYWNpdGUtcmVsZWFzZS1kYXRlLmpwZw==.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvYXd4YXh5N2l0cDA0eWx5ZGlwY2FuaWtlLWFpci1tYXgtOTUtbnJnLWJsYWNrLWFudGhyYWNpdGUtcmVsZWFzZS1kYXRlLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvYXd4YXh5N2l0cDA0eWx5ZGlwY2FuaWtlLWFpci1tYXgtOTUtbnJnLWJsYWNrLWFudGhyYWNpdGUtcmVsZWFzZS1kYXRlLmpwZw==.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvYXd4YXh5N2l0cDA0eWx5ZGlwY2FuaWtlLWFpci1tYXgtOTUtbnJnLWJsYWNrLWFudGhyYWNpdGUtcmVsZWFzZS1kYXRlLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd29rOG0zcHgwMTF2ZWptNzJzeWRuaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd29rOG0zcHgwMTF2ZWptNzJzeWRuaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd29rOG0zcHgwMTF2ZWptNzJzeWRuaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd29rOG0zcHgwMTF2ZWptNzJzeWRuaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd29rOG0zcHgwMTF2ZWptNzJzeWRuaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd29rOG0zcHgwMTF2ZWptNzJzeWRuaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd2oxYXJ3Z21xazB4MHJqbm54aDduaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd2oxYXJ3Z21xazB4MHJqbm54aDduaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd2oxYXJ3Z21xazB4MHJqbm54aDduaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd2oxYXJ3Z21xazB4MHJqbm54aDduaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd2oxYXJ3Z21xazB4MHJqbm54aDduaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd2oxYXJ3Z21xazB4MHJqbm54aDduaWtlLWFpci1tYXgtOTctbWV0YWxsaWMtc2lsdmVyLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvanF3eW1laGJ5c21ueG1hd3JyNmJ3b21lbnMtYWlyLWZvcmNlLTEtc2FnZS1oaWdoLXdoaXRlLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvanF3eW1laGJ5c21ueG1hd3JyNmJ3b21lbnMtYWlyLWZvcmNlLTEtc2FnZS1oaWdoLXdoaXRlLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbWJpaDc5Z3htbG1veTFhbmx5cHZ3b21lbnMtYWYxLWplc3Rlci1oaS14eC1ibGFjay1kdXN0eS1wZWFjaC1yZWxlYXNlLWRhdGUuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbWJpaDc5Z3htbG1veTFhbmx5cHZ3b21lbnMtYWYxLWplc3Rlci1oaS14eC1ibGFjay1kdXN0eS1wZWFjaC1yZWxlYXNlLWRhdGUuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvanF3eW1laGJ5c21ueG1hd3JyNmJ3b21lbnMtYWlyLWZvcmNlLTEtc2FnZS1oaWdoLXdoaXRlLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvanF3eW1laGJ5c21ueG1hd3JyNmJ3b21lbnMtYWlyLWZvcmNlLTEtc2FnZS1oaWdoLXdoaXRlLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvanF3eW1laGJ5c21ueG1hd3JyNmJ3b21lbnMtYWlyLWZvcmNlLTEtc2FnZS1oaWdoLXdoaXRlLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvanF3eW1laGJ5c21ueG1hd3JyNmJ3b21lbnMtYWlyLWZvcmNlLTEtc2FnZS1oaWdoLXdoaXRlLWJsYWNrLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbWJpaDc5Z3htbG1veTFhbmx5cHZ3b21lbnMtYWYxLWplc3Rlci1oaS14eC1ibGFjay1kdXN0eS1wZWFjaC1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbWJpaDc5Z3htbG1veTFhbmx5cHZ3b21lbnMtYWYxLWplc3Rlci1oaS14eC1ibGFjay1kdXN0eS1wZWFjaC1yZWxlYXNlLWRhdGUuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbWJpaDc5Z3htbG1veTFhbmx5cHZ3b21lbnMtYWYxLWplc3Rlci1oaS14eC1ibGFjay1kdXN0eS1wZWFjaC1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvbWJpaDc5Z3htbG1veTFhbmx5cHZ3b21lbnMtYWYxLWplc3Rlci1oaS14eC1ibGFjay1kdXN0eS1wZWFjaC1yZWxlYXNlLWRhdGUuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvc2Jsc3F6aWthcHFnemk3OXl5cXNuaWtlLWFpci1tYXgtOTctbnJnLXRlYW0tcmVkLW1pZG5pZ2h0LXNwcnVjZS1yZWxlYXNlLWRhdGUuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvc2Jsc3F6aWthcHFnemk3OXl5cXNuaWtlLWFpci1tYXgtOTctbnJnLXRlYW0tcmVkLW1pZG5pZ2h0LXNwcnVjZS1yZWxlYXNlLWRhdGUuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcXh1MDBpaHh5dDVyZjBzZ2ZyYnVuaWtlLWFpci1tYXgtOTUtcHJlbWl1bS12b2x0LXZvbHQtZ2xvdy1ibGFjay1yZWxlYXNlLWRhdGUuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcXh1MDBpaHh5dDVyZjBzZ2ZyYnVuaWtlLWFpci1tYXgtOTUtcHJlbWl1bS12b2x0LXZvbHQtZ2xvdy1ibGFjay1yZWxlYXNlLWRhdGUuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/DomainLayer/UseCases/FetchShoesUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FetchShoesUseCase.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/11.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct FetchShoesUseCase: FetchDataUseCaseSpec {
12 |
13 | typealias DataModel = [ShoesModel]
14 |
15 | init(repository: ShoesRepositorySpec) {
16 | self.repository = repository
17 | }
18 |
19 | func fetchDataModel(_ completionHandler: @escaping FetchDataModelUseCaseCompletionHandler) {
20 | repository.fetchShoes(completionHandler)
21 | }
22 |
23 | // MARK: private
24 |
25 | private let repository: ShoesRepositorySpec
26 | }
27 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdTVkOGNscms4dGN3anR1aXlsYnZuaWtlLXdvbWVucy1haXItZm9yY2UtMS1yZWJlbC14eC1wcmFsaW5lLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdTVkOGNscms4dGN3anR1aXlsYnZuaWtlLXdvbWVucy1haXItZm9yY2UtMS1yZWJlbC14eC1wcmFsaW5lLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdWNycWRwMXdiYnh2cWp5MDdmcmV3b21lbnMtYWYxLWplc3Rlci1oaS14eC12aW9sZXQtYXNoLWJsdWUtZm9yY2UtcmVsZWFzZS1kYXRlLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdWNycWRwMXdiYnh2cWp5MDdmcmV3b21lbnMtYWYxLWplc3Rlci1oaS14eC12aW9sZXQtYXNoLWJsdWUtZm9yY2UtcmVsZWFzZS1kYXRlLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveXFhY3F2cWNqaHFwYmduZ3VheWVhaXItam9yZGFuLTEyLWludGVybmF0aW9uYWwtZmxpZ2h0LWNvbGxlZ2UtbmF2eS1yZWxlYXNlLWRhdGUuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveXFhY3F2cWNqaHFwYmduZ3VheWVhaXItam9yZGFuLTEyLWludGVybmF0aW9uYWwtZmxpZ2h0LWNvbGxlZ2UtbmF2eS1yZWxlYXNlLWRhdGUuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvc2Jsc3F6aWthcHFnemk3OXl5cXNuaWtlLWFpci1tYXgtOTctbnJnLXRlYW0tcmVkLW1pZG5pZ2h0LXNwcnVjZS1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvc2Jsc3F6aWthcHFnemk3OXl5cXNuaWtlLWFpci1tYXgtOTctbnJnLXRlYW0tcmVkLW1pZG5pZ2h0LXNwcnVjZS1yZWxlYXNlLWRhdGUuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvc2Jsc3F6aWthcHFnemk3OXl5cXNuaWtlLWFpci1tYXgtOTctbnJnLXRlYW0tcmVkLW1pZG5pZ2h0LXNwcnVjZS1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvc2Jsc3F6aWthcHFnemk3OXl5cXNuaWtlLWFpci1tYXgtOTctbnJnLXRlYW0tcmVkLW1pZG5pZ2h0LXNwcnVjZS1yZWxlYXNlLWRhdGUuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcThpMXJvZGd4OWNjaXI1Ymdzc2VuaWtlLWFpci1mb3JjZS0xLWx2OC11dGlsaXR5LXZvbHQtd29sZi1ncmV5LXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcThpMXJvZGd4OWNjaXI1Ymdzc2VuaWtlLWFpci1mb3JjZS0xLWx2OC11dGlsaXR5LXZvbHQtd29sZi1ncmV5LXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcXh1MDBpaHh5dDVyZjBzZ2ZyYnVuaWtlLWFpci1tYXgtOTUtcHJlbWl1bS12b2x0LXZvbHQtZ2xvdy1ibGFjay1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcXh1MDBpaHh5dDVyZjBzZ2ZyYnVuaWtlLWFpci1tYXgtOTUtcHJlbWl1bS12b2x0LXZvbHQtZ2xvdy1ibGFjay1yZWxlYXNlLWRhdGUuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcXh1MDBpaHh5dDVyZjBzZ2ZyYnVuaWtlLWFpci1tYXgtOTUtcHJlbWl1bS12b2x0LXZvbHQtZ2xvdy1ibGFjay1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcXh1MDBpaHh5dDVyZjBzZ2ZyYnVuaWtlLWFpci1tYXgtOTUtcHJlbWl1bS12b2x0LXZvbHQtZ2xvdy1ibGFjay1yZWxlYXNlLWRhdGUuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdmd4NXR6cmd2YTBpcmxzYnJjcGluaWtlLXNiLWxvdy1wcm8tZGlhbW9uZC1ibGFjay10cm9waWNhbC10d2lzdC1jaHJvbWUtcmVsZWFzZS1kYXRlLmpwZw==.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdmd4NXR6cmd2YTBpcmxzYnJjcGluaWtlLXNiLWxvdy1wcm8tZGlhbW9uZC1ibGFjay10cm9waWNhbC10d2lzdC1jaHJvbWUtcmVsZWFzZS1kYXRlLmpwZw==.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdTVkOGNscms4dGN3anR1aXlsYnZuaWtlLXdvbWVucy1haXItZm9yY2UtMS1yZWJlbC14eC1wcmFsaW5lLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdTVkOGNscms4dGN3anR1aXlsYnZuaWtlLXdvbWVucy1haXItZm9yY2UtMS1yZWJlbC14eC1wcmFsaW5lLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdTVkOGNscms4dGN3anR1aXlsYnZuaWtlLXdvbWVucy1haXItZm9yY2UtMS1yZWJlbC14eC1wcmFsaW5lLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdTVkOGNscms4dGN3anR1aXlsYnZuaWtlLXdvbWVucy1haXItZm9yY2UtMS1yZWJlbC14eC1wcmFsaW5lLXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdWNycWRwMXdiYnh2cWp5MDdmcmV3b21lbnMtYWYxLWplc3Rlci1oaS14eC12aW9sZXQtYXNoLWJsdWUtZm9yY2UtcmVsZWFzZS1kYXRlLmpwZw==.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdWNycWRwMXdiYnh2cWp5MDdmcmV3b21lbnMtYWYxLWplc3Rlci1oaS14eC12aW9sZXQtYXNoLWJsdWUtZm9yY2UtcmVsZWFzZS1kYXRlLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdWNycWRwMXdiYnh2cWp5MDdmcmV3b21lbnMtYWYxLWplc3Rlci1oaS14eC12aW9sZXQtYXNoLWJsdWUtZm9yY2UtcmVsZWFzZS1kYXRlLmpwZw==.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdWNycWRwMXdiYnh2cWp5MDdmcmV3b21lbnMtYWYxLWplc3Rlci1oaS14eC12aW9sZXQtYXNoLWJsdWUtZm9yY2UtcmVsZWFzZS1kYXRlLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveXFhY3F2cWNqaHFwYmduZ3VheWVhaXItam9yZGFuLTEyLWludGVybmF0aW9uYWwtZmxpZ2h0LWNvbGxlZ2UtbmF2eS1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveXFhY3F2cWNqaHFwYmduZ3VheWVhaXItam9yZGFuLTEyLWludGVybmF0aW9uYWwtZmxpZ2h0LWNvbGxlZ2UtbmF2eS1yZWxlYXNlLWRhdGUuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveXFhY3F2cWNqaHFwYmduZ3VheWVhaXItam9yZGFuLTEyLWludGVybmF0aW9uYWwtZmxpZ2h0LWNvbGxlZ2UtbmF2eS1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRveXFhY3F2cWNqaHFwYmduZ3VheWVhaXItam9yZGFuLTEyLWludGVybmF0aW9uYWwtZmxpZ2h0LWNvbGxlZ2UtbmF2eS1yZWxlYXNlLWRhdGUuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd3drY29xcHY5dnh5aDRwZ3dkZ25uaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1jb2xsZWN0aW9uLXRvdGFsLW9yYW5nZS1yZWxlYXNlLWRhdGUuanBn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd3drY29xcHY5dnh5aDRwZ3dkZ25uaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1jb2xsZWN0aW9uLXRvdGFsLW9yYW5nZS1yZWxlYXNlLWRhdGUuanBn.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcThpMXJvZGd4OWNjaXI1Ymdzc2VuaWtlLWFpci1mb3JjZS0xLWx2OC11dGlsaXR5LXZvbHQtd29sZi1ncmV5LXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcThpMXJvZGd4OWNjaXI1Ymdzc2VuaWtlLWFpci1mb3JjZS0xLWx2OC11dGlsaXR5LXZvbHQtd29sZi1ncmV5LXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcThpMXJvZGd4OWNjaXI1Ymdzc2VuaWtlLWFpci1mb3JjZS0xLWx2OC11dGlsaXR5LXZvbHQtd29sZi1ncmV5LXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvcThpMXJvZGd4OWNjaXI1Ymdzc2VuaWtlLWFpci1mb3JjZS0xLWx2OC11dGlsaXR5LXZvbHQtd29sZi1ncmV5LXdoaXRlLXJlbGVhc2UtZGF0ZS5qcGc=.jpg
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/compleximagesfl_lossy,q_autoc_crop,h_502,w_856,x_0,y_183c_scale,w_690,dpr_2.0v1ctxstklbxucwkotuhh72air-jordan-1-shattered-backboard-3-0-black-pale-vanilla-starfish-555088-028-rendering.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "compleximagesfl_lossy,q_autoc_crop,h_502,w_856,x_0,y_183c_scale,w_690,dpr_2.0v1ctxstklbxucwkotuhh72air-jordan-1-shattered-backboard-3-0-black-pale-vanilla-starfish-555088-028-rendering.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdmd4NXR6cmd2YTBpcmxzYnJjcGluaWtlLXNiLWxvdy1wcm8tZGlhbW9uZC1ibGFjay10cm9waWNhbC10d2lzdC1jaHJvbWUtcmVsZWFzZS1kYXRlLmpwZw==.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdmd4NXR6cmd2YTBpcmxzYnJjcGluaWtlLXNiLWxvdy1wcm8tZGlhbW9uZC1ibGFjay10cm9waWNhbC10d2lzdC1jaHJvbWUtcmVsZWFzZS1kYXRlLmpwZw==.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdmd4NXR6cmd2YTBpcmxzYnJjcGluaWtlLXNiLWxvdy1wcm8tZGlhbW9uZC1ibGFjay10cm9waWNhbC10d2lzdC1jaHJvbWUtcmVsZWFzZS1kYXRlLmpwZw==.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvdmd4NXR6cmd2YTBpcmxzYnJjcGluaWtlLXNiLWxvdy1wcm8tZGlhbW9uZC1ibGFjay10cm9waWNhbC10d2lzdC1jaHJvbWUtcmVsZWFzZS1kYXRlLmpwZw==.jpg
--------------------------------------------------------------------------------
/MVP-SampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MVP-SampleTests/Helper/Model/ShoesModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesModel.swift
3 | // MVP-SampleTests
4 | //
5 | // Created by NixonShih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | @testable import MVP_Sample
10 |
11 | extension ShoesModel {
12 |
13 | static var newShoes: ShoesModel {
14 | return ShoesModel(date: "2018-06-28", description: "123", imageName: "456", nickname: "Just Do It Collection", price: 130, title: "Nike Air Force 1 ’07 PRM")
15 | }
16 | }
17 |
18 | extension ShoesModel: Equatable {
19 |
20 | public static func == (lhs: ShoesModel, rhs: ShoesModel) -> Bool {
21 | return lhs.date + lhs.title + String(lhs.price) + lhs.imageName ==
22 | rhs.date + rhs.title + String(rhs.price) + rhs.imageName
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/BaseClass/BaseViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseViewController.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2018/11/9.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | view.backgroundColor = ThemeColor.darkBlack.color
16 | navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
17 | }
18 |
19 | override func viewWillAppear(_ animated: Bool) {
20 | super.viewWillAppear(animated)
21 | }
22 |
23 | override var preferredStatusBarStyle: UIStatusBarStyle {
24 | return UIStatusBarStyle.lightContent
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd3drY29xcHY5dnh5aDRwZ3dkZ25uaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1jb2xsZWN0aW9uLXRvdGFsLW9yYW5nZS1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd3drY29xcHY5dnh5aDRwZ3dkZ25uaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1jb2xsZWN0aW9uLXRvdGFsLW9yYW5nZS1yZWxlYXNlLWRhdGUuanBn.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd3drY29xcHY5dnh5aDRwZ3dkZ25uaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1jb2xsZWN0aW9uLXRvdGFsLW9yYW5nZS1yZWxlYXNlLWRhdGUuanBn.imageset/YWltYWdlc3RfcHJvZF9zc3dfOTYwLGNfbGltaXQsZl9hdXRvd3drY29xcHY5dnh5aDRwZ3dkZ25uaWtlLWFpci1mb3JjZS0xLXByZW1pdW0tanVzdC1kby1pdC1jb2xsZWN0aW9uLXRvdGFsLW9yYW5nZS1yZWxlYXNlLWRhdGUuanBn.jpg
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesList/ShoesListRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesListRouter.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/13.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol ShoesListRouterSpec {
12 | func pushDetailView(with shoes: ShoesModel)
13 | }
14 |
15 | struct ShoesListRouter: ShoesListRouterSpec {
16 |
17 | init(performer: ShoesListViewController) {
18 | self.performer = performer
19 | }
20 |
21 | func pushDetailView(with shoes: ShoesModel) {
22 | let vc = ShoesDetailViewBuilder(shoes: shoes).build()
23 | performer.navigationController?.pushViewController(vc, animated: true)
24 | }
25 |
26 | // MARK: private
27 |
28 | private weak var performer: ShoesListViewController!
29 | }
30 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesDetail/ShoesDetailViewBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesDetailViewBuilder.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/13.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /// The builder of ShoesDetailViewController
12 | struct ShoesDetailViewBuilder: ViewBuilderSpec {
13 |
14 | init(shoes: ShoesModel) {
15 | self.shoes = shoes
16 | }
17 |
18 | func build() -> ShoesDetailViewController {
19 | let vc = UIStoryboard.main.instantiateViewController(withClass: ShoesDetailViewController.self)
20 | vc.presenter = ShoesDetailPresenter(shoes: shoes)
21 | vc.presenter.eventReceiver = vc
22 | return vc
23 | }
24 |
25 | // MARK: private
26 |
27 | private let shoes: ShoesModel
28 | }
29 |
--------------------------------------------------------------------------------
/MVP-Sample/Assets.xcassets/Shoes/compleximagesfl_lossy,q_autoc_crop,h_502,w_856,x_0,y_183c_scale,w_690,dpr_2.0v1ctxstklbxucwkotuhh72air-jordan-1-shattered-backboard-3-0-black-pale-vanilla-starfish-555088-028-rendering.imageset/compleximagesfl_lossy,q_autoc_crop,h_502,w_856,x_0,y_183c_scale,w_690,dpr_2.0v1ctxstklbxucwkotuhh72air-jordan-1-shattered-backboard-3-0-black-pale-vanilla-starfish-555088-028-rendering.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powerwolf543/Swift-MVP-Sample/HEAD/MVP-Sample/Assets.xcassets/Shoes/compleximagesfl_lossy,q_autoc_crop,h_502,w_856,x_0,y_183c_scale,w_690,dpr_2.0v1ctxstklbxucwkotuhh72air-jordan-1-shattered-backboard-3-0-black-pale-vanilla-starfish-555088-028-rendering.imageset/compleximagesfl_lossy,q_autoc_crop,h_502,w_856,x_0,y_183c_scale,w_690,dpr_2.0v1ctxstklbxucwkotuhh72air-jordan-1-shattered-backboard-3-0-black-pale-vanilla-starfish-555088-028-rendering.jpg
--------------------------------------------------------------------------------
/MVP-Sample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/10/29.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 | var window: UIWindow?
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
16 | LocalShoesRepository.removeAll()
17 | setupFirstView()
18 | return true
19 | }
20 |
21 | // MARK: private
22 |
23 | func setupFirstView () {
24 | window = UIWindow()
25 | window?.rootViewController = ShoesListViewBuilder().buildWithNavigationController()
26 | window?.makeKeyAndVisible()
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesList/Banner/ShoesListBannerRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesListBannerRouter.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/15.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SafariServices
11 |
12 | protocol ShoesListBannerRouterSpec {
13 | func pushWebView(with url: URL)
14 | }
15 |
16 | struct ShoesListBannerRouter: ShoesListBannerRouterSpec {
17 |
18 | init(performer: ShoesListBannerViewController) {
19 | self.performer = performer
20 | }
21 |
22 | func pushWebView(with url: URL) {
23 | let vc = SFSafariViewController(url: url)
24 | performer.navigationController?.pushViewController(vc, animated: true)
25 | }
26 |
27 | // MARK: private
28 |
29 | private weak var performer: ShoesListBannerViewController!
30 | }
31 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Extension/UIColor+extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+extensions.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2018/11/9.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIColor {
12 |
13 | convenience init(red: Int, green: Int, blue: Int) {
14 | assert(red >= 0 && red <= 255, "Invalid red component")
15 | assert(green >= 0 && green <= 255, "Invalid green component")
16 | assert(blue >= 0 && blue <= 255, "Invalid blue component")
17 |
18 | self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
19 | }
20 |
21 | convenience init(rgb: Int) {
22 | self.init(
23 | red: (rgb >> 16) & 0xFF,
24 | green: (rgb >> 8) & 0xFF,
25 | blue: rgb & 0xFF
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Util/ScrollViewCalculator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollViewCalculator.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/15.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ScrollViewCalculator {
12 |
13 | enum Direction {
14 | case up
15 | case down
16 | }
17 |
18 | init(scrollView: UIScrollView, originOffset: CGFloat = 0) {
19 | self.scrollView = scrollView
20 | previousScrollOffset = originOffset
21 | }
22 |
23 | var scrollDifference: CGFloat {
24 | return scrollView.contentOffset.y - previousScrollOffset
25 | }
26 |
27 | var moveDirection: Direction {
28 | return scrollDifference > 0 ? .up : .down
29 | }
30 |
31 | var previousScrollOffset: CGFloat
32 |
33 | // MARK: private
34 |
35 | private let scrollView: UIScrollView
36 | }
37 |
--------------------------------------------------------------------------------
/MVP-Sample/SupportFiles/Dummy/banner_remote_source.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": 0,
3 | "msg": "success",
4 | "body": [
5 | {
6 | "imageName": "banner1",
7 | "url": "https://github.com/powerwolf543"
8 | },
9 | {
10 | "imageName": "banner2",
11 | "url": "https://github.com/powerwolf543/Swift-MVP-Sample"
12 | },
13 | {
14 | "imageName": "banner3",
15 | "url": "https://github.com/powerwolf543"
16 | },
17 | {
18 | "imageName": "banner4",
19 | "url": "https://github.com/powerwolf543/Swift-MVP-Sample"
20 | },
21 | {
22 | "imageName": "banner5",
23 | "url": "https://github.com/powerwolf543"
24 | },
25 | {
26 | "imageName": "banner6",
27 | "url": "https://github.com/powerwolf543/Swift-MVP-Sample"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesDetail/ShoesDetailDescriptionTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesDetailDescriptionTableViewCell.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/13.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct ShoesDetailDescriptionCellModel {
12 | let description: String
13 | }
14 |
15 | class ShoesDetailDescriptionTableViewCell: UITableViewCell, SetReuseViewModelable {
16 |
17 | @IBOutlet weak var descriptionLabel: UILabel!
18 |
19 | func setReuseViewMode(_ viewModel: ShoesDetailDescriptionCellModel) {
20 | descriptionLabel.text = viewModel.description
21 | }
22 |
23 | required init?(coder aDecoder: NSCoder) {
24 | super.init(coder: aDecoder)
25 | setup()
26 | }
27 |
28 | // MARK: private
29 |
30 | private func setup() {
31 | selectionStyle = .none
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Extension/UIStoryboard+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIStoryboard+Extensions.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/12.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIStoryboard {
12 |
13 | static var main: UIStoryboard {
14 | return UIStoryboard(name: "Main", bundle: Bundle.main)
15 | }
16 |
17 | /// Instantiate a UIViewController using its class name
18 | ///
19 | /// - Parameter name: UIViewController type
20 | /// - Returns: The view controller corresponding to specified class name
21 | func instantiateViewController(withClass name: T.Type) -> T {
22 | guard let viewController = instantiateViewController(withIdentifier: String(describing: name)) as? T else {
23 | fatalError("storyboard identifier not found.")
24 | }
25 | return viewController
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/BaseClass/BaseNavigationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseNavigationController.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2018/11/9.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseNavigationController: UINavigationController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | updatedTheme()
17 | interactivePopGestureRecognizer?.isEnabled = true
18 | }
19 |
20 | override var preferredStatusBarStyle: UIStatusBarStyle {
21 | return UIStatusBarStyle.lightContent
22 | }
23 |
24 | private func updatedTheme() {
25 |
26 | navigationBar.barStyle = .blackOpaque
27 | navigationBar.isTranslucent = false
28 | navigationBar.barTintColor = ThemeColor.pureBlack.color
29 | navigationBar.tintColor = ThemeColor.pureWhite.color
30 | navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesList/Banner/ShoesListBannerBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesListBannerBuilder.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /// The builder of ShoesListBannerViewController
12 | struct ShoesListBannerBuilder: ViewBuilderSpec {
13 |
14 | /// builds ViewController and injects dependency of components.
15 | func build() -> ShoesListBannerViewController {
16 |
17 | let fetcher = BannerFetcher()
18 | let repository = RemoteBannerRepository(fetcher: fetcher)
19 | let useCase = FetchBannerUseCase(repository: repository)
20 | let vc = UIStoryboard.main.instantiateViewController(withClass: ShoesListBannerViewController.self)
21 | let presenter = ShoesListBannerPresenter(fetchBannerUseCase: useCase, router: ShoesListBannerRouter(performer: vc))
22 | vc.presenter = presenter
23 | presenter.eventReceiver = vc
24 | return vc
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/Repositories/Remote/RemoteBannerRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RemoteBannerRepository.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct RemoteBannerRepository: BannerRepositorySpec where AnyNetworkFetchable: NetworkFetchable, AnyNetworkFetchable.DataModel == [RemoteBannerModel] {
12 |
13 | init(fetcher: AnyNetworkFetchable) {
14 | self.fetcher = fetcher
15 | }
16 |
17 | func fetchBanners(_ completionHandler: @escaping FetchBannerCompletionHandler) {
18 | fetcher.fire { (result) in
19 | switch result {
20 | case .success(let dataModel):
21 | completionHandler(Result.success(dataModel))
22 | case .failure(let error):
23 | completionHandler(Result.failure(error))
24 | }
25 | }
26 | }
27 |
28 | // MARK: private
29 |
30 | private let fetcher: AnyNetworkFetchable
31 | }
32 |
--------------------------------------------------------------------------------
/MVP-SampleTests/MVP_SampleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MVP_SampleTests.swift
3 | // MVP-SampleTests
4 | //
5 | // Created by NixonShih on 2018/11/11.
6 | // Copyright © 2018年 NixonShih. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import MVP_Sample
11 |
12 | class MVP_SampleTests: XCTestCase {
13 |
14 | override func setUp() {
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | }
21 |
22 | func testExample() {
23 | // This is an example of a functional test case.
24 | // Use XCTAssert and related functions to verify your tests produce the correct results.
25 | }
26 |
27 | func testPerformanceExample() {
28 | // This is an example of a performance test case.
29 | self.measure {
30 | // Put the code you want to measure the time of here.
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Nixon Shih
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.xcuserstate
3 |
4 | # Xcode
5 | #
6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
7 |
8 | ## Build generated
9 | build/
10 | DerivedData/
11 |
12 | ## Various settings
13 | *.pbxuser
14 | !default.pbxuser
15 | *.mode1v3
16 | !default.mode1v3
17 | *.mode2v3
18 | !default.mode2v3
19 | *.perspectivev3
20 | !default.perspectivev3
21 | xcuserdata/
22 |
23 | ## Other
24 | *.moved-aside
25 | *.xccheckout
26 | *.xcscmblueprint
27 |
28 | ## Obj-C/Swift specific
29 | *.hmap
30 | *.ipa
31 | *.dSYM.zip
32 | *.dSYM
33 |
34 | ## Playgrounds
35 | timeline.xctimeline
36 | playground.xcworkspace
37 |
38 | # Swift Package Manager
39 | #
40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
41 | # Packages/
42 | # Package.pins
43 | .build/
44 |
45 | # CocoaPods
46 | #
47 | # We recommend against adding the Pods directory to your .gitignore. However
48 | # you should judge for yourself, the pros and cons are mentioned at:
49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
50 | #
51 | Pods/
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/Repositories/Cache/CacheShoesRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CacheShoesRepository.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/11.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct CacheShoesRepository: ShoesRepositorySpec {
12 |
13 | init(remoteRepository: ShoesRepositorySpec, localRepository: LocalShoesRepositorySpec) {
14 | self.remoteRepository = remoteRepository
15 | self.localRepository = localRepository
16 | }
17 |
18 | func fetchShoes(_ completionHandler: @escaping FetchShoesCompletionHandler) {
19 | remoteRepository.fetchShoes { (result) in
20 | switch result {
21 | case .success(let dataModel):
22 | self.localRepository.save(shoes: dataModel, completionHandler: nil)
23 | completionHandler(result)
24 | case .failure(_):
25 | self.localRepository.fetchShoes(completionHandler)
26 | }
27 | }
28 | }
29 |
30 | // MARK: private
31 |
32 | private var remoteRepository: ShoesRepositorySpec
33 | private var localRepository: LocalShoesRepositorySpec
34 | }
35 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/CustomView/Loading/LoadingImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoadingImageView.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/19.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class LoadingImageView: UIImageView {
12 |
13 | // MARK: Initialize
14 |
15 | override init(frame: CGRect) {
16 | super.init(frame: frame)
17 | customSetting()
18 | }
19 |
20 | required init?(coder aDecoder: NSCoder) {
21 | super.init(coder: aDecoder)
22 | customSetting()
23 | }
24 |
25 | private override init(image: UIImage?, highlightedImage: UIImage? = nil) {
26 | super.init(image: image, highlightedImage: highlightedImage)
27 | customSetting()
28 | }
29 |
30 | // MARK: Private methods
31 |
32 | private func customSetting() {
33 |
34 | var images = [UIImage]()
35 |
36 | for i in 0...29 {
37 | guard let image = UIImage(named: "frame-\(i)") else { continue }
38 | images.append(image)
39 | }
40 |
41 | contentMode = .center
42 | animationImages = images
43 | animationDuration = 0.8
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesList/ShoesListTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesListTableViewCell.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/10.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct ShoesListCellModel {
12 | let title: String
13 | let price: String
14 | let date: String
15 | let imageName: String
16 | }
17 |
18 | class ShoesListTableViewCell: UITableViewCell, SetReuseViewModelable {
19 |
20 | @IBOutlet weak var shoesImageView: UIImageView!
21 | @IBOutlet weak var titleLabel: UILabel!
22 | @IBOutlet weak var dateLabel: UILabel!
23 | @IBOutlet weak var priceLabel: UILabel!
24 |
25 | func setReuseViewMode(_ viewModel: ShoesListCellModel) {
26 | shoesImageView.image = UIImage(named: viewModel.imageName)
27 | titleLabel.text = viewModel.title
28 | dateLabel.text = viewModel.date
29 | priceLabel.text = viewModel.price
30 | }
31 |
32 | required init?(coder aDecoder: NSCoder) {
33 | super.init(coder: aDecoder)
34 | setup()
35 | }
36 |
37 | // MARK: private
38 |
39 | private func setup() {
40 | selectionStyle = .none
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesDetail/ShoesDetailInfoTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesDetailInfoTableViewCell.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/13.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct ShoesDetailInfoCellModel {
12 | let title: String
13 | let price: String
14 | let date: String
15 | let nickName: String
16 | }
17 |
18 | class ShoesDetailInfoTableViewCell: UITableViewCell, SetReuseViewModelable {
19 |
20 | @IBOutlet weak var nickNameLabel: UILabel!
21 | @IBOutlet weak var dateLabel: UILabel!
22 | @IBOutlet weak var titleLabel: UILabel!
23 | @IBOutlet weak var priceLabel: UILabel!
24 |
25 | func setReuseViewMode(_ viewModel: ShoesDetailInfoCellModel) {
26 | nickNameLabel.text = viewModel.nickName
27 | titleLabel.text = viewModel.title
28 | dateLabel.text = viewModel.date
29 | priceLabel.text = viewModel.price
30 | }
31 |
32 | required init?(coder aDecoder: NSCoder) {
33 | super.init(coder: aDecoder)
34 | setup()
35 | }
36 |
37 | // MARK: private
38 |
39 | private func setup() {
40 | selectionStyle = .none
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/CustomView/FailHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FailHeaderView.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/21.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class FailHeaderView: UIView {
12 |
13 | // MARK: Initialize
14 |
15 | convenience init() {
16 | self.init(frame: .zero)
17 | }
18 |
19 | override init(frame: CGRect) {
20 | super.init(frame: frame)
21 | customSetting()
22 | }
23 |
24 | required init?(coder aDecoder: NSCoder) {
25 | super.init(coder: aDecoder)
26 | customSetting()
27 | }
28 |
29 | // MARK: Private methods
30 |
31 | private func customSetting() {
32 |
33 | backgroundColor = ThemeColor.darkRed.color
34 |
35 | let label = UILabel()
36 | label.text = "Failure: please try again later"
37 | label.textColor = ThemeColor.pureWhite.color
38 | addSubview(label)
39 | label.translatesAutoresizingMaskIntoConstraints = false
40 | label.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
41 | label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Util/NotificationUtility.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NotificationUtility.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/10.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol Notifiable {
12 |
13 | var name: Notification.Name { get }
14 |
15 | func observed(by observer: T, withSelector selector: Selector, object: Any?)
16 | func post(object: Any?, userInfo: [AnyHashable: Any]?)
17 | func remove(observer: T)
18 | }
19 |
20 | extension Notifiable {
21 |
22 | func observed(by observer: T, withSelector selector: Selector, object: Any? = nil) {
23 | NotificationCenter.default.addObserver(observer, selector: selector, name: name, object: object)
24 | }
25 |
26 | func post(object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) {
27 | NotificationCenter.default.post(name: name, object: object, userInfo: userInfo)
28 | }
29 |
30 | func remove(observer: T) {
31 | NotificationCenter.default.removeObserver(observer, name: name, object: nil)
32 | }
33 |
34 | var name: Notification.Name {
35 | let name = "kNotificationKeyOf\(String(describing: Self.self))_\(String(describing: self))"
36 | return Notification.Name(name)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/Repositories/Remote/RemoteShoesRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RemoteShoesRepository.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/11.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct RemoteShoesRepository: ShoesRepositorySpec where AnyNetworkFetchable: NetworkFetchable, AnyNetworkFetchable.DataModel == [ShoesModel] {
12 |
13 | init(fetcher: AnyNetworkFetchable) {
14 | self.fetcher = fetcher
15 | }
16 |
17 | func fetchShoes(_ completionHandler: @escaping FetchShoesCompletionHandler) {
18 | fetcher.fire { (result) in
19 | switch result {
20 | case .success(let dataModel):
21 | completionHandler(Result.success(dataModel.sorted(by: self.sortedShose)))
22 | case .failure(let error):
23 | completionHandler(Result.failure(error))
24 | }
25 | }
26 | }
27 |
28 | // MARK: private
29 |
30 | private let fetcher: AnyNetworkFetchable
31 |
32 | private let dateFormatter: DateFormatter = {
33 | let formatter = DateFormatter()
34 | formatter.dateFormat = "yyyy-MM-dd"
35 | return formatter
36 | }()
37 |
38 | private func sortedShose(shoes1: ShoesModel, shoes2: ShoesModel) -> Bool {
39 | return dateFormatter.date(from: shoes1.date)! > dateFormatter.date(from: shoes2.date)!
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesList/ShoesListViewBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesListViewBuilder.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/12.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /// The builder of ShoesListViewController
12 | struct ShoesListViewBuilder: ViewBuilderSpec {
13 |
14 | /// builds ViewController and injects dependency of components.
15 | func build() -> ShoesListViewController {
16 |
17 | let remoteRepository = RemoteShoesRepository(fetcher: ShoesListFetcher())
18 | let cacheRepository = CacheShoesRepository(remoteRepository: remoteRepository, localRepository: LocalShoesRepository())
19 | let fetchLocalShoesUseCaes = FetchShoesUseCase(repository: LocalShoesRepository())
20 | let fetchShoesUseCaes = FetchShoesUseCase(repository: cacheRepository)
21 | let vc = UIStoryboard.main.instantiateViewController(withClass: ShoesListViewController.self)
22 | vc.bannerViewController = ShoesListBannerBuilder().build()
23 | vc.presenter = ShoesListPresenter(
24 | fetchShoesUseCase: fetchShoesUseCaes,
25 | fetchLocalShoesUseCaseSpec: fetchLocalShoesUseCaes,
26 | router: ShoesListRouter(performer: vc),
27 | bannerPresenter: vc.bannerViewController.presenter,
28 | nameProvider: ProjectNameHelper()
29 | )
30 | vc.presenter.eventReceiver = vc
31 | return vc
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/Repositories/Remote/BannerFetcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BannerFetcher.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct BannerFetcher: NetworkFetchable {
12 |
13 | typealias DataModel = [RemoteBannerModel]
14 |
15 | func fire(_ completionHandler: @escaping (Result<[RemoteBannerModel], NetworkError>) -> ()) {
16 |
17 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
18 | let data = self.getDummyData()
19 | do {
20 | let response = try JSONDecoder().decode(RemoteBaseModel.self, from: data)
21 | guard response.code == 0 else {
22 | completionHandler(Result.failure(NetworkError.serviceError))
23 | return
24 | }
25 | completionHandler(Result.success(response.body))
26 | } catch {
27 | completionHandler(Result.failure(NetworkError.decode))
28 | }
29 | }
30 | }
31 |
32 | // MARK: private
33 |
34 | private func getDummyData() -> Data {
35 |
36 | guard
37 | let url = Bundle.main.url(forResource: "banner_remote_source", withExtension: "json"),
38 | let data = try? Data(contentsOf: url) else {
39 | fatalError("Couldn't get dummy data.")
40 | }
41 | return data
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/Repositories/Remote/ShoesListFetcher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesListFetcher.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/19.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct ShoesListFetcher: NetworkFetchable {
12 |
13 | typealias DataModel = [ShoesModel]
14 |
15 | func fire(_ completionHandler: @escaping (Result<[ShoesModel], NetworkError>) -> ()) {
16 |
17 | DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
18 | let data = self.getDummyData()
19 | do {
20 | let response = try JSONDecoder().decode(RemoteBaseModel.self, from: data)
21 | guard response.code == 0 else {
22 | completionHandler(Result.failure(NetworkError.serviceError))
23 | return
24 | }
25 | completionHandler(Result.success(response.body))
26 | } catch {
27 | completionHandler(Result.failure(NetworkError.decode))
28 | }
29 | }
30 | }
31 |
32 | // MARK: private
33 |
34 | private func getDummyData() -> Data {
35 |
36 | guard
37 | let url = Bundle.main.url(forResource: "shoes_list_remote_source", withExtension: "json"),
38 | let data = try? Data(contentsOf: url) else {
39 | fatalError("Couldn't get dummy data.")
40 | }
41 | return data
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/MVP-Sample/DomainLayer/UseCases/FetchBannerUseCase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FetchBannerUseCase.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct FetchBannerUseCase: FetchDataUseCaseSpec {
12 |
13 | typealias DataModel = [BannerModel]
14 |
15 | init(repository: BannerRepositorySpec) {
16 | self.repository = repository
17 | }
18 |
19 | func fetchDataModel(_ completionHandler: @escaping FetchDataModelUseCaseCompletionHandler) {
20 | DispatchQueue.global().async {
21 | self.repository.fetchBanners { (result) in
22 | switch result {
23 | case .success(let dataModel):
24 | DispatchQueue.main.async(execute: {
25 | completionHandler(Result.success(dataModel.map(self.mapBanner).shuffled()))
26 | })
27 | case .failure(let error):
28 | DispatchQueue.main.async {
29 | completionHandler(Result.failure(error))
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
36 | // MARK: private
37 |
38 | private let repository: BannerRepositorySpec
39 |
40 | private func mapBanner(_ remoteBanner: RemoteBannerModel) -> BannerModel {
41 | return BannerModel(imageName: remoteBanner.imageName, url: URL(string: remoteBanner.url))
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/MVP-Sample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIRequiredDeviceCapabilities
26 |
27 | armv7
28 |
29 | UIStatusBarStyle
30 | UIStatusBarStyleLightContent
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/CustomView/Loading/LoadingViewManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoadingViewManager.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/19.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /// Make a loading indicator
12 | class LoadingViewManager {
13 |
14 | /// Show loading indicator for R88
15 | class func show(autoClose time: TimeInterval = 0) {
16 |
17 | guard shared.window == nil else { return }
18 | shared.makeWindow()
19 | }
20 |
21 | /// Dismiss loading indicator for R88
22 | class func dismiss() {
23 |
24 | guard shared.window != nil else { return }
25 | shared.window = nil
26 | }
27 |
28 | // MARK: - Private
29 |
30 | private static let shared: LoadingViewManager = LoadingViewManager()
31 | private var window: UIWindow?
32 |
33 | private init() { }
34 |
35 | private func makeWindow() {
36 |
37 | let loadingView = LoadingImageView(frame: CGRect.zero)
38 | loadingView.startAnimating()
39 |
40 | let vc = BaseViewController()
41 | vc.view.backgroundColor = UIColor(white: 0, alpha: 0.8)
42 | vc.view.addSubview(loadingView)
43 |
44 | loadingView.translatesAutoresizingMaskIntoConstraints = false
45 | loadingView.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor).isActive = true
46 | loadingView.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor).isActive = true
47 |
48 | window = UIWindow(frame: UIScreen.main.bounds)
49 | window?.rootViewController = vc
50 | window?.windowLevel = UIWindow.Level(1300)
51 | window?.makeKeyAndVisible()
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://developer.apple.com/swift/)
2 |
3 | # Swift-MVP-Sample
4 |
5 | It's an iOS simple project that how I implement `MVP` (Model-View-Presenter) and `Clean Architecture` in Swift.
6 |
7 | ## Requirements
8 |
9 | - Xcode 10.2+
10 | - Swift 5.0+
11 |
12 | ## Architecture
13 |
14 | 
15 |
16 | ### View Layer (MVP)
17 | - `View` - Displays information from the `Presenter` and sends user interactions back to the `Presenter`.
18 | - `Presenter` - Contains the presentation logic and tells the `View` what to present
19 | - `ViewBuilder` - The `Builder’s` responsibility is to instantiate a specific `View` and injects the dependency for all components.
20 | - `Router` - Handles navigation logic for which screen should appear and when.
21 |
22 | ### Domain Layer
23 | - `UseCase` - Contains the business logic for a specific use case in the project. They are view agnostic and can be consumed by one or many `Presenters`.
24 | - `Model` - Simple data model objects.
25 |
26 | ### Data Layer
27 | - `Repository` - Query objects from different data sources (Core Data, Realm, web server, etc.) with a only single-entry point.
28 |
29 | ## References
30 |
31 | - [iOS: MVP clean architecture in Tiendeo app](https://medium.com/tiendeo-tech/ios-mvp-clean-architecture-in-tiendeo-app-a8a597c49bb9)
32 | - [Library - iOS - MVP + Clean Architecture Demo](https://github.com/FortechRomania/ios-mvp-clean-architecture/)
33 | - [The Clean Architecture, by Uncle Bob](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
34 | - [Test Double](https://www.martinfowler.com/bliki/TestDouble.html)
35 |
36 | ## Author
37 |
38 | Nixon Shih, powerwolf543@gmail.com
39 |
40 | ## License
41 |
42 | Swift-MVP-Sample is available under the MIT license. See the LICENSE file for more info.
43 |
--------------------------------------------------------------------------------
/MVP-SampleTests/DomainLayer/FetchShoesUseCaseTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FetchShoesUseCaseTests.swift
3 | // MVP-SampleTests
4 | //
5 | // Created by NixonShih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import MVP_Sample
11 |
12 | class FetchShoesUseCaseTests: XCTestCase {
13 |
14 | var repository = MockFetchShoesRepository()
15 | lazy var fetchShoesUseCase = FetchShoesUseCase(repository: repository)
16 |
17 | func testSuccessCallBack() {
18 |
19 | let shoes = [ShoesModel.newShoes]
20 | let expectedResult: Result<[ShoesModel], Error> = .success(shoes)
21 | repository.expectedResult = expectedResult
22 | let completionHandlerExpectation = expectation(description: "Fetch shoes expectation")
23 |
24 | fetchShoesUseCase.fetchDataModel { (result) in
25 | switch result {
26 | case .success(let model):
27 | XCTAssertEqual(model, shoes)
28 | completionHandlerExpectation.fulfill()
29 | case .failure: break
30 | }
31 | }
32 | waitForExpectations(timeout: 1, handler: nil)
33 | }
34 |
35 |
36 | func testFailCallBack() {
37 |
38 | let theError = MockFetchShoesRepositoryError.fail
39 | let expectedResult: Result<[ShoesModel], Error> = .failure(theError)
40 | repository.expectedResult = expectedResult
41 | let completionHandlerExpectation = expectation(description: "Fetch shoes expectation")
42 |
43 | fetchShoesUseCase.fetchDataModel { (result) in
44 | switch result {
45 | case .success: break
46 | case .failure(let error):
47 | switch error {
48 | case MockFetchShoesRepositoryError.fail:
49 | completionHandlerExpectation.fulfill()
50 | default: break
51 | }
52 | }
53 | }
54 | waitForExpectations(timeout: 1, handler: nil)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesDetail/ShoesDetailPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesDetailPresenter.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/13.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol ShoesDetailViewEventReceiverable: class {
12 | func receivedEventOfSetupViews(with setupModel: ShoesDetailViewSetupModel)
13 | }
14 |
15 | protocol ShoesDetailPresenterSpec {
16 |
17 | var eventReceiver: ShoesDetailViewEventReceiverable? { get set }
18 | var rows: Int { get }
19 |
20 | func setup()
21 | func getRowType(by row: Int) -> ShoesDetailRow
22 | func getInfoCellModel() -> ShoesDetailInfoCellModel
23 | func getDescriptionCellModel() -> ShoesDetailDescriptionCellModel
24 | }
25 |
26 | struct ShoesDetailViewSetupModel {
27 | let title: String
28 | let productImageName: String
29 | }
30 |
31 | class ShoesDetailPresenter: ShoesDetailPresenterSpec {
32 |
33 | var rows: Int { return 2 }
34 | weak var eventReceiver: ShoesDetailViewEventReceiverable?
35 |
36 | init(shoes: ShoesModel) {
37 | self.shoes = shoes
38 | }
39 |
40 | func setup() {
41 | let setupModel = ShoesDetailViewSetupModel(title: "Detail", productImageName: shoes.imageName)
42 | eventReceiver?.receivedEventOfSetupViews(with: setupModel)
43 | }
44 |
45 | func getRowType(by row: Int) -> ShoesDetailRow {
46 | guard let type = ShoesDetailRow(rawValue: row)
47 | else { fatalError("Unknown row.") }
48 | return type
49 | }
50 |
51 | func getInfoCellModel() -> ShoesDetailInfoCellModel {
52 | return ShoesDetailInfoCellModel(
53 | title: shoes.title, price: "$" + String(shoes.price), date: shoes.date, nickName: shoes.nickname ?? ""
54 | )
55 | }
56 |
57 | func getDescriptionCellModel() -> ShoesDetailDescriptionCellModel {
58 | return ShoesDetailDescriptionCellModel(description: shoes.description ?? "")
59 | }
60 |
61 | // MARK: Private
62 |
63 | private let shoes: ShoesModel
64 | }
65 |
--------------------------------------------------------------------------------
/MVP-Sample/DataLayer/Repositories/Local/LocalShoesRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocalShoesRepository.swift
3 | // MVP-Sample
4 | //
5 | // Created by Nixon.Shih on 2019/4/11.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol LocalShoesRepositorySpec: ShoesRepositorySpec {
12 |
13 | typealias SaveShoesCompletionHandler = (Result) -> ()
14 | func save(shoes: [ShoesModel], completionHandler: SaveShoesCompletionHandler?)
15 | }
16 |
17 | enum LocalShoesRepositoryError: Error {
18 | case noLocalCache
19 | }
20 |
21 | /// Cache dataModel to userDefaults
22 | struct LocalShoesRepository: LocalShoesRepositorySpec {
23 |
24 | func save(shoes: [ShoesModel], completionHandler: SaveShoesCompletionHandler?) {
25 | do {
26 | let data = try JSONEncoder().encode(shoes)
27 | UserDefaults.standard.set(data, forKey: LocalShoesRepository.localShoesListPersistKey)
28 | completionHandler?(Result.success(()))
29 | } catch {
30 | completionHandler?(Result.failure(error))
31 | }
32 | }
33 |
34 | func fetchShoes(_ completionHandler: @escaping FetchShoesCompletionHandler) {
35 |
36 | guard let data = UserDefaults.standard.data(forKey: LocalShoesRepository.localShoesListPersistKey) else {
37 | completionHandler(Result.failure(LocalShoesRepositoryError.noLocalCache))
38 | return
39 | }
40 |
41 | do {
42 | let dataModel = try JSONDecoder().decode([ShoesModel].self, from: data)
43 | completionHandler(Result.success(dataModel))
44 | } catch {
45 | completionHandler(Result.failure(error))
46 | }
47 | }
48 |
49 | static func removeAll() {
50 | UserDefaults.standard.removeObject(forKey: localShoesListPersistKey)
51 | }
52 |
53 | // MARK: private
54 |
55 | private static let localShoesListPersistKey = "kLocalShoesListPersistKey"
56 |
57 | private func getDummyData() -> Data {
58 |
59 | guard
60 | let url = Bundle.main.url(forResource: "shoes_list_firt_time", withExtension: "json"),
61 | let data = try? Data(contentsOf: url) else {
62 | fatalError("Couldn't get dummy data.")
63 | }
64 | return data
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Extension/UICollectionView+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+Extensions.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/8.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UICollectionView {
12 |
13 | func dequeueReusableCell(withClass name: T.Type, for indexPath: IndexPath) -> T {
14 | guard let cell = dequeueReusableCell(withReuseIdentifier: String(describing: name), for: indexPath) as? T else {
15 | fatalError("Dequeue error: Couldn't find UICollectionViewCell for \(String(describing: name))")
16 | }
17 | return cell
18 | }
19 |
20 | func dequeueReusableSupplementaryView(ofKind kind: String, withClass name: T.Type, for indexPath: IndexPath) -> T {
21 | guard let cell = dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: name), for: indexPath) as? T else {
22 | fatalError("Dequeue error: Couldn't find UICollectionReusableView for \(String(describing: name))")
23 | }
24 | return cell
25 | }
26 |
27 | func register(supplementaryViewOfKind kind: String, withClass name: T.Type) {
28 | register(T.self, forSupplementaryViewOfKind: kind, withReuseIdentifier: String(describing: name))
29 | }
30 |
31 | func register(nib: UINib?, forCellWithClass name: T.Type) {
32 | register(nib, forCellWithReuseIdentifier: String(describing: name))
33 | }
34 |
35 | func register(cellWithClass name: T.Type) {
36 | register(T.self, forCellWithReuseIdentifier: String(describing: name))
37 | }
38 |
39 | func register(nib: UINib?, forSupplementaryViewOfKind kind: String, withClass name: T.Type) {
40 | register(nib, forSupplementaryViewOfKind: kind, withReuseIdentifier: String(describing: name))
41 | }
42 |
43 | func register(nibWithCellClass name: T.Type, at bundleClass: AnyClass? = nil) {
44 |
45 | let identifier = String(describing: name)
46 | var bundle: Bundle?
47 |
48 | if let bundleName = bundleClass {
49 | bundle = Bundle(for: bundleName)
50 | }
51 | register(UINib(nibName: identifier, bundle: bundle), forCellWithReuseIdentifier: identifier)
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/MVP-SampleTests/DomainLayer/FetchBannerUseCaseTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FetchBannerUseCaseTests.swift
3 | // MVP-SampleTests
4 | //
5 | // Created by NixonShih on 2019/4/16.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import MVP_Sample
11 |
12 | class FetchBannerUseCaseTests: XCTestCase {
13 |
14 | var repository = MockRemoteBannerRepository()
15 | lazy var fetchBannersUseCase = FetchBannerUseCase(repository: repository)
16 |
17 | func testSuccessCallBack() {
18 |
19 | let banners = [RemoteBannerModel(imageName: "123", url: ""),
20 | RemoteBannerModel(imageName: "456", url: "789"),
21 | RemoteBannerModel(imageName: "098", url: "http://www.a.com")]
22 | let expectedResult: Result<[RemoteBannerModel], Error> = .success(banners)
23 | repository.expectedResult = expectedResult
24 | let expectedBanners = [BannerModel(imageName: "123", url: nil),
25 | BannerModel(imageName: "456", url: URL(string: "789")),
26 | BannerModel(imageName: "098", url: URL(string: "http://www.a.com"))]
27 | let completionHandlerExpectation = expectation(description: "Fetch shoes expectation")
28 |
29 | fetchBannersUseCase.fetchDataModel { (result) in
30 | switch result {
31 | case .success(let model):
32 | XCTAssertEqual(model, expectedBanners)
33 | completionHandlerExpectation.fulfill()
34 | case .failure: break
35 | }
36 | }
37 | waitForExpectations(timeout: 1, handler: nil)
38 | }
39 |
40 |
41 | func testFailCallBack() {
42 |
43 | let theError = MockRemoteBannerRepositoryError.fail
44 | let expectedResult: Result<[RemoteBannerModel], Error> = .failure(theError)
45 | repository.expectedResult = expectedResult
46 | let completionHandlerExpectation = expectation(description: "Fetch shoes expectation")
47 |
48 | fetchBannersUseCase.fetchDataModel { (result) in
49 | switch result {
50 | case .success: break
51 | case .failure(let error):
52 | switch error {
53 | case MockRemoteBannerRepositoryError.fail:
54 | completionHandlerExpectation.fulfill()
55 | default: break
56 | }
57 | }
58 | }
59 | waitForExpectations(timeout: 1, handler: nil)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/MVP-Sample/Other/Extension/UITableView+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITableView+Extensions.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2018/11/8.
6 | // Copyright © 2018 NixonShih. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UITableView {
12 |
13 | func dequeueReusableCell(withClass name: T.Type) -> T {
14 | guard let cell = dequeueReusableCell(withIdentifier: String(describing: name)) as? T else {
15 | fatalError("Dequeue error: Couldn't find UITableViewCell for \(String(describing: name))")
16 | }
17 | return cell
18 | }
19 |
20 | func dequeueReusableCell(withClass name: T.Type, for indexPath: IndexPath) -> T {
21 | guard let cell = dequeueReusableCell(withIdentifier: String(describing: name), for: indexPath) as? T else {
22 | fatalError("Dequeue error: Couldn't find UITableViewCell for \(String(describing: name))")
23 | }
24 | return cell
25 | }
26 |
27 | func dequeueReusableHeaderFooterView(withClass name: T.Type) -> T {
28 | guard let headerFooterView = dequeueReusableHeaderFooterView(withIdentifier: String(describing: name)) as? T else {
29 | fatalError("Dequeue error: Couldn't find UITableViewHeaderFooterView for \(String(describing: name))")
30 | }
31 | return headerFooterView
32 | }
33 |
34 | func register(nib: UINib?, withHeaderFooterViewClass name: T.Type) {
35 | register(nib, forHeaderFooterViewReuseIdentifier: String(describing: name))
36 | }
37 |
38 | func register(headerFooterViewClassWith name: T.Type) {
39 | register(T.self, forHeaderFooterViewReuseIdentifier: String(describing: name))
40 | }
41 |
42 | func register(cellWithClass name: T.Type) {
43 | register(T.self, forCellReuseIdentifier: String(describing: name))
44 | }
45 |
46 | func register(nib: UINib?, withCellClass name: T.Type) {
47 | register(nib, forCellReuseIdentifier: String(describing: name))
48 | }
49 |
50 | func register(nibWithCellClass name: T.Type, at bundleClass: AnyClass? = nil) {
51 |
52 | let identifier = String(describing: name)
53 | var bundle: Bundle?
54 |
55 | if let bundleName = bundleClass {
56 | bundle = Bundle(for: bundleName)
57 | }
58 | register(UINib(nibName: identifier, bundle: bundle), forCellReuseIdentifier: identifier)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/MVP-Sample/ViewLayer/Scenes/ShoesList/Banner/ShoesListBannerPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ShoesListBannerPresenter.swift
3 | // MVP-Sample
4 | //
5 | // Created by NixonShih on 2019/4/14.
6 | // Copyright © 2019 NixonShih. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol ShoesListBannerViewEventReceiverable: class {
12 | func receivedEventOfStartLoading()
13 | func receivedEventOfReload()
14 | func receivedEventOfLoadFail()
15 | }
16 |
17 | protocol ShoesListBannerPresenterSpec {
18 |
19 | var eventReceiver: ShoesListBannerViewEventReceiverable? { get set }
20 | var countOfItem: Int { get }
21 |
22 | func setup()
23 | func getImageName(by index: Int) -> String
24 | func didSelect(at index: Int)
25 | }
26 |
27 | class ShoesListBannerPresenter: ShoesListBannerPresenterSpec where AnyFetchBannerUseCase: FetchDataUseCaseSpec, AnyFetchBannerUseCase.DataModel == [BannerModel] {
28 |
29 | init(fetchBannerUseCase: AnyFetchBannerUseCase, router: ShoesListBannerRouterSpec) {
30 | self.fetchBannerUseCase = fetchBannerUseCase
31 | self.router = router
32 | }
33 |
34 | func setup() {
35 | eventReceiver?.receivedEventOfStartLoading()
36 | }
37 |
38 | weak var eventReceiver: ShoesListBannerViewEventReceiverable?
39 | var countOfItem: Int { return banners.count }
40 |
41 | func getImageName(by index: Int) -> String {
42 | return banners[index].imageName
43 | }
44 |
45 | func didSelect(at index: Int) {
46 | guard let url = banners[index].url else { return }
47 | router.pushWebView(with: url)
48 | }
49 |
50 | // MARK: private
51 |
52 | private var banners: [BannerModel] = []
53 | private let fetchBannerUseCase: AnyFetchBannerUseCase
54 | private let router: ShoesListBannerRouterSpec
55 |
56 | private func fetchBanners() {
57 | if banners.isEmpty { self.eventReceiver?.receivedEventOfStartLoading() }
58 |
59 | fetchBannerUseCase.fetchDataModel { [weak self] (result) in
60 | guard let self = self else { return }
61 | switch result {
62 | case .success(let dataModel):
63 | self.banners = dataModel
64 | self.eventReceiver?.receivedEventOfReload()
65 | case .failure:
66 | self.eventReceiver?.receivedEventOfLoadFail()
67 | }
68 | }
69 | }
70 | }
71 |
72 | extension ShoesListBannerPresenter: ShoesListSubpresenterOfBannerSpec {
73 |
74 | func receivedEventOfReloadFromSuperPresenter() {
75 | fetchBanners()
76 | }
77 | }
78 |
--------------------------------------------------------------------------------