Oddiy retseptlar ilovasini yaratish orqali iOS-ning eng yaxshi tajribalarini bilib oling

Manba: ChefStep

Mundarija

  • Ishni boshlash
  • Xcode va Swift versiyasi
  • Qo'llab-quvvatlash uchun minimal iOS versiyasi
  • Xcode loyihasini tashkil qilish
  • Ilova tuzilmasi
  • Kod konventsiyalari
  • Hujjatlar
  • Kod bo'limlarini markalash
  • Resursni boshqarish
  • Bog'lanishlar
  • Loyihaga kirish
  • API
  • Ekranni ishga tushirish
  • Ilova belgisi
  • SwiftLint yordamida kodni kiritish
  • Xavfsiz manba
  • Menga kodni ko'rsating
  • Modelni loyihalash
  • FlowController bilan yaxshiroq navigatsiya
  • Avtomatik tartib
  • Arxitektura
  • Massive View Controller
  • Kirishni boshqarish
  • Yolg'onning xususiyatlari
  • Kod parchalari
  • Tarmoq
  • Tarmoq kodini qanday sinovdan o'tkazish kerak
  • Oflayn qo'llab-quvvatlash uchun keshni ishga tushirish
  • Keshni qanday sinash mumkin
  • Masofadan rasmlarni yuklash
  • UIImageView uchun rasm yuklashni qulayroq qilish
  • UITableView va UICollectionView uchun umumiy ma'lumot manbasi
  • Controller va View
  • Bola bilan ko'rish nazorati
  • Tarkibi va qaramlik in'ektsiyasi
  • Ilova transport xavfsizligi
  • Scrollable Custom view
  • Qidiruv funktsiyalarini qo'shish
  • Taqdimot kontekstini tushunish
  • Qidiruv harakatlarini bekor qilish
  • Inverted kutish bilan diskontlash sinovini o'tkazish
  • UITests yordamida foydalanuvchi interfeysini sinab ko'rish
  • Asosiy ipni qo'riqchi
  • Ishlash va muammolarni o'lchash
  • O'yin maydonchasi bilan prototiplash
  • Bu erdan qayerga borish kerak

Men iOS 7 ni iOS 7 e'lon qilinganida boshladim. Ishlash, hamkasblar va iOS hamjamiyatining maslahatlari bilan bir oz ma'lumotga ega bo'ldim.

Ushbu maqolada oddiy retseptlar ilovasini misol qilib ko'plab yaxshi tajribalar bilan bo'lishmoqchiman. Dastlabki kod GitHub Recipes-da.

Ilova - bu batafsil ma'lumot bilan birga retseptlar ro'yxatini namoyish etadigan an'anaviy master batafsil ilovasi.

Muammoni hal qilishning minglab usullari mavjud va muammoni hal qilish ham shaxsiy didga bog'liq. Umid qilamanki, ushbu maqola davomida siz biron bir foydali narsani o'rganasiz - men ushbu loyihani amalga oshirayotganda juda ko'p narsalarni o'rgandim.

Men bir necha kalit so'zlarga havolalarni qo'shdim, shunda qo'shimcha o'qish foydali bo'ladi deb o'yladim. Shuning uchun ularni aniq ko'rib chiqing. Har qanday mulohazalar qabul qilinadi.

Shunday qilib, boshlaymiz ...

Bu erda siz qurmoqchi bo'lgan narsalar haqida yuqori darajadagi sharh berilgan.

Ishni boshlash

Keling, biz foydalanadigan asbob va loyiha sozlamalari to'g'risida qaror qabul qilaylik.

Xcode va Swift versiyasi

WWDC 2018-da Apple Swift 4.2 bilan Xcode 10-ni taqdim etdi. Ammo, yozish paytida Xcode 10 hali ham beta 5da. Shunday ekan, keling, barqaror Xcode 9 va Swift 4.1 bilan ish tutaylik. Xcode 4.2 ba'zi ajoyib xususiyatlarga ega - siz ushbu ajoyib o'yin maydonchasi orqali u bilan o'ynashingiz mumkin. U Swift 4.1-dan katta o'zgarishlarni kiritmaydi, shuning uchun agar kerak bo'lsa, biz yaqin orada dasturimizni osongina yangilashimiz mumkin.

Siz maqsad sozlamalari o'rniga Swift versiyasini Project sozlamasida o'rnatishingiz kerak. Bu loyihadagi barcha maqsadlar bir xil Swift (4.1) versiyasini bo'lishishini anglatadi.

Qo'llab-quvvatlash uchun minimal iOS versiyasi

2018 yil yozidan boshlab iOS 12 ommaviy beta 5-da va biz iOS 12-ni Xcode 10-ga ulanmasdan ishlay olmaymiz. Ushbu xabarda biz Xcode 9-ni, SDK bazasi esa iOS 11-ni ishlatamiz. Talab va foydalanuvchi bazasiga qarab, ba'zi ilovalar mavjud. eski iOS versiyalarini qo'llab-quvvatlash kerak. Garchi iOS foydalanuvchilari Android-ni ishlatadiganlarga qaraganda yangi iOS versiyalarini tezroq qabul qilishga moyil bo'lishsa-da, ba'zi eski versiyalarda qoladiganlari ham bor. Apples maslahatlariga ko'ra, biz eng so'nggi ikkita versiyani - iOS 10 va iOS 11-ni qo'llab-quvvatlashimiz kerak. App Store tomonidan o'lchanganidek, 2018 yil 31-may kuni foydalanuvchilarning atigi 5 foizi iOS 9 va undan oldingi versiyalaridan foydalanadi.

IOS-ning yangi versiyalarini yo'naltirish, biz Apple muhandislari har yili takomillashtirib boradigan yangi SDK-larning afzalliklariga ega bo'lishimiz mumkinligini anglatadi. Apple ishlab chiqaruvchisi veb-saytida yaxshilangan o'zgartirishlar qaydlari ko'rinishi mavjud. Endi qo'shilgan yoki o'zgartirilgan narsalarni ko'rish osonroq.

Ideal holda, eski iOS versiyalari qachon qo'llab-quvvatlanishi kerakligini aniqlash uchun foydalanuvchilar bizning ilovamizdan qanday foydalanishlari to'g'risida tahlilga muhtojmiz.

Xcode loyihasini tashkil qilish

Biz yangi loyihani yaratishda "Birlik testlarini qo'shish" va "UI testlarini qo'shing" ni tanlang, chunki testlarni erta yozib qo'yish tavsiya etiladi. XCTest tizimidagi so'nggi o'zgarishlar, ayniqsa UI testlarida, shabada sinovini o'tkazmoqda va juda barqaror.

Loyihaga yangi fayllarni qo'shishdan oldin, pauza qiling va ilovangizning tuzilishi haqida o'ylang. Fayllarni qanday tartiblashni xohlaymiz? Bizda bir nechta variant bor. Fayllarni xususiyat / modul yoki rol / turlari bo'yicha tartibga solishimiz mumkin. Ularning har birining ijobiy va salbiy tomonlari bor va men ularni quyida muhokama qilaman.

Rol / turi bo'yicha:

  • Taroziga soling: Fayllarni qaerga joylashtirish haqida kamroq fikr yuritish kerak. Skriptlar yoki filtrlarni qo'llash ham osonroq.
  • Kamchiliklari: Agar bir xil xususiyatga tegishli bir nechta fayllarni topishni xohlasak, ularni solishtirish qiyin. Agar kelajakda ularni qayta ishlatiladigan tarkibiy qismlarga aylantirmoqchi bo'lsak, ularni qayta tuzish uchun vaqt kerak bo'ladi.

Xususiyat / modul bo'yicha

  • Taroziga soling: Bu hamma narsani modulli qiladi va kompozitsiyani rag'batlantiradi.
  • Kamchiliklari: Turli xil fayllar bir-biriga o'ralganida, tartibsizlik paydo bo'lishi mumkin.

Modulli bo'lish

Shaxsan men kodimni imkon qadar xususiyatlar / komponentlar bo'yicha tartibga solishga harakat qilaman. Bu tuzatish uchun tegishli kodni aniqlashni osonlashtiradi va kelajakda yangi xususiyatlarni qo'shadi. U "Ushbu fayl nima?" O'rniga "Ushbu ilova nima qiladi?" Degan savolga javob beradi. Bu erda yaxshi maqola bor.

Qaysi tuzilishni tanlashingizdan qat'i nazar, bosh barmog'ingizning yaxshi qoidasi - izchil bo'lishdir.

Ilova tuzilmasi

Quyida bizning retsept bo'yicha qo'llanadigan dastur tuzilishi keltirilgan:

Manba

O'z ichiga tarkibiy qismlarga bo'lingan dastlabki kodli fayllar kiradi:

  • Xususiyatlar: ilovadagi asosiy xususiyatlar
  • Uy: retseptlar ro'yxati va ochiq qidiruvni ko'rsatadigan bosh ekran
  • Ro'yxat: retseptlar ro'yxati, jumladan, retseptni qayta yuklash va retsepti mavjud bo'lmagan hollarda bo'sh ko'rinishni ko'rsatish
  • Qidiruv: qidirish va diskontlashni boshqarish
  • Detail: batafsil ma'lumotni ko'rsatadi

Kutubxona

Ilovamizning asosiy tarkibiy qismlarini o'z ichiga oladi:

  • Oqim: oqimlarni boshqarish uchun FlowController mavjud
  • Adapter: UICollectionView uchun umumiy ma'lumot manbai
  • Kengaytma: oddiy operatsiyalar uchun qulay kengaytmalar
  • Model: JSON tomonidan olingan Ilovadagi model

Manba

Plist, manba va Storyboard fayllarini o'z ichiga oladi.

Kod konventsiyalari

Men raywenderlich / swift-style-guide va github / swift-style-guide-larning aksariyat uslubiy qoʻllanmalariga qoʻshilaman. Bular Swift loyihasida foydalanish uchun sodda va oqilona. Bundan tashqari, Swift kodini yaxshiroq yozish haqida Apple-ning Swift jamoasi tomonidan rasmiy API dizayn ko'rsatmalariga murojaat qiling.

Qaysi uslubni tanlashni tanlaysiz, kodni aniq belgilash sizning eng muhim maqsadingiz bo'lishi kerak.

Ajratish va kosmik urush - bu nozik mavzu, ammo yana ta'mga bog'liq. Men Android-ning loyihalarida to'rtta bo'shliqni, ikkinchisida iOS va React-ning bo'sh joylarini ishlataman. Ushbu retseptlar ilovasida men bu erda va bu erda yozgan izchil va tushunarli izohlarga rioya qilaman.

Hujjatlar

Yaxshi kod aniq tushuntirish berishi kerak, shuning uchun sharhlar yozishingiz shart emas. Agar kodni tushunish qiyin bo'lsa, uni to'xtatish va tavsiflovchi nomlar bilan ba'zi usullarga qaytarish yaxshidir, shuning uchun kodni tushunish ancha aniq bo'ladi. Biroq, men hujjatlashtirish darslari va usullari sizning hamkasblaringiz va kelajakda o'zingiz uchun ham foydalidir. Swift API dizayn ko'rsatmalariga muvofiq,

Har bir deklaratsiya uchun hujjat izohini yozing. Hujjatlarni yozish orqali olingan tushunchalar sizning dizayningizga jiddiy ta'sir ko'rsatishi mumkin, shuning uchun uni qo'ymang.

Cmd + Alt + / bilan Xcode-da sharhlar shablonini yaratish juda oson. Agar siz kelajakda boshqalar bilan bo'lishish uchun kodingizni ramkaga aylantirishni rejalashtirmoqchi bo'lsangiz, jazzy kabi vositalar hujjatlarni yaratishi mumkin, shunda boshqalar ham kuzatib borishlari mumkin.

Kod bo'limlarini markalash

MARK-dan foydalanish kod bo'limlarini ajratishda foydali bo'lishi mumkin. Shuningdek, u navigatsiya panelida yaxshi vazifalarni guruhlarga ajratadi. Bundan tashqari, kengaytma guruhlari, tegishli xususiyatlar va usullardan foydalanishingiz mumkin.

Oddiy UIViewController uchun biz quyidagi MARKlarni aniqlashimiz mumkin:

// MARK: - Boshlang
// MARK: - hayot tsiklini ko'rish
// MARK: - Sozlash
// MARK: - harakat
// MARK: - Ma'lumotlar

Resursni boshqarish

Git - hozirda ommabop manbalarni boshqarish tizimi. Biz gitignore.io/api/swift-dan .gignignore shablonidan foydalanishimiz mumkin. Mustaqillik fayllarini tekshirishda ham ijobiy, ham kamchiliklar mavjud (CocoaPods and Carthage). Bu sizning loyihangizga bog'liq, lekin men kodlar bazasiga tegmaslik uchun manbalarni boshqarishdagi qaramlikni (node_modules, Carthage, Pods) qilmaslikka moyildirman. Bundan tashqari, Pull so'rovlarini ko'rib chiqishni osonlashtiradi.

Siz Pods katalogida tekshirilasizmi yoki yo'qmi, Podfile va Podfile.lock har doim versiya nazorati ostida bo'lishi kerak.

Men ikkalasi ham iTerm2-ni buyruqlarni bajarish uchun va Source Tree-ni filiallar va bosqichlarni ko'rish uchun ishlataman.

Bog'lanishlar

Men uchinchi tomon ramkalaridan foydalanganman, shuningdek ochiq manbalarga ko'p hissa qo'shganman. Ramkadan foydalanish sizga boshida kuch bag'ishlaydi, ammo kelajakda sizni ham cheklab qo'yishi mumkin. Atrofda ishlash juda qiyin bo'lgan ba'zi arzimas o'zgarishlar bo'lishi mumkin. Xuddi shu narsa SDK-larni ishlatganda sodir bo'ladi. Men faol ochiq kodli ramkalarni tanlashni afzal ko'raman. Dastlabki kodni o'qing va ramkalarni diqqat bilan tekshiring va agar siz ulardan foydalanmoqchi bo'lsangiz, o'zingizning jamoangiz bilan maslahatlashing. Biroz ehtiyot bo'lish hech qanday zarar keltirmaydi.

Ushbu ilovada, iloji boricha kamroq qaramlikdan foydalanishga harakat qilaman. Bog'lanishlarni qanday boshqarish kerakligini namoyish etish kifoya. Ba'zi tajribali ishlab chiquvchilar qaramlikni boshqarish menejeri Karfagenni afzal ko'rishlari mumkin, chunki u sizga to'liq nazoratni ta'minlaydi. Bu erda men CocoaPodlarni tanlayman, chunki ulardan foydalanish oson va u shu paytgacha juda yaxshi ishlaydi.

CocoaPods-ga ushbu loyiha Swift 4.1-ni ishlatishini aytib berish uchun qiymatning 4.1-versiyasining .swift-versiyasi deb nomlangan fayl mavjud. Bu juda sodda, ammo tushunishga bir oz vaqtim ketdi.

Loyihaga kirish

Loyihaga chiroyli ko'rinish berish uchun ba'zi rasm va piktogrammalar yarataylik.

API

IOS tarmog'ini o'rganishning oson usuli ommaviy bepul API xizmatlari orqali amalga oshiriladi. Bu erda men food2fork-dan foydalanaman. Hisob uchun http://food2fork.com/about/api manzilida ro'yxatdan o'tishingiz mumkin. Ushbu ommaviy api omborida ko'plab boshqa ajoyib API-lar mavjud.

Hisob ma'lumotlarini xavfsiz joyda saqlash yaxshi. Men parollarni yaratish va saqlash uchun 1Password-dan foydalanaman.

Kodlashni boshlashdan oldin, qanday API talablari va ularga javoblar qaytarilishini bilib olaylik. Men API javoblarini sinash va tahlil qilish uchun Uyqusizlik vositasidan foydalanaman. Bu ochiq, bepul va juda yaxshi ishlaydi.

Ekranni ishga tushirish

Birinchi taassurot juda muhim, shuning uchun ekranni ishga tushirish ham muhimdir. Afzal usul bu LaunchScreen.storyboard-dan foydalanish, statik ishga tushirish tasvirining o'rniga.

Obyektlar katalogiga ishga tushirish rasmini qo'shish uchun LaunchScreen.storyboard-ni oching, UIImageView-ni qo'shing va UIView-ning chekkalariga osib qo'ying. Tasvirni to'liq ekranli bo'lishini xohlaganimiz uchun, biz rasmni Xavfsiz zonaga joylashtirmasligimiz kerak. Shuningdek, Auto Layout cheklovlaridagi har qanday belgini olib tashlang. UIImageView-ning contentMode-ni Aspect Fill sifatida o'rnating va u to'g'ri tomonlar nisbati bilan cho'zilib ketadi.

LaunchScreen-da tartibni sozlash.

Ilova belgisi

Yaxshi amaliyot bu siz qo'llab-quvvatlaydigan har bir qurilma uchun, shuningdek, Notification, Settings va Springboard kabi joylar uchun barcha zarur ilovalar piktogrammalarini taqdim etishdir. Har bir rasmda shaffof piksellar yo'qligiga ishonch hosil qiling, aks holda u qora fonga olib keladi. Ushbu qo'llanma Inson interfeysi bo'yicha ko'rsatmalar - App Icon.

Fonni sodda qilib qo'ying va shaffoflikdan saqlaning. Sizning ikonkangiz shaffof emasligiga ishonch hosil qiling va fonga aralashmang. Yaqin atrofdagi boshqa dastur piktogrammalaridan ustun turmasligi uchun unga oddiy fon rasmini bering. Butun ikonkani tarkib bilan to'ldirishingiz shart emas.

Biz har biri kichikroq hajmdagi rasmlarni kichraytirishi uchun 1024 x 1024 dan katta o'lchamdagi kvadrat rasmlarni loyihalashimiz kerak. Siz buni qo'lda, skriptda qilishingiz yoki men yaratgan bu kichik IconGenerator ilovasidan foydalanishingiz mumkin.

IconGenerator ilovasi iPhone, iPad, macOS va watchOS ilovalarida iOS uchun piktogrammalar yaratishi mumkin. Natijada AppIcon.appiconset mavjud bo'lib, biz uni Aktivlar katalogiga to'g'ridan-to'g'ri olib o'tamiz. Aktivlar katalogi zamonaviy Xcode loyihalari uchun boradigan yo'ldir.

SwiftLint yordamida kodni kiritish

Qaysi platformada ishlamasligimizdan qat'i nazar, doimiy ravishda o'tkaziladigan anjumanlarni qo'llab-quvvatlash uchun lenta bo'lsa yaxshi bo'ladi. Swift loyihalari uchun eng mashhur vosita bu SwiftLint bo'lib, u Realm-da ajoyib odamlar tomonidan yaratilgan.

Uni o'rnatish uchun Podfile-ga 'SwiftLint', '~> 0.25' podkastlarini qo'shing. Bog'lanish versiyasini belgilash ham yaxshi amaliyotdir, shuning uchun pod o'rnatilishi tasodifan ilovangizni buzishi mumkin bo'lgan asosiy versiyaga yangilanmaydi. Keyin o'zingiz xohlagan konfiguratsiya bilan .swiftlint.yml qo'shing. Namunaviy konfiguratsiyani bu erda topish mumkin.

Nihoyat, kompilyatsiya qilinganidan keyin tezkor yozuvni bajarish uchun yangi Run Script Phrase qo'shing.

Xavfsiz manba

Resurslarni ishonchli boshqarish uchun men R.swift-dan foydalanaman. U shriftlarga, mahalliy satrlarga va ranglarga kirish uchun tipdagi xavfsiz sinflarni yaratishi mumkin. Resurs fayllari nomlarini o'zgartirganda, biz aniq xatolar o'rniga kompilyatsiya xatolariga duch kelamiz. Bu bizni faol foydalanadigan resurslar bilan aloqa qilishni oldini oladi.

imageView.image = R.image.notFound ()

Menga kodni ko'rsating

Keling, kodni modeldan, oqim nazorati va xizmat ko'rsatish sinflaridan boshlaylik.

Modelni loyihalash

Bu zerikarli tuyulishi mumkin, ammo mijozlar API javobini taqdim etishning eng yaxshi usulidir. Model, ehtimol, eng asosiy narsa va biz uni dasturda juda ko'p ishlatamiz. U juda muhim rol o'ynaydi, ammo ba'zi bir xatolar bo'lishi mumkin, ular noto'g'ri modellar va modelni tahlil qilish kerakligi haqidagi taxminlarni ko'rib chiqish kerak.

Biz ilovaning har bir modelini sinab ko'rishimiz kerak. Ideal holda, agar model orqa tomondan o'zgargan bo'lsa, biz API javoblaridan modellarni avtomatlashtirilgan sinovdan o'tkazishga muhtojmiz.

Swift 4.0-dan boshlab, biz o'z modelimizni JSON-ga va undan osonlikcha seriyalashtirish uchun Codable-ga moslashtira olamiz. Bizning model o'zgarmas bo'lishi kerak:

tarkibiy retsepti: kodlanishi mumkin {
  ruxsat noshiri: String
  URL manzili: URL
  let SourceUrl: satr
  ruxsat id: satr
  sarlavha: String
  let imageUrl: satr
  bo'lsin socialRank: ikki baravar
  PublisherUrl: URL manzili
enod CodingKeys: String, CodingKey {
    case noshiri
    case url = "f2f_url"
    case sourceUrl = "source_url"
    case id = "retsepti"
    ish sarlavhasi
    case imageUrl = "image_url"
    case socialRank = "social_rank"
    case publisherUrl = "Publisher_url"
  }
}

Agar siz fantastika sintaksisini yoki RSpec uslubini yoqtirsangiz, biz ba'zi sinov ramkalaridan foydalanishimiz mumkin. Ba'zi uchinchi tomon sinov tizimlarida muammolar bo'lishi mumkin. Men XCTestni yaxshi deb topaman.

import XCTest
@testable import retseptlari
sinf RecipesTests: XCTestCase {
  func testParsing () otadi {
    jsonga ruxsat bering: [String: Istalgan] = [
      "nashriyot": "Ikki no'xat va ularning podasi",
      "f2f_url": "http://food2fork.com/view/975e33",
      "sarlavha": "Non-pishiriqsiz shokoladli yerfıstığı yog'i, oldindan pishiriladigan pishiriqlar",
      "source_url": "http://www.twopeasandtheirpod.com/no-bake-chocolate-peanut-butter-pretzel-cookies/",
      "retsept_id": "975e33",
      "image_url": "http://static.food2fork.com/NoBakeChocolatePeanutButterPretzelCookies44147.jpg",
      "ijtimoiy_rank": 99.9999999999997474,
      "publisher_url": "http://www.twopeasandtheirpod.com"
    ]
ma'lumotlarga ruxsat bering = JSONSerialization.data-ni sinab ko'ring (withJSONObject: json, options: [])
    ruxsat beruvchi dekoder = JSONDekoder ()
    retsepti ber = = dekoder.decode-ni sinab ko'ring (Recipe. o'zi, dan: ma'lumotlar)
XCTAssertEqual (retsept.title, "Shokoladsiz shokoladli yong'oq moyi piyozidan tayyorlangan pishiriqlar")
    XCTAssertEqual (retsept.id, "975e33")
    XCTAssertEqual (retsept.url, URL (satr: "http://food2fork.com/view/975e33")!)
  }
}

FlowController bilan yaxshiroq navigatsiya

Ilgari men loyihalarimda Compass-ni marshrutizator sifatida ishlatardim, ammo vaqt o'tishi bilan oddiy Routing kodini yozish juda ko'p ishlayotganiga amin bo'ldim.

FlowController umumiy xususiyatga bog'liq bo'lgan ko'plab UIViewController tarkibiy qismlarini boshqarish uchun ishlatiladi. Boshqa foydalanish holatlari uchun FlowController va Koordinatorni o'qishni va yaxshiroq tushunishni xohlashingiz mumkin.

O'zgaruvchan rootViewController-ni boshqaradigan AppFlowController mavjud. Hozirda u RecipeFlowController-ni ishga tushiradi.

oyna = UIWindow (ramka: UIScreen.main.bounds)
? .rootViewController = appFlowController oynasi
deraza? .makeKeyAndVisible ()
appFlowController.start ()

RecipeFlowController HomeViewController, RecipesDetailViewController, SafariViewController-ni boshqaradigan UINavigationController-ni (aslida u) boshqaradi.

yakuniy sinf RecipeFlowController: UINavigationController {
  /// Oqimni boshlang
  func start () {
    xizmatga ruxsat = RecipesService (tarmoq: NetworkService ())
    kontrollerga ruxsat berish = HomeViewController (retseptXizmat: xizmat)
    viewControllers = [boshqaruvchi]
    Controller.select = {[o'zini o'zi boshqarish retsepti]
      o'z-o'zini? .startDetail (retsept: retsept)
    }
  }
xususiy func startDetail (retsept: retsept) {}
  xususiy func startWeb (URL: URL) {}
}

UIViewController oqimdagi o'zgarishlar yoki keyingi ekranlar to'g'risida FlowControllerni xabardor qilish uchun vakil yoki yopishni ishlatishi mumkin. Vakillar uchun bitta sinfning ikkita holati mavjudligini tekshirish kerak bo'lishi mumkin. Bu erda biz yopish uchun oddiylikdan foydalanamiz.

Avtomatik tartib

Auto Layout iOS 5-dan beri bo'lib kelmoqda, u yildan yilga yaxshilanmoqda. Garchi ba'zi odamlar hali ham bu bilan bog'liq muammolarga duch kelsalar-da, asosan cheklovlar va unumdorlikni chalkashtirib yuborishganligi sababli, lekin shaxsan men Auto Layout dasturini etarlicha yaxshi deb bilaman.

Moslashuv interfeysi yaratish uchun Auto Layout-dan iloji boricha ko'proq foydalanishga harakat qilaman. Deklarativ va tezkor avtoulovni amalga oshirish uchun biz langar kabi kutubxonalardan foydalanishimiz mumkin. Biroq, ushbu ilovada biz faqat NSLayoutAnchor-ni ishlatamiz, chunki u iOS 9-dan olingan. Quyidagi kod Constraint tomonidan ilhomlantirilgan. Shuni esda tutingki, Auto Layout eng sodda shaklda tarjimalarAutoresizingMaskIntoConstraints va isActive cheklovlarini faollashtirishni o'z ichiga oladi.

NSLayoutConstraint kengaytmasi {
  statik funktsiyani faollashtirish (_ cheklovlar: [NSLayoutConstraint]) {
    cheklovlar.forEach {
      ($ 0.firstItem? UIView sifatida) ?. translatesAutoresizingMaskIntoConstraints = false
      $ 0.isActive = haqiqiy
    }
  }
}

GitHub-da aslida ko'plab boshqa sxemali dvigatellar mavjud. Qaysi biri ishlatilishi mumkinligini tushunish uchun LayoutFrameworkBenchmark-ni tekshiring.

Arxitektura

Arxitektura, ehtimol, eng shubhali va muhokama qilinadigan mavzu. Men arxitekturalarni o'rganishning muxlisiman, siz bu erda turli xil arxitekturalar haqida ko'proq postlar va ramkalarni ko'rishingiz mumkin.

Men uchun barcha arxitektura va naqshlar har bir ob'ekt uchun rollarni va ularni qanday ulashni aniqlaydi. Arxitekturani tanlashda ushbu qo'llanma printsiplarini unutmang:

  • turlicha
  • merosdan ustun bo'lgan kompozitsion
  • dasturni amalga oshirish uchun emas, balki interfeys uchun dastur

Turli xil arxitekturalarda, Rx bilan va bo'lmasdan o'ynaganimdan so'ng, oddiy MVC etarlicha yaxshi ekanligini bilib oldim. Ushbu oddiy loyihada faqat yordamchi xizmat ko'rsatish sinflarida mantiq bilan jihozlangan UIViewController,

Massive View Controller

Siz UIViewController qanchalik katta ekanligi haqida odamlarning hazillashayotganini eshitgan bo'lishingiz mumkin, ammo aslida, ulkan ko'rish nazorati yo'q. Bu shunchaki yomon kod yozish. Biroq, uni pasaytirish usullari mavjud.

Men foydalanadigan retseptlar ilovasida,

  • Bitta vazifani bajarish uchun ko'rinishni boshqarish vositasiga kiritish xizmati
  • Ko'rish va deklaratsiyani View qatlamiga o'tkazish uchun Umumiy ko'rinish
  • Ko'proq xususiyatlarni yaratish uchun bolalarga qarash nazoratchilarini yaratish uchun bolalar ko'rinishi nazorati

Bu erda juda katta maqola katta kontrollerlarni tushirish bo'yicha 8 ta maslahatlar mavjud.

Kirishni boshqarish

SWIFT hujjatlarida aytilishicha, "kirishni boshqarish, boshqa manbali fayllar va modullardagi kodlardan sizning kod qismlariga kirishni cheklaydi. Ushbu xususiyat sizga kodni amalga oshirish tafsilotlarini yashirish va ushbu kod orqali kirish va foydalanish mumkin bo'lgan afzal interfeysni belgilash imkonini beradi. "

Sukut bo'yicha hamma narsa shaxsiy va yakuniy bo'lishi kerak. Bu shuningdek kompilyatorga yordam beradi. Ommaviy mulkni ko'rib, u bilan boshqa biron bir ish qilishdan oldin uni loyiha bo'ylab qidirishimiz kerak. Agar mulk faqat sinf ichida ishlatilgan bo'lsa, uni shaxsiy qilib belgilab qo'yish, boshqa joyda buzilish holatlari haqida qayg'urmasligimiz kerakligini anglatadi.

Iloji bo'lsa, xususiyatlarni yakuniy deb e'lon qiling.

yakuniy sinf HomeViewController: UIViewController {}

Xususiyatlarni xususiy yoki hech bo'lmaganda xususiy (o'rnatilgan) deb e'lon qiling.

yakuniy sinf RecipeDetailView: UIView {
  shaxsiy ruxsat scrollableView = ScrollableView ()
  shaxsiy (o'rnatilgan) dangasa var imageView: UIImageView = self.makeImageView ()
}

Yolg'onning xususiyatlari

Keyinchalik kirish mumkin bo'lgan xususiyatlar uchun biz ularni dangasa deb e'lon qilishimiz mumkin va tez qurish uchun yopilishdan foydalanishimiz mumkin.

yakuniy sinf RecipeCell: UICollectionViewCell {
  shaxsiy (sozlangan) dangasa var контейнерView: UIView = {
    ko'rishga ruxsat berish = UIView ()
    view.clipsToBounds = haqiqiy
    view.layer.cornerRadius = 5
    view.backgroundColor = Color.main.wallAlphaComponent (0.4)
qaytish ko'rinishi
  } ()
}

Agar biz bir xil funktsiyalarni bir nechta xususiyatlar uchun qayta ishlatishni rejalashtirsak, make funktsiyalaridan ham foydalanishimiz mumkin.

yakuniy sinf RecipeDetailView: UIView {
  shaxsiy (o'rnatilgan) dangasa var imageView: UIImageView = self.makeImageView ()
xususiy func makeImageView () -> UIImageView {
    imageView = UIImageView () ga ruxsat berish
    imageView.contentMode = .scaleAspectFill
    imageView.clipsToBounds = haqiqiy
    return imageView
  }
}

Bu shuningdek ravon foydalanishga intilish maslahatlariga ham mos keladi.

Zavod usullarining nomlarini "make" bilan boshlang, masalan, x.makeIterator ().

Kod parchalari

Ba'zi kod sintaksisini eslab qolish qiyin. Kodni avtomatik yaratish uchun kod parchalarini ishlatishni ko'rib chiqing. Bu Xcode tomonidan qo'llab-quvvatlanadi va demo chiqarishda Apple muhandislari tomonidan ma'qul keladigan usul.

agar # mavjud bo'lsa (iOS 11, *) {
  viewController.navigationItem.searchController = searchController
  viewController.navigationItem.hidesSearchBarWhenScrolling = noto'g'ri
} else {
  viewController.navigationItem.titleView = searchController.searchBar
}

Ko'pchilik foydalanadigan juda foydali "Swift" bo'laklari bilan repo qildim.

Tarmoq

Swift-da tarmoq o'rnatish - bu hal qilinadigan muammoning bir turi. HTTP javoblarini tahlil qilish, so'rovlar navbati bilan ishlash, parametrlar so'rovlarini ko'rib chiqish kabi zerikarli va xatolarga olib keladigan vazifalar mavjud. Men PATCH so'rovlari, HTTP-ning pasaytirilgan usullari haqida xatolarni ko'rdim ... Biz shunchaki Alamofire-dan foydalanishimiz mumkin. Bu erda vaqtni behuda sarflashning hojati yo'q.

Ushbu ilova uchun, chunki bu juda oddiy va keraksiz qaramlikdan saqlanish. Biz faqat to'g'ridan-to'g'ri URLSession-dan foydalanishimiz mumkin. Resurs odatda URL, yo'l, parametrlar va HTTP usulini o'z ichiga oladi.

Tarkib Resurs {
  URL manzili: URL
  ruxsat bering: String?
  ruxsat httpMethod: String
  ruxsat parametrlari: [String: String]
}

Oddiy tarmoq xizmati faqatgina Resursni URLRequest-ga tahlil qilishi mumkin va URLSessionni bajarishni buyuradi

yakuniy sinf NetworkService: Networking {
  @discardableResult func fetch (manba: Resurs, yakunlash: @escaping (Ma'lumotlar?) -> Bo'sh joy) -> URLSessionTask? {
    Guard let request = makeRequest (manba: manba) else {
      tugatish (nol)
      nil qaytaring
    }
let task = session.dataTask (bilan: request, completeHandler: {data, _, in error
      qo'riqchi ma'lumot = ma'lumotlar, xato == nol boshqa {
        tugatish (nol)
        qaytish
      }
tugatish (ma'lumotlar)
    })
task.resume ()
    qaytish vazifasi
  }
}

Qarindoshlik in'ektsiyasidan foydalaning. Qo‘ng‘iroq qiluvchiga URLSessionConfiguration oynasini ko‘rsatishga ruxsat bering. Bu erda biz eng keng tarqalgan variantni ta'minlash uchun Swift standart parametridan foydalanamiz.

init (konfiguratsiya: URLSessionConfiguration = URLSessionConfiguration.default) {
  self.session = URLSession (konfiguratsiya: konfiguratsiya)
}

Bundan tashqari, men iOS 8-dan kelgan URLQueryItem-dan foydalanaman, bu elementlarni yaxshi va kamroq zerikarli qilib so'rash uchun parametrlarni tahlil qiladi.

Tarmoq kodini qanday sinovdan o'tkazish kerak

Tarmoq javoblari uchun stub qo'shish uchun biz URLProtocol va URLCache-dan foydalanishimiz mumkin yoki URLSessionConfiguration-ni o'zgartiradigan Mockingjay kabi ramkalardan foydalanishimiz mumkin.

Men o'zim sinash uchun protokoldan foydalanishni afzal ko'raman. Protokol yordamida test stub javobini berish uchun soxta so'rovni yaratishi mumkin.

Tarmoq protokoli {
  @discardableResult func fetch (manba: Resurs, yakunlash: @escaping (Ma'lumotlar?) -> Bo'sh joy) -> URLSessionTask?
}
yakuniy sinf MockNetworkService: Tarmoqlarni boshqarish {
  let data: Ma'lumotlar
  init (fileName: String) {
    bundle = to'plamni qo'shish (uchun: MockNetworkService.self)
    let url = bundle.url (forResource: fileName, withExtension: "json")!
    self.data = harakat qilib ko'ring! Ma'lumotlar (tarkibiOf: URL)
  }
func fetch (manba: Resurs, yakunlash: @escaping (Ma'lumotlar?) -> Bo'sh joy) -> URLSessionTask? {
    tugatish (ma'lumotlar)
    nil qaytaring
  }
}

Oflayn qo'llab-quvvatlash uchun keshni ishga tushirish

Men Kesh deb nomlangan kutubxonadan ko'p hissa qo'shardim va foydalanardim. Yaxshi kesh kutubxonasidan bizga kerak bo'lgan narsa bu xotira va disk keshi, tez kirish uchun xotira, qat'iylik uchun disk. Saqlaganimizda, biz ham xotira, ham diskka saqlaymiz. Biz yuklaganimizda, agar xotira keshi ishlamay qolsa, biz diskdan yuklaymiz va yana xotirani yangilaymiz. Keshni tozalash, tugatish, kirish chastotasi kabi ko'plab ilg'or mavzular mavjud. Bu erda ular haqida o'qing.

Ushbu sodda ilovada, uy sharoitida keshga xizmat ko'rsatish sinfi etarli va keshlash qanday ishlashini o'rganish uchun yaxshi usul. Swift-dagi hamma narsa Ma'lumotga aylantirilishi mumkin, shunda biz ma'lumotlarni keshga saqlashimiz mumkin. Swift 4 kodlanuvchi ma'lumotni ob'ektga ketma-ket joylashtirishi mumkin.

Quyidagi kod FileManager-ni diskni keshlash uchun qanday ishlatishni ko'rsatib beradi.

/// Ma'lumotni saqlash va xotira va disk keshiga yuklash
oxirgi sinf CacheService {
/// Ma'lumotni xotirada olish yoki yuklash uchun
  shaxsiy xotiraga ruxsat berish = NSCache  ()
/// Keshlangan fayllarni (mp3 fayllar va rasm fayllari) o'z ichiga olgan yo'lning URL manzili
  shaxsiy ruxsat diskPath: URL
/// Fayl yoki katalogni tekshirish uchun belgilangan yo'l mavjud
  shaxsiy ruxsat berilgan faylManager: FileManager
/// Barcha operatsiyalar ketma-ket bajarilganligiga ishonch hosil qiling
  private let serialQueue = DispatchQueue (yorliq: "Retseptlar")
init (fileManager: FileManager = FileManager.default) {
    self.fileManager = fileManager
    qilish {
      documentDirectory = sinashga ruxsat beringManager.url (
        uchun: .documentDirectory,
        ichida: .userDomainMask,
        tegishliFor: nil,
        yaratish: haqiqiy
      )
      diskPath = documentDirectory.appendingPathComponent ("Retseptlar")
      createDirectoryIfNeeded () ni sinab ko'ring
    } catch {
      jiddiy xato()
    }
  }
func save (ma'lumotlar: ma'lumotlar, kalit: satr, tugatish: (() -> Void)? = nil) {
    ruxsat tugmasi = MD5 (tugma)
serialQueue.async {
      self.memory.setObject (ma'lumotlar NSData, forKey: NSString sifatida kalit)
      qilish {
        data.write-ni sinab ko'ring (uchun: self.filePath (kalit: kalit))
        tugatdimi? ()
      } catch {
        bosib chiqarish (xato)
      }
    }
  }
}

Noto'g'ri shakllangan va juda uzun fayl nomlarining oldini olish uchun ularni hash qilishimiz mumkin. Men SwiftHash-dan MD5-ni ishlataman, bu o'lik oddiy foydalanishga ruxsat beradi = MD5 (key).

Keshni qanday sinash mumkin

Men Kesh operatsiyalarini asenkron holatga keltiradigan qilib yaratganim sababli, biz test kutishlaridan foydalanishimiz kerak. Oldingi sinov holati joriy sinovga xalaqit bermasligi uchun har bir sinovdan oldin holatni tiklashni unutmang. XCTestCase-da kutish har doimgidan ko'ra asenkron kodni sinashni osonlashtiradi.

sinf CacheServiceTests: XCTestCase {
  ruxsat berish = CacheService ()
func setUp () {ning o'rnini bosish
    super.setUp ()
sinab ko'ring service.clear ()
  }
funktsiya testClear () {
    kutish = o'z-o'zidan kutish (tavsif: # funktsiya)
    let string = "Salom dunyo"
    data = string.data (foydalaning: .utf8) ruxsat bering!
service.save (ma'lumotlar: ma'lumotlar, kalit: "kalit", tugatish: {
      sinab ko'ring o'z.service.clear ()
      self.service.load (kalit: "kalit", tugatish: {
        XCTAssertNil ($ 0)
        kutish.fulfill ()
      })
    })
kutish (kutish: [kutish], kutish vaqti: 1)
  }
}

Masofadan rasmlarni yuklash

Men shuningdek, Imaginary-ga o'z hissamni qo'shaman, shuning uchun u qanday ishlashini bir oz bilaman. Masofadagi rasmlar uchun biz uni yuklab olishimiz va keshlashimiz kerak, va kesh kaliti odatda masofadagi rasmning URL-manzilidir.

Bizning retseptlar ilovamizda NetworkService va CacheService-ga asoslangan oddiy ImageService quraylik. Asosan rasm bu biz yuklab oladigan va saqlaydigan tarmoq manbai. Biz tarkibni afzal ko'ramiz, shuning uchun ImageService-ga NetworkService va CacheService-ni qo'shamiz.

/// Mahalliy keshni tekshiring va masofadan tasvirni oling
ImageService yakuniy sinfi {
shaxsiy ruxsat berilgan tarmoqService: Tarmoq
  shaxsiy ruxsat berish cacheService: CacheService
  shaxsiy var vazifasi: URLSessionTask?
init (networkService: Networking, cacheService: CacheService) {
    self.networkService = networkService
    self.cacheService = cacheService
  }
}

Odatda UIIollageView va UITableView hujayralari UIImageView-ga ega. Va katakchalar qayta ishlatilganligi sababli, yangi so'rov berishdan oldin, mavjud so'rovlar vazifasini bekor qilishimiz kerak.

func fetch (URL: URL, tugatish: @escaping (UIImage?) -> Bo'sh joy) {
  // Agar mavjud bo'lsa, mavjud vazifani bekor qilish
  vazifa? .cancel ()
// keshdan yuklamani sinab ko'ring
  cacheService.load (kalit: url.absoluteString, tugatish: {[kuchsiz] keshlangan ma'lumotlar
    agar data = cachedData bo'lsa, rasm = UIImage (ma'lumotlar: ma'lumotlar) {
      DispatchQueue.main.async {
        tugatish (rasm)
      }
    } else {
      // Tarmoqdan so'rashga harakat qiling
      let resource = Resurs (url: url)
      o'zini? .task = o'zini? .networkService.fetch (manba: manba, yakunlash: {networkData in
        agar data = networkData bo'lsa, rasm = UIImage (ma'lumotlar: ma'lumotlar) {
          // keshga saqlash
          o'zini? .cacheService.save (ma'lumotlar: ma'lumotlar, kalit: url.absoluteString)
          DispatchQueue.main.async {
            tugatish (rasm)
          }
        } else {
          bosib chiqarish ("Rasmni yuklashda xatolik \ (URL)"))
        }
      })
o'z-o'zidan .task? .resume ()
    }
  })
}

UIImageView uchun rasm yuklashni qulayroq qilish

URL-dan masofaviy tasvirni o'rnatish uchun UIImageView-ga kengaytmani qo'shamiz. Ushbu ImageService-ni saqlab qolish va eski so'rovlarni bekor qilish uchun men bog'liq ob'ektdan foydalanaman. ImageService-ni UIImageView-ga biriktirish uchun biz bog'langan ob'ektdan yaxshi foydalanamiz. Gap shundaki, so'rov qaytadan boshlanganida, joriy so'rovni bekor qilish kerak. Rasm ko'rinishi aylantirish ro'yxatida ko'rsatilsa, bu qulay.

UIImageView kengaytmasi {
  func setImage (URL: URL, to'ldirgich: UIImage? = nil) {
    agar imageService == nil {
      imageService = ImageService (tarmoq xizmati: NetworkService (), keshService: CacheService ())
    }
self.image = to'ldiruvchi
    self.imageService? .fetch (url: URL, tugatish: {[o'zini o'zi zaif tasvir)
      o'zini? .image = rasm
    })
  }
private var imageService: ImageService? {
    olish {
      objc_getAssociatedObject (self, & AssociateKey.imageService) kabi qaytarmoq ImageService
    }
    sozlash {
      objc_setAssociatedObject (
        o'zini,
        & AssociateKey.imageService,
        yangiValue,
        objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
      )
    }
  }
}

UITableView va UICollectionView uchun umumiy ma'lumot manbasi

Biz deyarli barcha ilovalarda UITableView va UICollectionView-dan foydalanamiz va deyarli bir xil ishlarni takroran bajaramiz.

  • yuklash paytida yangilashni boshqarish
  • ma'lumotlar bo'lsa ro'yxatni qayta yuklang
  • ishlamay qolgan taqdirda xatoni ko'rsatish.

UITableView va UICollection atrofida ko'plab o'rash materiallari mavjud. Har biri yana bir mavhum qavatni qo'shadi, bu bizga ko'proq kuch beradi, lekin bir vaqtning o'zida cheklovlarni qo'llaydi.

Ushbu ilovada men ma'lumotlarning umumiy manbasini olish va xavfsiz turdagi to'plam yaratish uchun Adapter-dan foydalanaman. Chunki oxir-oqibat bizga kerak bo'lgan narsa - bu modeldan hujayralarni xaritalash.

Men shuningdek, ushbu fikrga asoslanib, yuqoridan oqimlarni ishlataman. UITableView va UICollectionView-ni o'rash qiyin, chunki bu ko'p marotaba, o'ziga xosdir, shuning uchun Adapter kabi ingichka o'rash kifoya qiladi.

oxirgi sinf adapteri : NSObject,
UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  var elementlari: [T] = []
  var configure: ((T, Uyali) -> Bo'sh)?
  var tanlang: ((T) -> Bo'sh)?
  var cellHeight: CGFloat = 60
}

Controller va View

Men cheklovlar va ko'plab muammolar tufayli Storyboard-ni chiqardim. Buning o'rniga, men nuqtai nazarni yaratish va cheklovlarni aniqlash uchun koddan foydalanaman. Unga amal qilish unchalik qiyin emas. UIViewController-dagi qozon kodining ko'p qismi ko'rinishni yaratish va tartibni sozlash uchun mo'ljallangan. Keling, ularni ko'rinishga o'tkazaylik. Bu haqda ko'proq ma'lumotni bu erda o'qishingiz mumkin.

/// Tekshirish va ko'rish o'rtasida ajratish uchun ishlatiladi
sinf BaseController : UIViewController {
  Ildizi = T ()
func loadView () {o'rnini bosish
    view = root
  }
}
yakuniy sinf RecipeDetailViewController: BaseController  {}

Bola bilan ko'rish nazorati

View kontrolör konteyneri kuchli tushunchadir. Har bir ko'rish nazoratchisi alohida e'tiborga ega va rivojlangan xususiyatlarni yaratish uchun birgalikda tuzilishi mumkin. Men UICollectionView-ni boshqarish va retseptlar ro'yxatini ko'rsatish uchun RecipeListViewController-dan foydalanganman.

yakuniy sinf RecipeListViewController: UIViewController {
  shaxsiy (o'rnatilgan) var collectionView: UICollectionView!
  adapterga ruxsat bering = Adapter  ()
  private let emptyView = EmptyView (matn: "Retseptlar topilmadi!")
}

Bu RecipeListViewController-ni o'z ichiga oladigan HomeViewController mavjud

/// Retseptlar ro'yxatini ko'rsating
yakuniy sinf HomeViewController: UIViewController {
/// Qachon retsept tanlang
  var tanlang: ((Recipe) -> Bo'sh)?
var var refreshControl = UIRefreshControl ()
  shaxsiy ruxsat berilgan retseptlarXizmat: RecipesService
  shaxsiy ruxsat izlash komponenti: SearchComponent
  shaxsiy ruxsat berilganLiskViewController = RecipeListViewController ()
}

Tarkibi va qaramlik in'ektsiyasi

Iloji bo'lsa, tarkibiy qismlarni yaratishga va kod yozishga harakat qilaman. Biz ImageService NetworkService va CacheService xizmatlaridan foydalanganligini va RecipeDetailViewController Recipe va RecipesService xizmatlaridan foydalanganligini ko'ramiz.

Ideal holda ob'ektlar o'z-o'zidan qaramlikni yaratmasligi kerak. Bog'lanishlarni tashqarida yaratish va ildizdan pastga tushirish kerak. Bizning dasturimizda ildiz AppDelegate va AppFlowController hisoblanadi, shuning uchun qaramlik shu erdan boshlanishi kerak.

Ilova transport xavfsizligi

IOS 9-dan beri barcha ilovalar App Transport Security-ni qabul qilishlari kerak

App Transport Security (ATS) ilova va uning orqa uchi o'rtasidagi xavfsiz ulanishlarning eng yaxshi usullarini ta'minlaydi. ATS tasodifiy oshkor qilishni oldini oladi, xavfsiz asl holatini ta'minlaydi va qabul qilish oson; u ham iOS 9 va OS X v10.11-da sukut bo'yicha yoqilgan. Siz yangi ilovani yaratayotganingiz yoki yangisini yangilamasligingizdan qat'iy nazar, siz ATS-ni iloji boricha tezroq qabul qilishingiz kerak.

Bizning ilovamizda ba'zi rasmlar HTTP ulanishi orqali olinadi. Biz uni xavfsizlik qoidasidan chiqarib tashlashimiz kerak, ammo faqat shu domen uchun.

 NSAppTransportSecurity 

   NSExceptionDomains 
  
     food2fork.com 
    
       NSIncludesSubdomains 
      
       NSExceptionAllowsInsecureHTTPLoads 
      
    
  

Scrollable Custom view

Batafsil ekran uchun biz turli xil hujayrali UITableView va UICollectionView-dan foydalanishimiz mumkin. Bu erda qarashlar statik bo'lishi kerak. Biz UIStackView-dan foydalanishimiz mumkin. Moslashuvchanlik uchun biz faqat UIScrollView-dan foydalanishimiz mumkin.

/// UIScrollView-da Auto Layout-dan foydalanib, vertikal ravishda ko'rinishi
yakuniy sinf ScrollableView: UIView {
  shaxsiy ruxsat scrollView = UIScrollView ()
  shaxsiy ruxsat berish contentView = UIView ()
tashabbusni bekor qilmoq (freym: CGRect) {
    super.init (freym: ramka)
scrollView.showsHorizontalScrollIndicator = noto'g'ri
    scrollView.alwaysBounceHorizontal = noto'g'ri
    addSubview (scrollView)
scrollView.addSubview (contentView)
NSLayoutConstraint.activate ([
      scrollView.topAnchor.constraint (tengga: topAnchor),
      scrollView.bottomAnchor.constraint (teng: quyiAnchor),
      scrollView.leftAnchor.constraint (teng: chapgaAnchor),
      scrollView.rightAnchor.constraint (tengga: o'ngAnchor),
contentView.topAnchor.constraint (tenglik: scrollView.topAnchor),
      contentView.bottomAnchor.constraint (tenglik: scrollView.bottomAnchor),
      contentView.leftAnchor.constraint (teng: chapAnchor),
      contentView.rightAnchor.constraint (tengga: o'ngAnchor)
    ])
  }
}

UIScrollView-ni chekkalarga yopishtiramiz. Biz contentView chap va o'ng langarlarni o'zimizga biriktiramiz, contentView yuqori va pastki langarlarni UIScrollView-ga biriktiramiz.

ContentView ichidagi ko'rinishlar yuqori va pastki cheklovlarga ega, shuning uchun ular kengayganda, contentView-ni ham kengaytiradi. UIScrollView, contentSize-ni aniqlash uchun ushbu contentView-dan Auto Layout ma'lumotlarini ishlatadi. RecipeDetailView-da ScrollableView-ning qanday ishlatilishi.

scrollableView.setup (juftliklar: [
  ScrollableView.Pair (ko'rish: imageView, ichki: UIEdgeInsets (yuqori: 8, chap: 0, pastki: 0, o'ng: 0)),
  ScrollableView.Pair (ko'rish: ingredientHeaderView, ichki: UIEdgeInsets (yuqori: 8, chap: 0, pastki: 0, o'ng: 0)),
  ScrollableView.Pair (ko'rish: ingredientLabel, ichki: UIEdgeInsets (yuqori: 4, chap: 8, pastki: 0, o'ng: 0)),
  ScrollableView.Pair (ko'rish: infoHeaderView, kiritish: UIEdgeInsets (yuqori: 4, chap: 0, pastki: 0, o'ng: 0)),
  ScrollableView.Pair (ko'rish: yo'riqnoma tugmasi, kiritmoq: UIEdgeInsets (yuqori: 8, chap: 20, pastki: 0, o'ng: 20)),
  ScrollableView.Pair (ko'rish: originalButton, ichki: UIEdgeInsets (yuqori: 8, chap: 20, pastki: 0, o'ng: 20)),
  ScrollableView.Pair (ko'rish: infoView, ichki: UIEdgeInsets (yuqori: 16, chap: 0, pastki: 20, o'ng: 0))
])

Qidiruv funktsiyalarini qo'shish

IOS 8-dan boshlab, biz UISearchController-dan qidirish paneli va natijalarni boshqarish vositasi bilan standart qidirish tajribasini olish uchun foydalanishimiz mumkin. Biz qidirish funktsiyalarini SearchComponentga qo'shamiz, shunda ular ulanishi mumkin.

yakuniy sinf SearchComponent: NSObject, UISearchResultsUpdating, UISearchBarDelegate {
  retseptlar xizmatiga ruxsat bering: Reseptlar xizmati
  SearchController-ga ruxsat berish: UISearchController
  ga ruxsat beringListViewController = RecipeListViewController ()
}

IOS 11-dan boshlab, UINavigationItem-da qidiruv panelini qidiruv panelini ko'rsatishni osonlashtiradigan xususiyat mavjud.

func add (ko'rishController uchun: UIViewController) {
  agar # mavjud bo'lsa (iOS 11, *) {
    viewController.navigationItem.searchController = searchController
    viewController.navigationItem.hidesSearchBarWhenScrolling = noto'g'ri
  } else {
    viewController.navigationItem.titleView = searchController.searchBar
  }
viewController.definesPresentationContext = true
}

Ushbu ilovada hozir hidesNavigationBarDuringPresentation-ni o'chirib qo'yishimiz kerak, chunki bu juda xatarli. Umid qilamanki, bu kelajakda iOS yangilanishlarida hal qilinadi.

Taqdimot kontekstini tushunish

Taqdimot kontekstini tushunish ko'rish nazorati taqdimoti uchun juda muhimdir. Qidiruvda biz searchResultsController-dan foydalanamiz.

self.searchController = UISearchController (qidiruv natijalariController: retseptiListViewController)

Biz manbani ko'rish boshqaruvchisida defenderPresentationContext-dan foydalanishimiz kerak (qidirish satrini qo'shadigan ko'rish boshqaruvchisi). Bu holda biz qidiruv natijalariControllerini to'liq ekranga chiqaramiz!

Ko'rish boshqaruvchisini taqdim qilish uchun joriy kontekst yoki over CurrententContext uslubidan foydalanganda, ushbu xususiyat ko'rish nazoratchisi ierarxiyasida mavjud bo'lgan nazoratni boshqarish vositasini aslida yangi tarkib bilan qamrab oladi. Kontekstga asoslangan prezentatsiya paydo bo'lganda, UIKit taqdimot ko'rinishini boshqaruvchisidan boshlanadi va ko'rinishni boshqarish ierarxiyasini boshlaydi. Agar u ushbu xususiyatning qiymati haqiqiy bo'lsa, ko'rish boshqaruvchisini topsa, u ko'rish boshqaruvchisidan yangi ko'rish boshqaruvchisini taqdim etishni so'raydi. Agar biron-bir nazorat qilish vositasi taqdimot kontekstini aniqlamasa, UIKit oynaning ildiz ko'rinishini boshqaruvchisidan taqdimotni boshqarishni so'raydi.
Ushbu xususiyat uchun asl qiymati noto'g'ri. UINavigationController kabi ba'zi bir tizim tomonidan ta'minlangan ko'rish nazoratchilari standart qiymatni "true" ga o'zgartiradilar.

Qidiruv harakatlarini bekor qilish

Qidiruv satridagi foydalanuvchi turlarini bosgan har bir tugmachani qidirish so'rovlarini bajarmaymiz. Shuning uchun qandaydir gazakka ehtiyoj bor. Biz DispatchWorkItem-dan amalni kapsulalash va navbatga yuborish uchun foydalanishimiz mumkin. Keyinchalik biz buni bekor qilishimiz mumkin.

yakuniy sinf Debouncer {
  shaxsiy ruxsatni kechiktirish: TimeInterval
  shaxsiy var workItem: DispatchWorkItem?
init (kechikish: TimeInterval) {
    self.delay = kechikish
  }
/// Biroz kechikishdan keyin harakatni boshlang
  funktsiya jadvali (harakat: @escaping () -> Void) {
    workItem? .cancel ()
    workItem = DispatchWorkItem (blok: harakat)
    DispatchQueue.main.asyncAfter (muddati: .now () + kechiktirish, bajarish: workItem!)
  }
}

Inverted kutish bilan diskontlash sinovini o'tkazish

Debouncer-ni sinab ko'rish uchun biz teskari tartibda XCTest kutish imkoniyatidan foydalanishimiz mumkin. Bu haqda ko'proq ma'lumotni sinxron Swift kodini sinash bo'limida o'qing.

Sinov paytida vaziyat yuzaga kelmasligini tekshirish uchun, kutilmagan vaziyat yuzaga kelganda kutilgan natijani yarating va uning inverted xususiyatini "true" ga qo'ying. Agar teskari taxmin kutilgan bo'lsa, sizning sinovingiz darhol muvaffaqiyatsiz bo'ladi.
sinf DebouncerTests: XCTestCase {
  func testDebouncing () {
    letEExpectation = self.expectation (tavsif: "bekor qilish")
    CancExpectation.isInverted = true
completeExpectation = self.expectation (izoh: "yakunlandi")
    let debouncer = Debouncer (kechikish: 0.3)
debouncer.schedule {
      CancExpectation.fulfill ()
    }
debouncer.schedule {
      completeExpectation.fulfill ()
    }
kuting (kuting: [bekor qilish, to'liqExpektsiya], kutish vaqti: 1)
  }
}

UITests yordamida foydalanuvchi interfeysini sinab ko'rish

Ba'zida kichik refaktorizatsiya katta ta'sir ko'rsatishi mumkin. O'chirilgan tugma keyinchalik ekranning ishlamay qolishiga olib kelishi mumkin. UITest ilovaning yaxlitligi va funktsional jihatlarini ta'minlashga yordam beradi. Sinov deklarativ bo'lishi kerak. Biz Robot naqshidan foydalanishimiz mumkin.

sinf retseptlariUITests: XCTestCase {
  var ilova: XCUIApplication!
  func setUp () {ning o'rnini bosish
    super.setUp ()
    continueAfterFailure = noto'g'ri
    ilova = XCUIApplication ()
  }
  func testScrolling () {
    App.launch ()
    collectionView = app.collectionViews.element-ga ruxsat bering (borderBy: 0)
    collectionView.swipeUp ()
    collectionView.swipeUp ()
  }
  funktsiya testGoToDetail () {
    App.launch ()
    collectionView = app.collectionViews.element-ga ruxsat bering (borderBy: 0)
    let firstCell = collectionView.cells.element (chegarasi: 0)
    firstCell.tap ()
  }
}

Sinovga tegishli ba'zi maqolalarim.

  • IOS-da Facebook-ga kirish bilan UITests-ni o'tkazish
  • Swift-da, qachon naqsh berilganligini sinab ko'rish

Asosiy ipni qo'riqchi

UI-ga orqa navbatdan kirish potentsial muammolarga olib kelishi mumkin. Ilgari, Men MainThreadGuard-dan foydalanishim kerak edi, endi Xcode 9-da Main Thread Checker mavjud bo'lib, men buni faqat Xcode-da yoqdim.

Main Thread Checker - bu Swift va C tillari uchun mustaqil vosita bo'lib, u orqa fonda AppKit, UIKit va boshqa API-larning noto'g'ri ishlatilishini aniqlaydi. UI-ni asosiy ipdan tashqari oqim bilan yangilash odatiy xatodir, natijada o'tkazib yuborilgan foydalanuvchi interfeysi yangilanishlari, vizual nuqsonlar, ma'lumotlar buzilishi va ishdan chiqishi mumkin.

Ishlash va muammolarni o'lchash

Ilovani sinchkovlik bilan ko'rish uchun biz asboblardan foydalanishimiz mumkin. Tez o'lchash uchun biz Debug Navigator yorlig'iga o'tamiz va protsessor, xotira va tarmoqdan foydalanishni ko'ramiz. Asboblar haqida ko'proq bilish uchun ushbu ajoyib maqolani ko'rib chiqing.

O'yin maydonchasi bilan prototiplash

O'yin maydonchasi - bu prototip va ilovalarni yaratish uchun tavsiya etilgan usul. WWDC 2018-da Apple modelni tayyorlash uchun Playground-ni qo'llab-quvvatlaydigan Create ML-ni taqdim etdi. Swift-da o'yin maydonchalarini boshqarishni rivojlantirish haqida ko'proq bilish uchun ushbu ajoyib maqolani ko'rib chiqing.

Bu erdan qayerga borish kerak

Buni hozirgacha qilganingiz uchun tashakkur. Sizga foydali narsalarni o'rgandingiz, deb umid qilaman. Biror narsani o'rganishning eng yaxshi usuli shunchaki buni qilishdir. Agar siz xuddi shu kodni qayta-qayta yozib qo'ysangiz, uni tarkibiy qism sifatida qiling. Agar muammo sizga qiyin kechsa, bu haqda yozing. O'z tajribangizni dunyo bilan baham ko'ring, siz ko'p narsalarni o'rganasiz.

Men ushbu maqolani ko'rib chiqishni tavsiya etaman iOS rivojlanishi haqida ko'proq ma'lumot olish uchun iOS-ni ishlab chiqishni o'rganadigan eng yaxshi joylar.

Agar sizda biron bir savol, sharh yoki fikringiz bo'lsa, ularni sharhlarga qo'shishni unutmang. Va agar siz ushbu maqolani foydali deb bilsangiz, qarsak chalishni unutmang.

Agar sizga ushbu xabar yoqsa, mening boshqa maqolalarim va ilovalarimga tashrif buyuring